news 2026/2/18 11:06:45

从零构建毕业设计选题系统:基于「计算机毕业设计之家」的工程化实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零构建毕业设计选题系统:基于「计算机毕业设计之家」的工程化实践


背景:毕业季的“三座大山”

每年三月,高校的毕设管理群都会准时上演“三大名场面”:

  1. 选题冲突——同一课题被 30 人同时点击,数据库瞬间出现多条student_id指向同一topic_id的脏数据;
  2. 流程黑盒——学生看不到导师的审核进度,导师看不到学生的最新文档,双方靠微信“盲聊”;
  3. 权限混乱——辅导员、教研室主任、教务秘书都想看数据,结果一个root账号被传来传去,谁改了配置没人知道。

「计算机毕业设计之家」就是在这样的“修罗场”里孵化出来的。目标只有一个:用最小开发成本,让毕设流程可管、可控、可回溯。

技术选型:为什么不是 Django + PostgreSQL?

维度Spring Boot 2.7Django 4.1
事务粒度依赖声明式@Transactional,可精确到方法级默认自动提交,手动控制需显式transaction.atomic()
依赖注入原生 IoC,AOP 无侵入需第三方django-river或信号量实现,代码分散
微校生态教务系统多基于 Java,对接 SOAP/ bod 方便Python 胶水语言优势,但需额外桥接层
学习曲线对只写 CRUD 的应届生略陡上手快,后期性能调优经验资料偏少

数据库层面,MySQL 8.0 的SELECT ... FOR UPDATEinnodb_deadlock_detect足以应付千级并发选题;PostgreSQL 的SKIP LOCKED固然香,但校园机房只给了 4C8G 的 CentOS,DBA 对 MySQL 更熟,最终保守选了 MySQL。

核心实现细节

1. 选题幂等性校验

场景:N 个学生同时抢 1 个课题。
方案:Redis 分布式锁 + 数据库唯一索引兜底。

// TopicService.java public String chooseTopic(Long studentId, Long topicId) { String lockKey = "topic_lock::" + topicId; String requestId = UUID.randomUUID().toString(); try { // 1. 尝试获取锁,超时 3s,防止羊群效应 boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, requestId, Duration.ofSeconds(3)); if (!locked) { return "选题人数过多,请稍后再试"; } // 2. 双重检查:缓存穿透场景下仍有并发 Topic topic = topicMapper.selectById(topicId); if (topic.getRemain() <= 0) { return "名额已满"; } // 3. 扣减库存与插入选题记录放在同一事务 topicMapper.decrementRemain(topicId); // SQL: UPDATE topic SET remain=remain-1 ... chooseRecordMapper.insert(studentId, topicId); return "选题成功"; } finally { // 4. 仅当当前线程持有锁才释放,避免误删 if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) { redisTemplate.delete(lockKey); } } }

关键点:

  • 锁超时 < 事务超时,防止锁已释但事务回滚导致超卖;
  • 使用requestId做 value,杜绝 A 线程误删 B 线程锁。

2. 导师-学生双向匹配算法

需求:导师最多带 5 人,学生可填 3 个志愿,需最大化整体满意度(志愿序越小越好)。
建模:二分带权匹配 → 转化为最小费用最大流

  • 节点:源点S→ 学生 → 导师 → 汇点T
  • 边容量:学生到导师为 1;导师到T为 5
  • 边费用:学生第一志愿 1 分,第二志愿 3 分,第三志愿 5 分

使用 Google OR-Tools 的MinCostFlow求解,10 毫秒级完成 800 学生×200 导师规模运算。

3. WebSocket 进度通知

技术栈:Spring 原生@MessageMapping+ STOMP + Redis Pub/Sub。
当导师点击“通过”按钮,后端发布一条消息到 Redis channel,网关层(Spring Gateway)里的 WebSocket 微服务订阅并推送给指定studentId,前端 Vue3 使用@stomp/stompjs实时刷新进度条。

性能与安全考量

  1. SQL 注入:MyBatis-Plus 一律#{}占位,禁止$拼接;
  2. JWT 刷新:AccessToken 5min + RefreshToken 2h,刷新时旧 RefreshToken 加入 Redis 黑名单,防止回放;
  3. 冷启动延迟:Spring AOT + GraalVM 静态编译把启动时间从 8s 降到 1.3s,K8s 滚动发布不再杀旧 Pod;
  4. 文件上传:限制 10MB,使用commons-io检查 Magic Number,禁止../路径穿越,统一存至 MinIO,返回 UUID 链接,数据库只存对象名。

