ARTICLE DETAIL

资讯详情

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

SpringBoot校园医保系统实战:权限管理、报销流程与MyBatis配置全解析

SpringBoot校园医保系统实战:权限管理、报销流程与MyBatis配置全解析 这个项目我上手完整跑了一遍SpringBoot全家桶搭建的校园医保系统从数据库初始化到前后端联调踩了不少坑也有不少值得展开说的设计细节。如果你正在找JavaWeb方向的课设选题或者想拆一套完整的权限管理业务流转案例这篇复盘值得花几分钟看完。1. 系统整体设计与需求拆解1.1 校园医保业务的真实场景校园医疗保险和社保医保不太一样它更像一个校内福利性质的补充保险。学生每学年缴纳几十到上百不等的保费在校医院或者指定医疗机构看病然后凭票据走校内报销流程。传统做法是纸质申请表人工审核医保办老师对着Excel表核对到了报销高峰期办公桌上堆的全是病历本和发票。这套系统要解决的就是把“学生提交申请、校医保办审核、财务打款记录、参保信息维护”这条链路搬到线上。标题里写的是“校园医疗保险管理系统18525”18525应该是数据库表设计时的一个编号或者作者的项目流水号不用太纠结。核心在于它把业务拆成了参保管理、报销申请、审核流转、政策发布、统计查询五大板块。如果你是刚开始做课设或者想拿这套系统练手最值得学的不是CRUD本身而是它怎么把角色权限和业务流程绑定在一起。学生只能看到自己的报销记录医保办老师能审所有人的单据系统管理员管用户和基础配置这种数据隔离逻辑在真实项目里非常常见。1.2 六类核心功能模块拆解系统完整的功能结构大致如下模块功能点目标用户个人中心修改密码、查看个人信息全部角色参保管理参保信息登记、续保、停保学生、医保办报销申请提交报销单据、上传凭证学生审核管理材料审核、报销比例核算医保办政策公告医保政策发布与查看医保办、学生系统管理用户管理、角色分配管理员这个模块划分几乎可以照搬去套大多数管理类系统区别只在业务字段不同。比如报销申请表里有就诊日期、医疗机构名称、总费用、医保内费用、报销比例、实际报销金额、发票编号、审核状态、审核意见这些字段换成一个设备报修系统字段就变成了设备编号、故障描述、报修人、处理人。界面是典型的Bootstrap风格后台管理页面左侧菜单栏右侧内容区表格上方有搜索框数据列表支持分页。这套界面模板的优点是开发速度快不用自己写复杂的前端交互后端人员一天就能把页面全部铺完。2. 核心技术选型与关键配置2.1 技术栈选型背后的取舍后端用的SpringBoot持久层配的MyBatis数据库是MySQL前端以Bootstrap和Thymeleaf为主。这套组合在JavaWeb课设和毕业设计里几乎是统治级的存在原因是逻辑清晰、资料多、调试方便。SpringBoot简化了Spring的Bean配置过程内嵌Tomcat让打包部署变成一个可执行Jar不用再单独装Servlet容器。MyBatis把SQL写在XML里复杂查询好调整不用像JPA那样让框架猜SQL语义。MySQL的InnoDB引擎支持事务和外键报销这种需要多表关联更新的场景必须有事务保护。为什么不选微服务、不选前后端完全分离因为这套系统的目标场景是单机部署、几十个并发、内部使用SpringCloud那套反而把简单问题搞复杂了。前后端分离需要单独处理跨域、Token刷新、接口文档维护如果前端功底一般开发周期至少多出三天。SpringBoot集成Thymeleaf让后端直接把数据渲染到HTML里学生登录后打开报销页面能看到自己的申请列表整个流程走起来非常顺。2.2 application.yml核心配置解读整份配置里最关键的三个文件是pom.xml、application.yml和数据库初始化脚本。pom里引入了SpringBoot Web、MyBatis、MySQL驱动、Druid连接池和Lombok依赖核心片段是这样的dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.20/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependencyMyBatis版本这里我用的是2.3.1配合SpringBoot 2.7.18完全没问题。如果强行升级到MyBatis 3.5.16SpringBoot 3.x需要把javax包全部替换成jakarta很多教程里的代码直接跑不起来。application.yml里重点看数据源配置spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/medical_insurance?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.insurance.entity configuration: map-underscore-to-camel-case: truemap-underscore-to-camel-case这个配置建议打开数据库字段insure_date就能自动映射到实体类的insureDate属性省去大量resultMap手写工作量。serverTimezone这个参数也值得注意不配的话高版本MySQL驱动会报时间区域错误。Redis在这里没有引入因为学生医保系统的会话状态不复杂。登录成功后把用户对象存进Session用拦截器校验是否已登录角色通过URL权限控制spring-security不是必须引入的。只有权限粒度细到每个按钮时才需要引入Shiro或Security做注解鉴权这个系统用拦截器Session方案完全够用。2.3 数据库表结构设计思路系统核心表我梳理了一下一共8张左右用户表、角色表、参保信息表、报销申请表、报销明细表、政策公告表、机构信息表、操作日志表。报销申请表是核心中的核心它把一次报销申请的所有必要信息都记录下来CREATE TABLE reimburse_apply ( id int(11) NOT NULL AUTO_INCREMENT, apply_no varchar(32) DEFAULT NULL COMMENT 申请单号, student_id int(11) DEFAULT NULL COMMENT 学生用户id, hospital_name varchar(100) DEFAULT NULL COMMENT 就诊机构, visit_date date DEFAULT NULL COMMENT 就诊日期, total_amount decimal(10,2) DEFAULT NULL COMMENT 总费用, insured_amount decimal(10,2) DEFAULT NULL COMMENT 医保内费用, reimburse_ratio decimal(5,2) DEFAULT NULL COMMENT 报销比例, reimburse_amount decimal(10,2) DEFAULT NULL COMMENT 报销金额, bank_card varchar(30) DEFAULT NULL COMMENT 收款银行卡号, status tinyint(1) DEFAULT 0 COMMENT 0待审核 1通过 2驳回, audit_opinion varchar(255) DEFAULT NULL COMMENT 审核意见, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;报销比例为什么不直接写死在代码里而是以字段形式存在因为校医院、校外定点医院、异地就医这三类场景报销比例不同如果写死在代码里每次调整政策都得改代码重新打包。存进数据库之后医保办老师自己在页面上改就行。这也是设计上比较聪明的一点。学生表通过student_id关联报销申请表查询学生报销记录时用userId去用户表反查学号、姓名、学院信息避免在报销表里冗余太多字段。这种设计在数据库第三范式上站得住脚但查询时多一次表关联。如果数据量超过十万条可以适当冗余比如在报销表里直接存学生姓名和学院这是典型的空间换时间优化。3. 核心模块实现与业务逻辑走通3.1 登录鉴权与角色控制实现登录接口的设计非常直接前端提交用户名和密码后端接收后用Md5加密和数据库比对成功后跳转到对应用户首页。PostMapping(/login) public String login(String username, String password, HttpSession session, Model model) { // 密码加密后查询 String md5Password DigestUtils.md5DigestAsHex(password.getBytes()); User user userService.login(username, md5Password); if (user ! null) { session.setAttribute(loginUser, user); return redirect:/index; } model.addAttribute(error, 用户名或密码错误); return login; }密码加密这里用的Spring自带的DigestUtils简单够用。正式项目推荐加盐或者直接用BCrypt否则遇到彩虹表碰撞弱口令很容易被反解出来。学生端密码默认一般是学号后六位医保办老师的初始密码是系统管理员设定的首次登录后强制要求改密。这个强制改密码的功能在拦截器里判断如果标记是首次登录就跳转到修改密码页面。拦截器实现登录校验的核心代码不建议写在Controller里用Spring的HandlerInterceptor统一处理更优雅public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User user (User) session.getAttribute(loginUser); if (user null) { response.sendRedirect(/login); return false; } return true; } }3.2 报销申请模块的完整走通学生登录后提交报销申请页面上的关键字段是总费用、医保内费用和就诊日期。后端拿到数据后要校验医保内费用是否大于总费用、报销金额是否超过上限这些校验逻辑放在Service层前端只做基础非空校验。核心流程是这样的学生填写报销申请上传门诊发票照片或PDF生成申请单号格式BX年月日随机三位数如BX20241220001医保办老师查看待审核列表点击详情核对票据老师录入审核意见选择通过或驳回通过后系统计算报销金额进入待打款状态财务人员确认打款状态变为已完成这里的生成申请单号的方法值得学习public String generateApplyNo() { String dateStr new SimpleDateFormat(yyyyMMdd).format(new Date()); String randomNum String.format(%03d, new Random().nextInt(1000)); return BX dateStr randomNum; }这个方法在并发高的情况下可能会生成重复号比如同一秒内两个学生同时提交。实战里可以把随机数换成数据库自增id或者用Redis生成序号。课程设计层面这个写法没问题但要是在真实生产环境建议改成雪花算法或者数据库序列。3.3 分页查询与条件检索的经典写法报销列表页支持按状态筛选、按学号搜索、按日期范围查询这里涉及的就是典型的MyBatis动态SQL分页插件。public PageResultReimburseApply queryApplyList(int page, int limit, ReimburseApplyQuery query) { PageHelper.startPage(page, limit); ListReimburseApply list reimburseApplyMapper.selectByCondition(query); PageInfoReimburseApply pageInfo new PageInfo(list); return new PageResult(pageInfo.getTotal(), pageInfo.getList()); }PageHelper用起来确实方便但有个坑得提醒一下startPage方法必须紧跟在要分页的查询语句之前中间如果插入其他数据库操作PageHelper会把分页参数绑定到错误的SQL上。我遇到过一种情况Service层先是查询了一条系统配置然后才执行列表查询结果分页完全不生效查源码才发现是这个原因。最稳妥的方式是让分页查询直接写在Mapper调用的上一行。3.4 前端界面与接口联调的细节前端的页面文件在src/main/resources/templates目录下layouts.html是公共布局模板左侧菜单在这里统一配置。菜单项根据登录用户角色动态渲染用Thymeleaf的sec:authorize或自定义的条件判断实现。页面和Controller之间通过Model传值。Controller里return reimburse/list对应找templates/reimburse/list.htmlThymeleaf的th:each可以循环渲染表格行th:if控制审核按钮在不同状态下显示还是隐藏。如果学生提交的申请正在待审核状态页面上显示“撤销申请”按钮一旦审核通过按钮消失。Bootstrap的大栅格系统在电脑端浏览器的呈现效果很好但如果在手机上打开表格会有一点点拥挤。这套系统本来就是面向PC后台操作的不建议花太多精力在移动端适配能用就行。前端和后端联调时最常踩的坑是浏览器缓存。修改了JS文件但界面没变化按CtrlF5强制刷新一般能解决。还有static目录下新加了一个CSS文件Thymeleaf引入时手误把路径写错了页面样式直接崩溃检查network面板就能定位到404。4. 从零跑通项目的完整实战记录4.1 开发环境准备与工具清单这套项目的标准环境组合如下工具版本建议JDK1.8或11Maven3.6.3以上MySQL5.7或8.0IDEIDEA 2020及以上数据库客户端Navicat或DataGripJDK版本是第一个坑。很多人电脑上装了JDK17甚至JDK21但SpringBoot 2.7基于javax命名空间在JDK17下编译没有问题但有些老版本插件不兼容。如果用的是JDK17建议把SpringBoot版本提到2.7.18这样兼容性最好。Maven仓库下载依赖慢的问题直接修改settings.xml里的阿里云镜像几分钟就能把依赖全拉完。4.2 数据库初始化与项目启动过程拿到源码后数据库初始化分三步新建数据库medical_insurance字符集选utf8mb4、排序规则选utf8mb4_general_ci将sql文件导入我一般直接用Navicat的“运行SQL文件”功能选择数据库后执行即可修改application.yml里的数据库名、用户名、密码导入SQL时如果报错“Unknown database”检查是不是忘了先创建数据库。如果报错语法错误大概率是SQL文件版本和MySQL版本不匹配MySQL 8.0下导入某些老SQL文件时排序规则可能会报错在文件里把utf8mb4_general_ci替换成utf8mb4_0900_ai_ci就行。启动项目先在IDEA里点击mvn spring-boot:run或者在命令行执行mvn clean package -DskipTests java -jar target/medical-insurance-0.0.1-SNAPSHOT.jar看到“Started Application in 3.2 seconds”的日志就代表启动成功了浏览器访问http://localhost:8080页面能正常加载就说明整个过程是通的。数据库连接失败一般会报红色错误日志检查MySQL服务有没有启动以及账号密码是否正确。Druid连接池还提供了监控页面项目启动后访问/druid/login.html输入配置的用户名密码能看到SQL执行耗时、连接池活跃数等数据。这个功能排查慢查询很有用报销列表页如果响应太久就可以去看哪条SQL消耗了最多时间。4.3 数据库同步工具在项目调试中的实战用法这里我要特别聊一个很多人没注意的细节。开发环境数据库数据更新频繁本地库和服务器库经常不一致反复手动导入导出SQL文件很费事。我在跑这套项目时用了数据库同步工具搭配自动化逻辑极大减少了重复劳动。工具选择上比较推荐Navicat的结构同步功能它可以比对两个连接的差异然后生成更新脚本。比如本地库新增了一张表服务器库还没有Navicat会对比出缺失的表结构一键生成ALTER语句手动执行或者让工具直接同步都能完成。另一类工具是类似Flynx的数据库同步软件配置完源数据库和目标数据库连接信息后选择需要同步的表可以设置定时任务每隔几分钟自动拉取增量数据。这套项目是单机部署不涉及复杂的生产环境多库同步用它做开发库和服务器库之间的单向同步就够了。如果你的团队里有多个人同时在改一个项目还是建议把SQL变更统一提交到Git仓库每次修改执行一个增量SQL文件这样版本变更可控可回滚。数据库同步工具适合自己一个人开发、两边库结构不一致又不想手工整理的情况操作前一定要先备份目标库防止误同步覆盖数据。4.4 前后端登录调试的关键步骤项目跑起来后第一个要验证的就是登录功能。学生角色的初始账号在SQL文件里已经插好了用户名一般是学号密码是学号后六位。医保办老师的账号通常是admin开头初始密码123456具体以数据库里user表的数据为准。登录不了的时候按这个顺序排查先看控制台日志如果是Unknown column报错说明user表结构和实体类不对应如果提示用户名或密码错误到数据库里把user表里对应记录的password字段打印出来手动用MD5工具生成一遍密码比对一下如果跳转到了500页面大概率是Session取值的类名导错了或者Thymeleaf模板有语法错误我在第一次跑这套项目的时候密码一直验证不过后来发现是安装MySQL时选择的大小写敏感配置导致的表名是大写还是小写在Linux上会有影响Windows上反而没事。改一下MySQL配置lower_case_table_names1就好改完记得重启MySQL服务。5. 高频报错与排查技巧实录5.1 常见错误速查表我把跑项目过程中的报错整理成表方便后来者对照排查错误现象根本原因解决方案端口被占用8080被其他服务占用把application.yml里server.port改成8081java.sql.SQLNonTransientConnectionException数据库连接不上检查MySQL服务、用户名密码、数据库名登录后页面404静态资源路径错误确认/source目录下模板文件位置和Controller返回值路径一致Invalid bound statementMapper接口方法和XML里的id不一致核对namespace和方法名Cause: java.lang.IllegalArgumentException参数个数不匹配检查Param注解是否添加Whitelabel Error PageController没有捕获异常添加ControllerAdvice全局异常处理器Druid监控页面访问不了没有放行拦截器在拦截器配置里exclude /druid/**上传文件超过大小限制SpringBoot默认单文件1MB配置spring.servlet.multipart.max-file-size10MB表格里的前三条出现频率最高其中数据库连接失败占到了一大半。排查数据库连接问题的思路是先在Navicat里测试一下能否连上如果能连上但程序报错再去检查配置文件。如果Navicat也连不上那就得看MySQL服务本身有没有启动。端口冲突也很常见尤其装了某些应用会自动占用8080。用netstat -ano | findstr 8080查到是哪个进程占用了端口然后结束进程或者改项目端口都行。5.2 两个容易忽略的坑第一个坑是Druid和MyBatis的兼容问题。MyBatis-Plus版本如果太低和Druid连接池一起使用时会报ClassNotFound原因是没有引入mybatis-spring-boot-starter的对应版本。解决办法是把MyBatis starter版本和SpringBoot版本对齐或者在pom里显式引入mybatis-spring依赖。第二个坑是数据库字符集导致的中文乱码。虽然application.yml里写了characterEncodingutf8但是MySQL数据库本身如果建成了latin1查询出来依然是乱码。导入SQL之前先确认数据库的字符集执行show variables like character%查看结果如果不是utf8mb4需要先ALTER database修改字符集。这两个坑都不会报特别明显的错乱码容易看出来兼容问题一般只在启动时出现一次异常。比较隐蔽的是MyBatis打印出的SQL里中文条件正常但数据库里查询不到结果这种基本就是字符集不一致导致索引无法正常匹配。5.3 部署到服务器时的实战技巧本地跑通只是第一步部署到远程服务器时有一些差异点需要注意。打包时记着执行mvn clean package -DskipTests不要跳过测试类的时候把编译也给跳了。数据库同步工具在服务器部署这里又能派上用场。如果本地和服务器数据库版本不一样直接用SQL文件导入可能会出问题。MySQL 5.7的SQL文件导到MySQL 8.0里时有些列属性定义会有细微差异这种时候用数据库同步工具做结构比对再同步会更安全一些。部署运行时使用nohup命令让项目在后台保持运行nohup java -jar -Xms256m -Xmx512m /opt/medical/medical-insurance-0.0.1-SNAPSHOT.jar /opt/medical/log.log 21 -Xms和-Xmx分别设置JVM的初始内存和最大内存。服务器内存有限的情况下把最大内存压到512m也能跑但并发高的时候可能会出现频繁GC。如果是2核4G的服务器建议-Xmx开到1g。启动完成后用tail -f /opt/medical/log.log看启动日志一直到看到“Started Application”输出了才算启动成功。再提醒一句服务器上的防火墙和安全组要放行项目对应的端口否则浏览器访问不了。云服务器一般有两层防火墙一层是云平台的安全组一层是Linux系统自带的firewalld两层都得放行。6. 这套项目的后续扩展方向6.1 业务功能增强跑通这套系统后如果你想继续加深理解或者让项目在答辩时更有亮点有几个方向值得试试。一个是增加统计报表模块。医保办老师每个月需要知道“有多少学生提交了报销、总报销金额是多少、哪个学院的报销频次最高”这些数据如果靠人工从Excel里统计非常累。在后端用SQL的GROUP BY加上时间范围筛选到Controller层封装成接口前端引入ECharts画柱状图和饼图功能做完之后整个系统的实用性会提升很多。另一个方向是接入短信通知或者邮件通知。报销申请审核通过或驳回后学生希望第一时间收到结果。在审核Service的方法里状态变更后调用阿里云短信接口或者Spring的JavaMailSender发送邮件这个功能不需要复杂的架构改动费用也不高很多本科生毕设都会选这个作为创新点。6.2 性能与代码结构优化从代码维护角度看Controller层的逻辑偏厚可以引入Service接口和实现类分离的规范做法。目前是统一定义Service类如果后续有好几个人一起开发接口隔离会避免冲突。数据访问层可以引入通用Mapper或者升级到MyBatis-Plus这样单表CRUD不用手写XML直接用BaseMapper提供的方法开发效率提升明显。要注意MyBatis-Plus的分页插件用法和PageHelper不太一样它需要配置MybatisPlusInterceptor设置PaginationInnerInterceptor。最后项目可以考虑引入日志框架统一管理操作记录。目前是基于Logback默认配置的可以在logback-spring.xml里按天切割日志文件保留30天内的日志。这个配置不仅方便问题排查在项目答辩时也可以作为“便于运维”的设计亮点。6.3 个人实践总结卡了最久的问题反而是最简单的数据库连接失败查了一个多小时才发现是密码中包含了符号而YAML文件里没有用引号包裹导致配置解析异常。这类细节很琐碎但恰恰是实际开发中每天都会踩的坑。建议你拿到源码后不要急着写代码先花半小时读一下Controller层和Mapper层的对应关系弄清每个URL是怎么映射到方法、再转发到哪个页面的。把这个流程摸透了后面无论改功能还是加页面思路都会清晰很多。我自己就是从这套项目完全跑通开始逐步把SpringBoot相关知识点梳理成一个完整链路再回头看其他系统很多模块设计思路都是相通的。
返回列表