新闻详情

新闻详情

首页 / 资讯中心 / 详情

SpringBoot+Vue3+MyBatis城乡居民医保管理系统全栈开发实战

发布时间:2026/9/30 7:35:14来源:尧图网络
SpringBoot+Vue3+MyBatis城乡居民医保管理系统全栈开发实战
这段时间好几个朋友在问城乡居民医保管理系统这类全栈项目怎么做正好我手头有一套SpringBootVue3MyBatis的完整源码跑过业务把整个开发过程和一些踩坑经验理一理。项目本身不复杂但是麻雀虽小五脏俱全涉及参保人员建档、缴费记录、报销审核、统计报表、权限管理这些典型业务特别适合做毕设、接私活或者想系统入门全栈开发的人拿来自学。前后端分离这种架构选型配合MySQL存储理解透了一整套Web信息管理系统的套路基本就通了。1. 项目背景与整体设计思路1.1 这个系统到底要解决什么问题城乡居民基本医疗保险和我们日常听到的职工医保不太一样。职工医保是单位代扣代缴个人账户和统筹账户分得清楚城乡居民医保则是按年度缴费保障门诊和住院报销覆盖的是没有固定工作单位的城镇居民和农村居民。这个系统就是为这类业务场景做的信息化管理工具核心要管好三件事人、钱、账。具体拆开来看业务方需要做参保登记居民信息要能录入、修改、暂停参保每年缴费期要批量处理缴费记录还要能区分个人缴费档次居民看病后产生报销申请工作人员要审核报销材料计算报销金额最后管理层要看统计报表比如参保率、缴费率、基金支出情况。这些业务点串起来就是一套完整的管理闭环。这个系统最适合谁去看、去学如果你是一个刚接触全栈开发的Java学习者这套代码的技术栈非常主流。如果你是用作毕业设计城乡居民医疗信息管理这个选题既接业务地气又没有特别复杂的行业壁垒答辩时业务逻辑讲得清楚就是加分项。如果是拿来接私活当地社区、乡镇卫生院这类单位对这种小系统的需求是真实存在的。1.2 为什么选SpringBootVue3前后端分离我见过太多老项目还是用JSP虽然能跑但维护起来很遭罪。这个系统之所以采用前后端分离架构理由其实很简单前端和后端可以独立开发、独立部署、独立扩展。后端只提供JSON接口不关心页面长什么样前端通过HTTP请求拿数据渲染交给浏览器。这样做的好处一个是开发效率高前端用Vue的组件化开发改页面样式不动后端逻辑。另一个是部署灵活后端打一个jar包前端构建出的静态文件扔给Nginx就行。最重要的是接口化的后端天然适配多端场景以后要出小程序端、App端后端几乎不用改。Vue3相比Vue2最大的变化就是组合式API。写业务逻辑的时候可以把某个功能相关的响应式数据、计算属性、方法都放在一个setup函数里不像Vue2的选项式API那样把data、methods、computed强行拆开。对于医疗信息这种一个页面要展示大量关联字段的场景组合式API的代码聚合度明显更高。1.3 技术选型的权衡取舍这套系统的核心选型是SpringBoot 2.7.x MyBatis MySQL 8.x Vue3 Element Plus Pinia Vue Router鉴权用JWT。为什么不选MyBatis-Plus说实话我日常写代码是喜欢用MyBatis-Plus的因为它内置了单表CRUD方法开发速度快很多。但如果你是做毕设或者想深入理解MyBatis核心原理建议用原生MyBatis把Mapper接口、XML映射文件、动态SQL这些底层机制亲手写一遍。这套源码就是用原生MyBatis写的搞懂它MyBatis-Plus上手就是半小时的事。为什么不选Spring Cloud清醒一点一个城乡居民医保管理系统用户量级在几千到几万单机部署完全够用。引入微服务那一套Nacos、Feign、网关全上既增加了部署复杂度也偏离了这个项目本身该有的学习重点。一个单体应用把代码写清晰、事务边界处理好、SQL索引优化到位远比空上微服务更具工程价值。数据库选型也很明确MySQL在中小型管理系统里的统治地位至今没被撼动社区资料多、运维经验丰富Navicat或者官方Workbench都能很好管理。本项目用的是MySQL 8.x8.0在窗口函数、JSON类型支持上都比5.7更好用。2. 核心模块拆解与数据库设计2.1 功能模块划分做管理系统的第一步不是写代码而是把模块边界划清楚。我习惯按角色去拆功能因为不同角色看到的菜单和操作权限是完全不一样的。管理员负责系统基础配置比如用户账号管理、角色权限分配、医保政策参数设置报销比例、缴费档次。业务经办员负责参保管理做居民信息的录入、变更、暂停、恢复以及缴费记录登记。审核员看的是报销申请单核对费用明细、计算可报销金额。查询统计人员的话主要看各类报表参保人数变化、缴费进度、报销支出趋势。菜单结构按这个逻辑走就很清晰系统管理、参保管理、缴费管理、报销管理、统计报表。数据库表之间的外键关系也是围绕这个模块树展开的。2.2 数据库表设计要点数据库设计是整个项目中我花时间最多的地方表结构设计不合理后面写SQL全是在还债。这里简单列一下核心表sys_user用户表存登录账号、加密后的密码、真实姓名、角色ID。sys_role角色表用role_code区分admin/operator/auditor/statistician。resident_info参保居民信息表身份证号、姓名、性别、户籍地址、参保状态、缴费档次等。payment_record缴费记录表年度、缴费金额、缴费日期、缴费方式、关联居民ID。claim_record报销申请表关联居民、就诊类型门诊/住院、总费用、报销比例、报销金额、审核状态。claim_detail报销明细表按费用项拆分比如药品费、检查费、治疗费。有几个字段设计上的细节要特别强调。每张表都要有主键我全部用BIGINT自增ID不做业务主键。充电线再好看也不如原装线稳定业务主键一旦后期要调整会牵连所有外键关系。居民表里id_card_no身份证号必须加唯一索引这是业务上的硬约束同一个身份证号只能有一条有效参保记录。医保业务里重复参保是很敏感的问题。金额字段统一用DECIMAL(10,2)绝不用FLOAT或DOUBLE。医疗费用涉及钱浮点数精度丢失那个坑线上出过一次就是事故。时间字段用DATETIMETIMESTAMP虽然省空间但有2038年问题而且受时区影响业务系统里统一用DATETIME加应用层默认值逻辑最清晰。payment_record表里设置了一个UNIQUE KEY uk_year_resident (pay_year, resident_id)联合唯一索引确保同一个人同一年度只能有一条缴费记录。这是从数据库层面防止重复缴费的终极兜底。2.3 关键业务逻辑说明城乡居民医保报销计算是这个系统的业务核心。报销金额不是简单地总费用乘以比例还要看费用科目和起付线。以我这里的实现为例报销规则是门诊报销年度累计起付线50元超过部分按60%报销年度封顶300元。住院报销按医院等级区分比例一级医院报85%二级医院报70%三级医院报55%。这些规则在系统里不是写死在Java代码里的而是放到数据库配置表insurance_policy中管理员可以在页面上修改。这样政策调整了不用改代码重启服务只需要在后台界面改参数即可。做这类政务、医疗系统的经验就是一条业务规则要参数化不要硬编码。审核流程也值得说。报销申请提交后状态是PENDING审核员初审后变为APPROVED或REJECTED对于金额大的报销单还需要复审环节。所以claim_record表里有audit_status和second_audit_status两个字段。流程管理是最容易被初学者忽略的等到业务方说大额报销单要两个人审你才发现表结构设计里没想到这层。3. 后端SpringBootMyBatis落地实现3.1 项目工程结构与分层后端工程结构我习惯这样组织src/main/java/com/example/medical/ ├── common/ -- 统一返回结果、全局异常、常量 ├── config/ -- Spring配置、MyBatis配置、WebMvc配置 ├── controller/ -- 接口层 ├── service/ -- 业务层含Impl ├── mapper/ -- MyBatis的Mapper接口 ├── entity/ -- 数据库实体类 ├── dto/ -- 请求/响应对象 ├── vo/ -- 视图对象用于组合展示数据 └── utils/ -- 工具类JWT、加密、Excel分层的逻辑是Controller只做参数接收和结果包装不写业务Service里放业务规则和事务控制Mapper只做最简单的数据库读写。我以前见过有人把SQL写在Controller里的操作短期看少写几个类等业务复杂起来改一次能掉一层皮。统一返回结果类ResultT很关键。所有接口统一返回{code, message, data}结构前端不用为每个接口单独处理异常格式。code为200表示成功其他为业务错误码。配合全局异常处理器任何未捕获异常都能转成友好提示而不是把堆栈直接甩给前端。3.2 MyBatis使用的几个关键点用原生MyBatis最核心的莫过于XML映射文件。很多初学者只在接口上写Select注解这样对简单查询没问题但涉及动态SQL就很挣扎。我这里的做法是简单查询用注解所有复杂的、多表关联的、需要动态条件的查询一律写XML。拿参保信息分页查询来举例select idselectPage resultTypecom.example.medical.vo.ResidentVO SELECT r.*, u.user_name AS create_user FROM resident_info r LEFT JOIN sys_user u ON r.create_by u.id where if testname ! null and name ! AND r.name LIKE CONCAT(%, #{name}, %) /if if testidCard ! null and idCard ! AND r.id_card_no LIKE CONCAT(%, #{idCard}, %) /if if teststatus ! null AND r.status #{status} /if /where ORDER BY r.create_time DESC /selectwhere标签是MyBatis里最实用的东西它会智能去掉第一个多余的AND保证拼接的SQL语法正确。#{}预编译占位符必须用它能防SQL注入${}那个字符串拼接少用为妙。MyBatis的缓存机制也值得提一句。一级缓存是SqlSession级别同一个会话里重复查询会命中断言二级缓存是Mapper级别但这里有个经典大坑如果表数据经常变更二级缓存没及时失效就会出现脏读数据。这个系统里缴费和报销都是高频写操作我直接选择关闭二级缓存用一级缓存就够用了避免那些奇奇怪怪的数据不一致问题。TypeHandler在医保系统里也有用武之地。比如数据库里status字段存的是Integer类型但Java对象里我想用枚举ResidentStatusEnum就需要自定义TypeHandler做自动转换。写了TypeHandler之后Mapper接口里直接用枚举类型作为参数和返回值代码看着舒服很多。3.3 事务与数据一致性处理医保系统对数据一致性要求极高缴费和报销绝对不能出现钱收了但记录没了这种问题。SpringBoot里事务控制很简单在Service方法上标注Transactional注解就行。但要注意几点默认事务只回滚RuntimeException和Error对于Exception的子类比如IOException不会回滚。如果你代码里catch了异常自己处理了那需要注意业务补偿逻辑。更稳妥的做法是在Transactional注解里明确指定rollbackFor Exception.class。事务失效的经典场景一是同类内部方法调用A方法调B方法B上标了Transactional也不生效因为Spring的代理机制只在外部调用时进场二是方法不是public的。我排查过不少这类问题经验就是事务注解规范性地放在public Service方法上同类内部调用要拆分到不同Bean或者用TransactionTemplate做编程式事务。对于缴费这种需要先查后写的操作我加了一行SELECT ... FOR UPDATE悲观锁Transactional(rollbackFor Exception.class) public void createPayment(PaymentSaveDTO dto) { ResidentInfo resident residentMapper.selectByIdForUpdate(dto.getResidentId()); if (resident null) { throw new BusinessException(参保人员不存在); } // 检查该年度是否已缴费 int count paymentMapper.countByYearAndResident(dto.getPayYear(), dto.getResidentId()); if (count 0) { throw new BusinessException(该居民本年度已有缴费记录); } paymentMapper.insert(...); }selectByIdForUpdate会让这条记录的行锁持有到事务提交两个请求同时提交缴费时后一个必然等待从而避免重复缴费。这种机制虽然简单但在单体应用里相当可靠。4. 前端Vue3实战要点4.1 Vue3工程初始化和目录结构Vue3这一侧我用Vite作为构建工具创建工程时直接用官方脚手架npm create vitelatest medical-web -- --template vueVite比Webpack最大的优势就是快冷启动几乎秒开开发体验提升非常明显。工程创建完我习惯把目录整理成下面这样src/ ├── api/ -- 按模块封装的接口请求 ├── assets/ -- 静态资源 ├── components/ -- 通用组件 ├── router/ -- 路由配置 ├── stores/ -- Pinia状态管理 ├── views/ -- 页面视图 ├── utils/ -- 工具函数request封装等 └── App.vue写接口请求的地方一定要单独放一层不要在组件里直接写axios.get。为了维护和后端联调所有接口按模块放在api目录比如api/resident.js里导出getResidentPage、saveResident之类的方法组件里调用这些封装好的函数接口地址集中管理。axios实例封装是前端最基础也是最重要的工作。我在utils/request.js里统一做了三件事请求拦截器里把JWT token加到请求头响应拦截器里统一处理业务码遇到401跳转登录页遇到后端返回错误码直接弹出消息提示。4.2 接口请求与状态管理状态管理我用的Pinia。Pinia比Vuex轻量太多没有mutations概念直接在store里定义state和action配合组合式API的写法非常顺手。登录后的用户信息和权限数据放在store里管理export const useUserStore defineStore(user, () { const token ref(localStorage.getItem(token) || ) const userInfo ref({}) const roles ref([]) function setToken(newToken) { token.value newToken localStorage.setItem(token, newToken) } function setUserInfo(info) { userInfo.value info } const isLoggedIn computed(() !!token.value) return { token, userInfo, roles, isLoggedIn, setToken, setUserInfo } })这里有个细节token一定要持久化到localStorage否则刷新页面登录态就丢了。当然localStorage有XSS风险这个项目里我在前端做了基础的输入校验和编码安全性够用真正的生产级系统会更倾向用HttpOnly Cookie。4.3 页面组件设计与权限控制表单页面我基于Element Plus开发。以参保信息录入为例主要是一个el-form配合表单校验规则。身份证号校验可以用Element Plus自带的规则但为了更严谨我还写了正则二代身份证是18位最后一位可能是数字或X。权限控制这块前端做了两层的控制。第一层是路由级根据用户角色动态生成可访问的路由菜单管理员能看到系统管理菜单统计人员只能看到报表页面。第二层是操作级在页面里用v-permission自定义指令比如只有审核员角色才能看到审核通过按钮。实现方式其实不复杂。登录接口返回用户角色列表后前端用router.addRoute动态添加匹配的路由表然后根据角色生成侧边栏菜单。需要说明的是前端权限控制只是体验优化真正安全的后端接口一定要做权限校验。只靠前端藏按钮懂技术的人直接调接口照样能操作这种低级失误不能犯。Vue3组件通信也提一句。我偏爱用defineProps加defineEmits页面级共享数据放Pinia。那些props传不下去、emit又传不上来的问题大多来自层级太深解决办法是中间抽一层公共状态进store。5. 常见问题与排查技巧实录5.1 后端常见坑SpringBoot版本不兼容问题。这个问题遇到得多。SpringBoot 3.x要求JDK17以上把Java 8的用户劝退了一大半。如果你本机是JDK8老老实实用SpringBoot 2.7.x配套的MyBatis starter用2.x版本。从Maven中央仓库拉依赖时版本坐标写错会直接导致启动失败控制台报一堆ClassNotFound很多新手在这里卡一整天。接口返回时间格式问题。后端返回LocalDateTime前端展示出现2025-05-01T10:30:00这种带T的格式。解决方式是在application.yml里全局配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8如果字段是LocalDateTime类型光配date-format不够还要在pom里引入jackson-datatype-jsr310依赖并配置JavaTimeModule否则序列化出来的格式还是不对。驼峰命名映射缺失。MySQL里字段是id_card_noJava属性是idCardNo如果MyBatis没开启驼峰映射查出来的属性全是null。配置项非常简单mybatis: configuration: map-underscore-to-camel-case: true这个配置我几乎每次写新项目都会检查一遍。5.2 前端常见坑跨域问题。本地开发前端在8080端口后端在8081axios请求必然跨域。开发环境我在Vite配置文件里设置代理server: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } }生产环境则交给Nginx做反向代理。关于CORS有人在后端加CrossOrigin注解这个开发环境没问题生产环境如果前端和后端不同域需要在Nginx层配合处理只加注解不一定够。动态路由白屏问题。使用router.addRoute动态添加路由后刷新页面发现白屏。原因是路由是在登录后动态加的页面一刷新Pinia里的状态没了动态路由也失效了。解决思路是做一个全局前置守卫在beforeEach里判断store里有用户信息就走正常流程没有就尝试调getUserInfo接口恢复登录态然后再动态添加路由最后next({ ...to, replace: true })重新进入。这个坑我帮人排查过很多次本质是路由注册和状态恢复的顺序问题。5.3 数据库相关问题MySQL 8.x连接报SSL错误。用Connector/J 8.x连MySQL时控制台报The server certificate is not trusted这类SSL错误。解决方案是连接URL上加参数jdbc:mysql://localhost:3306/medical_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8serverTimezone必须指定否则连8.x数据库默认时区不对会报错。字符集也一定要指定UTF-8否则中文乱码后患无穷。大批量缴费数据导入性能。表格批量导入居民缴费记录时一条条插入太慢。我的优化思路是改用MyBatis的批量插入在Mapper XML里用foreach拼接批量SQL一万条数据批量插入比逐条插入快几十倍。注意MySQL单条SQL有max_allowed_packet限制数据量特别大时要分批每批500条左右比较稳。insert idbatchInsert INSERT INTO payment_record (resident_id, pay_year, pay_amount, pay_date, pay_type) VALUES foreach collectionlist itemitem separator, (#{item.residentId}, #{item.payYear}, #{item.payAmount}, #{item.payDate}, #{item.payType}) /foreach /insert慢查询排查。报表统计页面在数据量多了之后明显变慢我用EXPLAIN查看执行计划发现是claim_record表的resident_id字段没建索引全表扫描导致慢查询。后来给所有外键字段补了索引再配合覆盖索引查询报表接口从秒级降到了毫秒级。索引对数据库性能的提升是立竿见影的但是也别过度建索引写多读少的表索引太多反而拖慢写入。5.4 部署与运维经验打包部署我用的是Maven的package命令后端打成可执行jar前端用npm run build生成dist目录。最终部署结构是Nginx托管前端静态文件同时反向代理/api请求到Java服务MySQL单独一台机器或同机部署。有一个细节jar包启动时默认内存使用较大可以在启动命令里限制java -Xms256m -Xmx512m -jar medical-system.jar --spring.profiles.activeprod生产环境的数据库连接、文件路径等配置放到application-prod.yml里用spring.profiles.active切换不要把生产库密码写进仓库这个习惯一定要养成。日志方面用Logback按天滚动保留30天日志。排查线上问题时日志就是最后的救命稻草。我用Slf4j在Service层关键方法里打日志入参、出参、异常信息都记录出了问题能快速定位到具体环节。6. 一点想说的实践经验跑完这套系统我个人最大的感触是全栈项目的坑永远都在你以为已经搞定的地方。后端接口用Postman测试全通过以为万事大吉结果前端一联调就暴露了字段命名不一致、格式不兼容的一堆问题。所以我现在做项目坚持一个流程每个接口开发完后端先自测然后立刻跟前端联调不让问题过夜。顺手把MyBatis打印执行的SQL打开上线排查时能看清每条SQL实际是什么样子的。这个系统后续如果要扩展建议往这几个方向走接入Excel导出功能把参保名单和缴费清单导出成报表这个需求几乎一定会来增加消息通知模块缴费期到了给居民推送提醒用WebSocket或者简单的邮件通知都行把报销审核流程加一个会签功能让多级审核能够并行处理。从技术层面看SpringBootVue3MyBatis这套组合在中小型管理系统中确实能打。学习的时候不要光看视频真正把源码自己跑起来从改一个接口、加一个页面开始慢慢把整个流程走通你对前后端分离、数据库设计、权限控制这些概念的理解会完全上一个台阶。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

医疗影像LoRA微调DeepSeek大模型:完整实战指南 2026/9/30 8:34:20

医疗影像LoRA微调DeepSeek大模型:完整实战指南

简介:《医疗影像分析:基于LoRA技术实现DeepSeek垂直领域微调的5步方案》是一份聚焦DeepSeek模型在医疗影像场景落地应用的PDF文档,面向想将大模型用于垂直领域微调的算法工程师、医学影像研究人员及AI学习者。文档从医疗影像分析与LoRA技术概…

阅读更多 →
Windows 下 Ubuntu 20.04/18.04 三系统安装与引导修复 2026/9/30 8:34:20

Windows 下 Ubuntu 20.04/18.04 三系统安装与引导修复

手里一台 Windows 机器,想同时跑 Ubuntu 20.04 和 18.04,这个需求其实挺常见的。做 ROS1 开发的人最清楚,Noetic 只支持 20.04,Melodic 又绑死在 18.04 上,两个项目的依赖库、Python 版本、OpenCV 版本经常打架。再加上…

阅读更多 →
FunCode桌球课程设计:C++状态机、碰撞反弹与精灵编程实战 2026/9/30 8:34:20

FunCode桌球课程设计:C++状态机、碰撞反弹与精灵编程实战

简介:这是一份基于FunCode引擎的C游戏设计课程设计PDF文档,面向高校C初学者和游戏编程入门者,完整讲解从零实现桌球小游戏的开发思路。文档从FunCode中创建C工程、导入snooker模板讲起,逐步覆盖球与球杆初始化、鼠标坐标同步、运动…

阅读更多 →
【从0到1学习JVM · 02】JVM其实不认识Java,为什么任何语言变成字节码就能跨平台? 2026/9/30 8:34:20

【从0到1学习JVM · 02】JVM其实不认识Java,为什么任何语言变成字节码就能跨平台?

前言 学上一篇我们知道,编译出 .class 是为了把繁重的语法解析提前做完。但字节码如果只为 Java 服务,它的价值其实只发挥了一半。这篇文章我们聊聊 JVM 是怎么跟 Java 语言彻底解绑的,为什么任何一门语言只要能生成字节码就能白嫖跨平台&am…

阅读更多 →
3DGS训练工具Spirula Studio的GPLv3许可与SAM权重法律边界:新手必读的完整合规指南 2026/9/30 8:34:20

3DGS训练工具Spirula Studio的GPLv3许可与SAM权重法律边界:新手必读的完整合规指南

3DGS训练工具Spirula Studio的GPLv3许可与SAM权重法律边界:新手必读的完整合规指南 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/…

阅读更多 →
先序+中序构造二叉树:递归划分区间详解与PTA实战 2026/9/30 8:34:13

先序+中序构造二叉树:递归划分区间详解与PTA实战

PTA上这道“树与二叉树 2 根据先序中序遍历序列构造二叉树”,我当年第一次做的时候,整整卡了一个下午。不是看不懂题目,而是对着样例左右推演都觉得自己懂了,一写代码就Recursion到底、数组越界、Segment Fault。身边同学也几乎都…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