新闻详情

新闻详情

首页 / 资讯中心 / 详情

SpringBoot+Vue+MySQL+MyBatis实现健美操评分系统全栈实战

发布时间:2026/10/2 1:45:27来源:尧图网络
SpringBoot+Vue+MySQL+MyBatis实现健美操评分系统全栈实战
搞过几个管理系统之后我越来越觉得“健美操评分系统”这种看着不起眼的小项目反而是练全栈功底的好材料。它不像电商、论坛那样业务铺得大但麻雀虽小五脏俱全有多角色权限、有带业务规则的评分算法、有实时性要求不高的数据展示、还有评委与选手之间典型的实体关系。如果只用SpringBoot Vue MySQL MyBatis这套最主流的组合去实现正好能把Java后端、关系型数据库设计、前端交互串成一条完整的链路。这个项目解决的实际问题很具体健美操比赛里传统的打分方式是评委纸质打分工作人员手工录入Excel、去掉最高分最低分、再算平均分最后人工排名公示。流程繁琐不说还容易出错——Excel公式拉错一行、分数抄错一个数字都会影响比赛公平。这个系统就是把整个环节搬到线上评委登录后看到自己负责的选手列表在线打分提交系统自动完成分数计算和排名刷新管理员还可以管理选手、评委、比赛场次比赛一结束成绩单就能立刻公示。对于正在做毕业设计、课程设计或者刚学完Java和Vue想找个完整项目练手的朋友来说这套实现思路能让你少走不少弯路。接下来我按项目从设计到落地的完整过程把核心环节拆开讲清楚全部都是实测过的方案。1. 项目整体设计与技术选型解析很多同学一上来就纠结“到底用SpringBoot还是SSH”“用不用JPA”其实选型这件事核心看两个维度一是团队或者你自己对技术栈的熟悉程度二是项目本身的业务复杂度。健美操评分系统属于典型的中小型信息管理系统数据量级就是几千条评分记录、几百个选手没有高并发、没有复杂分布式问题这时候选择成熟稳定、生态好、资料多的技术组合就是最优解。1.1 为什么是SpringBoot Vue MySQL MyBatis这套组合先说我推荐这套组合的真实理由。SpringBoot的本质是“约定大于配置”它对Spring生态做了一层自动装配把原来SSH框架里那些繁琐的XML配置文件、Bean定义、事务声明全部简化了。我记得第一次用IDEA新建SpringBoot项目一个空的Web项目跑起来只需要几秒钟内嵌Tomcat直接启动不用单独装服务器。开发效率的提升是非常直观的。MyBatis和JPA的对比可能是很多人的纠结点。JPA确实省事但遇到评分系统这种“多表联查 动态条件 分组统计”的场景写JPQL反而别扭调试的时候还得先脑内翻译成SQL。MyBatis就非常直接SQL自己写动态SQL用if标签拼条件查询结果映射成实体类。出了问题打开日志就能定位是哪条SQL写错了对新手特别友好。评分系统里恰好有一堆需要精细控制的SQL——比如去掉最高分最低分后的平均分计算、多表联查排名、按组别分组汇总MyBatis的掌控力是JPA给不了的。Vue是前端MVVM框架里文档最友好、社区最活跃的一个。评分系统前端的典型页面评分页、排名榜页、管理后台每个页面都有交互逻辑用Vue的组件化开发拆起来特别清晰。双向绑定让“打分滑块 实时显示分数”这种交互一行v-model就搞定。配合Vue Router做页面跳转、Axios发请求前端的整套套路非常稳定。MySQL就不用多说了免费、跨平台、资料多到查都查不完。评分系统这个量级MySQL的性能绰绰有余哪怕一场比赛有50名选手、7名评委全部分数记录也就350条加上索引查询都是毫秒级响应。1.2 系统核心角色与功能边界划分评分系统最核心的设计前提是角色之间职责必须清晰。我把角色分成三类每个角色的权限边界从一开始就定死避免后期改起来头疼。管理员维护基础数据选手、评委、比赛场次管理用户账号、重置密码查看全部成绩和排名。评委登录后只能看到分配给自己的评分任务提交评分后在自己的评委视角下不可修改防止赛后改分争议。访客/观众查看公示的成绩排名这个角色按需加有些比赛要求大屏实时展示分数趋势可以做只读页面。功能模块上我划分为选手管理、评委管理、比赛场次管理、在线评分、成绩计算、排名展示六大块。其中在线评分和成绩计算是核心中的核心后续的设计都围绕这两块展开。整个项目用Maven管理依赖后端标准的三层架构Controller - Service - Mapper分层清晰前端用Vue CLI搭建工程配合Element UI组件库快速铺页面。2. 核心业务规则与数据库设计一个好的评分系统代码写得再好如果业务规则没有在数据层面考虑周全上线就会被各种边界情况打脸。所以数据库设计这一步宁可多花时间想清楚也不要急着建表。2.1 评分规则如何落到系统里健美操比赛的评分规则常见的模式是多位评委各自独立打分然后去掉一个最高分、去掉一个最低分取剩余评委分数的平均值作为选手的最终得分。这个规则看似简单但落实到代码里要考虑几个边界情况。第一是评委人数不足。如果一场比赛只安排了2个评委去掉了最高和最低那就没分数了。我当时的方案是评委人数小于3人时不做去头尾处理直接取平均分。这条规则要在配置里做成可选项而不是写死在代码里。第二是分数范围控制。健美操打分通常精确到0.1分满分可以设定为10分也可以按比赛规程自定义。系统里要做一个全局的评分参数配置表把“总分上限”“是否去掉最高最低”“精确到几位小数”都放到配置里这样换一场规则不同的比赛也能复用系统。第三是评分的多轮次问题。有些比赛分预赛和决赛同一个选手在不同场次分别打分。所以评分记录必须和比赛场次关联一套选手数据多次使用。为了避免业务规则散落在代码各处理解困难我的做法是把规则参数独立出来参数项说明默认值score_max单次评分上限10.0score_min单次评分下限0.0allow_trim是否去掉最高最低truetrim_count去掉个数双边各11precision分数精度位数12.2 数据库表结构设计与关键约束数据库我一共设计了6张核心表把每张表的用途和关键字段梳理清楚你就明白为什么评分系统不能只建三张表了。第一张是用户表sys_user存所有登录账号包括管理员和评委。字段有id、username、password存MD5加密后的密文、roleadmin/judge、real_name。这里要特别注意password字段的长度MD5加密后是32位字符长度至少要设成64给后续升级加密算法留余地。第二张是选手表athlete字段包括id、athlete_no参赛编号、name、team_name代表队、category项目组别比如“有氧操”“啦啦操”“街舞”、group_type单人/集体。参赛编号建议手动录入格式可以统一成“A001”这种带前缀的字符串方便比赛现场检索。第三张是比赛场次表match_info字段包括id、match_name场次名称、match_time、status0未开始/1进行中/2已结束。这里有个实用细节比赛开始前管理员要能够“初始化场次”系统自动为该场次下所有没有评分的选手生成空的评分占位记录这样评委登录后看到的是“待评分”列表而不是一张白纸。第四张是评分记录表score_record这是全系统最核心的表。字段包括id、match_id比赛场次、athlete_id选手、judge_id评委、score评分、create_time。这张表上必须加一个唯一约束UNIQUE KEY uk_match_athlete_judge (match_id, athlete_id, judge_id)——这个约束极其重要从数据库层面保证同一个评委对一个选手在同一场次只能有一条评分记录这是防止重复打分的最后一道防线。代码层面还要配合做判断收到重复提交时返回友好提示。第五张是最终成绩表final_score字段包括id、match_id、athlete_id、final_score最终得分、rank_no排名、calculate_time。最开始我偷懒没建这张表想着每次展示排名实时计算就好。后来实测发现随着数据量增大排行榜页面每次查询要同时算出几十个选手的去掉最高最低分平均值SQL越来越复杂响应时间也越来越慢。后来我改成“提交评分时同步计算最终成绩并写入这张表”排行榜查询就退化成了一次简单的JOIN ORDER BY性能立竿见影。第六张是评分参数表score_config存的是2.1节里那些规则参数。配置表用单行记录配合Redis缓存或者应用内缓存避免每次评分都查一次数据库。这个设计在后续扩展其他比赛项目时价值会越来越明显。数据库字符集我统一用utf8mb4别用utf8。utf8在MySQL里最多存3字节的字符遇到生僻字、表情符号、特殊符号时会报错或乱码。连接串也务必加上characterEncodingutf8。3. 后端实现SpringBoot MyBatis核心实操后端这部分是评分系统的技术核心。我按照代码组织的顺序从工程结构、核心接口、数据库操作三个层面把实现细节过一遍附上可直接参考的关键代码。3.1 工程结构与分层设计后端包结构我按照常见的业务分层来组织但做了两个优化。com.example.aerobicscore ├── controller // 控制层接收前端请求 ├── service // 业务层核心逻辑在此 ├── mapper // MyBatis Mapper接口 ├── entity // 数据库实体类 ├── dto // 数据传输对象 ├── common // 通用类统一返回结果、异常处理 └── config // 配置类跨域、MyBatis、拦截器第一个优化是添加了dto包。很多入门项目只在前端传参时用Map接数据导致代码里全是魔法字符串维护起来像在读天书。我在评分提交接口里定义了ScoreSubmitDTO里面是matchId、athleteId、score三个字段用NotBlank、NotNull做参数校验代码清晰度完全不一样。第二个优化是统一封装返回结果。common包下定义了Result类包含code、message、data三个字段。接口统一返回Result.success(data)或Result.error(msg)。配合全局异常处理器RestControllerAdvice所有异常都能返回结构统一的JSON前端拦截器就可以集中处理错误提示而不是每个页面单独写。3.2 在线评分接口的设计与实现评分提交是系统里最关键的接口我把这个接口的完整实现逻辑讲透。Controller层代码如下。RestController RequestMapping(/api/score) public class ScoreController { Autowired private ScoreService scoreService; PostMapping(/submit) public Result? submit(RequestBody Validated ScoreSubmitDTO dto) { // 当前登录评委ID由拦截器写入ThreadLocal Long judgeId UserContext.getUserId(); scoreService.submitScore(dto.getMatchId(), dto.getAthleteId(), judgeId, dto.getScore()); return Result.success(null); } }Service层的核心逻辑我在实际开发中是这样验算的先检查比赛状态只有“进行中”才允许打分再检查该评委是否已经打过分数查询score_record表是否存在记录存在则抛异常“您已为该选手打分不能重复提交”分数范围校验必须在score_min和score_max之间。都通过后插入评分记录然后触发成绩重新计算。这里有一个非常重要的实践心得插入评分记录和计算最终成绩必须放在同一个事务里。我当时接手这个项目的第一版代码时评分提交和成绩计算是两个独立事务结果出现了一个问题——评委提交评分后立刻看排行榜分数没变化过了一两秒才刷新出来。虽然最终数据是对的但现场评委体验很不好。加上事务后提交和计算是原子操作排行榜即时更新。Transactional(rollbackFor Exception.class) public void submitScore(Long matchId, Long athleteId, Long judgeId, BigDecimal score) { // 1. 校验比赛状态 MatchInfo match matchMapper.selectById(matchId); if (match null || match.getStatus() ! 1) { throw new BizException(比赛未在进行中无法打分); } // 2. 防止重复评分 int count scoreRecordMapper.countByMatchAthleteJudge(matchId, athleteId, judgeId); if (count 0) { throw new BizException(您已为该选手打分); } // 3. 插入评分记录 ScoreRecord record new ScoreRecord(); record.setMatchId(matchId); record.setAthleteId(athleteId); record.setJudgeId(judgeId); record.setScore(score); scoreRecordMapper.insert(record); // 4. 重新计算最终成绩 recalcFinalScore(matchId, athleteId); }3.3 成绩计算算法与排行榜SQL成绩计算算法就是“去掉最高分最低分取平均”的Java实现。这里我特别想纠正一个常见的思路误区很多人觉得这个计算应该用一条复杂的SQL在数据库里完成显得“技术牛”。但实测下来把轻量级计算放Java里反而更可靠。一个选手一场比赛的评分记录最多也就十几条全部查出来在内存里排序、掐头去尾、算平均值逻辑一目了然出错了也好排查。public void recalcFinalScore(Long matchId, Long athleteId) { ListScoreRecord records scoreRecordMapper.selectByMatchAndAthlete(matchId, athleteId); if (records.isEmpty()) { return; } ScoreConfig config scoreConfigMapper.getCurrentConfig(); // 按分数排序 ListBigDecimal scores records.stream() .map(ScoreRecord::getScore) .sorted(Comparator.naturalOrder()) .collect(Collectors.toList()); BigDecimal finalScore; if (config.getAllowTrim() scores.size() 5) { // 去掉最低和最高 ListBigDecimal trimmed scores.subList(1, scores.size() - 1); finalScore trimmed.stream() .reduce(BigDecimal.ZERO, BigDecimal::add) .divide(BigDecimal.valueOf(trimmed.size()), config.getPrecision(), RoundingMode.HALF_UP); } else { finalScore scores.stream() .reduce(BigDecimal.ZERO, BigDecimal::add) .divide(BigDecimal.valueOf(scores.size()), config.getPrecision(), RoundingMode.HALF_UP); } finalScoreMapper.upsert(matchId, athleteId, finalScore); }注意上面我设置的一个条件scores.size() 5才去皮而不是 3。原因是如果只有3个评委去掉最高最低后只剩1个分数而这个分数刚好是中间值那单个评委的结果就等于全场决定很容易引发争议。而在5个评委的情况下去掉头尾剩3个分数取平均规则公平性更合理。当然这个阈值也应该放进配置表不同比赛要求不同。排行榜的查询SQL因为有了final_score表反而简单了。但有一个召集技巧值得分享排名要按组别分开还要显示每个选手的评委人数和均分明细这时候关联查询和子查询配合使用效果最好。最终实现如下。select idselectRanking resultTypecom.example.aerobicscore.dto.RankingDTO SELECT a.athlete_no, a.name AS athlete_name, a.team_name, a.category, fs.final_score, (SELECT COUNT(*) FROM score_record sr WHERE sr.match_id #{matchId} AND sr.athlete_id a.id) AS judge_count, RANK() OVER (PARTITION BY a.category ORDER BY fs.final_score DESC) AS rank_no FROM final_score fs LEFT JOIN athlete a ON fs.athlete_id a.id WHERE fs.match_id #{matchId} ORDER BY a.category, fs.final_score DESC /select这是MySQL 8.0及以上版本的写法用了窗口函数RANK() OVER (PARTITION BY category ...)分组排名直接搞定。如果你的环境是MySQL 5.7那没办法用窗口函数得改成在三层嵌套子查询里用用户变量模拟SQL会绕不少。我建议直接上MySQL 8.0已经是完全免费且稳定没必要守着5.7。3.4 MyBatis动态SQL的几个实用技巧评分系统里查询条件变化很多——按组别筛选、按比赛状态筛选、按代表队筛选这些都需要动态SQL。我在实操里遇到过三个典型问题值得记下来。第一个是if标签里的逗号处理。在写update语句时很多新手会写update idupdateAthlete UPDATE athlete set if testname ! nullname #{name},/if if testteamName ! nullteam_name #{teamName},/if /set WHERE id #{id} /updateset标签会自动处理末尾多余的逗号这是MyBatis内置的智能处理。但如果用trim自定义拼接一定要小心最后一项后面不能有逗号。我的建议是能用set就用set别自己造轮子。第二个是where标签与if的配合。排名查询里根据前端传入的category过滤where标签会自动去掉开头的AND不必担心SQL语法错误。第三个是批量插入的效率问题。初始化一场比赛的评分占位记录时可能一次性要插入几百条score_record空记录。如果循环单条插入性能和数据库压力都不好。用MyBatis的foreach批量插入一次SQL搞定。insert idbatchInsert INSERT INTO score_record (match_id, athlete_id, judge_id, score, create_time) VALUES foreach collectionlist itemitem separator, (#{item.matchId}, #{item.athleteId}, #{item.judgeId}, NULL, NOW()) /foreach /insert这里评分字段score先置为NULL表示“待打分”。查询待评分列表时过滤条件就是score IS NULL非常直观。4. 前端实现Vue核心实操前端用Vue 2 Element UI Axios这套方案虽然Vue 3已经很成熟但对于课程设计和中小型管理系统Vue 2 Element UI的资料量、稳定性、踩坑解决方案的丰富程度仍然是最好的选择。当然如果你熟悉Vue 3 Element Plus思路完全一致。4.1 路由与权限控制的实现前端页面结构我规划了五个路由页面登录页、评委评分页、排行榜页、选手管理页、系统设置页。路由配置里最核心的是全局前置守卫。router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { next(); } else { if (!token) { next(/login); } else if (to.meta.role to.meta.role ! localStorage.getItem(role)) { next(/403); } else { next(); } } });这个守卫逻辑做了两件事未登录的用户全部跳到登录页登录了但权限不够的用户跳到403页。比如评委账号手动输入管理员页面的URL会被拦截下来。当然这只是前端路由层的控制真正的安全还是要靠后端接口的权限校验前端控制更多是用户体验层面的优化。4.2 评分页面的交互设计评分页是整个前端工作量最大的部分。评委进入页面后看到的是待评分选手列表。每一行展示选手信息、参赛编号、代表队右侧是分数输入区和提交按钮。我推荐用v-model绑定一个分数对象评分输入用数字输入框限制范围并加上实时校验逻辑分数必须大于等于0且小于等于配置的最大分且只能保留一位小数。避免评委把9.5输成95这种低级失误。Axios请求统一封装成一个request.js模块设置baseURL为http://localhost:8080/api在请求拦截器里自动添加token到请求头在响应拦截器里统一处理后端返回的code。后端返回code为401时自动跳转登录页code为500时弹出错误信息。这样一个项目里所有页面都复用这套逻辑不会在几十个组件里各写各的请求代码。打分提交成功后不要立即清空列表而是把该选手的状态改为“已评分”并置灰。这样评委能直观看到自己的完成进度避免反复对照纸质评分表确认。这个小细节是评测老师给的反馈说比原来纯数字列表好用很多。4.3 排行榜展示与数据刷新排行榜页面我用了Element UI的el-table展示排名列表配合ECharts做一个分数分布的可视化图表。表格展示名次、选手、代表队、组别、最终得分、评委人数。实时刷新这里很多人第一反应是上WebSocket。但实测下来评分系统的实时性要求没有那么高——评委提交评分后一两秒内看到结果刷新就完全够用。我用的是setInterval定时轮询每5秒请求一次最新排行榜数据。逻辑简单也不增加服务器负担。fetchRanking() { getRanking(this.matchId).then(res { this.rankingList res.data; }); }, mounted() { this.fetchRanking(); this.timer setInterval(this.fetchRanking, 5000); }, beforeDestroy() { clearInterval(this.timer); }ECharts部分我用一个柱状图展示前10名选手的得分对比一个折线图展示某一单项比如完成度的得分趋势。这类可视化虽然不影响核心功能但放在比赛现场的大屏上观感完全是两个档次。前端还有一个必须提的坑打包部署。Vue项目运行npm run build之后生成dist目录把这个目录下的文件复制到SpringBoot的src/main/resources/static目录下后端启动后直接访问http://localhost:8080/index.html就能看到整个前端页面。但有一个问题SpringBoot默认静态资源映射在classpath:/static/你的Vue路由如果是history模式直接刷新某个子路由页面会404因为后端找不到/score、/ranking对应的Controller。解决办法有两个一是Vue路由用hash模式URL里带#号二是在后端加一个转发Controller把非API路径全部转发到index.html。Controller public class PageForwardController { RequestMapping(value {/, /score, /ranking, /manage}) public String forward() { return forward:/index.html; } }我实际项目里用的是hash模式简单直接而且部署到任何静态服务器都不会出问题。但如果你追求URL美观用history模式就得配置后端的fallback。5. 常见问题与排查技巧实录这部分整理的是我实际开发中踩过的坑和排查思路很多问题看起来不起眼但真遇到了能卡上大半天。按照问题、原因、解决方案的格式整理出来。5.1 前后端联调时的跨域问题这是几乎所有前后端分离项目的第一个坎。现象是前端Vue页面发起请求浏览器控制台报错Access-Control-Allow-Origin。原因其实不复杂前端运行在http://localhost:5173Vue默认端口后端在http://localhost:8080不同端口访问属于跨域浏览器为了安全默认拦截。后端解决方式是在SpringBoot里配置全局跨域。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }这里有一个我踩过的坑如果开启了allowCredentials(true)allowedOrigins(*)会失效必须使用allowedOriginPatterns(*)。这个问题排查了很久报错信息不太直观。另外记得前端Axios请求要加withCredentials: true否则携带cookie或认证信息时又会出现新的跨域问题。当然高级一点的方案是通过Nginx反向代理统一前端和后端的访问地址连跨域都省了但单机开发时Vue的devServer代理配置也足够用。5.2 MyBatis查询结果全是NULL字段这个坑特别隐蔽。现象是接口返回200但数据对象的每个字段都是null。排查了半天SQL也没问题最后发现是MyBatis的实体类属性映射问题——数据库字段athlete_no实体类属性是athleteNo下划线和驼峰没有自动转换。如果数据库里全是athlete_no、team_name这种下划线命名MyBatis默认不会自动转驼峰需要开启配置。mybatis: configuration: map-underscore-to-camel-case: true这一个配置加上之后所有下划线字段都能自动映射到驼峰属性。强烈建议设置不管是在SpringBoot的application.yml里还是MyBatis的XML配置文件里。如果项目里已经有一部分手动写了resultMap加上这个配置后手动映射依然生效不会冲突。5.3 MySQL数据库中文乱码问题现象很典型页面上正常显示中文但写入数据库后变成问号或乱码。排查思路按照“连接 - 数据库 - 表 - 客户端”的链路逐层检查。MySQL连接串上一定要加characterEncodingutf8这是最容易被忽略的url: jdbc:mysql://localhost:3306/aerobics_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8数据库创建时指定默认字符集CREATE DATABASE aerobics_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;表没有单独指定字符集时会继承数据库的默认设置。如果你已经建好了表但发现乱码可以执行ALTER TABLE athlete CONVERT TO CHARACTER SET utf8mb4;修改已有表的字符集。归根结底在项目备案初期我前面强调过新建库的第一步就要把所有字符集都统一成utf8mb4后面能省掉大量麻烦。5.4 Vue打包后资源加载404前端npm run build后生成的dist目录文件直接复制到SpringBoot的static目录打开页面发现样式、JS全部404。排查发现原因是Vue CLI默认的publicPath是/打包后的资源引用路径是/js/app.js这种绝对路径。如果把项目部署在服务器根目录下没问题但如果访问路径是http://localhost:8080/index.html此时浏览器会从localhost:8080/js/app.js加载而SpringBoot的静态资源根路径映射的是classpath:/static/看起来应该没问题——实际出现问题时多半是你把dist资源放到了子目录下。解决办法是在vue.config.js中配置publicPath: ./让打包后的资源引用相对路径。这样部署到任何子目录都能正常访问。我在真实服务器上部署时用过这两种方式最稳的是默认/加Nginx根路径部署其次是相对路径适配所有场景。5.5 评委重复提交评分这是最容易出现线上事故的问题。现象是评委手滑点了两次提交导致该选手有了两条评分记录。我在2.2节里说过数据库唯一约束uk_match_athlete_judge是最后一道防线但实际开发中代码层面的双重判断也必须做。我在Service层的submitScore方法里先countByMatchAthleteJudge查询一次再做插入。两条路径都有保护哪怕前端绕过按钮直接调用接口也无法重复打分。另外还有一个实操中的细节评分提交按钮在点击后立即置为loading状态防止评委双击。这个虽然前端防御性更强但配合后端校验整个系统才算真正稳了。6. 从课程设计到真实部署的进阶思考到这里一个基于SpringBoot Vue MySQL MyBatis的健美操评分系统已经完整走通了。最后分享一些我在实操中深有体会的进阶细节也是这个项目最值得扩展的几个方向。成绩计算逻辑放在Service层还是在数据库存储过程里我最终选择了Service层核心原因在3.3节里已经说明数据量小、规则变化频繁、代码可读性和可维护性优先。但我保留了一个扩展接口如果将来某场比赛有几百个评委、每秒钟都有评分提交计算逻辑可以平滑迁移到消息队列加异步计算。现在不急着做留好接口就行。评委未打分时选手成绩怎么展示我做了兜底处理final_score表里没有成绩记录的选手排行榜页面展示为“未完成评分”而不是0分避免观众误会该选手得了零分。这个细节问题我们在测试阶段就发现了如果直接显示0分会造成大乌龙处理成“待评分”状态是最稳妥的。另外我强烈建议你在项目里加入一个“系统初始化”功能。用于开发时快速填充测试数据一键生成20个测试选手、7个测试评委、3场比赛并随机生成一批评分数据。这样前后端联调、压力测试、演示答辩都方便得多不用一个个手动录数据。我当初写了个DataInitializer工具类提供API接口自动生成数据。在测试阶段省下的时间比写这个工具本身的时间多得多。如果你正在准备类似的课程设计或毕业设计我建议不要满足于“能跑就行”的状态。把权限控制做严一点、把事务边界想清楚、把数据库约束设计到位这三件事做完你的项目和网上那些千篇一律的“管理系统”就拉开了档次。评分系统最核心的价值就是“公平、准确、高效”这六个字技术只是实现它的手段。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

