1. 项目定位这套系统到底解决什么问题总有人拿着“SpringBootVue 高校疫情防控web系统管理平台源码【适合毕设/课设/学习】JavaMySQL”这个标题来问我它到底能不能直接拿来当毕业设计、能不能跑通、要不要改代码。先说我的结论这套系统选型非常典型技术链路足够完整业务逻辑也贴近实际场景是一个很适合作为毕业设计或课程设计主体的项目。它本质上做了一件事把校园里日常健康信息采集、异常上报、出行返校申请审批、公告通知、数据统计这些工作从线下表格搬到了一个前后端分离的 Web 平台上。这类系统大的功能范围大家都懂但真正决定毕设含金量的是细节有没有闭环。比如学生今天上报过一次之后能不能重复提交辅导员驳回申请之后学生能不能再次申请导出的 Excel 能不能直接交到学院这些细节往深了做就是一套完整的管理系统往浅了做就是一眼看穿的 CRUD 大杂烩。下面我按自己实际敲代码和维护这类项目的经验把从设计到部署的完整链路拆开讲一遍包括每张表为什么要这样建、每个接口为什么要这样写、前端路由权限怎么控制、答辩会被问到哪些问题。对刚准备动手做毕设的同学来说这篇基本可以当一份“项目说明书”来读。1.1 从毕设选题角度理解需求本质把标题拆开看关键信息是“SpringBoot Vue 高校疫情防控 web 系统管理平台”。SpringBoot 和 Vue 说明这是前后端分离架构Java 后端 Node 前端MySQL 说明数据持久化方案高校疫情防控是业务场景web 系统管理平台说明这是一个偏管理端的后台系统不是普通的信息展示网站。毕设导师通常不会要求你做一个改变行业的产品而是看你能不能把需求转化成功能模块、把功能模块转化成数据库设计、再通过接口把前后端串起来。这类管理平台正好能把一个完整项目的所有环节都覆盖到后端有登录认证、CRUD、事务、条件查询、导出前端有页面跳转、表单校验、权限控制、图表展示。而且业务场景真实答辩的时候讲“为什么需要这个功能”非常好讲不需要凭空编需求。从工作量角度看管理类平台很适合个人独立完成。它不像推荐系统那样依赖数据和算法效果也不像 APP 开发那样要处理大量交互和推送逻辑。只要把角色权限和几个核心流程理顺剩下的模块大多是套路化的增删改查。这也是历年来管理平台一直是毕设主流选题的根本原因——需求清晰、工作量可控、技术点密集。1.2 角色与权限模型怎么定高校校园里使用这套系统的人大致可以分成三类学生、辅导员、系统管理员。有的项目还会拆出“校医院人员”或“院系管理员”但核心脉络是一样的。角色定得太多反而会让权限代码变得复杂我建议按最贴近实际的三角色来做如果后面想加角色表结构完全支持扩展。角色核心业务典型操作学生日常健康信息上报、出行/返校申请、查看公告与个人记录提交表单、查看审批结果、撤销待审申请辅导员管理所带班级的学生数据、审批申请、发布班级通知查看班级上报情况、处理待审批事项、导出 Excel管理员系统配置、全校数据统计、用户管理、发布全局公告账号启用禁用、数据导出、图表统计查看权限控制的实现思路是“RBAC基于角色的访问控制”也就是先给用户分配角色再给角色分配菜单或操作权限。毕设阶段不需要做太细的操作权限表把“每个角色能进哪些页面、能调哪些接口”控制住就够了。前端做菜单和路由的隐藏后端在拦截器里校验接口权限双管齐下才能真正挡住越权访问。1.3 核心业务流程先在心里跑一遍拿到项目不要急着写代码先在纸上把流程捋一遍。以一个学生的一天为例早上打开系统进入健康信息上报页面填写体温、当前所在地、身体状况、是否接触异常人员等信息提交。后端先判断该学生今天是否已经上报过如果重复提交就直接拦截。数据入库后如果体温异常或勾选了异常接触系统自动给该学生的辅导员生成一条待办提醒。辅导员登录后在待办列表里看到“某同学 3 月 20 日体温 37.8℃已标记异常”点进去查看详情标记处理结果。另一个高频流程是出入校申请。学生提交“返校申请”填写预计返回时间、来源地、交通工具、健康信息等。辅导员收到申请后审核通过或驳回。如果学生提交完后发现填错了可以在审核前撤销申请。管理员在后台能看到全校的申请通过率和每日异常数量并导出发给学院。这两条主线跑通之后剩下的公告管理、用户管理、个人信息维护都是往框架里填内容。这也是我写这类项目一贯的做法先把核心流程的数据结构和状态设计清楚再让通用模块去“填空”。2. 技术选型为什么是 SpringBoot Vue MySQL这个组合今天看起来是“标配”但在毕设阶段确实是最稳的组合。SpringBoot 让后端搭建变得极其轻量内嵌 Tomcat不用为部署专门配置外部容器Vue 配合组件库能让页面快速成型MySQL 普及度高遇到问题随便一搜就有答案。对需要快速出活、还要在答辩时把技术讲清楚的学生来说选这套技术栈的收益最高。2.1 后端技术栈与版本选择我的建议是一个相对保守的版本组合因为这个项目是拿来“交作业”和“学习”的不是冲新技术去的。新版本框架不是不能用但网上能搜到的坑和解决方案往往滞后毕设阶段时间宝贵没必要替框架踩坑。JDK 1.8虽然是老版本但绝大多数高校实验环境和答辩机器都兼容部署时不用额外折腾。Spring Boot 2.7.x稳定且资料多。Spring Boot 3 要求 JDK 17如果你不想在答辩前被环境问题卡住建议用 2.7。MyBatis-Plus 3.5.x单表 CRUD 基本不用写 SQL内置 LambdaQueryWrapper 也很好用。相比原生 MyBatis 能省掉大量 XML 配置。MySQL 8.0主流版本支持 utf8mb4 编码和窗口函数教学案例多。JWTjjwt 0.9.1做前后端分离的登录认证无状态、不用在服务端保存 Session。代码量不大原理也容易讲清楚。Hutool 5.8.x一个 Java 工具包封装了日期、加密、POI 等常用操作写导出和日期处理时很方便。EasyExcel导出 Excel 用相比直接用 Apache POI 写原生的 Workbook内存占用小很多代码也更简洁。2.2 前端技术栈与组件库选择前端我用的是 Vue 2.6 Element UI 2.15 Axios Vue Router 3 Vuex 3 ECharts。有人会问为什么不直接用 Vue 3 Element Plus。这里有一个很现实的原因大部分毕设参考资料、源码讲解、老师答疑都是基于 Vue 2 的Element UI 的中文文档和社区讨论也最多。如果你对前端不算特别熟用 Vue 2 踩坑成本会低很多。Vue 的工程化结构其实很固定src 下分 views页面、router路由、store状态管理、api接口封装、utils工具类。页面组件负责展示API 模块统一封装 Axios 请求路由负责页面跳转和权限拦截Vuex 负责保存登录用户信息和菜单权限。这套结构既清晰又容易解释答辩问“前端怎么管理状态”的时候能讲得出来。图表部分用 ECharts 做两个核心页面就够了一个用折线图展示近七天上报趋势一个用饼图展示健康数据占比。2.3 本地环境准备与工程结构规划环境准备按顺序做安装 JDK 1.8 并配好 JAVA_HOME安装 Maven 3.8 并配置本地仓库安装 MySQL 8.0 并设置好 root 密码安装 Node.js 14 或 16前端包管理器用 npm。这些都装好后目录结构建议分成两个独立工程一个是后端接口工程一个是前端工程最后通过 HTTP 接口联调这是前后端分离的标准做法。campus-health-server # 后端工程 ├── src/main/java/com/campus │ ├── controller # 接口层 │ ├── service # 业务层 │ ├── mapper # 数据访问层 │ ├── entity # 数据库实体类 │ ├── config # 配置类 │ ├── interceptor # JWT 拦截器 │ ├── utils # 工具类 │ └── common # 统一返回结果、异常处理 └── src/main/resources ├── application.yml └── mapper # MyBatis XML 文件如果需要 campus-health-web # 前端工程 ├── src │ ├── api # axios 接口封装 │ ├── assets # 静态资源 │ ├── router # 路由配置 │ ├── store # vuex │ ├── views # 页面组件 │ └── utils # 请求工具封装 └── package.json后端代码分层为 Controller、Service、Mapper 三层这是目前 Java 后端最主流的写法。Controller 只接收参数和返回结果不写业务逻辑Service 处理业务规则Mapper 负责和数据库打交道。这样做的好处是接口、逻辑、数据分离出问题的时候能快速定位。3. 数据库设计先想清楚表和状态数据库是整个项目的底座表设计得好不好直接决定后面代码是越写越顺还是越写越难受。我习惯在设计阶段先回答三个问题系统里有多少个实体实体之间是什么关系哪些字段是有状态的流转3.1 数据表整体布局这套系统核心就四张业务表加一张操作日志表。不建议一开始就把表分得很细比如把异常记录、审批记录、消息记录全部单独建表因为表越多联表查询越复杂答辩时反而容易把自己绕晕。下面这个表结构可以覆盖绝大部分功能需求又不会显得刻意堆量表名用途关键字段sys_user系统用户表id、username、password、real_name、role、college、class_name、phone、statushealth_report健康信息上报表id、user_id、report_date、temperature、symptom、location、contact_status、statusaccess_apply出行/返校申请表id、user_id、apply_type、start_time、end_time、destination、reason、status、approve_user_id、approve_remarksys_notice公告通知表id、title、content、notice_type、publish_user_id、publish_time、statussys_log操作日志表id、username、operation、ip、create_time所有表我都建议带上三个公共字段create_time、update_time、deleted。create_time 和 update_time 用来记账和排序deleted 是逻辑删除标记0 表示正常1 表示已删除。逻辑删除的意义在于管理系统的数据是有审计价值的一旦物理删除就彻底没了误操作也救不回来。MyBatis-Plus 对逻辑删除有原生支持只要在实体字段上加TableLogic注解删除操作会自动变成更新操作。3.2 用户表与健康上报表字段详解用户表里 role 字段用 int 类型1 表示管理员2 表示辅导员3 表示学生。有人喜欢用字符串“admin”“teacher”这种也能跑但 int 类型在比较和扩展时更省事。password 字段长度至少 64因为存的是 BCrypt 加密后的字符串长度不止 32 位。健康上报表是整个系统里数据量增长最快的表设计上要特别注意两个点。第一每天每个学生只能有一条上报记录所以需要建立UNIQUE KEY uk_user_report (user_id, report_date)。如果没有这个约束就得在代码里先用 select 查一遍再 insert并发高一点就可能出现重复数据。第二temperature 字段用DECIMAL(3,1)也就是三位有效数字一位小数既能精确存储 36.5 这样的体温又不会出现浮点数精度问题。symptom 字段可以设计成字符串用逗号分隔的代码来标记比如“0”表示无不适“1”表示发热“2”表示咳嗽“3”表示乏力。也可以单纯存一段文本描述。更规范的做法是再加一张症状字典表但毕设阶段没有必要。contact_status 用来标记是否接触过异常人员0 否、1 是字段用 tinyint。3.3 审批表与公告表字段设计access_apply 表里 apply_type 区分三种申请类型0 表示临时外出1 表示离校2 表示返校。start_time 和 end_time 是申请的开始、结束时间用 datetime 类型。status 字段是整个审批流程的核心我用 int 表示状态机的当前节点。公告表 sys_notice 的核心是 notice_type 字段0 表示全局公告1 表示某个辅导员给对应班级发布的通知。同一条数据通过发布者身份来判断可见范围比强行拆成两张表简单。为了实现公告的“已读状态”有的同学会再加一张 user_notice_read 关联表就是公告 id 和用户 id 一一对应。这个可以作为一个加分项提一下但我不建议新手做因为一旦公告发了很多这张关联表的数据量会非常大。3.4 状态流转设计状态机审批流程写得好不好关键就是状态设计。我给 access_apply 设计了四种状态并用一张状态转换表约束所有合法操作当前状态操作目标状态0 待审批学生撤销3 已撤销0 待审批辅导员通过1 已通过0 待审批辅导员驳回2 已驳回1 已通过学生确认返校/销假4 已完成可选2 已驳回学生重新提交生成新的申请记录有个细节值得强调被驳回之后不要直接修改原记录的状态回到待审批而是让学生重新提交一条新记录。这么做有两个好处第一能保留每次申请的历史痕迹第二避免“自己把自己改回待审”这种绕过审核的漏洞。如果一个状态机允许从已驳回直接跳回待审核就是在告诉用户你可以无限次修改申请内容去试探审批人的底线。这在实际业务里是大忌。4. 后端核心实现登录、上报、审批后端代码是整个系统的核心我挑四个最有技术点、也是答辩时最常被抠细节的地方来讲。4.1 登录鉴权与拦截器实现前后端分离之后登录认证不能再用传统的 HttpSession因为 Vue 项目可能部署在另一个端口甚至另一台机器上Session 天然就不方便。JWT 的思路是用户登录成功后后端生成一个包含用户身份信息的签名 Token返回给前端。前端每次请求都把这个 Token 放在 HTTP 请求头里后端拦截器校验 Token 合法之后才把请求放行到真正的接口。JWT 工具类核心代码大致长这样public class JwtUtil { private static final String SECRET campus-health-secret-key; private static final long EXPIRE 24 * 60 * 60 * 1000L; public static String createToken(Long userId, Integer role, String username) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .claim(username, username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() EXPIRE)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } }拦截器里除了校验 Token还要处理跨域预检请求。浏览器在发送带自定义头 Authorization 的请求之前会先发一个 OPTIONS 请求如果拦截器直接拦截了 OPTIONS前端就永远拿不到数据。正确的写法是一进来就先判断请求方法是不是 OPTIONS如果是就立刻放行。Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token null || token.isEmpty()) { response.setStatus(401); return false; } try { Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, Long.valueOf(claims.getSubject())); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { response.setStatus(401); return false; } }密码存储不要用明文也不要自己写个 MD5 就完事。用 BCrypt 加密每次校验时用 BCryptPasswordEncoder 的 matches 方法比对。明文密码一旦数据库泄露所有账号就全暴露了MD5 可以通过彩虹表直接逆向。BCrypt 自带随机盐同一个密码加密后每次结果都不一样这才是密码存储的底线方案。4.2 健康上报接口与重复提交处理健康上报的核心业务规则是“每天每人只能上报一次但允许补报昨天的数据”。这个规则听起来简单写的时候却很容易翻车。很多人只判断“今天是否已经上报”然后直接插入没考虑并发情况。两个请求同时进来都判断出“今日未上报”然后同时执行插入数据库里就出现两条今天的数据。我的方案是双保险数据库加唯一约束代码里也做前置判断。接口收到上报请求后先查今天有没有记录有就直接返回“今日已上报请勿重复提交”没有就执行插入一旦唯一索引冲突就抛异常再由全局异常处理器转成友好提示。代码大致是这样PostMapping(/report) public Result saveReport(RequestBody HealthReport report) { Long userId (Long) request.getAttribute(userId); LocalDate today LocalDate.now(); Long count healthReportMapper.selectCount( new LambdaQueryWrapperHealthReport() .eq(HealthReport::getUserId, userId) .eq(HealthReport::getReportDate, today) ); if (count 0) { return Result.error(今日已经上报不能重复提交); } report.setUserId(userId); report.setReportDate(today); if (report.getTemperature() ! null report.getTemperature().doubleValue() 37.3) { report.setStatus(1); // 标记为异常 } healthReportMapper.insert(report); return Result.success(上报成功); }这里 status 字段的语义要定清楚0 表示正常1 表示异常。异常判定不能只靠体温一个维度接触史或者身体不适症状也要能触发异常标记。业务规则可以写在 service 层统一处理尽量别把 if 散落在 controller 里。4.3 审批流程与状态守卫写法审批接口是校验“状态守卫”最好的地方。所谓状态守卫就是在更新状态时必须带上“当前状态等于某个值”的条件防止一个已被处理的申请被二次处理。学生提交申请后辅导员点击“通过”按钮后端不能直接写死 update set status 1 where id xxx。万一这个申请在辅导员点之前已经被其他人处理了呢或者学生自己在待审批状态下撤销了呢正确的做法是拼上当前状态条件Integer update accessApplyMapper.update(null, new LambdaUpdateWrapperAccessApply() .eq(AccessApply::getId, id) .eq(AccessApply::getStatus, 0) .set(AccessApply::getStatus, 1) .set(AccessApply::getApproveUserId, operatorId) .set(AccessApply::getApproveTime, LocalDateTime.now()) ); if (update 0) { throw new BusinessException(申请状态已发生变化请刷新后再操作); }update 返回的受影响行数如果是 0说明条件不满足也就是这个申请的当前状态已经不是 0 了。这时候必须报错而不是假装成功。这种“乐观锁”的思路在并发场景下非常重要它不需要引入额外依赖只靠一条 SQL 就解决了重复操作问题。答辩时把这个点讲出来很容易体现你对并发控制的理解。4.4 Excel 导出与统计查询管理后台免不了要导出数据常见做法是 Apache POI 手动建 workbook但 POI 在数据量大时会占用大量内存。用 EasyExcel 可以让导出行数达到十万级也不至于 OOM。EasyExcel 的核心写法是先定义一个要导出的字段模型加 ExcelProperty 注解指定列名再调用 write 方法即可GetMapping(/export) public void export(HttpServletResponse response) throws IOException { ListHealthReportVO list healthReportService.listAbnormalReports(); response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setCharacterEncoding(utf-8); String fileName URLEncoder.encode(健康上报记录, UTF-8); response.setHeader(Content-disposition, attachment;filename fileName .xlsx); EasyExcel.write(response.getOutputStream(), HealthReportVO.class) .sheet(上报记录) .doWrite(list); }统计查询方面常见的聚合需求有每日上报人数、异常人数占比、各学院上报率、近一周体温异常趋势。这些用 MyBatis-Plus 的 QueryWrapper 加 selectCount 和 groupBy 就能实现。最实用的是写一个 SQL按月统计每天的上报总数和异常数SELECT report_date, COUNT(*) AS total_count, SUM(CASE WHEN status 1 THEN 1 ELSE 0 END) AS abnormal_count FROM health_report WHERE report_date #{startDate} GROUP BY report_date ORDER BY report_date5. 前端页面落地路由权限、报表与表单后端做好之后前端页面才能看出系统有没有“落地”。毕设答辩时老师先看的是界面能不能点、流程能不能走通然后才会问技术细节。所以前端部分要保证两条核心链路顺畅登录后不同角色看到的菜单不一样表单提交和数据展示不出低级错误。5.1 权限路由与按钮级控制前端权限控制分成两步。第一步是“能不能进这个页面”用路由前置守卫实现。Vue Router 在每次路由跳转之前会执行 beforeEach里面可以做三件事判断有没有 Token没有就跳到登录页有 Token 再判断目标路由的 meta.roles 里是否包含当前用户角色不包含就跳 403最后判断页面是否需要重新登录。router.beforeEach((to, from, next) { const token localStorage.getItem(token) const role Number(localStorage.getItem(role)) if (to.path /login) { next() } else if (!token) { next(/login) } else if (to.meta.roles !to.meta.roles.includes(role)) { next(/403) } else { next() } })第二步是“页面上能不能看到这个按钮”也就是按钮级控制。比如学生页面上的“审核”按钮不应该出现辅导员页面上的“系统用户管理”菜单不应该出现。最简单的方式是在组件里判断当前用户角色再用 v-if 控制显隐。菜单的动态渲染也是同样思路根据角色从后端接口拿菜单列表或者在前端路由表里写死角色标识然后过滤出来后渲染。这里要强调一句前端控制只是提升体验不是安全边界。真正防止越权还是得靠后端拦截器在每个接口上校验角色前端隐藏按钮顶多算“眼不见为净”。5.2 可视化大屏与统计报表一个管理平台如果全是表格看起来会特别单调。加一两个 ECharts 图表页面整个项目的观感会明显上一个档次答辩也更有亮点。我建议做两个图表一个是健康上报概况页展示“今日上报人数”“今日异常人数”“累计申请数量”等卡片下面放一个近一周上报趋势折线图和一个异常原因分布饼图。另一个是学院上报率排行用横向柱状图展示。前端接 ECharts 时注意一个细节接口返回的时间字段如果是字符串直接塞给折线图没问题如果是 Long 类型的时间戳需要先格式化。后端统计接口返回的数据结构建议统一成 ListMapString, Object比如[{date: 2024-03-01, total: 1200, abnormal: 15}]前端拿到后直接拆成 xAxis 数据和 series 数据代码会很清爽。5.3 表单校验与交互细节学生每天都要填上报表单表单体验好不好会直接影响系统观感。Element UI 的表单校验是声明式的在 data 里定义 rules 对象给 el-form-item 绑定 prop。做三个基本校验体温必填且范围在 35.0 到 42.0 之间当前所在地必填身体状况至少选择一项。日期组件处理有个坑Element UI 的 el-date-picker 默认返回的格式可能带“T”字母或者毫秒数直接往后端传容易报日期格式错误。最简单的方式是给 el-date-picker 加上value-formatyyyy-MM-dd HH:mm:ss这样前端传给后端的就是标准字符串后端用DateTimeFormat或者JsonFormat就能直接接住。交互细节上还要处理三个场景上报成功之后弹个提示并清空表单审批按钮点击后要二次确认防止辅导员误点数据列表加载时要显示 loading请求失败要弹出错误提示。这些细节看着不起眼但评分老师现场演示的时候页面反馈不友好很容易被扣印象分。6. 联调、部署与答辩准备代码写出来只是第一步跑通、部署、讲清楚才是完整交付。这个环节最磨人也最能让一个项目真正变成“你自己的东西”。6.1 本地联调九个常见坑我把自己在联调阶段踩过的高频坑列成一张表遇到问题时可以直接对照排查现象原因解决办法前端请求报 404后端控制台没日志前端代理没配请求打到 8080 端口后端确认 vue.config.js 里的 devServer.proxy 是否配置报错“The server time zone...”MySQL 时区配置问题数据库连接 URL 加 serverTimezoneAsia/Shanghai后端接口 401Authorization 头被前端拦截器删掉或拼错检查 axios 拦截器是否把 token 放在 headers 里日期传过去变成 0 或 1970前后端日期格式不一致前端加 value-format后端加 JsonFormat中文乱码控制台或数据库编码不对数据库统一 utf8mb4IDEA 编码设为 UTF-8Excel 导出后打不开响应头 Content-Disposition 写错或文件名编码有问题按前文代码设置 header文件名用 URLEncoder.encode上传图片 413Nginx 默认上传大小限制 1MNginx 配置 client_max_body_size 10M端口被占用重启时上次的进程没杀掉找到进程杀掉或者换端口前端页面刷新后 404直接访问 Vue 路由的路径经过 Nginx 后找不到页面Nginx 配置 try_files $uri $uri/ /index.html6.2 前后端分离部署方案毕设答辩通常是在本机演示但提交部署文档时给出线上方案会显得更完整。部署思路不复杂后端打成 jar 包用 Java 启动前端构建出 dist 静态文件由 Nginx 托管再把接口请求反向代理到后端的 8080 端口。后端打包时用 Maven 命令跳过测试mvn clean package -DskipTests java -jar target/campus-health-server.jar前端构建npm install npm run build构建完成后dist 目录里就是静态文件。Nginx 的配置要同时处理“静态资源”和“接口代理”两件事server { listen 80; server_name localhost; root /home/campus/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }注意这里 proxy_pass 后面的斜杠是有特殊含义的http://127.0.0.1:8080/会把请求路径里匹配到的/api/前缀去掉再转发。如果你后端接口路径本来就不带 /api这样写刚好能对上如果后端带 /api 前缀就不要写斜杠。这个细节我见过不止一次弄反结果接口全部 404。6.3 答辩时容易被问倒的问题答辩不是看你项目写了多少功能而是看你能不能把设计理由讲清楚。以下几个问题几乎必问提前准备一下第一个问题是“为什么选这个选题”。不要只答“学校要求做毕设”而是说“高校场景下的健康管理流程涉及多角色、多状态、多审批节点能完整覆盖一个管理系统的常见技术点且贴近现实应用”。第二个问题是“权限是怎么控制的”。标准答法是前端做路由守卫和菜单过滤后端通过 JWT 解析用户角色在拦截器里对敏感接口二次校验。第三个问题是“如果并发访问量很大怎么办”。结合项目谈数据库加索引、分页查询、热数据用 Redis 缓存、高并发申请时用乐观锁防止重复更新。能答到这个层次老师基本不会再追问。还有一个高频问题“项目的难点在哪里”。不要提“难在增删改查”那是减分项。可以说“审批流程的状态机设计和并发情况下的状态守卫是难点我用乐观锁思路保证状态只能从待审批流转到通过或驳回避免超卖和重复操作”。这是真实存在且能讲清楚的技术点。6.4 常见问题排查速查表除了列举现象我觉得值得把排查思路总结成一套方法论。第一步看后端控制台有没有异常日志大部分问题都是空指针、SQL 语法、字段类型不匹配第二步看前端浏览器 Network 面板接口返回的 HTTP 状态码基本能定位问题层次4xx 是请求或权限问题5xx 是后端逻辑异常0 是跨域或网络中断第三步直接用 Postman 或 Apifox 调接口绕过前端把问题隔离在后端还是前端。排查跨域问题的时候要区分两种情况本地开发用脚手架自带的代理不走浏览器跨域逻辑但前端请求必须写相对路径/api/xxx而不是完整的http://localhost:8080/xxx否则代理不生效部署后跨域交给 Nginx 反向代理解决后端不需要额外配置 CORS。如果你的项目后端配了 CorsConfig同时又配了 Nginx 代理就会造成“预检请求通过 Nginx 转发但后端又把 Access-Control-Allow-Origin 设置了一遍”反而容易出状况。最后再分享一个小技巧开发阶段把 Spring Boot 的日志级别调成 debug在 application.yml 里写logging.level.com.campus.mapperdebug这样能直接在控制台看到每条 SQL 的具体执行语句和参数。排查“查询结果不对”“更新没生效”这类问题时一眼就能看出 SQL 到底拼成了什么样比靠大脑推测快得多。这套系统做完之后我自己最大的体会是真正花时间的不是写代码而是设计状态和约束数据。健康上报为什么要用唯一索引审批为什么要做状态守卫这些细节一开始看起来是“多余的”但一旦跑起来就会发现它们才是保证一个管理系统不混乱的根基。后来我把同样的设计思路放到其他管理系统里比如校园报修系统、实验室预约系统发现思路完全通用。所以如果能趁毕设把“先定状态、再定约束、最后写代码”这个习惯养成这套源码的价值就远不止一个学分那么简单了。
阅读完成 · 觉得有帮助?