
1. 项目背景与需求分析校园竞赛作为高校教育的重要组成部分每年都会举办各类学科竞赛、创新创业大赛等活动。传统的人工管理方式存在诸多痛点报名信息通过Excel表格传递容易丢失、评审结果统计耗时费力、不同部门间的数据无法共享。这些问题直接影响了竞赛组织的效率和质量。我们团队在调研了国内30余所高校的竞赛管理现状后发现以下核心需求多角色协同工作管理员、评委、参赛学生线上报名与材料提交自动化评审流程实时成绩统计与排名历史数据可追溯2. 技术选型与架构设计2.1 技术栈决策过程选择SpringBootVueMyBatis的组合主要基于以下考量开发效率SpringBoot的约定优于配置原则大幅减少XML配置性能需求MyBatis的SQL优化能力满足高并发查询场景前后端分离Vue.js的组件化开发适合多角色权限系统社区支持MySQL作为成熟的关系型数据库有完善的文档和工具链技术选型对比参考JPA vs MyBatis需要复杂SQL优化时MyBatis更灵活Thymeleaf vs Vue前后端分离项目更适合VueMongoDB vs MySQL事务性操作多的场景选择MySQL2.2 系统架构详解采用经典的三层架构前端层(Vue) ↑↓ HTTP/HTTPS 业务逻辑层(SpringBoot) ↑↓ JDBC 数据持久层(MyBatisMySQL)关键设计决策使用JWT进行无状态认证采用RBAC权限模型重要操作记录审计日志数据库读写分离设计3. 核心功能实现3.1 竞赛生命周期管理竞赛状态机设计草稿 → 已发布 → 报名中 → 进行中 → 已结束 → 归档状态转换触发条件管理员手动发布到达报名截止时间自动转换评委提交全部评分后结束3.2 报名审核流程// 伪代码示例 public ApplyResult submitApply(ApplyForm form) { // 1. 检查竞赛状态 if(contestService.getStatus(form.contestId) ! OPEN){ throw new BizException(竞赛未开放报名); } // 2. 查重校验 if(applyMapper.existsByUserAndContest(form.userId, form.contestId)){ throw new BizException(请勿重复报名); } // 3. 保存申请记录 ApplyRecord record convertToRecord(form); applyMapper.insert(record); // 4. 发送站内通知 messageService.sendToAdmins( NEW_APPLY_NOTICE, record.getApplyId() ); return ApplyResult.success(record.getApplyId()); }3.3 评审打分模块评分业务规则每个作品至少需要3位评委去掉最高最低分取平均值支持评分标准模板配置实时更新排名看板-- 评分统计视图 CREATE VIEW contest_ranking AS SELECT p.participant_id, u.real_name, c.contest_name, AVG(s.score_value) AS avg_score, RANK() OVER(PARTITION BY s.contest_id ORDER BY AVG(s.score_value) DESC) AS rank FROM judge_score s JOIN user_apply p ON s.participant_id p.apply_id JOIN user_info u ON p.user_id u.user_id JOIN contest_info c ON s.contest_id c.contest_id GROUP BY p.participant_id, u.real_name, c.contest_name, s.contest_id;4. 数据库优化实践4.1 索引设计策略关键索引配置-- 竞赛表 ALTER TABLE contest_info ADD INDEX idx_status_time (contest_status, start_time); -- 报名表 ALTER TABLE user_apply ADD UNIQUE INDEX uk_user_contest (user_id, contest_id); -- 评分表 ALTER TABLE judge_score ADD INDEX idx_contest_participant (contest_id, participant_id);4.2 分表方案对于可能产生海量数据的操作日志表采用按年分表策略TableName(sys_log_#{T(java.time.LocalDate).now().getYear()}) public class SystemLog { // 日志实体 }5. 安全防护措施5.1 接口安全所有API添加速率限制敏感操作需二次认证使用HTTPS传输参数过滤XSS脚本5.2 数据安全密码加盐哈希存储敏感字段加密定期备份验证数据库审计日志6. 部署与监控6.1 生产环境配置# application-prod.yml spring: datasource: url: jdbc:mysql://master.db:3306/contest?useSSLfalseserverTimezoneUTC slave: url: jdbc:mysql://slave.db:3306/contest?useSSLfalseserverTimezoneUTC redis: cluster: nodes: redis1:6379,redis2:6379,redis3:63796.2 监控指标Prometheus采集API响应时间P99数据库连接池使用率缓存命中率JVM内存状态7. 典型问题排查7.1 并发报名问题现象高峰期出现重复报名记录解决方案数据库唯一索引约束分布式锁控制前端防重复提交public void applyWithLock(Long userId, Long contestId) { String lockKey apply: userId : contestId; try { if (redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) { // 核心报名逻辑 } } finally { redisLock.unlock(lockKey); } }7.2 评分统计延迟优化方案引入Redis缓存排名数据使用异步计算队列预生成统计结果8. 扩展性设计8.1 多赛事类型支持通过策略模式实现不同竞赛的差异化处理public interface ContestStrategy { void validateApply(ApplyForm form); void calculateScore(ScoreContext context); } Service public class InnovationContestStrategy implements ContestStrategy { // 创新创业大赛特殊逻辑 }8.2 微服务改造预留定义清晰的API契约配置中心接入准备服务发现兼容设计9. 前端关键技术9.1 动态表单渲染根据竞赛类型动态生成报名表单template div v-forfield in formSchema :keyfield.name component :isfield.componentType v-modelformData[field.name] :rulesfield.rules / /div /template9.2 实时排名展示使用WebSocket实现成绩实时推送const socket new WebSocket(/api/ranking-updates); socket.onmessage (event) { this.rankingData JSON.parse(event.data); };10. 测试策略10.1 测试金字塔实施单元测试覆盖率80%API契约测试全链路场景测试压力测试指标支持500并发报名评分提交响应1s10.2 混沌工程实践随机杀死服务实例网络延迟注入数据库故障转移测试11. 项目演进路线v1.0基础功能实现v1.5移动端适配v2.0数据分析看板v3.0AI辅助评审在实际部署过程中我们发现定时任务的时间精度对竞赛状态转换至关重要。最初使用cron表达式配置后改为基于事件驱动的状态机实现避免了服务器时间不同步导致的问题。对于评审模块初期没有考虑评委权重后期增加了专家评委的加权评分机制使结果更加客观。