野生动物数据集8089张双格式:YOLO+VOC目标检测训练全流程实践 2026/10/2 2:42:36

野生动物数据集8089张双格式:YOLO+VOC目标检测训练全流程实践

简介:面向目标检测与计算机视觉研究的野生动物识别数据集,包含8089张野外水域固定视角拍摄的清晰动物图片,覆盖大角斑羚、大象、长颈鹿、犀牛等9类常见动物。图片未做数据增强,已同时提供yolo与voc两种标注格式,总计19…

阅读更多 →
石榴成熟阶段检测:YOLO数据集格式转换与训练全流程 2026/10/2 2:42:36

石榴成熟阶段检测:YOLO数据集格式转换与训练全流程

简介:面向石榴成熟阶段检测任务的数据集包,适合农业视觉、智慧果园等场景下的目标检测算法训练与验证。数据按VOC与YOLO两种格式组织,内含五千八百五十五张清晰图像,标签覆盖花苞、早期果实、花朵、中期生长、成熟果实五个阶段&am…

阅读更多 →
DeepLabv3+实战指南:基于Pytorch的语义分割训练与部署全流程 2026/10/2 2:42:36

DeepLabv3+实战指南:基于Pytorch的语义分割训练与部署全流程

简介:一套基于Pytorch实现DeepLabv3图像分割算法的实战项目资源,面向视觉分割方向的学习者、科研人员及开发者,可在VOC与Cityscapes两个主流数据集上完成模型训练、验证与预测推理,尤其适合毕设项目、课程实验或算法对比研究。资源…

