1. 为什么选健美操评分系统,以及它真正要解决什么问题
三年前第一次接触类似的课程设计题目时,我还分不清“健美操评分系统”和所谓的“管理系统”之间到底有什么本质区别,以为无非是增删改查套个漂亮的网页壳子。等真正动手做了几版、又被答辩老师追问过几轮之后,我才意识到这类题目的核心不在于“管理系统”,而在于“评分”这两个字背后的业务逻辑。
健美操评分和普通考试打分完全不是一回事。一个健美操比赛现场,通常有多位评委同时打分,每个队伍完成一套动作之后,评委会根据动作完成度、艺术表现力、难度系数、队形变化等维度给出分数。为了保证公平,最终成绩不能简单地把所有评委分数加总平均——体操类项目普遍采用“去掉一个最高分、去掉一个最低分,再取平均”的规则。如果是多人项目,可能还需要对评分维度做加权处理,比如完成分占60%、艺术分占40%。更麻烦的是,现场还有裁判长,裁判长拥有一票否决权或调整权,可以在特殊情况下修正某个队伍的最终成绩。
如果这些东西全部靠Excel表格手工处理,一场比赛几十支队伍跑下来,光是在评委席和计分席之间传递纸条、核对分数,就足够让人崩溃。更别提现场直播大屏需要实时展示排名,稍微慢几秒,观众和领队就会开始催。
所以这个系统的真实定位是:一套能承载完整评分业务流程的Web应用。它要覆盖从赛前准备(创建比赛、录入队伍、配置评委、设置规则),到赛中操作(评委打分、成绩计算、排名刷新、大屏展示),再到赛后收尾(成绩导出、证书打印数据整理)的全过程。作为毕业设计或课程设计,它的难度曲线也刚刚好——既有足够的管理功能展示传统CRUD功底,又有评分计算这种可深可浅的业务算法,还能通过前后端分离展示工程化能力,一个题目把Java后端、数据库设计、前端交互全部串起来。
适合参考这篇内容的人,我总结下来有三类。第一类是正在选毕设题目、需要从零搭建一个全栈项目的计算机相关专业学生;第二类是已经写好了一版代码但总感觉业务逻辑不够完整、想看看评分规则怎么落地的同学;第三类是纯粹想学SpringBoot+Vue前后端分离项目怎么组织代码结构的开发者。无论你是哪一类,我下面讲的内容都建议不要只照着源码抄,而是把思路吃透——因为答辩时老师问的往往不是“这个按钮怎么实现的”,而是“你为什么这么设计”。
2. 技术选型为什么是SpringBoot+MyBatis+Vue,而不是其他组合
2.1 SpringBoot的取舍逻辑
很多同学做毕设时习惯一上来就说“我用SpringBoot”,但要问他为什么不用传统的SSH或者Servlet+JSP,他往往答不上来。这部分我要把选型逻辑讲清楚,免得答辩时被一句话噎住。
SpringBoot解决的核心痛点是零配置起步。拿SSH时代做对比,那时候要配web.xml、spring-mvc.xml、spring-dao.xml,还要处理各种jar包版本冲突,光是搭环境就能耗掉一周。SpringBoot用自动配置和起步依赖把这些全干了,你只需要在pom.xml里引一个spring-boot-starter-web,写一个main方法,就能跑起来一个HTTP服务。对于评分系统这种业务复杂度中等、开发周期紧张的题目,SpringBoot能让你把时间花在业务逻辑上,而不是和配置文件搏斗,这是它成为首选的根本原因。
另外,SpringBoot内置了Tomcat,部署时可以打成可执行的JAR包,服务器上只要装了JDK就能跑。这对最后要写“系统部署说明”的毕设来说非常友好——你不用再给老师讲怎么配置外部Tomcat的坑。
2.2 MyBatis为什么比JPA更合适这种题目
持久层我选的是MyBatis,而不是Spring Data JPA,这也在答辩时被老师问过。我的回答思路是这样的:JPA的优势在于实体管理和自动建表、多表关联的透明化处理,适合业务模型复杂、表关系多的企业级应用。但健美操评分系统的核心查询往往带有明显的统计特征——按比赛分组聚合评分、按项目计算平均分、排名分页、多表联查成绩单,这种SQL是JPA的JPQL不太好表达的,而MyBatis允许你直接写原生SQL,把统计逻辑放在XML映射文件里,性能和行为都可控。
还有一点很重要:MyBatis让你保留了对SQL的完全控制权。比如评分明细表需要按“比赛ID+队伍ID+评委ID”联合去重,这个用注解式的@Insert和@Select虽然也能写,但可读性和维护性远不如在XML里清晰。如果你在简历或毕设说明里写“熟练使用MyBatis”,那么多年的面试题基本都会围绕SQL映射、动态SQL、一级缓存二级缓存来问。用MyBatis做一个带复杂统计查询的评分系统,正好能在项目里支撑这些技能点,答辩时也有真实案例可说。
2.3 Vue端选择的细节
前端为什么选Vue?最现实的原因是它的学习曲线比React平缓,而且中文资料极其丰富。Vue2的选项式API对新手非常友好,模板语法直观,v-model做表单绑定几乎是零成本;Vue3的组合式API则更适合项目结构复杂化之后的逻辑复用。我当时用的是Vue2 + Element UI的组合,现在新做的同学可以考虑Vue3 + Element Plus,但核心交互思路是完全一样的。
真正需要想清楚的是前端在整个系统里的定位。评分系统有两个使用场景:评委打分端和管理员后台端。评委端的核心诉求是“快”——界面要大、按钮要清晰、提交要顺畅、不能误操作;后台端的核心诉求是“全”——比赛配置、队伍管理、成绩列表、数据导出都要有。如果这两种场景都用一套Vue页面去做,代码会非常臃肿。更合理的方案是用Vue Router做路由区分,评价台(ScoringBoard)和后台管理(Admin)分成两套独立布局,甚至可以拆成两个入口页面。
我实测下来,用Vue做一个“大屏评分界面”比想象中要花更多心思。评委打分时往往面对的是投影仪或者大显示器,鼠标操作精度不如普通网页,所以打分按钮一定要做成大块的可点击区域,而不是一个窄窄的输入框。数字输入要有加减步进器,还要支持键盘盲打。这些细节在本文第5部分我会详细展开。
3. 数据库设计:评分系统最核心的部分不是用户表,而是评分流水表
3.1 表结构总览
很多人做管理系统,上来就建用户表、角色表、菜单表,然后围着这几张表做CRUD,做完发现业务空洞得很。评分系统的数据模型核心不在这里,而在于比赛、队伍、评委、评分、规则这五张业务表怎么组织。我的设计如下:
| 表名 | 用途 | 关键字段 | 说明 |
|---|---|---|---|
| competition | 比赛场次 | id, name, status, score_rule_json, start_time | 一场比赛对应多条队伍参赛记录 |
| team | 参赛队伍 | id, competition_id, name, organization, order_no | 队伍隶属于比赛 |
| judge | 评委 | id, competition_id, name, is_captain | 裁判长标识用于特殊权限 |
| scoring_record | 评分流水 | id, competition_id, team_id, judge_id, score, create_time | 每支队伍的每条评分独立成行 |
| scoring_rule | 评分规则 | id, competition_id, item_name, weight, max_score | 按维度配置规则,如完成度权重0.6 |
还有一个容易被忽视的字段:打分状态。scoring_record表里我给每条记录加了submit_status字段,用来表示这条评分是暂存还是正式提交。为什么需要这个?因为评委在打分的操作过程中,可能先输入一个初稿,检查一下分数均衡性,再正式提交。如果一输入就生效,会造成误提交后还得走“成绩修改审批”流程,很麻烦。这个字段在后续做“成绩复议”功能时也能用上——裁判长可以找到某位评委的原始评分流水进行复核,而不是直接改最终成绩。
3.2 评分流水表为什么不能只存一个最终分
这是我在设计第二版时踩过的一个坑,必须单独拿出来说。第一版我图省事,在team表里直接加了final_score字段,每次算完平均分就update一次,显示时直接取这个字段。后来答辩老师一句话把我问住了:“如果裁判长调整了某个队伍的成绩,原始评委分去哪了?”我才意识到,评分系统的核心资产是评分明细,不是算好的结果。
正确的做法是:评分流水表scoring_record保存每次打分的原始记录,一笔一档,永不可覆盖。最终成绩要么通过查询实时计算,要么单独存一张score_summary表,但在score_summary里也要保留calc_source_id,指向某条流水记录,以便追溯。从数据库规范化角度看,这其实就是“明细数据与汇总数据分离”的原则。
实际实现时,我用了MyBatis的方式建表,核心建表SQL如下:
CREATE TABLE scoring_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, competition_id BIGINT NOT NULL COMMENT '比赛ID', team_id BIGINT NOT NULL COMMENT '队伍ID', judge_id BIGINT NOT NULL COMMENT '评委ID', category VARCHAR(50) NOT NULL COMMENT '评分维度:completion/artistry/difficulty', score DECIMAL(5,2) NOT NULL COMMENT '单项得分', status TINYINT DEFAULT 0 COMMENT '0暂存 1已提交', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_team_category_judge (team_id, judge_id, category) );这里的联合唯一键非常关键:它保证了同一评委对同一队伍的同一评分维度只能有一条记录。如果评委手误重复提交,系统会通过INSERT ... ON DUPLICATE KEY UPDATE把原记录更新掉,而不是再插一条新数据。这从数据库层面杜绝了“同一个人打了两次分”的脏数据风险。
3.3 规则配置的灵活设计:用JSON还是用独立表
每种比赛的评分维度有可能不同,有的健美操比赛分完成分、艺术分、难度分三项,有的只分完成度和艺术表现两项。如果我把评分维度写死在数据库字段里,比如建一个completion_score列和一个artistry_score列,那么新增维度就必须改表结构,非常僵化。
我的方案是把规则配置做成了两种层次。第一层是比赛级别的评分规则表(scoring_rule),存维度名称、满分值、权重,每一行代表一个可配置的评分项;第二层是每场比赛的评分上下限,存在competition表的score_rule_json字段里。用JSON存评分上下限是因为这块数据比较零散、且只在计算时用一次,不需要单独建表。这样动态加减维度时,只需要前端动态渲染评分项,后端接口传入一个List 就行,后续无论比什么类型的健美操,系统都不用改代码。
数据库设计到这里,基本能把一个评分系统的“骨头”立起来了。接下来就是后端接口和评分算法怎么实现的问题。
4. 评分算法与后端接口实现:去掉最高最低分之后,数据怎么流转
4.1 核心算法:去掉最高分最低分的边界情况
评分系统的后端核心函数是calculateFinalScore。算法听起来简单:每个评委的分数相加,去掉一个最高和一个最低,然后取平均。但这里有至少三个边界情况需要注意。
第一,评委人数不同时规则要变。比如只有3个评委,去掉一个最高、去掉一个最低,最后就只剩下一个人打的分,这显然不公平。所以实际项目里必须约定:评委人数小于等于4人时,不去除任何分数,直接取算术平均;5到6人时,去掉一个最高一个最低;7人以上时,可以根据比赛规则去掉两个最高两个最低。具体参数通过前端配置保存到score_rule_json里,后端只读配置、不写死逻辑。
第二,分数相同怎么办。比如9.5分出现了3次,去掉一个最高分时要去掉哪一个?业务上只需要在排序后去掉一个即可,不影响结果,因为相同分数去掉哪一个最后的平均分都一样。比较考验实现的是List的排序稳定性,建议用Stream排序时保留原始顺序,避免不必要的误解。
第三,裁判长的调整权。裁判长在特殊情况下可以绕过“去掉最高最低”的算法,直接修正某个队伍的成绩,比如动作严重失误或比赛中断、重赛。修正后的成绩要单独在score_adjustment表里留痕,不能直接改scoring_record,否则原始评委数据就没了。这是我实现“成绩复议”功能的思路,答辩时是加分项。
核心计算方法的代码实现如下(基于Java 8+和SpringBoot):
public BigDecimal calculateFinalScore(List<BigDecimal> scores, RuleConfig rule) { if (scores.size() <= rule.getNoRemovalThreshold()) { return average(scores); } List<BigDecimal> sorted = scores.stream() .sorted(Comparator.comparing(BigDecimal::valueOf)) .collect(Collectors.toList()); int removeCount = rule.getRemoveCount(); // 去掉的最高/最低个数 List<BigDecimal> validScores = sorted.subList(removeCount, sorted.size() - removeCount); return average(validScores); } private BigDecimal average(List<BigDecimal> values) { BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal result = sum.divide(BigDecimal.valueOf(values.size()), 2, RoundingMode.HALF_UP); return result; // 保留两位小数,四舍五入 }4.2 接口设计:按业务模块划分,而不是按表划分
很多毕设项目的Controller代码是“一张表一个接口组”,比如TeamController就是队伍的增删改查,ScoreController就是评分的增删改查。这种做法的毛病在于,前端页面需要的数据往往横跨多张表——评分台要同时显示队伍名称、评委名称、当前维度分数,如果前端要发多个请求拼数据,体验会很差。
我给这个项目划分接口的原则是“按页面场景聚合”。后端几个必要的模块如下:
- /api/competition:比赛管理的创建、启停、详情查询,创建比赛时同时批量初始化规则和评委。
- /api/team:队伍管理,附带比赛总览页需要的队伍列表(仅当比赛启动后可见)。
- /api/scoring:评委打分与提交操作,包含暂存、提交两个动作,提交时做校验。
- /api/result:成绩汇总与排名查询,返回的是已按规则计算好的最终成绩单。
- /api/export:成绩导出,支持导出Excel格式的成绩表。
以成绩汇总接口为例,它的返回结构设计为:
{ "competitionId": 1, "category": "决赛", "rankings": [ { "rank": 1, "teamId": 12, "teamName": "阳光健美操队", "organization": "XX大学", "finalScore": 9.23, "detail": [ {"category": "completion", "score": 9.10}, {"category": "artistry", "score": 9.36} ] } ] }这里最终成绩不是直接查表得到的,而是后端每次实时计算,好处是无论评委评分流水怎么变化(只要还未锁定比赛),排名永远是准确的。缺点是数据量大时查询压力会增大,但毕设场景下几十支队伍完全没问题。如果以后要并发跑几百上千支队伍,可以加一层Redis缓存汇总结果,但那是优化层面的事,不是当前的核心矛盾。
4.3 评分提交的并发控制
现场打分时,多个评委是同时操作的。假设同一场比赛有6个评委,每人打分后都往scoring_record表写入,如果两个评委选了同一支队伍的不同维度,数据库的唯一索引会保证不同评委之间的记录互不冲突。但如果同一评委在同一维度上更新两次,就可能在应用层面出现“后提交的覆盖先提交的”的时序问题。
我的处理方法是给提交操作加上版本号字段并配合乐观锁。scoring_record表增加version字段,提交时先查当前版本号,UPDATE语句带上WHERE version = 当前版本。如果更新影响行数为0,说明数据被其他操作改了,这时提示评委重新加载打分界面,而不是直接覆盖。这在答辩时可以作为一个技术亮点来讲,说明你考虑了数据库并发场景,而不仅仅是写了个单机CRUD。
5. Vue端打分操作台的交互设计:把“评分”做成能用、好用的页面
5.1 评委端页面的布局思路
评委端是评分系统使用频率最高的页面,它的首要特征是让评委完成打分的步骤要短。一个评委从看到队伍表演结束到提交分数,可能只有30秒时间,这30秒内他要在地图上找到队伍、输入分数、点击提交,任何一个环节卡顿都会被选手领队在后台抱怨。
我实际做出来的评委打分页布局极其简单:页面上方是一个当前比赛信息栏(显示比赛名称、队伍编号、队伍名称,字体要大,像体育比赛大屏视角),中部是评分维度区域,每个维度对应一个醒目的数字输入框和加减按钮,下部是三个操作按钮:暂存、提交、清空。所有输入框默认给一个合理初始分,比如9.0分,评委只需要在基础分上微调,而不是完全手动输入一个两位数。这样能大幅减少输入错误率。
5.2 v-model与动态评分项的渲染
海豚评测维度是动态的,这给Vue的模板渲染带来一个之前没预料到的问题。v-model绑定在动态渲染的表单控件上,要让数据模型能自动匹配评分项。做法是在data里维护一个scoreForm对象,键是动态维度的编码,值是分数。
data() { return { scoreForm: {}, ruleItems: [] // 从后端拉取,比如 [{code:'completion', name:'完成度', maxScore:10}, {code:'artistry', name:'艺术表现', maxScore:10}] } }, created() { this.fetchRuleItems().then(items => { this.ruleItems = items const form = {} items.forEach(item => { form[item.code] = 9.0 }) this.scoreForm = form }) }模板里的写法:
<div v-for="item in ruleItems" :key="item.code"> <span>{{ item.name }}</span> <el-input-number v-model="scoreForm[item.code]" :min="0" :max="item.maxScore" :step="0.1"/> </div>这里有个细节:el-input-number组件默认的步进是1,但健美操评分通常需要0.1的精度,所以必须显式设置:step="0.1"。还要设置:precision="1",否则会出现9.10显示成9.1、或者0.1相加产生浮点误差的问题。
5.3 误操作防护:提交前的二次确认与状态冻结
评分提交是压力最大的操作。评委手一抖把9.5输成9.2提交了,虽然可以走修改流程,但会给比赛组织方添麻烦。我从真正比赛现场听来的教训是:评委提交后的分数,至少在比赛结果公布前不能随意改。
所以我在前端做了三件事。第一,提交按钮点击后弹出确认框,显示“您为XX队伍打出的分数为9.5/9.6/9.2,确认提交吗?”,让评委在提交前有一道主动确认的心智关卡。第二,比赛锁定后(competition.status=2),前端把打分表单全部置为disabled,同时后端也校验锁定状态,双保险防止数据被改。第三,提交成功后,对应页面的队伍卡片变成灰色,表示已评分完成;评委如果想修改已提交的分数,需要点击“申请修改”按钮,这个操作会通知裁判长,由裁判长在后台决定是否解锁该队伍的评分。这个设计在答辩时演示效果很好,因为它是从真实业务里来的,不是凭空想的。
5.4 大屏排名展示页
除了评委打分,系统还有一个面向观众的“成绩展示大屏”页面,通过Vue Router的独立路由访问。它做的事情非常简单:每隔5秒轮询成绩汇总接口,用Element UI的进度条和排名列表把结果渲染出来。
这个页面比较考验的是接口轮询的时间间隔设计。太密了服务器压力大,太疏了观众看到的排名滞后。我实测用的5秒间隔,在几十支队伍的规模下压力可以忽略。后来做了一个更优雅的方案:后端用WebSocket推送排名变化,前端不用轮询,实时性更高。如果你时间充裕,可以在毕设里把WebSocket版加上,这是一个很不错的加分项;如果时间紧,短轮询也完全可以接受。
6. 从0到1跑通完整源码的步骤,以及我踩过的那些坑
6.1 环境准备:版本选择比你想的重要
这个项目涉及到的环境组件比较多,版本匹配稍有不慎就会浪费大量时间。我提一版稳定可用的版本组合:
| 组件 | 版本 | 说明 |
|---|---|---|
| JDK | 1.8 | SpringBoot 2.x系列兼容性最好 |
| SpringBoot | 2.7.x | 不要选3.0以上,部分适配Java 17,前端对接反而麻烦 |
| MyBatis | 2.2.2(starter版) | 直接引mybatis-spring-boot-starter |
| MySQL | 8.0.x | 注意mysql-connector-java的版本与驱动类名 |
| Node.js | 14.x或16.x | Vite构建Vue3的话推荐16+ |
| Vue CLI | 4.x | 如果用Vue2 + Element UI |
其中最大的坑在JDK和SpringBoot的搭配。SpringBoot 2.7在JDK8下跑得顺畅,但如果你装了JDK17,SpringBoot2.7依然可以运行,前提是pom里要加spring-boot-starter-parent作为父依赖。我遇到过一位同学,机器上默认JDK是17,项目里某些依赖用了反射调用,在JDK17的强封装下直接报IllegalAccessError,排查到夜里两点才发现是版本问题。所以我的建议很直接:毕设项目用JDK8,别挑战新版本。新特性对管理系统毫无意义,而老版本的稳定性和资料丰富度是绝对优势。
6.2 初始化步骤:从建库到启动
源码附带的SQL脚本文件通常是一个init.sql,你需要先手动建一个数据库,然后执行脚本:
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS aerobics_scoring DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" mysql -u root -p aerobics_scoring < init.sql执行完以后,脚本会自动建表并插入默认的演示数据(两个比赛、若干队伍、四个评委、若干条评分流水)。这个演示数据很重要,你第一次启动项目登录后台时就能看到排名数据,不需要自己手工录入一遍。
后端启动是标准的SpringBoot流程:
cd backend mvn spring-boot:run这里有两个常见问题。第一,mvn命令不能识别,说明Maven没有安装或没有加入PATH环境变量,去配MAVEN_HOME即可。第二,启动时报数据库连接失败,先检查application.yml里的数据库密码是不是你本机的MySQL密码;再检查MySQL是不是只监听本地端口,如果MySQL跑的是一台远程服务器,要确认bind-address和账号权限。很多同学忽略后者——在云服务器上装MySQL,默认监听127.0.0.1,本地连不上,要改/etc/mysql/mysql.conf.d/mysqld.cnf 里的bind-address为0.0.0.0,并给账号授权允许远程访问。
前端启动:
cd frontend npm install npm run serve如果你第一次执行npm install卡住不动,多半是registry网络问题。把npm源切换到国内镜像:npm config set registry https://registry.npmmirror.com,再装就快了。依赖安装完以后,还要检查src/utils/request.js里的axios baseURL是不是指向后端地址http://localhost:8080。跨域问题在后端已经通过CORS配置解决了,前端基本不用额外处理。
6.3 踩坑记录:MyBatis映射不够灵活
使用MyBatis做动态条件查询时,最容易出现的错误是动态SQL里的where条件写死。比如我的成绩汇总接口要根据competition_id、team_id、category三个可选条件过滤,如果我在XML里直接用#{}拼接三个条件,其中一个参数为空时SQL就会报错。
正确做法是使用MyBatis的 和 标签:
<select id="queryScoringRecords" resultType="ScoringRecordDO"> SELECT * FROM scoring_record <where> <if test="competitionId != null"> AND competition_id = #{competitionId} </if> <if test="teamId != null"> AND team_id = #{teamId} </if> <if test="category != null and category != ''"> AND category = #{category} </if> </where> ORDER BY create_time DESC </select>注意一个问题: 标签里的test是OGNL表达式,写的时候别把字符串和数字用==比较混了。数字类型用!= null判断,字符串类型还要加!=''判断,这是语法细节,也是最常见的报错来源。
6.4 完整源码的组织结构
拿到完整源码之后,第一件事不是双击运行,而是先看懂目录结构。这个项目的源码组织结构如下:
- backend/
- src/main/java/com/scoring/controller/ —— Controller层
- src/main/java/com/scoring/service/ —— Service层,评分计算的核心逻辑
- src/main/java/com/scoring/mapper/ —— MyBatis的Mapper接口
- src/main/resources/mapper/ —— MyBatis的XML映射,动态SQL都在这里
- src/main/resources/application.yml —— 配置文件
- frontend/
- src/views/ —— 前端页面,包括评委打分台和大屏展示
- src/router/ —— 路由配置,分为admin和scoring两个模块
- src/api/ —— 封装的接口请求模块
- doc/ —— 项目说明、数据库设计文档、答辩PPT目录
其中service层的ScoreCalculationService不要只当普通类看,它是整个项目最核心的算法模块,我在代码中提供的scoreItem聚合、最高最低分去除、权重计算等方法,都在这个类里。读懂它比读懂任何一张表都重要。
7. 作为项目的作者,我最后想提醒你的几个细节
7.1 演示时准备好“比赛数据剧本”
如果你要把这个系统用于答辩演示,千万不要现场从零创建比赛、录入队伍再打分。那既浪费时间,又会让评委觉得你对系统不熟悉。我的经验是提前初始化一个完整的数据剧本:一场“第二十三届阳光杯健美操大赛”,有8支队伍、6个评委、1个裁判长,每支队伍都有评分记录和排名结果。演示时你只需要打开系统,展示比赛列表、排名大屏、评委打分界面,然后说“接下来模拟两位评委对A队重新打分”,现场实时看到排名刷新,这个演示效果比干巴巴地讲功能列表好一百倍。
7.2 答辩时怎么讲技术亮点
老师最常问的一句话是“哪里体现了你自己的工作量”。我的建议是挑三个点深入讲,每个点都能展开三分钟。第一个是评分规则的可配置化——不是写死算法,而是把维度和权重都存库,前端可以动态渲染。第二个是评分追溯机制——原始流水不可覆盖,最终成绩永远能从明细算出来。第三个是前后端分离的工程化——跨域处理、接口按页面聚合、前端路由权限控制。这三个点任何一个都比“我用了SSM框架”更有说服力。
7.3 代码可以抄,思路一定要变成自己的
说到底,这个系统并不难——SpringBoot和Vue都是成熟框架,评分算法也没有用到什么高深的数学知识。真正有价值的是你通过这个题目,理解了业务分析怎么转化成数据模型,数据模型怎么转化成接口设计,接口设计又怎么驱动前端页面实现。这一整条链路的思维方式,才是做这类项目真正要带走的东西。我自己当时做完这个题目之后,再看其他“XX管理系统”的题目,都能一眼看出它的业务骨架应该怎么搭,这才是学习的目的。
如果你在运行源码的过程中遇到具体报错,比如依赖下载失败、MySQL版本连不上、前端路由404,先按我在第6部分列出的排查思路过一遍,大部分问题都能自己解决。希望这篇内容能帮你从选题到答辩,走得比我当年更顺利。