生产环境踩坑实录

  • 时区陷阱:服务器默认 UTC,教务要求“截止时间 23:59” 被误判成第二天凌晨。解决:Dockerfile 里加ENV TZ=Asia/Shanghai,MySQL 连接串追加&serverTimezone=Asia/Shanghai
  • 路径硬编码:早期把上传目录写成/home/upload,上 K8s 后节点重启丢失。改为 MinIO + 可配置spring.servlet.multipart.location
  • 事务嵌套:选题方法里调用sendWebsocket(),后者又读库,导致Connection is read-only异常。解决:把通知逻辑放到事务提交后,用TransactionSynchronizationManager.registerAfterCompletion()

完整可运行片段:防并发选题锁

@Configuration public class RedisLockConfig { @Bean public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, String> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new StringRedisSerializer()); return template; } }
@RestController @RequestMapping("/topic") public class TopicController { @Autowired private TopicService topicService; @PostMapping("/choose") public R choose(@RequestParam Long studentId, @RequestParam Long topicId) { return R.ok(topicService.chooseTopic(studentId, topicId)); } }

下一步:从“毕设之家”到“学术协作平台”

当前系统只服务计算机学院,但模型层(用户、角色、学院、流程模板)已预留tenant_id。后续要做:

  1. 多租户隔离:在 MyBatis-Plus 拦截器统一追加AND tenant_id = ?
  2. 流程编排:把“选题-开题-中期-答辩”做成 BPMN,嵌入 Camunda,各学院自定义环节;
  3. 微服务拆分:将通知、文件、匹配独立成 Service,配合 Nacos 注册中心,支持弹性扩缩;
  4. 开放接口:OAuth2 授权,对接学校一站式平台,实现单点登录。

如果你也在做校务系统,不妨先跑通最小闭环,再横向扩展——毕竟,毕业设计只是学术协作的起点,而不是终点。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/13 4:51:52

Unity UI遮罩优化从入门到精通:3大技巧打造丝滑边缘效果

Unity UI遮罩优化从入门到精通&#xff1a;3大技巧打造丝滑边缘效果 【免费下载链接】SoftMaskForUGUI UI Soft Mask is a smooth masking component for Unity UI (uGUI) elements. 项目地址: https://gitcode.com/gh_mirrors/so/SoftMaskForUGUI 在Unity UI开发中&…

作者头像 李华
网站建设 2026/2/13 19:59:28

3步解锁系统优化工具:让Windows运行如飞的实用指南

3步解锁系统优化工具&#xff1a;让Windows运行如飞的实用指南 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以简化和改善…

作者头像 李华
网站建设 2026/2/7 3:41:55

系统瘦身工具:3步清除冗余软件,让Windows重获新生

系统瘦身工具&#xff1a;3步清除冗余软件&#xff0c;让Windows重获新生 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以…

作者头像 李华
网站建设 2026/2/15 19:35:46

系统焕新工具:让老旧电脑重获新生的实用指南

系统焕新工具&#xff1a;让老旧电脑重获新生的实用指南 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以简化和改善你的Wi…

作者头像 李华
网站建设 2026/2/16 9:13:06

UIE-PyTorch通用信息抽取框架2023实践指南

UIE-PyTorch通用信息抽取框架2023实践指南 【免费下载链接】uie_pytorch PaddleNLP UIE模型的PyTorch版实现 项目地址: https://gitcode.com/gh_mirrors/ui/uie_pytorch UIE-PyTorch是基于PyTorch实现的通用信息抽取&#xff08;Universal Information Extraction&#…

作者头像 李华
网站建设 2026/2/11 17:55:36

SQLGlot实战手册:从安装到精通的数据库翻译官指南

SQLGlot实战手册&#xff1a;从安装到精通的数据库翻译官指南 【免费下载链接】sqlglot tobymao/sqlglot: 这是一个用于SQL查询的构建器和解析器&#xff0c;支持多种数据库。适合用于需要动态构建和解析SQL查询的场景。特点&#xff1a;易于使用&#xff0c;支持多种数据库&am…

作者头像 李华