阅读更多 →
野生动物目标检测数据集处理与YOLOv8训练避坑实战 2026/10/2 2:42:36

野生动物目标检测数据集处理与YOLOv8训练避坑实战

简介:面向目标检测与野生动物识别任务,这份数据集汇集 8089 张野外动物图像,覆盖大角斑羚、大象、长颈鹿、黑斑羚、捻角羚、羚羊、犀牛、角马、斑马 9 类,图片来自固定视角的野外水域场景,画面清晰且未做增强&#xff…

阅读更多 →
1400张车辆数据集接入YOLOv8:标注格式、训练参数与避坑指南 2026/10/2 2:42:36

1400张车辆数据集接入YOLOv8:标注格式、训练参数与避坑指南

简介:面向深度学习目标检测开发者和人工智能初学者,这套车辆检测数据集涵盖公交车、家用车、消防车、工程车等常见车型,图片经手工专业标注,xml文件与图片一一对应,可直接用于YOLOv3、YOLOv4、YOLOv5等主流框架训练车辆…

阅读更多 →
C++期末课程设计实战:用Qt Graphics View完整重写植物大战僵尸 2026/10/2 2:42:29

C++期末课程设计实战:用Qt Graphics View完整重写植物大战僵尸

简介:面向C程序设计课程期末设计的Qt植物大战僵尸游戏源码包,基于Graphics View框架构建场景与视图,利用面向对象的封装、继承、多态,将植物和僵尸分别抽象为基类,并延伸出太阳花、豌豆射手、坚果、普通僵尸、路障僵尸…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