
1. 先想清楚业务再动手防疫系统的核心角色与权限边界每年做毕业设计都有大量同学选“XX信息管理系统”这类题目而“社区疫情防控信息管理系统”这个题目在Spring Boot方向的毕设里属于典型的高性价比选题——业务场景清晰、功能边界好划定、技术栈覆盖面广既不会因为太复杂做不完也不会因为太简单没亮点。但说实话我看到不少同学拿到题目的第一反应是打开IDEA直接建工程这是最容易走弯路的地方。疫情管理类系统的核心难点从来不是写增删改查而是业务角色的权限边界。社区防疫场景里至少要区分四类人普通居民需要健康打卡、查看公告、上报行程只能操作自己的数据网格员/社区工作人员录入返乡人员信息、审核健康申报、登记核酸结果、管理居家隔离人员街道/社区管理员查看辖区汇总数据、发布公告、管理网格员账号能看全量数据但不能操作居民个人隐私字段系统管理员负责账号管理、角色分配、系统配置从这四类角色出发你会发现整个系统的菜单结构其实就呼之欲出了居民端小程序或者Web页面负责申报和查询管理端按角色区分网格工作台、汇总看板和系统设置。我个人的建议是把权限设计放在数据库设计之前先画出角色-菜单-接口的矩阵清单再开始写代码。不然等到前后端联调阶段再补权限逻辑改动成本很高。还有一点很重要这类系统在答辩时老师一定会追问“你的系统安全性怎么保证”。如果你在设计和实现阶段就把密码加密存储、登录鉴权、操作日志这些考虑进去答辩时能省掉很多麻烦。有些同学做的系统所有接口裸奔登录不登录都能访问这在毕业设计里属于硬伤一开始就要避免。2. 技术选型与工程初始化Spring Boot版本、构建方式与项目结构2.1 框架版本不是越新越好标题里明确写了Spring Boot但具体用哪个版本很多同学是随便选的。以我在实际开发和指导毕设的经验来看Spring Boot 2.7系列是当前毕业设计最稳妥的选择。为什么不建议直接用Spring Boot 3.x原因有三点一是Spring Boot 3强制要求JDK 17以上部分同学电脑上还是JDK 8环境不匹配会带来一堆莫名其妙的启动报错二是Spring Boot 3里很多旧版API被移除或修改网上能搜到的大部分教程和博客都是基于2.x写的照抄容易踩坑三是MyBatis、PageHelper、EasyExcel这一类常用库对Spring Boot 3的官方适配版本更新较慢兼容问题需要自己去排查。不过如果你的机器上已经装好了JDK 17导师也认可用新技术那用Spring Boot 3.2也没问题只是要把日志、切面、WebMvc配置这类地方的差异提前查清楚。我在文章里给的示例代码基于Spring Boot 2.7.x JDK 8这是毕业设计环境中最主流、出错率最低的组合。2.2 构建工具Maven还是Gradle热词里有“springboot gradle项目搭建”的搜索记录说明确实有同学选了Gradle。我的建议很简单导师用什么就用什么自己选的话优先Maven。Maven在毕设场景里有三个不可替代的优势第一学校的课程、教材、网上教程90%以上都是Maven项目遇到问题搜解决方案最方便第二Maven中央仓库的依赖坐标写起来更直观比如dependency标签里写groupId和artifactId大家已经很习惯了第三IDEA对Maven的支持最成熟clean、package、deploy这些操作基本不会出幺蛾子。Gradle的学习曲线明显更陡Groovy或Kotlin DSL的写法、依赖配置方式、与IDEA的配合都需要额外花时间去适应。除非你以后的工作方向是Android开发或者明确对Gradle感兴趣否则在毕业设计阶段没必要给自己加戏。2.3 前后端分离还是单体打包这是毕设开题时最容易纠结的问题。我直接说结论开发阶段用前后端分离Vue开发服务器 Spring Boot上线演示阶段把前端打包放进Spring Boot的static目录里。换句话说你的最终交付物是一个单一的jar包双击就能跑起来。评委老师打开浏览器输入http://localhost:8080就能看到整个系统不用关心你前端怎么启动的。这也是热词里“vue打包放进springboot中”这个搜索背后的真实需求——很多同学开发时用的前后端分离最后部署时不知道该怎么办。具体做法很简单# 在Vue项目根目录执行 npm run build然后把生成的dist目录下的所有文件复制到Spring Boot项目的src/main/resources/static目录下重新打包Spring Boot即可。如果你的前端是通过/api前缀请求后端接口的记得在后端配置里把/api的路径映射和你前端的baseURL保持一致遇到跨域问题用Spring Boot的CorsFilter或者CrossOrigin注解处理。2.4 项目整体结构示范一个清晰的项目结构不仅方便自己开发更是答辩时向老师展示工程素养的直接窗口。这是我推荐的包结构方案com.example.epidemic ├── common # 通用模块返回结果封装、全局异常处理、常量定义 ├── config # 配置类跨域、拦截器注册、MyBatis-Plus配置 ├── controller # 控制层接收请求、参数校验、返回统一格式 ├── service # 业务层核心逻辑事务边界在这层控制 │ └── impl ├── mapper # 数据访问层MyBatis接口或MyBatis-Plus继承BaseMapper ├── entity # 实体类与数据库表一一对应 ├── dto # 传输对象接收前端传入参数避免实体类直接暴露 ├── vo # 视图对象返回给前端的数据封装 ├── security # 认证授权相关配置和工具类 └── task # 定时任务类很多同学写代码喜欢把Controller写得又厚又能干Service层形同虚设。这种做法到后期功能一多就会非常痛苦——接口间互相调用不方便事务控制也没法做测试更是无从下手。Controller只做参数接收和结果返回业务逻辑全部下沉到Service层这是职业开发和学生作业的分水岭。3. 数据库设计从“人、房、事件”三个维度拆解社区防疫数据模型社区疫情防控信息管理系统表面上看起来表不多但如果直接在Navicat里建五六张表就开搞中期大概率重构。我建议按“人、房、事件”三个维度来组织数据模型这也是整个系统最核心的部分建议花至少两三天时间把表结构设计清楚。3.1 核心表的划分人Person居民信息表姓名、身份证号、手机号、户籍地址、现住址、工作单位、健康状态、是否特殊人群老人/孕妇/慢性病患者亲属关系表可选用于处理隔离状态下家属代申报场景房House楼栋信息表小区/社区、楼栋号、单元号、房号房屋与居民绑定表一户多人、人户分离的实际情况事件Event健康申报表每天的体温、健康状况、是否接触疑似/确诊人员、行程轨迹出入登记表进出小区的时间、事由、体温、目的地核酸检测记录表检测批次、检测时间、检测机构、检测结果返乡/外来人员登记表来源地、乘坐交通工具、到达时间、风险等级隔离人员管理表隔离开始/结束时间、隔离类型居家/集中、每日体温记录物资发放表可选加分项口罩、消毒液等物资库存和发放记录除此之外基础模块还需要居民账号表、网格员账号表、公告表、系统日志表。一共12~16张表是一个比较丰满但又不会过于夸张的设计数量。3.2 两张关键表的字段设计说明拿健康申报表举例它的设计直接体现了对这个业务场景的理解程度。最低限度的字段是申报人ID、申报日期、体温、健康状况、是否接触风险人员、申报时间。但真要做得像样我会加上住户地址快照把用户申报时所在的楼栋单元房号冗余存在表里这样网格员筛选当天异常体温时可以直接按地址维度聚合查询不用每次join居民和房屋表。隔离人员管理表上有个细节要记录“纳入隔离日期”和“解除隔离日期”这两个日期之间系统每天生成一条体温记录。建议用一个隔离批次的概念来管理比如同一次重点地区返乡形成的隔离人员属于同一批次网格员可以选择某个批次统一查看所有人的体温上报情况。这个“批次”设计很多学生会忽略但在实际业务里非常常见。3.3 逻辑删除与时间字段所有业务表统一保留deleted字段做逻辑删除保留create_time和update_time。前者解决误删数据找回的问题后者是答辩时展示数据可追溯的必要字段。另外居民身份证号存储要求加密脱敏数据库里存的是加密后的密文查询时统一脱敏展示前三位后两位。这一点在论文的数据安全章节可以写成技术亮点也能应对老师“隐私数据怎么保护”的追问。关于表结构的命名建议统一风格表名用小写下划线字段用snake_case而且不要出现user这种过于通用的表名建议用resident_info、community_user这类具体名字避免不同模块的表名冲突也让论文里的E-R图更清晰。4. 核心功能模块的实现细节登录认证、健康申报、导出报表与定时任务4.1 登录认证从Session到JWT毕设系统的登录认证方案十年前的标配是Session现在的主流是JWT。为什么毕业设计推荐JWT因为前后端分离架构下前端请求头带一个Authorization: Bearer token字段后端通过拦截器校验token整个过程无状态、不需要服务端保存会话信息部署也简单。后端用一个JwtUtil工具类负责生成和解析token核心代码结构大致如下public class JwtUtil { private static final String SECRET_KEY your-secret-key-change-in-production; private static final long EXPIRE_TIME 24 * 60 * 60 * 1000L; public static String generateToken(Integer userId, String role) { return Jwts.builder() .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } }这里有两个实际的坑提醒一下。第一个SECRET_KEY不要写死在一个常量里至少放到application.yml中通过Value读取答辩时在论文里写“通过配置文件外部化密钥避免硬编码风险”是很漂亮的加分点。第二个JWT的过期时间是24小时但这个系统是社区服务类应用居民可能一次性打开页面后持续使用因此前后端要考虑token快过期时刷新的逻辑或者简单一点把过期时间设长一点比如7天。不要设计成用户每访问一个页面都要重新登录那是答辩时最尴尬的演示事故。4.2 拦截器与角色鉴权生成token只是第一步关键是后续每次请求都要校验token并判断角色权限。在Spring Boot里只要实现一个HandlerInterceptor再注册到WebMvcConfigurer里即可public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { try { Claims claims JwtUtil.parseToken(token.substring(7)); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { response.setStatus(401); return false; } } response.setStatus(401); return false; } }至于角色鉴权我建议在Service层做。为什么不用Spring Security因为毕业设计的系统业务复杂度不高引入Spring Security会带来大量的配置适配成本答辩时也很难把整个过滤器链讲清楚。用拦截器 注解自定义RequireRole的方式反而清晰明了老师问起的时候你能讲清楚每一步是怎么拦截的。当然自定义拦截器只适合做白名单控制。那些不需要登录就能访问的接口比如登录接口本身、居民健康打卡提交接口可能来自小程序端、验证码生成接口要把它们配置到拦截器的排除列表里否则演示到一半发现居民端进不去就尴尬了。4.3 健康申报参数校验与幂等设计健康申报是高频接口居民每天要操作一次甚至多次。这里的实现重点不是“保存一条记录”而是避免重复提交。我的方案是给申报表加一个唯一索引联合字段为resident_id和report_date。这样同一居民同一天只能有一条申报记录如果前端重复提交数据库层面就能拦截。Service层再用try...catch捕获DuplicateKeyException给出友好提示“今日已申报请勿重复提交”。参数校验用Spring Boot自带的Validated加NotNull、DecimalMax等注解就好了。体温字段要限制在35~42摄氏度之间超过范围的输入直接拒绝。这一点答辩时同样可以加分说明你考虑到了脏数据对后续统计报表的影响。4.4 定时任务汇总统计与到期提醒热词里有搜索“springboot定时任务”说明这个点是很多同学的共性问题。在防疫系统里定时任务的典型应用场景至少有四个每天晚上自动汇总各社区的健康申报率生成日报表隔离人员隔离期即将结束前自动发送提醒给网格员定期清理过期的临时token和验证码公告定时发布比如夜间更新中高风险地区名单Spring Boot里用Scheduled注解实现非常简单Component public class StatisticsTask { Scheduled(cron 0 0 6 * * ?) // 每天凌晨6点执行 public void generateDailyReport() { // 统计前一天健康申报人数、申报率、异常人员名单 // 生成汇总记录存入日报表 } }这里的Cron表达式写法建议提前学一下答辩时老师特别喜欢问“定时任务怎么保证执行一次”你可以回答项目采用的是单节点部署Scheduled天然保证同一时刻只执行一次如果是多节点部署可以用分布式锁方案但毕设场景用不到。定时任务执行时如果数据量比较大注意耗时要控制在业务容忍范围内日志里输出开始和结束时间便于排查问题。4.5 数据导出用EasyExcel而不是POI“报表导出”几乎是必考功能。网格员要导出一周内体温异常人员名单、每日核酸统计表管理员要导出全社区疫苗接种情况汇总。直接用Apache POI写Excel代码量大格式调整费劲还特别容易内存溢出。用阿里开源的EasyExcel两行核心代码搞定// Service层 ListHealthReportExportVO list healthReportMapper.selectExportData(startTime, endTime); EasyExcel.write(outputStream, HealthReportExportVO.class) .sheet(健康申报数据) .doWrite(list);对应VO类里的每个字段用ExcelProperty(姓名)注解标注导出列名。有几个经验值得记下来导出数据量超过几万行时EasyExcel能边写边释放内存POI可能会直接OOM导出接口要设置response的ContentType为application/vnd.openxmlformats-officedocument.spreadsheetml.sheet不然前端下载下来的文件打开会乱码文件名称用URLEncoder.encode()处理一下中文避免下载时乱码。4.6 MyBatis-Plus带来的效率提升很多同学纠结Mapper层用MyBatis还是MP。我的建议是用MyBatis-Plus理由只有一条它把单表CRUD和分页查询的样板代码省掉了一大批让你把时间花在真正有难度的多表关联和复杂统计上。继承BaseMapperT之后单表操作像selectById、selectPage、updateById都是现成的public interface ResidentMapper extends BaseMapperResidentInfo { }分页插件配置一下就行。真正要手写SQL的是那些多表关联统计场景比如“查询每个楼栋的申报率”这种必须写XML里的自定义SQL。MyBatis-Plus也支持在Mapper接口里声明方法并写自定义SQL互不冲突。5. 真实踩坑记录跨域、CGLIB代理、前端打包联调与版本兼容5.1 跨域问题开发环境的前后端通信前后端分离开发时Vue跑在localhost:5173Vite默认端口后端跑在8080前端请求后端必然会遇到跨域。解决方案是在后端里加一个跨域配置类Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); // 注意是allowOriginPattern不是allowOrigin config.addAllowedHeader(*); config.addAllowedMethod(*); config.setAllowCredentials(true); config.setMaxAge(3600L); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }这里有个非常隐蔽的坑如果配置了setAllowCredentials(true)那么allowedOrigin不能用*必须用具体的域名或者allowedOriginPattern(*)否则浏览器直接拦截请求报The value of the Access-Control-Allow-Origin header ... must not be the wildcard *。这种报错百度能搜到但很多人第一次遇到时完全不知道问题出在哪儿。如果你的系统最后按第一节说的“前端打包进后端”方式部署那跨域问题在生成环境下根本不存在——因为静态页面和后端接口同源了。所以跨域配置只在开发阶段需要部署阶段可以保留但已经没有实际影响。5.2 Spring Boot默认使用CGLIB代理的坑热词里那条“springboot默认使用cglib代理”搜索背后是一个很经典的教学误区。很多同学的印象还停留在“JDK动态代理要求实现接口Spring默认用JDK代理CGLIB代理用于没有接口的类”。但Spring Boot 2.x之后默认代理方式改成了proxyTargetClass true也就是一律使用CGLIB代理。这个改动对日常开发没什么影响但有一个场景特别容易踩雷Service层类的内部方法自调用。比如UserService里有一个方法A调用了同类的另一个方法B如果B方法上有Transactional注解这个事务其实是不会生效的因为B方法的调用走的是this引用而不是代理对象。解决方法是把B方法放到另一个Service里调用或者用AopContext.currentProxy()获取当前代理对象。这个知识点在答辩时经常被问到。如果老师问“事务失效的几种场景”你能答出“同类内部方法调用不走代理导致事务失效”这一条并且说出Spring Boot 2.x默认用CGLIB代理这个技术背景这个问题的回答就很完整了。5.3 MyBatis的XML文件放错目录导致启动报错毕业设计最常见的技术翻车现场MyBatis的Mapper XML文件放在src/main/java目录下或者建在了resources外面结果是启动时报Invalid bound statement (not found)。正确做法XML目录放在src/main/resources/mapper/下然后在application.yml里配置mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.epidemic.entity如果一定要把XML和Mapper接口放同一个包下那就必须在pom.xml里配置resources把src/main/java中的XML文件一并打包这个操作不熟悉反而更容易出错所以还是建议老老实实放resources。5.4 Spring Boot版本与依赖库的兼容矩阵整理一张常用的兼容关系表照着选不会出错组件推荐版本备注JDK1.8或11Spring Boot 2.7最高支持JDK 8~20Spring Boot2.7.18最后一版2.x稳定且教程多MyBatis-Plus3.5.3.x与2.7兼容MySQL5.7或8.05.7更省心8.0要注意驱动类名变更Druid连接池1.2.20监控页面好用答辩加分Hutool5.8.x工具库封装了Excel、加密、ID生成等Knife4j4.x对应OpenAPI 3接口文档可视化演示必备特别注意MySQL 8.0的驱动类名从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.DriverURL还要加serverTimezoneAsia/Shanghai跟时区相关参数。用MySQL 5.7的如果驱动写错也会报错直接统一用8.0的驱动连5.7库最省事。5.5 前端打包后放进Spring Boot的完整步骤沿着前面提到的“vue打包放进springboot中”这个问题我把完整流程写一遍这个步骤实操性强建议收藏在Vue项目根目录执行npm run build生成的产物出现在dist/目录修改Vue的vite.config.js或vue.config.js中的base配置写成/默认值通常就是如果接口前缀是/api后端使用Servlet上下文无所谓但前后端同一域名下时baseURL可以保持/api并用反向代理或后端路径重写解决将dist/目录下所有内容复制到后端项目的src/main/resources/static/后端重新mvn clean package打包运行jar包浏览器访问http://localhost:8080/即可看到前端页面有同学会卡在第3步打包后页面能打开但接口请求全部404。这是因为前端请求后端的路径和实际Controller的映射路径不一致。两种解法一是在Controller统一加上/api前缀二是用Spring Boot的转发配置把前端请求转发到Controller。推荐第一种简单直接。6. 部署上线与答辩准备从打包到演示全流程盘点系统写完之后下一步就是把项目打包上线。这一节的内容不复杂但非常关键我按实际操作顺序来讲。6.1 后端打包命令与部署方式后端打包极其简单但也因此很多人会在细节上翻车mvn clean package -DskipTests这个命令的意思是先清理target目录再重新打包跳过测试。如果Spring Boot项目的pom.xml里忘了加spring-boot-maven-plugin打包出来的jar包是不能直接运行的会报“没有主清单属性”加了插件后才能打出可执行jar包。这个坑每年都有大批人踩。生产环境部署最简单的是直接用java -jar xxx.jar启动。如果想做得正规一点可以配置成系统服务或使用宝塔面板的Docker部署方式。热词里有“宝塔docker部署springboot”宝塔面板确实把部署流程简化了很多在后端项目根目录新建Dockerfile配置数据库容器和Spring Boot容器通过宝塔的可视化界面启动容器设置反向代理和端口映射Docker方式的好处是本地可以做到“一次构建处处运行”避免“在我电脑上明明没问题”这种尴尬。6.2 数据库初始化脚本准备答辩演示时有一个高频事故项目连的数据库没有初始化评委老师要求现场演示结果一进去全是空表。解决方法是准备一份完整的init.sql脚本包含建库语句、建表语句、初始管理员账号和演示数据。演示数据要提前造好10~20个居民、3~5个楼栋、几十条健康申报记录、若干隔离人员记录、几张公告。数据不要全是绿灯数据要准备一些“疑似发热”“待复核”的数据这样演示筛选功能时有真实感。到时候你只需要用Navicat新建连接执行一次脚本系统所有表和数据全部就位。6.3 论文结构怎么组织论文不展开细讲只说和创新点直接相关的三个部分需求分析从角色权限边界、业务流程、数据流三个角度写别只写“本系统实现了XX功能”系统设计重点写数据库设计过程尤其是唯一索引、逻辑删除、冗余字段这些设计决策的动机——为什么这么设计避免什么问题系统实现每章一个功能模块配上核心代码段和界面截图代码不要贴一整段只贴关键20行以内并配文字解释思路6.4 答辩演示的顺序设计答辩演示时间通常只有5~10分钟功能太多反而什么都说不好。我建议按这条主线来用管理员账号登录展示首页看板今日健康申报率、隔离人数、待办提醒切到网格员视角演示“筛选风险人员→导出Excel报表”这条操作链路展示一张定时跑出来的日报表说明定时任务能力打开接口文档页面Knife4j示意整套后端接口都有文档可查这套流程里风险筛选和导出Excel是两个最有亮点的功能一个体现了多表联查的SQL能力一个体现了实际工程中的常用技能。答辩时间短则讲这两点长则补充权限设计细节。6.5 常见答辩提问与参考回答最后整理几个高频答辩问题。这些问题不一定是评委刁难你而是想确认项目真的是你自己做的、关键概念是否理解到位。Q为什么用Spring Boot而不是传统的SSH/SSM框架参考思路Spring Boot简化了配置流程内嵌Tomcat避免外部容器部署问题生态成熟、社区活跃。同时自动装配机制让依赖管理更简洁。如果老师追问自动装配原理可以从SpringBootApplication这个复合注解讲起它由SpringBootConfiguration、EnableAutoConfiguration、ComponentScan三个注解组成其中EnableAutoConfiguration通过META-INF/spring.factories加载自动配置类再配合ConditionalOnClass、ConditionalOnMissingBean这些条件注解实现按需加载——这就是Spring Boot自动装配的核心链路。能把这串讲清楚这个问题的回答就算满分了。Q你的系统如何保证数据一致性参考思路一是数据库层面的唯一索引约束比如每日健康申报只能一条二是Service层的事务管理比如批量导入居民数据和初始化工单在同一个事务里要么全成功要么全回滚三是缓存和数据库的一致性毕设场景如果不引入Redis可以强调用的是数据库直接查询保证数据的强一致性。Q如果系统要上线到多个街道你怎么扩展参考思路架构层面按街道-社区-网格再加一个层级字段即可表结构里预留street_id字段部署层面把静态资源放到CDN后端接口服务多实例水平扩展并在前面加一层负载均衡。毕设有这个意识就够了不用真做分布式。最后的实操体会这个系统我从设计到写完前后大概花了四周白天查资料写代码晚上调页面最崩溃的阶段是前端打包进后端的静态资源路径问题404了整整一下午。后来冷静下来从请求路径逐层排查才发现是前端baseURL和后端拦截器白名单冲突了。这类问题的排查思路其实很简单——先在浏览器开发者工具的Network面板看请求到底发出了没有状态码是多少响应内容是什么然后再往代码层面定位不要瞎猜。还有一个心得想分享给你社区防疫信息管理系统这类题目真正拉开差距的地方不在于功能数量而在于细节设计与业务深度。同样是“健康申报”一个人做的是简单的插入一条记录另一个人会考虑唯一索引防止重复、体温范围校验、异常数据高亮提醒网格员、每天自动汇总生成日报——这几层设计下来系统含金量完全不一样。如果你正在做这个题目我建议你把重心放在数据库设计、权限模型、核心业务链路的打磨上增删改查是最容易的部分也是老师最不关心的部分。做到上述这些点你的毕设基本就在优良线以上了。另外提一句系统里所有涉及个人隐私的数据身份证号、手机号、详细住址在论文和演示时一定要注意脱敏处理这既是职业操守也是做这类系统的基本素养。