ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue+Java+MySQL构建城乡居民医疗信息管理系统开发解析

SpringBoot+Vue+Java+MySQL构建城乡居民医疗信息管理系统开发解析 每年三四月份找我聊毕业设计选题的人就明显多起来。如果你正在SpringBoot、Vue、Java、MySQL这几个词之间反复纠结说实话SpringBoot Vue 前后端分离架构配合 Java MySQL 做一个城乡居民基本医疗信息管理系统管理平台是一个很经典也很稳妥的选项。这个选题为什么值得做因为它的业务边界清晰需求不是凭空编造的而是贴近真实场景居民信息维护、参保登记、缴费记录、报销审核、门诊住院记录查询这些模块有明确的数据流和状态流转。对毕设和课设来说既能体现完整的后端业务设计能力又能展示前端交互与工程化水平而且答辩时“为什么这样设计”特别容易讲清楚——因为每一个设计决策都可以对应到真实业务问题。这篇文章我不打算只讲“这是个好项目”而是把这类系统从需求拆解、数据库设计、后端核心模块到前端配合、部署落地的完整链路拆开结合我自己做项目和带人踩坑的经验把能直接用的思路和代码逻辑写出来。你可以把它当作一份“拿到源码之后怎么理解、怎么改动、怎么讲出深度”的参考。1. 为什么是“城乡居民基本医疗信息管理系统”选题价值与业务拆解1.1 这个题目背后到底在做什么业务很多同学拿到一个项目源码第一反应是“把数据库导入run 起来看看页面长什么样”。但如果你只是停留在这一步答辩的时候很容易被问倒。所以我建议先想清楚一个问题城乡居民基本医疗信息管理系统它管的到底是什么“城乡居民基本医疗”这八个字指的不是某个单体系统而是一套围绕居民参保和费用报销的持续业务流程。简单概括核心生命周期是这样一条线居民信息建档管理辖区内居民的姓名、身份证号、户籍地址、联系方式等基础档案。参保登记与缴费记录居民参保是有时间周期的每年度需要登记、续保、缴费系统要能记录每一年度的参保状态。就医记录登记居民在定点医疗机构发生门诊或住院行为后相关记录需要录入系统作为后续报销的依据。报销申请与审核居民提交医疗费用报销申请业务人员审核材料审批通过后计算报销金额并生成支付记录。这套业务逻辑的妙处在于它天然包含一对多关系一个居民有多条参保记录、多条报销记录、状态机概念参保状态从正常到暂停/终止报销申请从待审核到通过/驳回还有金额计算逻辑报销比例、报销金额。这些全都在考察你的数据库设计和后端编码能力正是毕设评分最看重的部分。1.2 模块边界怎么划三个角色三种视角业务理解了接下来是系统模块怎么拆。看源码的时候你会发现几乎所有同类型的系统都绕不开“管理员 业务人员 居民”这三类角色。但真正好的项目不会把功能堆成一个扁平菜单而是按角色的职责边界划分角色核心职责典型功能范围系统管理员基础数据与权限维护用户管理、角色管理、菜单权限配置、人员信息管理业务经办人员日常业务办理居民档案录入与修改、参保登记审核、缴费记录登记、报销审批普通居民查询与申请查看个人信息、参保状态、缴费历史、提交报销申请、查看审核进度我之前看过很多毕设项目最大的问题在于权限等于摆设所有角色进了系统看到的页面一模一样居民也能去后台删别人的档案。这样的项目即使功能齐全答辩时也经不起追问。反之只要你在路由或者菜单层面做了角色过滤哪怕逻辑再简单也能体现出“设计意识”——这在评分标准里属于系统设计部分的加分项。所以拿到这个主题的源码后第一步不要急着读代码先把系统的角色和功能树画出来然后去代码里找“角色对应的功能入口在哪里被控制的”这是你理解整个项目最快的方式。2. 技术选型不是碰运气SpringBoot Vue MySQL 这套组合的取舍2.1 后端版本坑SpringBoot 2.7 与 3.x 怎么选现在网上的源码下载下来依赖版本五花八门。如果你刚接触这套技术栈一定会遇到搜索词里那个高频问题“springboot版本太高”。这确实是个坑因为 SpringBoot 3.x 相比 2.x 有破坏性升级最核心的一点是SpringBoot 3 强制要求 JDK 17而很多机构教学和毕设演示环境都还在 JDK 8。做这个项目的选型逻辑应该是如果你的 JDK 是 1.8那就用 SpringBoot 2.7.x这是 2.x 的最后一个稳定版本生命周期也持续到 2025 年后社区资料最丰富。如果你的环境是 JDK 17 及以上可以直接用 SpringBoot 3.x但要注意部分老教程里的依赖写法需要调整比如javax.*包名变成了jakarta.*。我自己做演示的时候默认都是用 JDK8 SpringBoot 2.7.18因为兼容性最稳网上搜得到的报错和解决方案也最多。毕设阶段最大的敌人不是技术老旧而是报错查到天亮也没人踩过同一条坑。2.2 前端 Vue 与组件库的配套选择前端部分的选型也一样。Vue 2 已经进入维护状态Vue 3 是当前主流但对于毕设源码来说你经常会看到 Vue 2 Element UI 的组合原因是这俩人搭配最成熟中文资料、案例数不胜数。更合理的建议是如果你是从零开始写推荐 Vue 3 Vite Element Plus更现代组件库维护也更积极。如果你拿到的是现成源码它是 Vue 2 Element UI除非你有把握系统迁移否则不要强行升级。“vue安装及环境配置”“Vue入门”这些都是绕不开的环节先把工程跑起来再谈重构。组件库这块选择 Element 系列几乎是没有悬念的因为这类管理平台的核心页面全是表格表单弹窗树形控件Element 的 table、form、dialog、tree 几个组件就能解决 90% 的界面需求。像“城乡居民医保”这种业务信息管理平台界面要求是清晰、规整、操作路径短而不是花哨的视觉创意。2.3 持久层框架为什么选 MyBatis-Plus现在网上主流的 JavaWeb 毕设后端持久层基本就是 MyBatis-Plus 一家独大。很多人会问为什么不用 JPA、Hibernate原因也很实际MyBatis 和 MyBatis-Plus 的 SQL 控制力更强。医疗信息管理系统里有很多复杂查询——按时间区间、按状态、按身份证号模糊搜索、多表关联统计SQL 看得见摸得着调试时直接把日志里的 SQL 复制出来就能跑。MyBatis-Plus 解决了单表 CRUD 的重复劳动。它内置BaseMapper和LambdaQueryWrapper不用写 XML 就能完成简单的条件查询、分页查询这对以业务逻辑为主的毕设项目非常友好。学习曲线低。你只需要搞懂实体类上的注解TableName、TableId、TableField以及 wrapper 构造器就基本掌握了 80% 的数据访问写法。但这不意味着你不需要 SQL 能力。恰恰相反如果你能在项目里写出一两个“稍微有点复杂”的多表关联查询 SQL比如统计某年度某街道居民的缴费汇总答辩时是可以主动拿出来讲的。3. 数据库设计是这类项目的命门核心表结构与关系3.1 六张核心表的职责一个合格的城乡居民基本医疗信息管理系统数据库至少要包含下面这些核心表。我按业务重要程度排一下居民信息表resident_info存居民基础档案核心字段包括姓名、身份证号、性别、出生日期、户籍地址、联系电话、参保状态等。这里“身份证号”必须设唯一索引这是整个系统的业务主键。参保缴费表insurance_record存居民每年的参保登记和缴费情况核心字段包括居民ID、年度、参保类型、缴费金额、缴费日期、参保状态正常/暂停/终止。报销申请表reimburse_apply存居民的报销申请核心字段包括居民ID、就诊类型门诊/住院、总费用、申请金额、诊断说明、申请时间、审核状态待审核/通过/驳回、审核人、审核意见。门诊/住院记录表medical_record存就医明细包括就诊医院、科室、诊断结果、医疗总费用、开始和结束时间。这张表和报销申请是上下游关系。系统用户表sys_user登录账号包含用户名、密码加密切勿明文、真实姓名、角色ID、状态。角色表sys_role角色定义管理员、经办人员、普通居民这三类至少要有基础的数据初始化。这些表不是凭空定的而是从业务生命周期里自然推出来的。你每设计一张表都能对应回“居民参保→看病→报销→审核”这条链路中的某一个环节。3.2 表关系怎么设计从业务推导外键数据库设计最怕的是“按页面来建表”——你看到一个页面展示什么字段就建一张表完全不管表之间的业务关联。正确的做法是顺着业务关系推导外键一个居民可以有多个年度的参保缴费记录所以insurance_record里要有resident_id外键指向居民表。一个居民可以有多次门诊/住院记录所以medical_record里要有resident_id。一次就医记录对应一次报销申请报销申请表里通常会冗余medical_record_id或者至少保留诊断摘要和费用总额方便审核人对照。一个用户对应一个角色角色表独立出来为后续做菜单权限控制留空间。外键约束在毕设里建不建物理外键我个人的经验是可以只建索引不建物理外键。因为 MyBatis-Plus 这类主流组件并不是靠数据库外键驱动业务而是靠 Java 代码维护关联。物理外键在删除数据时会带来很多约束麻烦对演示项目不友好。但你要在文档里写清楚“逻辑外键的含义与关联方式”这样就不算设计缺陷。3.3 几个容易踩坑的字段设计细节我在检查别人数据库的时候几乎每次都会发现同几个问题这里单独列出来你们对照自己的表检查一遍金额字段必须用 decimal不能用 float 或者 double。医疗费用和报销金额涉及精确计算浮点数在累计、比较时会出现精度丢失。字段建议decimal(10,2)能覆盖千万级以内的金额。状态字段用 int 或 varchar加注释说明含义。比如报销审核状态0待审核1通过2驳回。不要真的一长串字符串存进去后期改需求时你根本没法统计。时间和日期字段建议用 datetime 而不是 date。像缴费时间、申请时间都可能精确到时分秒将来做“按时间段查询最近三个月报销记录”时才不会丢数据。身份证号、手机号这类字段建议 varchar 而不是 int。这个错误很低级但经常出现身份证号超过整数范围后要么报错要么丢失精度更别提中间还可能含字母 X。这三条设计完成后你可以顺手做一个特别加分的动作给每个表加create_time和update_time两个通用字段。MyBatis-Plus 还支持自动填充这样你所有新增和修改操作自动维护时间戳答辩时提一句“通过自动填充减少重复代码”这是很务实的设计。4. 后端核心实现从登录鉴权到报销审核的链路4.1 统一返回结果与全局异常处理后端代码拿到手第一件事找com.xxx.common之类的工具包看它的统一返回结构。一个成熟的管理平台接口返回值不会直接返回 Map 或者裸的 List而是会包装成一个统一对象。常见设计是public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMsg(操作成功); result.setData(data); return result; } public static T ResultT error(String msg) { ResultT result new Result(); result.setCode(500); result.setMsg(msg); return result; } }对应的前端 axios 响应拦截器拿到data.code判断业务是否成功。这里面试官或者答辩老师经常会追问的一个点是“为什么不直接返回Map”你要能回答统一包装的好处是前端可以集中处理错误码、提示语和登录态过期等通用逻辑而不是每个接口单独判断。同时要有全局异常处理器用RestControllerAdvice捕获BusinessException和系统异常转换成统一返回结构。这一步的意义是离用户越近的地方越不应该把异常堆栈直接暴露出来而是给一个“参数校验失败”或者“操作失败”的友好提示同时后端日志记录完整堆栈。这是一个极具工程感的细节。4.2 基于 JWT 的登录态管理城乡居民基本医疗信息管理系统里有“居民查自己报销记录”这种需求所以权限必须能区分到用户级。当前主流、也是这套源码里最常见的技术方案是 JWT。核心流程是怎样的用户传用户名密码调用/login接口。后端校验账号密码生成一个 token里面可以包含用户 ID、用户名、角色 ID 等非敏感信息签名后返回给前端。前端把 token 存在 localStorage 或 Pinia/Vuex 里以后每次请求在请求头里带Authorization: Bearer token。后端写一个拦截器或过滤器校验 token 合法性和有效期再从 token 里解析出当前用户信息放入ThreadLocal或直接作为参数传给控制器。实现的时候推荐使用io.jsonwebtoken:jjwt依赖关键代码如下// 生成 token有效期设置为 24 小时 String token Jwts.builder() .setSubject(user.getUsername()) .claim(userId, user.getId()) .claim(roleId, user.getRoleId()) .setExpiration(new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact();这里有个特别值得注意的细节用户修改密码、被禁用之后旧 token 还没过期怎么办很多毕设源码不会处理这件事只校验 token 本身。你可以捎带手加一个“用户状态字段比对”的步骤鉴权时除了解析 token还要根据用户 ID 去数据库查一下当前状态如果被禁用则直接拒绝。代码量不大但是答辩时讲“我不仅做了 token 校验还每次查询用户最新状态”会让老师觉得你考虑得比一般学生周全。4.3 分页查询与模糊搜索MyBatis-Plus 的标准范式业务管理系统的后端接口几乎是清一色的“分页列表 条件筛选”。老旧写法是手动算 limit 偏移量再用 PageHelper 插件而基于 MyBatis-Plus 的源码里通常会这样写// 配置分页拦截器 Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }在 Service 层配合 LambdaQueryWrapper 构造查询条件配合 Page 对象完成分页PageResidentInfo page new Page(pageNum, pageSize); LambdaQueryWrapperResidentInfo wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(name), ResidentInfo::getName, name) .eq(StringUtils.hasText(idCard), ResidentInfo::getIdCard, idCard) .eq(residentInfo.getStatus() ! null, ResidentInfo::getStatus, residentInfo.getStatus()) .orderByDesc(ResidentInfo::getCreateTime); residentInfoService.page(page, wrapper);这里大家最容易忽略的一点是分页查询的返回值不要直接return page因为 MyBatis-Plus 的Page对象里有些字段前端不需要而且不同接口返回结构要稳定。更规范的做法是返回一个自定义 VO视图对象或统一的分页结构包含total、records、pageNum、pageSize四个字段。很多源码偷懒直接返回 Page功能上没毛病但重构时你就会发现到处都是 MyBatis-Plus 的痕迹想换成别的框架时全部要改。至少你应该在 Controller 层做一次数据组装。4.4 事务处理报销审核为什么会同时改三张表这类系统里最值得留意的事务场景是报销审核流程。业务人员点一下“审核通过”后端要同时做什么更新报销申请表的审核状态为“通过”写入审核人、审核时间。根据费用金额和报销比例计算并写入报销金额。如果设计了资金台账表还要新增一条支付记录。这三个操作必须在一个事务里完成。如果只把申请状态改了金额写入失败用户就“审核通过了但钱没算出来”业务数据就烂了。Spring 里最直观的做法是在 Service 方法上打Transactional(rollbackFor Exception.class)。但这里有个非常隐蔽且常见的问题Transactional走的是 Spring 代理如果你在同一个类的内部 self-invocationthis.xxx()调用事务方法事务是不会生效的。毕设里很多同学把 Controller 直接写业务逻辑、或者 Service 内部方法互相调用就会踩到这个坑。排查方法也很简单看方法报错后数据有没有回滚或者看日志里有没有TransactionInterceptor的调用记录。另外我再建议一个小优化把事务拆细。比如“登记缴费记录”和“发送短信通知”就不应该在同一个事务里短信服务不稳定一旦失败会把正常业务也回滚掉。事务的边界是写库的关键业务数据通知、日志、外部调用这些尽量放到事务外面。这个意识能让你在答辩时从“会使用注解”升级到“会设计事务边界”。5. 前端 Vue 的配合要点路由、请求封装与权限控制5.1 动态路由还是静态路由管理平台的菜单权限怎么落地这是我看见的另一个高频困惑。最简单的做法是在路由表里把管理员、经办人员、居民的功能页全部注册为静态路由然后根据登录用户角色在前端根据菜单配置去 v-if 隐藏入口。这种方法代码简单但这同样意味着用户知道 URL 就能直接访问未授权页面。稍微进阶一点的做法是动态路由用户登录后后端返回当前角色可访问的路由列表前端通过router.addRoute()动态注册。这就解决了“页面隐藏但路由可直达”的问题。它的核心流程登录后拿到用户角色和菜单权限列表。前端根据权限列表过滤动态路由表再router.addRoute添加。路由守卫里判断当前访问路径是否在权限列表中不在则 403 或跳转到第一个可用页面。动态路由的代码实现会复杂不少但它带来最大的好处是权限控制的源头集中在一个地方新增角色只需要配菜单前端不需要改代码。你在答辩时能讲清楚“为什么用动态路由”就比只会写页面循环的选手高出一截。5.2 axios 封装token 携带与错误码统一处理前端工程必然有一个utils/request.js文件对 axios 进行二次封装。你在理解源码时重点看三处第一请求拦截器里自动携带 tokenservice.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }, error Promise.reject(error))第二响应拦截器里统一处理业务码和 HTTP 状态码service.interceptors.response.use(response { const res response.data if (res.code ! 200) { Message.error(res.msg || 系统错误) if (res.code 401) { // 登录过期清除 token 并跳转登录页 router.push(/login) } return Promise.reject(new Error(res.msg)) } return res }, error { Message.error(error.message) return Promise.reject(error) })第三API 接口不要散落在各个组件里。合理的做法是按模块建src/api/resident.js、src/api/insurance.js、src/api/reimburse.js等文件每个文件导出一个函数组件里只调用函数。这样将来后端接口调整时你只需要改 API 文件不用全局搜。这也是一个可以讲给老师听的“前端工程化”点。5.3 跨域问题的解决Vue CLI/Vite 代理配置前端开发时调用后端接口最烦的就是跨域。“vue打包放进springboot中”这个词出现的频率极高就是因为大家把前后端部署方式搞混了。开发阶段最常见的解决方案是配置代理如果你用 Vue CLI 创建的项目在vue.config.js里module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }如果你用 Vite则在vite.config.js里配置server.proxy。代理的原理不复杂前端开发服务器接收/api开头的请求转发给后端地址这样浏览器看到的是同源请求就不存在跨域了。需要注意后端接口如果本身没有/api前缀代理配置里通常会再加一段pathRewrite: { ^/api: }把前缀去掉再转发。很多同学会问为什么不在后端加CrossOrigin或者配置全局 CORS答案是可以用但这只解决开发环境的问题。而且如果将来前端和后端部署在同一个域下代理和 CORS 都可以不配。这个问题在部署章节里继续展开。5.4 表格、表单与弹窗的重复劳动怎么减少医疗信息管理系统的页面本质上就是三种模式的循环列表页、新增/编辑弹窗、详情页。遇到“居民列表”“缴费记录列表”“报销列表”它们长得高度相似。如果你只是照着写工作量会非常大而且代码冗余严重。我建议至少做两件事第一封装一个通用的分页查询组件把“搜索条件区 表格区 分页器”整合成一个带插槽的组件。数据请求逻辑在组件内部统一处理页面只需传入接口函数和列配置。第二抽一个表单弹窗的基础组件包含v-model控制显隐、标题、确定/取消按钮、加载状态。每个具体的新增或编辑弹窗只需要往插槽里填表单项即可。这样的封装能让你在写“居民信息管理”和“业务人员管理”两个看似完全不同的页面时直接复用八成的基础代码。这也是项目里“代码量”和“工程质量”中最容易拉开差距的部分——评阅老师看你源码时会特别在意重复代码多不多。6. 从源码到跑起来环境搭建与部署全流程6.1 后端环境JDK、Maven、MySQL 配置拿到源码第一件事不是打开 IDE而是先把环境对齐了。这个项目的后端基础环境建议是JDK 8 或 17、Maven 3.6、MySQL 8.0。MySQL 安装是程序员的老朋友了我补充几个和高频搜索词“mysql安装教程”“mysql安装配置教程”“mysql安装教程8.0”有关的实操细节MySQL 8.0 默认加密规则是caching_sha2_password某些老版本 JDBC 驱动连接会报错解决办法是换最新驱动或者把用户加密规则改回mysql_native_password。连接字符串一定要带serverTimezoneAsia/Shanghai和useSSLfalse否则容易报时区和 SSL 错误。这也是搜索词“mysql ssl连接错误”的来源。导入 .sql 文件前先创建数据库并指定字符集CREATE DATABASE medical_system DEFAULT CHARACTER SET utf8mb4;。记得是utf8mb4不是utf8这样才能正常显示生僻字和表情符号。application.yml里典型的配置如下server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/medical_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这行建议保留控制台会打印每个 SQL。你排查“为什么查不到数据”“为什么 SQL 条件不对”时看这里是最快的。另外还有个毕设阶段很常见的问题Maven 依赖 download 特别慢或者下载失败。这跟网络有关建议把阿里云 Maven 镜像配进settings.xml。如果还卡优先检查是不是公司或者学校网络把镜像源禁了。6.2 前端环境Node 版本与 npm install前端子目录一般是一个独立的工程结构类似frontend或者vue-web。进入目录后执行npm install这一步通常是最劝退的环节。常见问题有两个第一Node 版本和依赖不匹配。Vue 2 项目用 Node 16/18 基本没问题Vue 3 Vite 项目则建议 Node 18。如果你用的是 Node 22某些老依赖在编译时会报错。我的经验是备一个 nvmNode 版本管理工具随时切换版本比反复重装 Node 高效得多。第二npm install 卡在某个依赖上。可以先删除node_modules和package-lock.json再重新执行。如果是网络问题设置淘宝镜像源npm config set registry https://registry.npmmirror.com启动命令通常是npm run serveVue CLI或npm run devVite。启动后访问http://localhost:8081如果页面能出来就说明前端工程本身没问题接下来看接口联调。6.3 前后端联调的关键配置前后端各自跑起来后如果前端页面接口全部请求失败99% 是两个原因一是代理没配对。检查vue.config.js或vite.config.js里的代理 target 是否指向后端实际端口。后端如果改过server.port前端代理也要同步改。二是跨域虽然代理配了但后端的 ContextPath 对不上。比如后端spring.mvc.servlet.path/api前端代理路径却又加了一层/api结果请求变成/api/api/user/login自然 404。解决方式是统一约定接口前缀要么后端加要么前端加不要两边各加一次。验证联调是否成功有一个笨但有效的办法打开浏览器开发者工具看网络请求的 URL 路径和响应状态。如果是 200再看数据结构是否符合期望如果是 404说明路径不对如果是 405多半是方法类型不匹配比如后端是 POST 前端用了 GET。6.4 打包部署vue 打包放进 SpringBoot 的两种做法这是“vue打包放进springboot中”被反复搜索的原因所在。毕设阶段不需要上复杂的 Nginx 集群两种做法非常实用做法一经典前后端分离部署。前端npm run build生成dist目录用 Nginx 托管静态文件并配置反向代理把/api请求转发到后端 SpringBoot 服务。这种方式最贴近企业真实环境答辩如果不是跑本地而是上云服务器建议用这种方式。做法二前端构建产物放进后端。把dist里的静态文件复制到 SpringBoot 的src/main/resources/static目录下然后重新打 jar 包。这样启动一个 Java 进程就能同时提供页面和接口。简化了部署但不太符合真实的前后端分工逻辑适合老师要求“一个 jar 跑起来”的场景。如果选了做法二需要注意一个坑Vue Router 使用 history 模式时刷新某个子路由会出现 404因为后端没有对应的静态资源路径。解决办法是加一个路由回退配置把未匹配的路径转发到index.html。如果你用的是 hash 模式就没有这个问题。毕设演示我一般建议直接用 hash 模式省心可靠。打包之后还有一步值得做检查application.yml里数据库账号密码是否还是本地测试用的如果别人在自己电脑上跑你的 jar他需要先改配置才能连接到自己的 MySQL。这个细节在源码分享里很体现职业素养。7. 答辩和展示时怎么把项目讲出深度7.1 主动准备好的两个技术亮点等到把项目跑通只是完成了最基本的任务。如果你想让答辩老师觉得这个项目“不是抄的”建议准备两个可以主动展开的技术点。第一个是登录鉴权链路。从“为什么用 JWT 而不是 Session”开始到“token 过期怎么处理”“用户被禁用后 token 怎么立即失效”“密码为什么要加密存储”这一整条链路每讲一层都有代码支撑。Session 和 JWT 的区别是最容易展开的话题Session 存在服务端天然支持主动失效但不利于水平扩展JWT 自包含、无状态适合前后端分离但被动失效难。你完全可以在项目里同时保留两者用 JWT 做无状态认证在用户每次请求时查一次状态字段弥补被动失效问题。这种“两全”的思考方式在答辩中非常有说服力。第二个是报销审核的事务边界。前面提到审核时同时更新申请表、计算金额、新增流水。你可以用“如果不开事务会出现什么情况”来反向说明用户报销申请状态显示通过但支付流水缺失对账就出问题了。然后说明 Spring 事务机制、事务失效的常见原因以及你如何确保了事务真正生效。这类“业务 技术”结合的问题是评委最常追问的方向。7.2 被问到“哪里还可以改进”时的回答思路答辩快结束的时候老师几乎必问“你觉得项目还有哪些不足”。这是个送分题但很多同学答成“我的项目很完美”或者“我对系统还不熟”。这两种都不好。最好的回答是给出合理的、基于真实技术的改进方向比如“目前报销比例的配置是写死在代码/配置里的后续可以做成独立参数表由业务人员动态维护不同类别的报销比例。”“目前上传的发票/病历还是以附件形式保存后续接入对象存储做统一管理并在审核页面做图片预览。”“日志方面目前主打控制台输出后续希望基于注解切面做操作日志审计记录谁在什么时候对哪些敏感数据做了修改。”这三个改进方向分别对应了系统参数化、文件存储、操作审计都是很典型的真实业务需求。你如果能把“改进方向”讲成一个具体的设计而不仅仅是两句口号老师会明显感受到你理解系统的深度。我个人做了一轮又一轮类似项目后最大的体会是这种管理信息系统技术上没有特别高深的内容功夫全在业务建模的完整度、事务边界的清晰度、权限设计的严谨度、以及前后端异常处理的一致性上面。你把这四个点做扎实整个项目的可信度和完成度立刻就不一样了。如果你正在拿这套 SpringBoot Vue Java MySQL 的源码做课设或者毕设我给一个具体的行动顺序先按要求把项目跑起来再画业务流程图和表结构图然后找一个模块比如报销审核把它的前后端完整链路看完最后在这个模块上做一个自己的小改进。这样走完一遍无论答辩问题怎么刁钻你都接得住。
返回列表