
简介这份资源是重庆工商大学2023届毕业设计项目「智能核保系统」的完整源码包面向计算机相关专业学生、Python后端初学者以及需要参考毕设实现思路的开发者。项目围绕保险核保业务场景提供了一套可运行的系统实现适合用于课程设计、毕业设计参考或后端开发练手。压缩包共42个文件整体约29KB以32个Python源码文件为核心涵盖业务逻辑与后端服务另含4个Shell脚本用于环境与数据库初始化以及Dockerfile、requirements.txt、README.md、HTML模板和配置文件等便于快速部署与二次开发。目录中可见server、apps、templates、manage.py等模块结构清晰体现了Django风格的项目组织方式。目前已有131人学习下载读者可从中获取完整的项目目录结构、核保业务代码实现、依赖清单与容器化部署脚本并参考其模块划分与配置方式用于理解一个真实毕设项目的落地过程。1. 从一份毕业设计压缩包说起智能核保系统能解决什么问题保险核保这件事外行看是填表、体检、等通知内行看是一堆规则引擎在打架。一个投保人提交申请后核保员要对照健康告知、年龄、职业、既往病史、保额、险种等几十个维度逐条判断给出标准承保、加费承保、除外承保还是拒保的结论。人工做这件事一份单子少则几分钟复杂件要半小时以上而且不同核保员对同一条规则的尺度把握还不一样。这份「智能核保系统」要干的事就是把这套判断逻辑搬到代码里让系统自动跑规则、自动出结论把核保员从重复劳动里解放出来。这个项目是某高校 2023 年的毕业设计技术栈以 Python 为主适合两类人一类是正在做类似选题的学生想找一个能跑通、结构完整的参考实现另一类是刚入行的后端开发想看看规则引擎、决策树这类东西在真实业务场景里怎么落地。它不是一个玩具 Demo而是把「用户提交投保单 → 系统逐条核保 → 输出核保结论 → 记录核保日志」这条链路完整走通了。下面我从项目结构、核心模块、运行方式到二次开发一层层拆开讲。2. 项目结构与技术选型一个核保请求从进入到返回要经过哪些模块2.1 目录结构总览拿到压缩包解压后常见的目录组织方式是这样的不同版本可能略有差异但核心模块不会变intelligent_underwriting/ ├── app/ │ ├── __init__.py │ ├── models/ # 数据模型层 │ │ ├── applicant.py # 投保人信息模型 │ │ ├── policy.py # 保单模型 │ │ └── underwriting.py # 核保记录模型 │ ├── rules/ # 核保规则引擎 │ │ ├── base_rule.py # 规则基类 │ │ ├── health_rule.py # 健康告知规则 │ │ ├── age_rule.py # 年龄规则 │ │ ├── occupation_rule.py # 职业风险规则 │ │ └── rule_engine.py # 规则调度引擎 │ ├── services/ # 业务服务层 │ │ ├── underwriting_service.py # 核保主流程 │ │ └── risk_service.py # 风险评估服务 │ ├── api/ # 接口层 │ │ └── routes.py # 路由定义 │ └── utils/ # 工具函数 │ ├── logger.py │ └── validators.py ├── config.py # 配置文件 ├── requirements.txt # 依赖清单 ├── run.py # 启动入口 └── tests/ # 测试用例 └── test_rules.py这个结构是典型的 Flask 分层架构models管数据rules管规则services管流程编排api管对外接口。分层的好处是改规则不用动接口改接口不用动规则各层职责清晰。2.2 技术选型与依赖说明项目以 Python 为主语言常见搭配是 Flask 做 Web 框架、SQLAlchemy 做 ORM、SQLite 做开发数据库。requirements.txt里一般会包含这些Flask2.3.2 SQLAlchemy2.0.19 Flask-SQLAlchemy3.0.5 pydantic2.0.3 pytest7.4.0选 Flask 而不是 Django是因为核保系统本质是一个规则计算服务不需要 Django 那套完整的 Admin 和模板体系Flask 更轻启动快改起来也直接。SQLite 适合毕业设计场景零配置一个文件就是数据库换到 MySQL 只需要改config.py里的连接串。提示如果requirements.txt里的版本号和你本地 Python 版本冲突优先保证 Flask 和 SQLAlchemy 大版本一致小版本可以放宽。2.3 核保请求的完整链路一个核保请求进来后经过的路径是这样的api/routes.py接收 POST 请求解析投保人 JSON 数据services/underwriting_service.py调用rule_engine启动规则链rule_engine按优先级依次执行health_rule、age_rule、occupation_rule每条规则返回pass、warn或reject引擎汇总所有结果汇总结果映射为最终核保结论写入underwriting表返回 JSON 响应给调用方这条链路的关键设计是「规则链 结果汇总」而不是「一条规则否决全部」。这样做的原因是核保业务里有些规则是硬性拒保比如确诊某类疾病有些是软性提示比如年龄偏大建议加费需要区分处理。3. 核心模块拆解规则引擎、健康告知与风险评估怎么写的3.1 规则基类与规则引擎规则引擎的核心是抽象出一个基类所有具体规则继承它实现自己的evaluate方法。先看基类# app/rules/base_rule.py from abc import ABC, abstractmethod class BaseRule(ABC): 所有核保规则的基类 def __init__(self, name, priority0): self.name name # 规则名称 self.priority priority # 优先级数字越小越先执行 abstractmethod def evaluate(self, applicant_data): 执行规则判断 :param applicant_data: dict投保人信息 :return: dict包含 result(pass/warn/reject) 和 reason pass def __repr__(self): return fRule: {self.name}, priority{self.priority}这个基类定义了规则的统一接口name标识规则priority控制执行顺序evaluate是具体判断逻辑。用ABC抽象基类而不是普通类是为了强制子类必须实现evaluate避免漏写导致运行时才报错。规则引擎负责调度所有规则# app/rules/rule_engine.py from app.rules.health_rule import HealthRule from app.rules.age_rule import AgeRule from app.rules.occupation_rule import OccupationRule class RuleEngine: 核保规则调度引擎 def __init__(self): # 注册规则按优先级排序 self.rules sorted([ HealthRule(), AgeRule(), OccupationRule(), ], keylambda r: r.priority) def run(self, applicant_data): 执行所有规则汇总结果 :param applicant_data: dict :return: dict包含最终结论和每条规则的明细 results [] final_result pass # 默认通过 for rule in self.rules: result rule.evaluate(applicant_data) results.append({ rule: rule.name, result: result[result], reason: result[reason] }) # 一旦出现 reject直接终止不再执行后续规则 if result[result] reject: final_result reject break # warn 不终止但会拉低最终结论 if result[result] warn and final_result pass: final_result warn return { final_result: final_result, details: results }这段代码有两个关键设计点。第一规则按priority排序执行健康告知优先级最高因为它是核保里最硬的指标。第二reject会短路终止warn只降级不终止。这个逻辑对应真实核保场景如果投保人确诊了拒保疾病后面的年龄、职业规则就没必要跑了但如果只是年龄偏大系统应该继续检查其他维度最后给出「加费承保」的结论。3.2 健康告知规则的具体实现健康告知是核保里最复杂的部分通常用问卷形式收集每条问题对应一个风险因子。实现上一般用字典映射# app/rules/health_rule.py from app.rules.base_rule import BaseRule class HealthRule(BaseRule): 健康告知规则根据问卷结果判断风险等级 # 拒保疾病清单 REJECT_DISEASES { malignant_tumor: 恶性肿瘤, severe_heart_disease: 严重心脏病, aids: 艾滋病, severe_mental_illness: 严重精神疾病 } # 加费疾病清单 WARN_DISEASES { hypertension: 高血压, diabetes: 糖尿病, hepatitis_b: 乙肝 } def __init__(self): super().__init__(name健康告知规则, priority1) def evaluate(self, applicant_data): health_info applicant_data.get(health_info, {}) # 检查拒保疾病 for disease_key, disease_name in self.REJECT_DISEASES.items(): if health_info.get(disease_key): return { result: reject, reason: f确诊{disease_name}不符合承保条件 } # 检查加费疾病 warn_reasons [] for disease_key, disease_name in self.WARN_DISEASES.items(): if health_info.get(disease_key): warn_reasons.append(disease_name) if warn_reasons: return { result: warn, reason: f存在{,.join(warn_reasons)}建议加费承保 } return {result: pass, reason: 健康告知无异常}这里用两个字典分别管理拒保和加费疾病好处是新增疾病只需要加一行配置不用改判断逻辑。health_info是一个布尔字典前端问卷的每个问题对应一个 key用户勾选后传True。这种设计在毕业设计里很常见简单直接也方便答辩时演示。3.3 年龄与职业规则的参数化配置年龄规则和职业规则相对简单但参数需要可配置不能写死在代码里# app/rules/age_rule.py from app.rules.base_rule import BaseRule class AgeRule(BaseRule): 年龄规则不同年龄段对应不同核保结论 # 年龄区间配置(最小年龄, 最大年龄, 结论, 说明) AGE_CONFIG [ (0, 17, warn, 未成年需监护人投保), (18, 50, pass, 标准承保年龄段), (51, 60, warn, 年龄偏大建议加费), (61, 200, reject, 超出承保年龄上限) ] def __init__(self): super().__init__(name年龄规则, priority2) def evaluate(self, applicant_data): age applicant_data.get(age) if age is None: return {result: reject, reason: 年龄信息缺失} for min_age, max_age, result, reason in self.AGE_CONFIG: if min_age age max_age: return {result: result, reason: reason} return {result: reject, reason: 年龄不在承保范围内}职业规则同理用职业风险等级1-6 类映射核保结论。这类规则的特点是「配置驱动」把业务参数从代码里抽出来改规则不用改逻辑。我一般会把这类配置放到config.py或者单独的 YAML 文件里方便非技术人员调整。3.4 核保服务层的流程编排服务层负责把规则引擎、数据持久化和日志串起来# app/services/underwriting_service.py from app.rules.rule_engine import RuleEngine from app.models.underwriting import UnderwritingRecord from app.utils.logger import logger class UnderwritingService: 核保主流程服务 def __init__(self, db_session): self.engine RuleEngine() self.db db_session def process(self, applicant_data): 处理核保请求 :param applicant_data: dict投保人信息 :return: dict核保结论 logger.info(f开始核保投保人: {applicant_data.get(name)}) # 1. 执行规则引擎 result self.engine.run(applicant_data) # 2. 持久化核保记录 record UnderwritingRecord( applicant_nameapplicant_data.get(name), applicant_ageapplicant_data.get(age), final_resultresult[final_result], detailresult[details] ) self.db.add(record) self.db.commit() logger.info(f核保完成结论: {result[final_result]}) return result服务层不关心规则怎么判断只负责「调用引擎 → 存记录 → 打日志」这条流水线。这种分层让单元测试变得容易测规则就 mock 数据直接调evaluate测服务就 mock 引擎返回固定结果。4. 跑起来才算数环境搭建、启动与接口验证4.1 环境准备与依赖安装先确认 Python 版本建议 3.9 以上python --version # Python 3.9.13然后创建虚拟环境并安装依赖# 创建虚拟环境 python -m venv venv # 激活Windows venv\Scripts\activate # 激活Mac/Linux source venv/bin/activate # 安装依赖 pip install -r requirements.txt如果安装过程中SQLAlchemy编译报错通常是缺少 C 编译环境Windows 下装个 Visual C Build Tools 就能解决Mac 下xcode-select --install即可。4.2 数据库初始化与启动项目一般会在run.py里做数据库初始化# run.py from app import create_app, db from app.models import applicant, policy, underwriting app create_app() if __name__ __main__: with app.app_context(): db.create_all() # 自动建表 app.run(host0.0.0.0, port5000, debugTrue)启动命令python run.py看到Running on http://0.0.0.0:5000就说明起来了。debugTrue模式下改代码会自动重启开发时很方便但上线要关掉。4.3 接口调用验证核保接口一般是 POST/api/underwriting用 curl 测一下curl -X POST http://127.0.0.1:5000/api/underwriting \ -H Content-Type: application/json \ -d { name: 张三, age: 35, occupation_risk: 2, health_info: { hypertension: true, diabetes: false, malignant_tumor: false } }预期返回{ final_result: warn, details: [ {rule: 健康告知规则, result: warn, reason: 存在高血压建议加费承保}, {rule: 年龄规则, result: pass, reason: 标准承保年龄段}, {rule: 职业规则, result: pass, reason: 职业风险等级可承保} ] }这个返回说明系统正确识别了高血压这个加费因子年龄和职业都没问题最终结论是「加费承保」。如果返回reject看details里哪条规则触发了拒保就知道问题出在哪。4.4 单元测试跑一遍项目里一般带了tests/test_rules.py直接跑pytest tests/ -v测试用例通常覆盖这几种场景健康告知全通过、有加费疾病、有拒保疾病、年龄超限、职业高风险。跑通测试说明规则逻辑没问题改代码后也能快速回归。5. 避坑与常见问题跑不起来、结果不对、改规则翻车怎么办5.1 启动报 ModuleNotFoundError现象python run.py报ModuleNotFoundError: No module named app。原因启动时工作目录不对或者虚拟环境没激活Python 找不到app包。解决确认在项目根目录有run.py的那层执行命令并且虚拟环境已激活。如果还不行在run.py开头加import sys; sys.path.insert(0, .)把当前目录加入路径。5.2 数据库表不存在现象调用接口报no such table: underwriting_record。原因db.create_all()没执行或者执行时模型没被导入。解决确保run.py里db.create_all()在app.app_context()内执行并且app/models/__init__.py里导入了所有模型类。SQLAlchemy 只对已导入的模型建表漏导入就不会建。5.3 规则执行顺序不对导致结论异常现象明明有拒保疾病返回的却是warn。原因规则priority设置反了或者RuleEngine里排序逻辑写错。解决检查每条规则的priority健康告知应该是 1年龄 2职业 3。sorted默认升序数字小的先执行。如果想让拒保规则优先就把它的 priority 设成最小。5.4 修改规则后测试全挂现象改了一条规则的判断逻辑跑pytest一堆失败。原因测试用例里硬编码了预期结果规则一改就对不上。解决测试用例应该测「输入 → 输出」的映射关系而不是测具体实现。改规则时同步更新测试预期或者把测试数据参数化用pytest.mark.parametrize管理多组用例。5.5 接口返回中文乱码现象curl 返回的 JSON 里中文显示为\uXXXX。原因Flask 默认JSON_AS_ASCIITrue会把非 ASCII 字符转义。解决在config.py里设置JSON_AS_ASCII False或者升级到 Flask 2.3 后用app.json.ensure_ascii False。这不影响程序解析只是可读性问题。6. 二次开发怎么改从规则扩展到模型替换的实操思路拿到这个项目后最常改的三个方向是加规则、换数据库、接前端。下面分别说。加一条新规则比如「保额规则」在app/rules/下新建amount_rule.py继承BaseRule实现evaluate方法然后在RuleEngine.__init__里注册进去。注意priority要排在合适的位置保额规则一般放在健康告知之后、年龄之前。改完后在tests/里加对应的测试用例跑一遍pytest确认没破坏原有逻辑。换数据库从 SQLite 换到 MySQL改config.py里的SQLALCHEMY_DATABASE_URI格式是mysqlpymysql://user:passwordhost:port/dbname然后pip install pymysql。注意 MySQL 下建表要用db.create_all()或者 Alembic 迁移SQLite 的create_all在 MySQL 下同样适用但字段类型可能有差异比如Text和String的长度限制。接前端如果要做成 Web 页面在app/api/routes.py里加一个返回 HTML 的路由或者单独起一个前端项目通过 CORS 调接口。Flask 这边需要装flask-cors配置CORS(app)允许跨域。前端表单字段要和applicant_data的 key 对齐尤其是health_info里的疾病 key拼错一个就判断不出来。一个实用技巧把规则配置抽到 YAML 文件里。比如年龄区间、疾病清单这些写在rules_config.yaml里启动时加载。这样改规则不用动 Python 代码非技术人员也能维护。我一般会写一个load_rules_config()函数在RuleEngine初始化时调用把配置注入到各条规则里。# app/utils/config_loader.py import yaml def load_rules_config(pathrules_config.yaml): with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) # 在 AgeRule 里使用 class AgeRule(BaseRule): def __init__(self, configNone): super().__init__(name年龄规则, priority2) self.age_config config or self.AGE_CONFIG这个改动不大但让整个系统的可维护性上了一个台阶。从那以后我每次做规则类项目都强制把业务参数和代码逻辑分开哪怕初期只有两三条规则——因为规则这东西一定会越加越多等到二十条的时候再重构成本就高了。希望这个拆解能帮你把项目跑起来也帮你在二次开发时少走点弯路。本文还有配套的精品资源点击获取