ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue疾病防控综合系统:架构设计与权限控制实战解析

SpringBoot+Vue疾病防控综合系统:架构设计与权限控制实战解析 1. 项目概述1.1 核心需求解析做毕设或者课设的同学看到“SpringBootVue 疾病防控综合系统管理平台”这个题目第一反应往往是这不就是又一个增删改查的CRUD项目吗说实话刚开始我也这么想但真正把需求捋清楚之后才发现这个题目比普通的图书管理、学生管理系统要厚重得多——它本质上是一个“业务规则驱动”的管理平台核心并不在页面漂不漂亮而在“数据怎么流转、权限怎么控制、流程怎么闭环”。这个系统的典型应用场景是疾控中心的日常业务管理包括传染病报告卡录入、突发公共卫生事件上报、密切接触者追踪、疫苗接种记录、健康宣教材料发布、应急物资台账管理等。换句话说它要服务的角色至少有三类系统管理员、业务科室人员比如传防科、免疫规划科、普通访客或居民端。每种角色看到的功能菜单、能做的操作完全不一样这就是为什么Vue前端必须做动态路由、SpringBoot后端必须做细粒度权限控制的原因。适合什么人拿来做毕设或课设我觉得有三类同学最合适第一已经学过Java Web基础想用一个完整项目把SpringBoot、MyBatis-Plus、Spring Security、Vue Router、Element Plus这些技术串起来第二准备找Java后端开发实习需要一个能写进简历里的“业务型项目”而不是纯demo第三对公共卫生领域感兴趣想了解疾控业务数据模型长什么样的。这个题目的技术栈非常主流MySQL 5.7以上即可部署成本也低一台普通笔记本就能跑起来。1.2 功能模块与角色划分先给这个系统画一个大致的功能地图。按照疾控业务和通用管理需求我习惯把整个平台拆成六大块传染病报告管理报告卡的新增、审核、订正、查重、统计导出这是核心业务模块。突发公共卫生事件管理事件登记、初步核实、应急响应级别调整、结案归档。疫苗接种与档案管理接种记录、疫苗库存、异常反应登记。重点人群管理密切接触者、疑似病例、健康监测人群的随访记录。健康宣教与公告文章发布、图片上传、前台展示。系统管理用户管理、角色管理、菜单管理、操作日志。对应的角色权限我建议按“三员”模型来做系统管理员拥有全部菜单权限疾控业务人员只有报告管理、事件管理、接种管理的权限且只能看到自己所在科室的数据居民或访客只能看到健康宣教内容不能进入后台。这里有个关键点Vue前端不能只靠路由守卫隐藏菜单后端接口必须做权限校验否则别人直接拼URL就能越权访问这在毕设答辩时是很容易被老师追问的考点。2. 技术方案选型与核心设计思路2.1 为什么是SpringBoot Vue MySQL很多同学会纠结既然做管理系统用SpringBoot Thymeleaf服务端渲染不是更简单吗前后端不分离不用处理跨域不用写那么多接口为什么非要上Vue我的看法是毕设选题除了要“能做出来”还要“能说出来”。SpringBoot Vue前后端分离是目前企业级项目的主流形态用这套方案你的论文里可以写的技术点立刻多出一大截——RESTful API设计、跨域处理、JWT身份认证、前端路由守卫、接口联调工具的使用、Nginx部署等等。答辩的时候老师一听你用了JWT而非传统的Session就愿意多听你说几句。选MySQL而不是Oracle或者PostgreSQL理由很简单MySQL是Java生态里最普遍的组合安装方便资料多遇到问题好搜。另外MySQL 5.7对JSON类型的支持、InnoDB引擎的事务能力对这个项目完全够用。疾病防控系统里有大量“填报-审核-复核”的操作事务控制必须可靠MySQL默认的可重复读隔离级别配合Spring的Transactional注解基本不会出问题。Vue方面建议选Vue 2 Element UI还是Vue 3 Element Plus如果你对Vue 3已经比较熟直接上Vue 3 Element Plus没毛病如果之前只学过Vue 2我建议不要为了赶时髦强行用Vue 3因为Vue 3的响应式原理、组合式API和生态差异会让你在查资料时多花不少时间。说实话毕设评分主要看功能完整性和代码规范版本新旧反而没那么重要。我的习惯是给学生推荐Vue 2 Element UI稳定、坑少、教程多。2.2 数据模型设计的关键决策疾控系统最重要的两张表一张是传染病报告卡tb_disease_report一张是突发公共卫生事件表tb_public_health_event。这两张表设计得好不好直接决定后续统计功能好不好写。传染病报告卡我建议把核心字段拆成三组患者基本信息姓名、性别、出生日期、证件号码、联系电话、现住址。疾病信息疾病名称用字典表存ICD编码或法定传染病病种、发病日期、诊断日期、诊断单位、病例分类疑似/临床诊断/实验室确诊。报告流转信息报告单位、报告医生、填写时间、审核状态、审核意见、审核人、审核时间。这里有一个细节很容易被忽略——查重逻辑。同一个患者短期内重复报告同一病种系统应该能提示。我实现的时候是设定规则同姓名 同证件号码 同病种 发病日期前后30天内就判定为疑似重复报告。这个规则在代码里用一条SQL就能查出来关键是索引要建好否则数据量大了会慢。我在(name, id_card, disease_code, onset_date)上建了联合索引实测性能没问题。突发公共卫生事件表相对简单核心字段是事件名称、事件类型传染病暴发/食品安全/职业中毒等、发生地点、涉及人数、首例病例发病时间、报告时间、初步判断、响应级别IV/III/II/I级、当前状态初报/进程报/结案。要注意事件的处理往往是一个过程所以我另外建了一张事件日志表用于记录每次处置操作的变更轨迹。这个设计在答辩时非常加分因为它体现了“审计追踪”的业务思想。2.3 权限模型从RBAC到数据权限大部分毕设项目的权限都是典型的RBAC用户-角色-菜单我也是这么做的但你可以在RBAC之上再加一层“数据权限”会让系统显得更专业。具体做法是给用户表加一个字段dept_id科室ID业务数据表也带dept_id报告归属科室。查询数据时系统管理员看全部普通业务人员默认只能看本部门的数据如果配置了“跨科室查看”的权限点则可以看到其他科室的数据。这种基于部门的数据隔离在疾控这种多科室业务场景里非常实用而且实现成本不高只需在MyBatis-Plus的查询条件里拼一个dept_id 当前用户部门的条件即可。后端权限校验我用的是Spring Security JWT。登录成功后生成一个Token把用户ID、角色编码、过期时间放进去每次请求通过拦截器解析Token拿到用户信息塞入ThreadLocal方便Service层随时获取当前用户。做接口权限时我在Controller方法上用PreAuthorize(hasAuthority(report:audit))这类注解比在代码里手动判断要优雅得多也方便后期维护。前端的动态路由是下一步。用户登录后后端根据角色返回菜单树前端用router.addRoutes动态挂载。注意Vue Router 3.xVue 2的addRoutes方法在刷新页面时会失效所以我配合了vuex存储菜单和路由并在全局路由守卫里做判断如果当前没有菜单但本地有Token就重新拉取菜单再放行。这个“刷新白屏问题”是前后端分离项目的经典坑写论文时可以作为一个技术难点单独描述。3. 核心功能实操与代码实现3.1 传染病报告卡的新增、审核与查重报告卡新增页是我前端的重头戏。表单字段很多我拆成了三个步骤Element Plus的Steps组件第一步填患者信息第二步填疾病信息第三步填报告单位并确认提交。每步都有独立的表单校验规则比如身份证号18位且校验位合法、出生日期不能晚于发病日期、病种不能为空等。后端接口设计为POST /api/report接收JSON数据。我在Controller层先做参数校验JSR-303注解然后调用Service层的saveReport方法。方法内部逻辑分三步Transactional(rollbackFor Exception.class) public Long saveReport(ReportCreateDTO dto) { // 1. 根据身份证号姓名查重返回重复报告提示 ListDiseaseReport duplicates reportMapper.findDuplicates( dto.getName(), dto.getIdCard(), dto.getDiseaseCode(), dto.getOnsetDate()); if (!duplicates.isEmpty()) { throw new ServiceException(存在疑似重复报告请核实后再提交); } // 2. 保存报告主表 DiseaseReport report new DiseaseReport(); BeanUtils.copyProperties(dto, report); report.setReportCode(generateReportCode()); // 生成报告卡编号如 CR20250615001 report.setStatus(0); // 0-待审核 report.setDeptId(CurrentUser.getDeptId()); report.setCreateBy(CurrentUser.getUserId()); reportMapper.insert(report); // 3. 记录日志 reportLogMapper.insert(new ReportLog(report.getId(), 新增报告, CurrentUser.getUsername())); return report.getId(); }这段代码里有两个细节值得说明一是查重和插入在同一个事务里防止并发情况下两条一模一样的报告同时插入成功二是generateReportCode是我写的一个工具方法采用“业务类型前缀 日期 当日自增序号”的格式比如CR20250615003这样报告编号在业务上有可读性后续打印汇报材料也方便。审核功能相对简单但要注意权限控制。审核接口POST /api/report/audit参数是报告ID和审核结论通过/退回。只有拥有report:audit权限的人才能调用。审核通过后报告状态变为1退回则状态变为2并写入退回原因。这个退回原因要展示给报告填写的用户看所以我在前端报告详情里特意加了一个“审核轨迹”时间线组件把每一步操作的时间、操作人、操作意见都展示出来。3.2 事件管理中的多阶段流转实现突发公共卫生事件的生命周期比报告卡长得多我把它设计成“初报—进程报—结案”三阶段。每一阶段对应一种报告模板也对应一个状态值。事件表本身存的是事件核心信息但事件的文字描述、新增进展我放在了一张“事件进程表”里。这就像写病历一样每次更新不是改原文而是追加一页新记录。这样做的好处是历史轨迹完整领导问“这个事件当初是什么情况中间改了什么什么时候决定升级响应级别”直接按时间排序查进程表就能还原全过程。实现上事件登记接口POST /api/event/create创建事件并生成第一条进程记录事件进展上报接口POST /api/event/progress插入新的进程记录并更新事件表的update_time。如果事件涉及响应级别变更我还要在进程记录里标记“级别变更”类型并且系统自动给相关角色发一条站内通知在通知表插入一条记录即可。这里有个事务边界的提醒创建事件和插入进程记录必须在同一个事务里否则会出现事件主表有了但进程表为空的情况。我用Transactional(rollbackFor Exception.class)强制任何异常都回滚防的就是这种半截数据。3.3 疫苗接种档案与库存预警疫苗接种模块可以考虑做成两个子功能个人接种档案和疫苗库存管理。个人接种档案表tb_vaccination_record的核心字段是接种人ID、疫苗名称、剂次、接种日期、接种机构、接种部位、批号、异常反应标记。为了兼容儿童和成人我在疫苗名称旁边加了一个vaccine_type字典一类疫苗和二类疫苗分开统计。统计报表可以用简单的SQL按疫苗名称分组计数再配合后端聚合接口返回给前端前端用ECharts画柱状图或者饼图。库存管理我设计得比较朴素就一张表疫苗名称、批号、入库数量、出库数量、当前库存、有效期。每次出库扣减库存时我会先检查当前库存是否充足不足则抛出“库存不足”异常。另外我在后端写了个定时任务每天凌晨扫描一次疫苗有效期如果发现某批疫苗距离有效期不足30天就生成一条预警记录推送负责人。SpringBoot里用Scheduled(cron 0 0 1 * * ?)就能实现非常简单。3.4 健康宣教模块文章发布与图片处理健康宣教这个模块给前端访客看的功能相对独立。我在后端做了两个接口一个给管理端维护文章新增、编辑、上架下架一个给门户端拉取已发布的文章列表。文章内容我用富文本编辑器wangEditor后端存储HTML字符串。图片上传单独写了一个接口POST /api/upload/image用MultipartFile接收文件保存到服务器指定目录返回图片的访问URL。保存路径我建议放在项目目录外的绝对路径比如/home/health/images/然后用配置类映射为静态资源路径访问而不是塞进SpringBoot的static目录。原因有两个一是项目重新部署打包时不会丢二是避免上传文件被当成classpath资源处理导致各种路径问题。上传时我会校验文件类型只允许jpg、png、gif和大小不超过5MB并且生成一个新的UUID文件名防止重名和路径穿越攻击。4. 前端工程化与联调实战4.1 Vue项目搭建与路由配置前端我使用Vue CLI创建项目目录结构参考企业级规范src/ ├─ api/ # 接口请求封装 │ ├─ report.js │ ├─ event.js │ ├─ user.js │ └─ common.js ├─ assets/ ├─ components/ # 公共组件 ├─ layout/ # 后台布局组件 ├─ router/ # 路由配置 ├─ store/ # Vuex状态管理 ├─ views/ # 页面组件 │ ├─ report/ │ ├─ event/ │ ├─ vaccine/ │ └─ system/ └─ utils/ ├─ auth.js # token存取 ├─ request.js # axios封装 └─ validate.js # 自定义校验规则axios封装要注意在请求拦截器里添加Token头在响应拦截器里统一处理错误码。我是这么设计的// utils/request.js import axios from axios import { getToken, removeToken } from /utils/auth import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token getToken() if (token) { config.headers[Authorization] Bearer token } return config }, error Promise.reject(error)) service.interceptors.response.use(response { const res response.data if (res.code 401) { // token失效清除本地信息并跳转登录 removeToken() router.push(/login) return Promise.reject(new Error(登录状态已过期)) } return res }, error { return Promise.reject(error) })这里有一个容易踩的坑如果你的前端项目和后端接口不在同一个域名端口就存在跨域问题。方案很多我推荐最简单的——在Vue项目的vue.config.js里配置devServer代理把/api前缀的请求转发到localhost:8080后端这样开发环境下浏览器看到的都是同源请求彻底避免跨域。生产环境部署时再用Nginx做同样的代理。4.2 动态菜单与权限按钮控制动态菜单的实现是前端最值得展示技术含量的点。我路由表分成两部分一部分是静态路由包括login、404页面另一部分是动态路由表映射比如reportManage、eventManage等它们的component是提前import进来的注意不能用字符串对应组件需要建立映射关系。登录后后端返回当前用户的菜单列表前端拿到后做两件事用router.addRoutes挂载动态路由。把菜单树存到Vuex里layout页面通过侧边栏组件遍历菜单树渲染。按钮级权限我用自定义指令v-permission实现。比如审核按钮只有拥有report:audit权限的用户才显示// main.js 注册指令 Vue.directive(permission, { inserted(el, binding) { const value binding.value const roles store.getters.buttons if (value !roles.includes(value)) { el.parentNode el.parentNode.removeChild(el) } } })el-button v-permissionreport:audit typeprimary审核/el-button这样写的好处是前端隐藏了越权按钮但后端接口权限校验依然保留前后端双层防御答辩时老师最吃这一套。4.3 前后端联调与接口文档管理联调阶段我建议使用Apifox或Postman。Apifox比较适合个人项目它一个工具就能管理接口文档、调试、mock数据。先把所有接口定义好前端开发时如果后端还没写完可以直接用mock数据先行渲染页面互相不阻塞。联调过程中最常遇到的问题就是字段名不统一。我踩过一次后端的createTime前端写成了create_time查了半天才发现是驼峰和下划线的差异。解决方法是统一规范后端实体类字段一律驼峰命名前端JS变量也尽量和它保持一致。如果后端数据库字段是下划线风格可以在MyBatis配置里开启mapUnderscoreToCamelCase让MyBatis-Plus自动映射省去很多xml里写resultMap的功夫。5. 部署上线与常见问题排查5.1 本地打包与环境准备后端我用Maven管理依赖。项目根目录执行:mvn clean package -DskipTests打包成功后在target目录下得到jar包命令行直接:java -jar disease-prevention-0.0.1.jar如果你在IDE里运行记得把application.yml里的数据库连接改成你自己的MySQL账号密码。MySQL这边我建议你在本机装一个MySQL 5.7或者8.0用Navicat或命令行执行项目里自带的sql脚本先建库CREATE DATABASE IF NOT EXISTS disease_prevention DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;然后依次导入表结构脚本。注意utf8mb4一定要用有的同学用了utf8结果存emoji或者特殊符号时报错浪费很多时间。前端打包更简单npm run build编译完的dist目录就是静态资源。本地调试前后端分离时我习惯用Nginx同时托管前端静态资源和反向代理后端接口server { listen 80; server_name localhost; root /path/to/dist; index index.html; location /api/ { proxy_pass http://localhost:8080/api/; } }这样浏览器里访问80端口遇到/api/的请求就自动转发到SpringBoot的8080端口非常干净。5.2 高频报错速查表我把毕设答辩期间同学们问得最多的问题整理成一张速查表基本都是自己在调试中踩过的报错现象原因解决办法启动报Failed to configure a DataSourceapplication.yml数据库配置错误检查URL、用户名、密码确认MySQL服务已启动接口请求返回401Token未携带或已过期登录后检查请求头是否带了Authorization前端登录后刷新白屏动态路由未持久化在Vuex中保存菜单路由守卫里重新拉取菜单跨域请求失败前后端端口不一致开发环境配置devServer代理生产配置Nginx反向代理上传文件报413Nginx上传大小限制在nginx.conf中添加client_max_body_size 10m读取日期字段为null前端传的日期格式不支持统一使用yyyy-MM-dd格式后端配置全局Jackson格式化数据权限失效能看到其他部门数据查询条件未拼接dept_id在MyBatis-Plus的QueryWrapper上加条件并写单元测试验证查重SQL执行慢联合索引缺失在(name, id_card, disease_code, onset_date)上建索引打包后jar运行找不到模板前端dist未放入后台或未配置建议前后端分离部署或把dist复制到resourses/static下5.3 经验心得如何让这个项目在答辩/面试中加分最后说几句真心话。这个项目做完之后你不光要会跑还要会讲。我辅导过不少学生功能都做完了答辩时却说不出设计思路老师问“为什么这样设计”只能回答“跟着教程做的”非常吃亏。建议你从三个角度准备第一讲清楚业务闭环。从报告填写、审核、订正、查重到统计分析再到归档整个链路是怎么走通的。你不需要背代码只需要画一个流程图说明每一步的状态变化和触发条件。第二讲清楚权限控制。前端动态路由、按钮权限、后端接口权限注解、数据权限隔离层层递进。这是面试官最感兴趣的部分。第三讲清楚优化点。比如查重索引、事务边界、上传文件处理、定时任务。哪怕你只是在自己项目里尝试过也足以证明你有问题意识和解决问题能力。还有一点我建议你把项目完整跑起来录一段演示视频再写一份README把启动步骤、账号密码、功能截图都放进去。这不仅是给老师看也是给你自己留一份“作品集”。找实习的时候把GitHub仓库地址贴在简历上面试官点进去就能看到这个项目的完整代码和文档比空口说“我做过一个毕业设计”要有说服力得多。这个系统如果你愿意还可以继续扩展方向比如接入ECharts做大屏疫情态势展示或者用WebSocket做一个站内消息实时推送都能让项目的技术层次更高一些。不过那是后话。先把当前这套跑通、跑明白你就已经领先大多数人了。
返回列表