每年四月中旬开始,我手里就会集中收到一批“卡在中期”的毕设项目,出现频率最高的题目之一就是高校后勤报修系统。它看起来非常规整——提交报修、后台派单、师傅维修、学生验收,好像四个页面一套 CRUD 就能交差。可真把它做完,你会发现这个题目把权限设计、状态流转、数据一致性、文件上传、消息通知、报表统计全串在了一起,一个都没落下。
这篇内容围绕“高校后勤报修系统设计与实现”这个毕设场景展开,把从选题、需求梳理、技术选型、数据库设计、核心流程代码落地,到论文和答辩准备的完整链路全部捋一遍。无论你还在选题观望、代码写了一半,还是已经准备动笔写论文,都能从中找到可以直接照抄的思路和话术。
1. 为什么这个毕设题目能“以小见大”:选题价值与功能边界
1.1 高校后勤报修系统凭什么年年上榜
计算机专业本科毕设选题,其实讲究一个“纵向有深度、横向有广度”。纯算法题容易做不出结果,纯前端页面又撑不起论文的工作量,一个恰到好处的信息系统题目才能让代码量、论文篇幅和答辩可讲性都达标。
报修系统属于典型的管理信息系统(MIS),业务本身不复杂,但绕不开几个硬核设计点:
- 多角色权限:学生、维修工、调度员、系统管理员各有各的操作边界;
- 工单状态流转:每次状态变更都要被校验和记录,不是简单改一个字段;
- 数据可见性:不同角色看到的工单列表完全不一样,涉及条件过滤和权限隔离;
- 并发场景:维修工同时接单、调度员同时派单,数据库层要处理冲撞;
- 可扩展性:评价、通知、统计看板都是加分项,可以按时间余量往上叠。
这些点凑在一起,恰好覆盖了本科阶段最重要的 Java Web 技能树。对导师来说,这样一个题目既能看出你对业务的理解,也能检验你的工程能力。对你自己来说,哪怕只有四张表也能讲出设计感,完全够写一篇有内容的毕业论文。
1.2 功能边界:宁可少做,也要做闭环
我见过太多学生开题时豪情万丈,一口气列了 APP 端、微信小程序端、地图找维修点、语音识别报修、实时消息推送,最后连一个完整流程都没跑通,答辩时只能拿原型图出来画饼。
这里说一个很实在的观点:毕设系统最怕的不是功能少,而是功能没有闭环。一个工单从提交到被驳回、被取消、被评价、被统计,全链路能跑通,比十个只做了页面的半成品有价值得多。
我建议的最小可行版本长这样:
- 报修人端:提交报修、查看进度、取消未接单工单、完成后评价
- 调度端:审核报修、驳回并填理由、指派维修工、查看维修统计
- 维修端:查看待接单列表、接单、开始维修、提交维修结果
- 管理端:用户管理、维修类别管理、工单总览、基础数据看板
如果你时间比较充裕,再往上加这三个模块,性价比很高:公告通知、站内消息中心、Excel 导出。这三个功能不改变核心逻辑,但对答辩展示“系统完整性”帮助很大。
2. 需求拆分:四类角色、三条主线,先把业务逻辑钉死
2.1 四个角色的需求清单与权限边界
很多同学拿到这个题目后第一反应是建表,我建议反过来,先把每个角色“能看到什么、能做什么”写清楚。这份角色清单在后面做权限和 SQL 过滤时能省大量返工时间。
| 角色 | 核心功能 | 数据可见范围 | 备注 |
|---|---|---|---|
| 报修人(学生/教职工) | 提交工单、查看我的工单、取消、评价 | 仅本人创建和关联的工单 | 一卡通号或学号可作登录名 |
| 维修工 | 查看待接单列表、接单、开始维修、提交结果 | 本人已接单工单 + 公共待接单池 | 建议限制同时进行中的工单数量 |
| 后勤调度员 | 审核、驳回、派单、催办、统计 | 区域内全部工单 | 审核环节决定工单是否进入维修池 |
| 系统管理员 | 用户管理、维修类别管理、数据字典、日志 | 全量数据,但一般不直接操作工单 | 与调度员角色必须分开 |
注意一个细节:报修人和调度员看到的“全部工单”含义不同。报修人只能看自己的;调度员可以看全校的,但通常需要按区域或类别筛选;维修工只能看派给自己的以及“待接单池”里允许接的工单。这个边界在做列表查询接口时很容易漏,漏了就会出现越权访问,答辩时被老师一追问就露馅。
2.2 工单生命周期:状态机决定事故率
我辅导学生时有一个固定动作,要求他们先不写代码,而是把工单状态从“提交”到“关闭”的全过程画出来,画完才能动工。这一步不是形式主义,因为状态机是这个系统的灵魂。
一个比较合理的状态流转是这样:
- 待审核(PENDING):报修人提交后进入调度池,此时可被驳回或派单
- 已驳回(REJECTED):调度员认为无需维修或信息不完整,驳回时必须填理由
- 待接单(ASSIGNED):调度员已指定维修工,但维修工尚未接单,此时报修人可取消
- 维修中(IN_PROGRESS):维修工接单并点击开始维修,此时不可取消,只能等完成
- 待验收(COMPLETED):维修工提交结果,等待报修人确认
- 已关闭(CLOSED):报修人确认完成并评价,工单生命周期结束
- 已取消(CANCELLED):报修人在未接单前主动取消或管理员强制关闭
这里有一个很多初写者会踩的坑:把“待接单”和“维修中”合并成同一个状态“已派单”。表面上看字段少了,实际操作会非常别扭——维修工接单后如果临时不能去,调度员想改派都没法区分“还没人管”和“正在处理中”。状态拆细一点不会增加太多工作量,但能让每个角色的操作语义非常明确。
三条业务主线也建议在需求阶段锁定:报修主线(报修人发起->调度审核->维修工处理->报修人验收)、管理主线(类别维护、用户维护、通知发布)、统计主线(按状态、按维修工、按时长的多维度统计)。三条主线对应论文里的用例图和功能模块划分,后面写“系统设计”一章时直接拿来用。
3. 稳妥的技术选型分析:不是选最新,而是选最能讲清楚
3.1 我推荐的后端组合
每年都会有人问要不要上 Spring Cloud、要不要用微服务、要不要引入 Redis 做缓存。我的回答一直是:本科毕设,不要为了炫技而增加不可控因素。
最稳妥的组合是:
- JDK 8 或 JDK 17
- Spring Boot 2.7.x
- MyBatis Plus 3.5.x
- MySQL 8.0
- Maven 3.6+
为什么不用 Spring Data JPA?不是说 JPA 不好,而是在毕设场景里,MyBatis Plus 的 SQL 可控性更适合讲道理。JPA 帮你自动生成 SQL,遇到复杂查询和联表时反而难排查。MyBatis Plus 既有通用的单表 CRUD,又能手写 XML 里的复杂 SQL,评审老师问起来你也好解释“这里为什么用这种查询方式”。
为什么不引入 Redis?如果工单量根本达不到缓存瓶颈,那 Redis 就是纯增加复杂度。你觉得缓存用户列表很方便,答辩时老师就会追问缓存更新策略、过期时间、缓存穿透怎么解决,反而给自己挖坑。矩形结论是:Redis、MQ、微服务这些,放在论文的“系统展望”章节作为可扩展方案提一句就够了,核心代码里不要碰。
3.2 前端的两条路线怎么选
前端我给出两条路线,大家按自己的时间预算和底子选。
路线 A:前后端分离
- Vue 3 + Element Plus + Axios
- 后端提供 RESTful API,用 Knife4j 生成接口文档
- 适合想展示“前后端分离”能力、愿意花时间学前端工程化的同学
路线 B:服务端渲染
- Thymeleaf + Bootstrap + JQuery
- 后端一套代码搞定页面和接口,部署简单
- 适合时间紧、不熟悉 Node 环境和 npm 各种报错的纯后端同学
对比下来,路线 A 的界面更现代,工作量分散到前端也多一些;路线 B 开发效率高,但动态表格和弹窗交互写起来会比较原始。选哪条都行,关键是别中途切换。我见过不少项目从 Vue 写着写着换回模板引擎,最后接口路径、跨域、会话管理全乱套了。
3.3 答辩必问:你为什么这样选型
这部分直接给一份能背下来的回答思路:
选择 Spring Boot,是因为它的自动装配和生态成熟度让我能集中解决业务问题而不是环境问题;ORM 选了 MyBatis Plus,是为了在通用单表操作上减少样板代码,同时保留手写 SQL 的能力;数据库用 MySQL,是因为学校机房和云服务器基本都是这个环境,资料多、排错成本低。没有引入 Redis 和微服务,是因为当前系统是单体应用,并发量远未达到需要分布式缓存和拆分的程度,引入反而增加维护成本。
这段话的要点是解释取舍理由,而不是报菜名。评审老师最烦的就是“因为我用过”“因为网上都说好”这种答案。
4. 数据库设计:六张核心表如何支撑完整业务闭环
4.1 基础用户表与角色设计
用户表是整个系统的地基,字段建议这样规划:
| 字段名 | 类型 | 说明 |
|---|---|---|
| user_id | bigint | 主键,自增 |
| username | varchar(64) | 登录账号,建议用学号/工号 |
| password | varchar(128) | BCrypt 加密后的密码 |
| real_name | varchar(32) | 真实姓名 |
| phone | varchar(20) | 联系电话 |
| role_type | int/tinyint | 1 报修人 2 维修工 3 调度员 4 管理员 |
| department | varchar(128) | 学院/后勤部门 |
| status | tinyint | 1 启用 0 禁用 |
| create_time | datetime | 创建时间 |
这里强调两个安全点。第一,密码绝不能存明文,也不能用 MD5。MD5 撞库太容易了,Spring Security 自带的 BCryptPasswordEncoder 几行代码就能解决。第二,role_type 不要用字符串存“admin”“student”这种,查询效率低还容易拼写错,用 int 对应枚举,后端统一管理。
4.2 工单表:整个系统的绝对核心
工单表字段设计直接决定你后面写查询接口是顺滑还是痛苦。我整理了一份经过实际项目验证的字段清单:
| 字段名 | 类型 | 说明 |
|---|---|---|
| order_id | bigint | 主键 |
| order_no | varchar(32) | 业务单号,如 BX20250512001,展示给用户 |
| reporter_id | bigint | 报修人 user_id |
| category_id | bigint | 维修类别,关联维修类别表 |
| title | varchar(128) | 报修标题,列表页展示 |
| description | text | 问题详细描述 |
| images | varchar(512) | 图片路径,多个用逗号分隔 |
| location | varchar(256) | 报修地点,如“3 号宿舍楼 502 室” |
| priority | tinyint | 1 紧急 2 普通 3 低 |
| status | tinyint | 当前状态码,对应状态枚举 |
| assignee_id | bigint | 指派的维修工 user_id,可空 |
| reject_reason | varchar(256) | 驳回或取消原因,可空 |
| accept_time | datetime | 维修工接单时间 |
| finish_time | datetime | 维修工提交完成时间 |
| evaluate_time | datetime | 报修人评价时间 |
| deleted | tinyint | 逻辑删除标记 |
| create_time / update_time | datetime | 创建和更新时间 |
建议用 order_no 而不是自增主键作为页面展示的单号,因为连续的 order_id 会把业务量暴露出去,虽然不是什么大问题,但作为“设计感”可以在论文里提一句。order_no 的生成规则用日期加当日序号即可,例如 BX + yyyyMMdd + 三位自增序列。
4.3 辅助表与关联表的取舍
- repair_category 维修类别表:category_id、parent_id、name、sort,支持两级分类。比如“水电维修”下有“水管漏水”“电路故障”。这是树形结构的典型应用场景,论文里可以展开。
- evaluation 评价表:evaluation_id、order_id、score(1-5)、content、anonymous(是否匿名)、create_time。评价与工单是一对一关系,可以加唯一约束避免重复评价。
- sys_notice 消息通知表:notice_id、target_user_id、type(工单状态通知/系统公告)、content、is_read、create_time。消息只解决站内信展示,不需要引入 WebSocket 和实时推送。
- operation_log 操作日志表:log_id、user_id、action(如“派单”“驳回”)、target_id、detail、create_time。记录关键操作,答辩时可以说这是为了可追溯性和安全审计。
这里的取舍原则是:能不要的字段不要存冗余,但能用来支撑业务的日志和通知必须留。操作日志表会让整个系统的完整性评价上一个台阶。
4.4 容易忽略的索引与约束
- 工单表建
(status, create_time)联合索引,列表页按状态筛选加时间排序时走索引; assignee_id单独建索引,后续统计“某个维修工的接单量”时用到;order_no建唯一索引,防止并发生成重复单号;- 工单表加
deleted逻辑删除标记,用户误删可恢复; - 操作日志表按
create_time做索引,避免数据量大后统计变慢。
索引不需要多,每个都要能说出“为了哪个查询而建”,这又是论文里可以写的一小节。
5. 从报修到验收的代码落地:状态机设计、接口与并发控制
5.1 用枚举统一管理状态,而不是散落魔法数字
最容易翻车的写法是在 Controller 和 Service 里直接写order.setStatus(3),完全不管这个 3 从哪里来、能到哪里去。后面改需求时,状态一变,代码就要满项目找。
我的建议是写一个状态枚举,把所有合法流转集中定义:
public enum OrderStatus { PENDING(0, "待审核"), REJECTED(1, "已驳回"), ASSIGNED(2, "待接单"), IN_PROGRESS(3, "维修中"), COMPLETED(4, "待验收"), CLOSED(5, "已关闭"), CANCELLED(6, "已取消"); private final int value; private final String desc; // 构造方法、getter 省略 }同时定义一个流转校验表,每个状态“下一步能到哪些状态”集中管理:
private static final Map<OrderStatus, List<OrderStatus>> TRANSITIONS = new EnumMap<>(OrderStatus.class); static { TRANSITIONS.put(OrderStatus.PENDING, List.of( OrderStatus.ASSIGNED, OrderStatus.REJECTED, OrderStatus.CANCELLED)); TRANSITIONS.put(OrderStatus.ASSIGNED, List.of( OrderStatus.IN_PROGRESS, OrderStatus.CANCELLED)); TRANSITIONS.put(OrderStatus.IN_PROGRESS, List.of(OrderStatus.COMPLETED)); TRANSITIONS.put(OrderStatus.COMPLETED, List.of(OrderStatus.CLOSED)); } public static boolean canTransfer(OrderStatus from, OrderStatus to) { return TRANSITIONS.getOrDefault(from, Collections.emptyList()).contains(to); }这样一来,任何状态跳转前都必须经过canTransfer校验,非法流转直接抛业务异常。这个设计在答辩时非常加分,因为它证明你考虑了系统的健壮性,而不是给了四个页面完事。
5.2 核心接口清单与实现思路
接口设计不用 RESTful 教条化,够用、语义清晰即可。我常用的接口清单:
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/order | 报修人 | 创建工单 |
| GET | /api/order/page | 所有角色 | 分页查询,按角色自动过滤数据范围 |
| GET | /api/order/{id} | 所有角色 | 工单详情,校验数据归属 |
| PUT | /api/order/{id}/cancel | 报修人/管理员 | 取消工单,仅 PENDING/ASSIGNED 可取消 |
| PUT | /api/order/{id}/assign | 调度员 | 派单给维修工,PENDING -> ASSIGNED |
| PUT | /api/order/{id}/reject | 调度员 | 驳回并填原因,PENDING -> REJECTED |
| PUT | /api/order/{id}/accept | 维修工 | 接单,ASSIGNED -> IN_PROGRESS |
| PUT | /api/order/{id}/finish | 维修工 | 提交维修结果,IN_PROGRESS -> COMPLETED |
| PUT | /api/order/{id}/evaluate | 报修人 | 验收评价,COMPLETED -> CLOSED |
强调一个容易踩的点:维修工不能“直接抢单”,而是调度员先指派。如果你的需求是“开放抢单池”,那状态机和并发控制会更复杂,不建议毕设阶段这么做。先指派玩家可以精确控制管理员权限,也不需要引入抢单队列。
5.3 并发与幂等:多个维修工同时操作怎么办
派单、接单这类操作最怕两个人同时操作同一张工单。比如维修工 A 和维修工 B 同时点了接单,后端如果先查出“状态是待接单”,再更新“状态为维修中”,两个请求都会通过查询,结果都写成功,数据就乱了。
解决方式有两种,我推荐用乐观锁式的条件更新,不需要额外依赖:
UPDATE work_order SET status = 3, assignee_id = #{userId}, accept_time = NOW() WHERE order_id = #{orderId} AND status = 2这条 SQL 的关键是WHERE status = 2。如果受影响行数为 0,说明状态已经被别人改过了,直接返回“工单已被处理”。Service 层这样写:
int rows = workOrderMapper.updateByStatus(orderId, OrderStatus.ASSIGNED.getValue(), OrderStatus.IN_PROGRESS.getValue(), userId); if (rows == 0) { throw new BizException("手慢了,工单已被其他维修工接走"); }同样的思路套用到“评价”“取消”“审核”这些操作上,一处会了全流程都会。
幂等性也要处理。前端用户手抖连点两次“提交评价”,第二次请求应该在 Service 层被拦住。最简单的方法是通过(order_id)唯一约束来保证一张订单只能有一条评价记录,插入时捕获 DuplicateKeyException 并返回“请勿重复操作”。
5.4 消息通知与日志:和状态变更绑在同一个事务里
有不少人把通知逻辑写在 Controller 层,状态更新一个事务、通知插入一个事务,中间只要抛个异常,工单状态和用户消息就不同步了。正确做法是在 Service 层统一封装一个transition()方法,把状态校验、状态更新、日志记录、消息通知放进同一个事务:
@Transactional(rollbackFor = Exception.class) public void transition(Long orderId, OrderStatus targetStatus, Long operatorId) { WorkOrder order = workOrderMapper.selectById(orderId); OrderStatus current = OrderStatus.of(order.getStatus()); if (!OrderStatus.canTransfer(current, targetStatus)) { throw new BizException("当前状态不允许该操作"); } // 1. 更新状态 workOrderMapper.updateStatus(orderId, targetStatus.getValue()); // 2. 写操作日志 operationLogMapper.insert(...); // 3. 插入站内信通知 sysNoticeMapper.insert(...); }通知内容按照角色不同自动生成。比如状态变为“维修中”,通知报修人“维修工已接单,预计上门时间请留意”;状态变为“已驳回”,通知报修人“报修申请已被驳回,理由:xxx”。这部分虽然简单,但对用户体验影响很大,也让系统看起来完整。
6. 我整理的高频踩坑点:时间、上传、跨域与权限
6.1 时间字段的时区问题,差八小时的坑必踩
数据库时间比页面时间少了八小时,这是 MySQL 和 Java 联调中最常见的灵异事件。解决方案写在三个地方:
# application.yml spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8-- 数据库连接串 jdbc:mysql://localhost:3306/campus_repair?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai同时建表时字段类型用datetime,Java 实体用LocalDateTime,三者统一后基本不会再出现时间错乱。如果你的服务器时间本身就强制用 UTC,那页面展示层再转一次时区,但这属于运维侧的异常情况,答辩时说清楚配置依据即可。
6.2 图片上传:别把路径存成相对路径就完事
报修场景经常要上传宿舍漏水照片、损坏设备照片,图片是刚需。我建议的处理方案:
- 本地磁盘单独目录存储,路径如
/data/upload/repair/{yyyyMMdd}/uuid.jpg - 文件名必须用 UUID 重命名,防止中文文件名和路径穿越问题
- 数据库
images字段只存相对路径,比如/upload/repair/20250512/uuid.jpg - 通过 Spring Boot 配置类映射虚拟路径,或者部署后用 Nginx 代理
代码里配置虚拟路径:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:/data/upload/"); } }这里给一个实战提醒:开发环境下重启后图片 404 的话,先看是不是用了 IDEA 的 target 目录或者 Tomcat 临时目录作为上传路径。这种路径在项目重新编译后会清空,图片和上传文件都会丢。稳妥做法是固定写到项目外部的一个磁盘目录,再由虚拟路径暴露出来。
6.3 前后端联调的 CORS 跨域问题
前后端分离开发时,Vue 跑在 5173 端口,后端跑在 8080 端口,浏览器直接请求 API 会被 CORS 拦截。开发期可以用 Vue 的 Vite Proxy 解决:
// vite.config.js server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }但如果你想让后端直接放开跨域,需要加一个 CORS 配置类:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600); } }注意allowedOriginPatterns和allowCredentials必须配套使用,很多跨域失败案例都是因为只设置了allowedOrigins("*")加上allowCredentials(true),结果浏览器依然拦截。生产部署时,前端打包后的静态文件直接放在后端static目录或者同一个 Nginx 下,就不存在跨域问题了。
6.4 权限只靠前端隐藏按钮?后端必须兜底
很多同学把管理端入口隐藏就当做了权限控制,这是大忌。前端隐藏按钮只是为了用户体验,后端每个接口都必须校验收登录状态和角色身份。
实现方式我推荐轻量级的拦截器加自定义注解:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RequireRole { int[] value(); }在拦截器中校验登录用户是否拥有注解指定角色,没有就直接返回 403。同时,查询详情和修改工单时还要做数据归属校验——维修工只能操作自己被指派的工单,报修人只能查看自己创建的工单。这条规则写在每个 Service 方法里,肉眼可见的严谨,也是答辩时的高频得分点。
6.5 分页查询漏配置插件,MySQL 明明有数据却不显示
MyBatis Plus 的分页功能不是默认开启的,需要手动配置拦截器。很多同学写完selectPage发现返回总条数是 0,或者 Page 对象里的 records 是空,一查就是少了这段配置:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }补上之后,分页和条件筛选才能正常工作。这个问题在联调阶段暴露率极高,先写好能少走一步冤枉路。
7. 论文写作与答辩准备:如何把 CRUD 讲出设计感
7.1 论文的章节结构与写作节奏
毕设论文不需要花哨,按标准套路来,但重点要突出。一份稳妥的目录结构:
- 第一章 绪论:选题背景与意义、国内外研究现状、主要工作
- 第二章 相关技术介绍:Spring Boot、MyBatis Plus、Vue、MySQL
- 第三章 系统分析:可行性分析、需求分析、用例图、功能需求与非功能需求
- 第四章 系统设计:总体架构、功能模块设计、数据库设计(E-R 图和数据表)、工单状态机设计
- 第五章 系统实现:每个核心功能模块的页面截图 + 关键代码 + 实现说明
- 第六章 系统测试:测试环境、功能测试用例、测试结果分析
- 第七章 总结与展望
写论文最忌讳的是前两章堆砌大段背景,第三章以后只剩几张截图。正确节奏是:第三章需求分析写角色和用例,第四章数据库设计写表和状态机,第五章按核心流程挑 4-5 个模块写“实现思路 + 核心代码 + 页面效果”,每一步都能对应到你自己真正写过的代码。
7.2 答辩高频问题清单和对策
答辩时间一般 10-15 分钟,展示完系统后老师提问 3-8 个问题。我整理了最常被问到的几类:
架构类:为什么用单体不用微服务?为什么不引入 Redis? 答题方向:说明当前业务规模和并发量,单体足够;引入分布式组件会增加开发和运维成本,放在展望里更有说服力。
状态类:工单状态怎么控制?两个人同时操作同一张工单怎么办? 答题方向:先讲状态枚举和合法流转表,再讲条件更新
UPDATE ... WHERE status = ?的乐观锁思路,最后说影响行数为 0 时抛出业务异常。权限类:不同角色怎么实现权限隔离? 答题方向:登录拦截器 +
@RequireRole注解 + 查询时数据归属条件,三层缺一不可。数据库类:工单表为什么这么设计?当前在第几范式? 答题方向:以工单表为核心,拆分评价、通知、操作日志的合理性;业务日志表适当冗余字段,说明符合实际查询需求。
安全类:密码怎么加密的?如何防止 SQL 注入? 答题方向:BCrypt 加密;MyBatis 的
#{}预编译机制,任何拼接都走 XML 里的<if>标签。测试类:系统测试覆盖了多少用例? 答题方向:按功能模块列出核心流程测试用例,特别说明并发接单、重复评价、越权访问这类边界场景的测试结果。
7.3 答辩前建议画好两张图
第一张是工单状态图,用 UML 状态图把 7 个状态间的所有合法流转标清楚,这张图放答辩 PPT 里,老师提问状态机相关问题时直接截图回应。第二张是系统架构图,画清浏览器、Nginx、Spring Boot、MySQL 之间的请求关系,能体现整体规划能力。
这两张图不需要画得多专业,PowerPoint 自带的图形控件就能完成,重要的是逻辑正确、标注清晰。有了它们,答辩时哪怕少讲几页代码,老师也能快速建立对你系统全貌的认知。
最后再分享一个我带毕设时反复强调的心得:这个项目能不能做扎实,关键不在代码量,而在你对状态机和权限控制的理解。只要你先把工单状态流转表画出来,把四类角色的权限边界写清楚,再动笔写代码,后面的开发节奏一定顺畅很多。如果你已经确定了这个题目,不妨今天就先把这两件事做完,比急着跑一个 Hello Spring Boot 模板有用太多了。