这个题目乍一看有点唬人:又是智能、又是家庭、又是医疗保险。其实拆开来看,它就是一个非常典型的 Java Web 信息管理系统——只是把传统单用户医保系统换成了“以家庭为账户单位”的业务场景,再加一点规则计算和提醒功能,把“智能”两个字坐实。用 Java + SpringBoot 做后端,前端走 Web 浏览器访问,核心价值就是把家庭成员的医保信息、缴费记录、就诊费用、报销审核全部串起来,做成一个可运行、可演示、可扩展的完整平台。
这篇博文我按我实际带毕设的惯用思路来写:不绕弯子,直接讲怎么搭、怎么算、怎么防坑。内容定位是给做计算机毕业设计的同学当一个可参照的“从零到答辩”行动手册。你如果是第一次做完整 Web 项目,会在这里看到数据库怎么建、报销金额怎么算、登录权限怎么拦、文件发票怎么传;如果你已经有基础,可以直接跳到第 6 章看常见坑,很多问题我在帮别人调项目时反复碰到。
1. 选题分析与整体定位
1.1 这个系统到底解决什么问题
家庭医疗保险这个场景,和普通的“个人医保报销系统”差异其实很大。个人版的核心是一人一档、一人一保,而家庭版天然带一种“账户共享”的味道:一个家庭里可能有老人、小孩、在职成年人,每个人的报销比例不同、额度不同,但保费可能是一起缴的,报销进度也可能共享同一个家庭年度总额度。
所以这个系统要覆盖的最基本业务链条是:创建家庭档案 → 录入家庭成员 → 选择/购买保险方案 → 产生就诊记录与医疗费用 → 提交报销申请 → 系统自动核算报销金额 → 审核人员确认 → 核销额度并生成报表。这就是一条很完整的业务主链路。
把这条链路用 Web 系统落地,本质上你做的就是一个 MIS(管理信息系统)。这和图书管理、宿舍管理的底层套路是一样的:CRUD + 状态流转 + 统计报表。但好在业务对象换成“医保”之后,选题看起来比图书管理系统有说服力得多,论文也好扩展。
1.2 面向的用户角色与核心场景
系统里至少要有三类角色,这也是你划权限、画用例图的基础:
- 家庭成员(家庭维度用户):登录后只能看自己家庭的数据,可以添加就诊记录、提交报销申请、查看报销进度、下载报销单。
- 审核管理人员:一般市级/区级审核窗口角色,能查看所有家庭的申请,做通过或驳回操作。
- 系统管理员:维护保险方案、医院等级配置、报销比例参数、公告信息,以及管理账号。
一个更贴近现实的场景是:家里老人生病住了院,产生了一笔住院费用;户主在家里的电脑上把这些费用录入系统,上传发票照片,提交报销申请;系统按“该成员的保险方案+医院等级+费用类型”自动算出报销金额;审核人员在管理端核对发票后点通过,系统把报销金额累加到该家庭的年度已报销额度里,同时检查是否触发 80% 额度的预警提醒。这就是一套完整的“智能感”闭环。
1.3 “智能”二字落在哪里
毕设题目里的“智能”经常被导师追问,不要只说“加了人工智能”,那样反而容易被问住。合理做法是把“智能”落地成可解释的业务规则:
- 智能核算:报销金额不需要人工笔算,系统按配置好的比例和限额自动计算。
- 额度预警:当家庭年度报销额达到额度的 80% 时,自动提示“本年度剩余报销额度紧张”。
- 规则可配置:医院等级、费用类型、报销比例都做成配置表,管理员调整参数后,新申请自动按新规则核算。
这三件事每件都对应明确的数据库字段和代码逻辑,答辩的时候你能把“智能”落在具体方法上,导师就会觉得这个系统是完整设计过的,不是套个壳子。
2. 技术选型与架构设计
2.1 为什么是 SpringBoot 而不是 SSH/SSM
现在做 Java Web 毕设,SpringBoot 基本是默认答案。理由很朴素:它把繁琐的 XML 配置去掉了,内嵌 Tomcat 让你不用再往本机装外部容器,打一个 jar 包就能跑,这对不熟悉服务器部署的同学非常友好。
版本选择我建议优先考虑 SpringBoot 2.7.x 配 JDK 8。不少学校的实验环境和答辩电脑还停留在 JDK 8,少数学校有 JDK 11。如果你一上来就选 SpringBoot 3.x,最低要求是 JDK 17,部分老机器的环境会出问题;跑不起来的时候,你分的清是代码问题还是 JDK 版本太高吗?没必要给自己增加这种不确定性。我自己给同学改项目,默认就是 SpringBoot 2.7 + JDK 8 + MySQL 5.7/8.0,一套组合从大二用到研三都不会出错。
持久层选 MyBatis-Plus 而不是纯 MyBatis 或 Spring Data JPA。理由是:单表 CRUD 不需要手写 SQL,它内置的 LambdaQueryWrapper 写起来非常流畅;分页插件也好用,一个配置类搞定。核心业务里只有查询统计需要手写 SQL,量不大。
2.2 前后端分离还是服务端渲染
这里有两套主流方案:
- 方案 A(推荐给有点前端基础的同学):后端 SpringBoot 提供 RESTful API,前端 Vue 3 + Element Plus,配合 Vite 和 Axios 做前后端分离。好处是项目结构看起来架构感更强,论文里的架构图好画,代码量也更像一个“平台级”系统。
- 方案 B(推荐给完全不想碰前端构建工具的同学):后端用 Thymeleaf 模板引擎,页面由服务端渲染。好处是只有一个工程,本地启动后浏览器直接访问,不容易出现跨域、打包这类工程化问题。缺点是交互体验一般,写复杂表单和动态表格要用 Bootstrap 或原生 JS 顶一下。
我见过不少同学卡在“前端打包后接口 404”“CORS 跨域报错”这类问题上,浪费三四天。如果你对 npm、Node、Vite 没把握,选 Thymeleaf 也不丢人,系统照样完整;如果你想让答辩时项目观感更现代,那就选 Vue3 前后端分离,但务必预留一周时间专门做联调。
2.3 文件存储方案:MinIO 要不要用
报销申请一定要上传发票图片或 PDF 文件,这就涉及文件存储。最简单的是存本地磁盘:配置文件里指定一个 upload 目录,上传成功后把访问 URL 存进数据库;再写一个静态资源映射,让上传的文件可以通过 /files/** 访问。这套方案在毕设里完全够用。
如果你想让技术栈更丰满,可以引入 MinIO。MinIO 是一个兼容 S3 协议的对象存储服务,部署方式很简单,本地下载安装启动,默认端口 9000,控制台 9001。SpringBoot 里引入 minio 的 Java SDK,配置 endpoint、accessKey、secretKey、bucketName,上传时直接用 putObject,返回文件名拼成访问地址。加了这个东西,论文里可以写“使用现代对象存储统一管理医疗票据文件”,评审观感确实会好一截。但提前说清楚:MinIO 在答辩现场如果没启动,报销票据模块就全挂了,所以部署演示前一定要检查服务状态。
核心依赖清单大致是这样:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.7</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi2-spring-boot-starter</artifactId> <version>4.4.0</version> </dependency>JWT 我选的是 java-jwt 而不是 Spring Security + 复杂的过滤器链。对毕设场景,Security 全链路配置太重,而且一旦配错会拦掉所有接口导致 401,排查起来比较劝退。直接用拦截器校验 JWT,一个小工具类生成和解析 Token,代码量少、逻辑清晰,也好讲。
3. 数据库设计与核心表结构
3.1 核心表清单与字段设计
这是整个系统的地基。我按一个相对简单但完整的方案来设计,一共 8 张核心表。别嫌表多,真正落过地就知道这些字段一个都省不下来。
| 表名 | 核心字段 | 说明 |
|---|---|---|
| family 家庭表 | id, family_no, family_name, address, head_member_id, created_time | 家庭账户主体,family_no 唯一编号 |
| member 家庭成员表 | id, family_id, name, id_card, gender, birth_date, relation, phone, member_type | 家庭成员档案,relation 表示户主/配偶/子女/父母 |
| user 用户账号表 | id, member_id, username, password, role, status | 登录账号,和成员一一对应,角色区分用户/审核/管理员 |
| insurance_plan 保险方案表 | id, plan_name, annual_limit, reimburse_rate, hospital_level, coverage_type, status | 方案定义,存年度额度、默认报销比例、适用条件 |
| policy 参保记录表 | id, member_id, plan_id, start_date, end_date, premium, status | 成员和方案的多对多关系,存参保起止期和保费 |
| medical_record 就诊记录表 | id, member_id, hospital_name, hospital_level, diagnosis, total_fee, medical_date, fee_type | 就诊明细,费用类型区分门诊/住院/药品 |
| claim 报销申请单表 | id, claim_no, family_id, applicant_id, total_fee, total_reimburse, status, audit_remark, apply_time, audit_time | 报销主表,一条申请对应多个就诊明细 |
| claim_item 报销明细表 | id, claim_id, medical_record_id, fee, reimburse_amount, rate | 申请与就诊记录关联,存每条的核算结果 |
几个必记的字段设计原则:
- 金额一律用 DECIMAL(12,2),绝不用 float/double。报销金额涉及精确计算,二进制浮点数带来的精度误差在财务数据上是不可接受的。
- 状态字段用 tinyint 或 varchar 存固定枚举值,比如报销状态 0 待提交 1 待审核 2 审核通过 3 已驳回 4 已核销。代码里用常量类或枚举类统一维护,不要散落在业务代码里写魔法数字。
- 所有表都加 create_time 和 deleted 字段。deleted 用逻辑删除 0/1,这样统计历史数据时不会因为物理删除导致账目对不上。
3.2 表关系与业务约束
表关系其实不复杂,核心是几个一对多:
一个家庭有多个成员,所以 family 与 member 是一对多;一个成员可以多次参保不同方案,所以 member 与 insurance_plan 通过 policy 建立多对多;一个成员有多条就诊记录,所以 member 与 medical_record 是一对多;一张报销主单包含多条明细,所以 claim 与 claim_item 是一对多。
真正容易忽略的是约束。比如同一张发票(同一笔医疗记录)不能被重复报销;参保状态为已过期时不能提交报销;报销金额不能超过该方案剩余额度。这些约束不写在数据库外键里,而是在 Service 层通过查询判断来拦截。数据库外键我建议不要建,MySQL 外键在很多真实项目里反而会导致删除时的锁问题,用代码逻辑保证一致性就够了。
3.3 关键统计 SQL:家庭年度报销额度计算
额度预警是“智能”的核心体现,代码最终要落到 SQL 上。比如查某个家庭在指定年度内累计已报销金额:
SELECT IFNULL(SUM(c.total_reimburse), 0) FROM claim c WHERE c.family_id = #{familyId} AND c.status = 4 AND YEAR(c.audit_time) = #{year} AND c.deleted = 0这里的 status = 4 表示已经完成核销,只有核销通过的钱才真正计入额度。如果你把“待审核”的钱也算进去,那额度预警就不准了。这个细节我在代码评审时帮同学揪出来过,属于典型的“逻辑边界不清晰”。
统计报销明细时,另一个常用 SQL 是按参保成员分组统计:
SELECT m.name, COUNT(mr.id) AS cnt, SUM(mr.total_fee) AS total_fee FROM medical_record mr JOIN member m ON mr.member_id = m.id WHERE mr.family_id = #{familyId} GROUP BY m.id ORDER BY total_fee DESC这类 SQL 建议单独写在 Mapper XML 里,不要全部堆在 MyBatis-Plus 的 Wrapper 上。一眼能看懂的统计 SQL,比满屏 QueryWrapper 嵌套可维护得多。
4. 后端核心功能实现与关键代码
4.1 登录鉴权与行级数据权限
登录流程是标准的:用户提交用户名密码 → 后端校验 → 签发 JWT Token → 前端后续请求在请求头携带 Authorization: Bearer xxx。JWT 的载荷里我一般只放 userId、memberId、familyId、role 这四个必要字段,不塞敏感信息。
关键点是“行级权限”。家庭成员登录后,不能看别人的家庭数据。最简单粗暴又有效的方案:接口根据 Token 里的 familyId 强制过滤,而不是相信前端传的参数。
@Component public class UserContext { public static Long getFamilyId() { // 从 ThreadLocal 中取当前登录用户上下文 return ((LoginUser) SecurityHolder.get()).getFamilyId(); } }在查询报销单列表时:
@Override public IPage<ClaimVO> pageClaim(ClaimQuery query) { Long currentFamilyId = UserContext.getFamilyId(); LambdaQueryWrapper<Claim> wrapper = new LambdaQueryWrapper<>(); // 关键:强制加上 familyId 条件,防止水平越权 if (currentFamilyId != null) { wrapper.eq(Claim::getFamilyId, currentFamilyId); } if (StrUtil.isNotBlank(query.getStatus())) { wrapper.eq(Claim::getStatus, query.getStatus()); } // 这里是完整的业务查询逻辑 return claimMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), wrapper); }这里的管理员属于特殊角色,familyId 为 null,此时不加家庭条件,可以查看全部数据。这一套就是面试题里常问的“行级权限/数据权限”的实现思路,毕设里写出来很加分。
4.2 报销核算引擎:别再用 double 算钱
报销核算的核心方法,是把就诊费用、报销比例、剩余额度三个变量组装成最终报销金额。我建议单独建一个 ReimburseCalculator 组件,不要写在 Controller 里。
@Component public class ReimburseCalculator { /** * 计算单条就诊记录的报销金额 * * @param fee 就诊总费用 * @param rate 报销比例,0.75 表示报销 75% * @param remainLimit 剩余可用额度 * @return 实际报销金额 */ public BigDecimal calc(BigDecimal fee, BigDecimal rate, BigDecimal remainLimit) { BigDecimal expect = fee.multiply(rate) .setScale(2, RoundingMode.HALF_UP); if (expect.compareTo(remainLimit) > 0) { // 剩余额度不够时,最多报剩余额度那么多 return remainLimit; } return expect; } }为什么用 BigDecimal 而不用 double:0.1 + 0.2 在二进制浮点里等于 0.30000000000000004,报销账目一旦出现这种误差,审核人员核对发票时就会发现问题。BigDecimal 的金额计算一定要用字符串构造入参,new BigDecimal("0.75") 而不是 new BigDecimal(0.75),后者同样会有精度问题。
核算后还要做一步“超额检查”。例如保险方案年度额度 3 万元,家庭已经用了 2.8 万,现在新申请核算出 5000 元,实际只能报 2000 元,剩余额度清零。具体的计算在事务里执行:
@Transactional(rollbackFor = Exception.class) public ClaimVO submitClaim(ClaimSubmitDTO dto) { // 1. 校验参保状态 // 2. 锁定方案额度,防止并发超报 InsurancePlan plan = planMapper.selectByIdForUpdate(dto.getPlanId()); // 3. 逐条核算 for (MedicalRecordDTO record : dto.getRecords()) { BigDecimal remain = plan.getAnnualLimit().subtract(plan.getUsedAmount()); BigDecimal reimburse = calculator.calc(record.getFee(), plan.getRate(), remain); // 4. 更新已用额度 plan.setUsedAmount(plan.getUsedAmount().add(reimburse)); } // 5. 生成报销单和明细 return claimVO; }这里藏着几个容易踩的坑:selectByIdForUpdate 是行级锁,防止两个报销单同时提交导致额度超用;事务要加在“校验+扣减+生成”的整个方法上,否则额度扣了但单子没生成,数据就错了;@Transactional只对抛 RuntimeException 回滚,如果手动捕获异常不抛出,事务不会回滚,这点很多新手容易翻车。
4.3 额度预警与通知提醒
预警不必做成复杂定时任务,至少有三个可行做法:
- 查询时计算:家庭看板接口返回时,动态计算 annualLimit、usedAmount、remainPercent,如果 remainPercent < 20,返回一个 warning 字段。前端看到 warning 就显示提示条。
- 提交报销后判断:报销单审核通过时,系统检查该家庭剩余额度,如果低于 20%,自动生成一条系统消息。
- 定时任务兜底:使用 Spring 的 @Scheduled 每天凌晨扫描一次接近额度的家庭,给户主账号生成站内信。
我建议至少实现前两种,演示效果立竿见影。第三种的 @Scheduled 可以写在论文里作为扩展点,不需要真的跑起来。消息提醒如果要用 WebSocket 实时推送,配置也不复杂,核心就是后端握手接口加一个 /topic 订阅通道,前端用 stomp 客户端接收。这属于加分项,时间不够就先用轮询。
4.4 文件上传发票与 PDF 导出
发票文件上传的重点是限制大小和类型。我见过有人没配大小限制,一次上传 200MB 的扫描件,直接把 Tomcat 默认的 1MB 限制顶炸了,报错还非常不直观。配置里应该这样:
spring: servlet: multipart: max-file-size: 5MB max-request-size: 10MB上传成功后把文件 URL 存到 medical_record 的 invoice_url 字段。这里要注意,本地磁盘存文件时,URL 里不要把磁盘绝对路径返回给前端,而是通过一个资源映射统一对外:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { String uploadPath = System.getProperty("user.dir") + "/upload/"; registry.addResourceHandler("/files/**") .addResourceLocations("file:" + uploadPath); } }PDF 导出我建议用这种方式:后端把审核通过的报销单数据渲染成 HTML 模板,再用 openhtmltopdf 或 itextpdf 转成 PDF 输出。这个方案比手写 PDF 坐标要快得多,而且样式可控。导出后的报销单支持在线预览和下载,正好补全“家庭医疗保险服务系统”的服务闭环。如果你时间紧,最简单的是前端直接调 window.print() 打印页面,但 PDF 导出这个功能在答辩时是加分项,值得做。
5. 前端页面设计与联调细节
5.1 页面模块与路由设计
前端如果选 Vue3 + Element Plus,可以按业务角色把页面分成三块:
家庭成员端:家庭看板(额度进度、近期报销记录)、成员档案、参保方案、就诊记录登记、报销申请提交、消息通知、报销单查看下载。
审核管理端:待审报销单列表(支持按家庭、时间、状态筛选)、报销单详情核对、通过/驳回操作、审核记录查询。
系统管理端:保险方案维护、医院等级和费用类型配置、家庭成员账号管理、系统公告发布、数据统计报表。
路由结构可以这样设计:
/family/dashboard 家庭看板 /family/member 成员档案 /family/claim/new 提交报销 /family/claim/list 我的报销单 /audit/list 审核任务列表 /audit/detail/:id 审核详情 /admin/plan 保险方案维护 /admin/statistics 统计报表页面间的跳转逻辑要交代清楚。比如报销申请流程的页面顺序是:选择成员 → 选择就诊记录(支持多选)→ 系统自动核算 → 确认提交。每一步对应后端一个接口,联调的时候按这个顺序一条条过,不容易漏。
5.2 前后端联调三大坑:CORS、Token、时间格式
这三个问题几乎每个做前后端分离的同学都会遇到,提前知道能省好几天。
CORS 跨域:开发环境 Vue 跑在 5173 端口,后端跑在 8080 端口,浏览器会拦截跨域请求。后端加一个配置类实现 WebMvcConfigurer 的 addCorsMappings,允许来源 http://localhost:5173,允许所有请求头和方法。更稳妥的做法是在前端 Vite 里配代理,把 /api 开头的请求转发到 8080,这样浏览器看到的是同源请求,完全不触发跨域。
Token 携带:前端用 axios 拦截器统一在请求头里塞 Token:
axios.interceptors.request.use(config => { const token = localStorage.getItem("token"); if (token) { config.headers.Authorization = "Bearer " + token; } return config; });后端拦截器校验失败时统一返回 401,前端 axios 响应拦截器检测到 401 就清掉本地 Token 并跳转登录页。这里关键是处理逻辑要放在拦截器里,而不是每个页面重复写。
时间格式:LocalDateTime 序列化后默认是数组或带 T 的 ISO 格式,前端展示会很难看。后端全局配置 Jackson 序列化格式,把 LocalDateTime 统一输出为 yyyy-MM-dd HH:mm:ss,LocalDate 输出为 yyyy-MM-dd。这样前端表格、详情页拿到就是可直接展示的字符串,不需要再拿 dayjs 去格式化。
6. 常见问题与踩坑避雷指南
6.1 必踩的坑与排查清单
我按遇到频率从高到低整理了一张速查表,这些都是真实项目调试里被反复验证过的问题,不是网上抄的:
| 现象 | 直接原因 | 处理办法 |
|---|---|---|
| 启动后访问接口报 404 | Controller 包路径不在启动类扫描范围内 | 启动类放在 controller/service/mapper 包的最外层 |
| 数据库中文乱码 | 连接串没指定 UTF-8 | URL 加 characterEncoding=utf8&useSSL=false |
| 上传文件报错 500 | Tomcat 默认限流太小 | 配置 spring.servlet.multipart.max-file-size |
| 时间显示为 “2025-04-01T10:20:30” | LocalDateTime 默认序列化格式 | 全局配置 Jackson 日期格式 |
| 报销金额显示 0.30000000000004 | 使用 double 计算金额 | 全链路改用 BigDecimal |
| 明明登录了还跳登录页 | Token 过期时间设太短 | JWT 过期时间设 24 小时,或者做续期 |
| 审核通过后额度没变 | 事务没加或者异常被吞 | 检查 @Transactional 和 catch 块是否重新抛出 RuntimeException |
| 列表接口数据超出当前家庭范围 | 没有做行级数据过滤 | Service 层强制拼接 family_id 条件 |
6.2 从“能跑”到“好讲”的优化点
如果你的时间有富余,我建议把下面三个点做了,这些在答辩时非常能体现工程意识:
- 接口文档:接入 Knife4j 后自动生成 Swagger 文档,Controller 注解一标,答辩时打开文档页面展示接口,比复制代码有说服力得多。
- 日志与审计:用一个 AOP 切面统一记录操作日志,记录谁在什么时间操作了哪条数据。医保系统本身对审计有要求,这个东西写在论文里是很自然的业务需求。
- 健康检查与启动 Banner:项目启动时打印一个自定义 banner,试试改掉默认的 SpringBoot 图案,这个虽然在功能上没意义,但在演示时观赏性好,也算一种细节感。
还有一个工程配置细节:application.yml 里的数据源密码不要明文写在配置里。可以写成环境变量引用${DB_PASSWORD},本地开发时用 IDEA 的环境变量或启动参数传入。虽然毕设不做生产部署,但这个习惯在面试时能聊两句,比如分布式配置中心、敏感信息脱敏,都是后端领域的高频面试话题。
7. 答辩经验与个人心得
最后聊一点答辩时很实用的经验。这个项目最容易打动人、也最容易被追着问的地方,其实就三个:数据权限怎么做、报销并发怎么防、智能规则怎么配置。我在带这块项目时见过导师最爱问“如果一个家庭同时提交两笔报销,剩余额度只有 5000 元,你系统怎么保证不会超额?”——这就是第 4.2 节的行级锁和事务问题,答得出来你就是真正理解了这个系统,答不出来就会显得项目是抄的。建议把这段代码的调用链路背下来:Controller 接收请求 → Service 开启事务 → selectByIdForUpdate 锁行 → 核算剩余额度 → 更新已用额度 → 提交事务。
还有一个容易忽略的点:演示前一定准备好一份“假数据脚本”。准确说,是把家庭成员、医保方案、就诊记录、待审核报销单提前插入数据库。我见过太多演示现场现敲数据,又慢又容易出 bug。提前把数据造好,演示时顺着看板页面的额度进度条点进去,一步一步展示报销的完整流程,这种演示节奏是最舒服的。
我自己做这类管理系统的整体感觉是:它比图书管理、学生管理等老题材更有业务厚度,又不至于复杂到无法驾驭。家庭医保的业务规则足够撑起一篇有逻辑的论文,数据结构也足以支撑清晰的图表设计。只要把报销核算这条主线做稳、把额度预警和文件上传这两个亮点做出来,再做点页面美化,整体完成度就相当能打了。后续想扩展的话,异步任务、对象存储、缓存预热、消息推送这些点都可以继续往里面加,而且每个扩展点都能对应一个成熟的开源组件,随手就能写出“本项目基于 xxx 实现了 xxx”的加分句。真到答辩前,把这套东西从头到尾走两遍,你心里就有底了。