简介这份资源是一份基于SpringBoot的在线刷题系统毕业设计文档面向计算机相关专业学生及需要完成类似课程设计、毕业设计的开发者。系统采用Java语言与SpringBoot框架搭建后端前端使用Vue技术数据存储于MySQL数据库开发平台为IntelliJ IDEA管理员端支持编辑题目、编辑试题与发布题目学生端具备刷题和查看错题本功能可有效解决线下刷题错题整理困难、教师统计不易、纸张浪费等问题。资源包共1个docx文件大小约5.79MB内容涵盖摘要、绪论、国内外研究现状、课题研究目的与意义等完整章节结构规范可直接作为论文写作与项目实现的参考模板。目前已有72人学习下载适合需要快速理解系统架构、梳理功能模块与撰写设计文档的读者借鉴使用。1. 从一份 .docx 需求到能跑起来的在线刷题系统SpringBoot 到底扛了什么很多人拿到「基于 SpringBoot 的在线刷题系统」这个题目时第一反应是去搜现成源码结果下载下来一堆跑不起来的压缩包或者只有几张截图没有数据库脚本。我当年做第一个刷题系统时也翻过车题目表、选项表、答题记录表三张表没设计好做到一半发现多选题没法存答案只能推倒重来。这个标题真正要解决的问题是把「题库管理 在线答题 自动判分 错题回顾」这条链路用 SpringBoot 串起来让一个普通 Java 开发者能在本地把它跑通、改得动、讲得清。它适合正在做课程设计或毕设的学生也适合想练手 SpringBoot 全栈 CRUD 的初级工程师。核心不在框架多新而在业务表结构和判分逻辑能不能立住。2. 题库与答题的数据模型先把表设计对再谈代码刷题系统看着简单本质是一个「题目—选项—作答—判分」的状态机。表设计错了后面写多少 Controller 都是白搭。我一般会先把实体关系画清楚再动手建表这一步花两小时能省两天返工。2.1 五张核心表与字段取舍一个能支撑单选、多选、判断三种题型的模型最少需要这几张表。注意答案字段的存储方式这是最容易踩坑的地方。表名关键字段说明questionid, content, type, difficulty, category_id, correct_answer, analysistype 用 1/2/3 区分单选/多选/判断question_optionid, question_id, option_label, option_content, sort_no选项单独成表方便乱序和扩展categoryid, name, parent_id支持章节/知识点两级分类answer_recordid, user_id, question_id, user_answer, is_correct, answer_time每次作答一条记录wrong_bookid, user_id, question_id, wrong_count, last_wrong_time错题本可定时从 answer_record 汇总correct_answer字段对单选题存A多选题存A,C,D按字母升序拼接判断题存T或F。这样判分时不用查选项表直接字符串比对性能好且逻辑简单。很多人把答案存成选项 id 数组判分时要 join 查询题量一大就慢。2.2 建表 SQL 与索引CREATE TABLE question ( id BIGINT NOT NULL AUTO_INCREMENT, content VARCHAR(1000) NOT NULL COMMENT 题干, type TINYINT NOT NULL COMMENT 1单选 2多选 3判断, difficulty TINYINT DEFAULT 1 COMMENT 1易 2中 3难, category_id BIGINT NOT NULL, correct_answer VARCHAR(50) NOT NULL COMMENT 多选按字母升序逗号拼接, analysis VARCHAR(2000) DEFAULT NULL COMMENT 解析, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_type_difficulty (type, difficulty) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE answer_record ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, question_id BIGINT NOT NULL, user_answer VARCHAR(50) NOT NULL, is_correct TINYINT NOT NULL DEFAULT 0, answer_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_time (user_id, answer_time), KEY idx_question (question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;answer_record上的idx_user_time联合索引很关键错题本和答题历史都按用户时间查没有这个索引数据量上万后列表页会明显卡顿。question表的idx_type_difficulty服务于「随机抽 N 道中等难度多选题」这类组卷查询。2.3 用 SpringBoot 实体类映射Data TableName(question) public class Question { TableId(type IdType.AUTO) private Long id; private String content; private Integer type; private Integer difficulty; private Long categoryId; private String correctAnswer; private String analysis; TableField(exist false) private ListQuestionOption options; }TableField(exist false)标记的 options 字段不映射数据库列查询题目详情时再单独填充避免每次列表查询都带出一堆选项。这里用 MyBatis-Plus 的注解如果你用原生 MyBatis把TableName换成 XML 里的 resultMap 即可逻辑一样。3. 判分逻辑与组卷接口把核心业务写扎实数据模型立住之后真正决定系统好不好用的是判分和组卷。这两块写歪了前端做得再漂亮也是花架子。3.1 判分服务的三种题型处理判分不能只做字符串相等多选题的答案顺序、大小写、空格都要归一化。我一般写一个独立的 JudgeService把归一化逻辑收在一处。Service public class JudgeService { public boolean judge(Question question, String userAnswer) { if (userAnswer null || userAnswer.trim().isEmpty()) { return false; } String correct normalize(question.getCorrectAnswer()); String user normalize(userAnswer); // 多选题排序后比对避免 A,C 与 C,A 被判错 if (question.getType() 2) { return sortLetters(correct).equals(sortLetters(user)); } return correct.equalsIgnoreCase(user); } private String normalize(String ans) { return ans.replace( , ).replace(, ,).toUpperCase(); } private String sortLetters(String ans) { char[] arr ans.replace(,, ).toCharArray(); Arrays.sort(arr); return new String(arr); } }normalize处理了中文逗号、空格和大小写sortLetters把A,C和C,A都变成AC再比。参数上要注意如果前端传的是选项 id 而不是字母这里就得改成查选项表映射别硬套。判分结果写回answer_record同时更新wrong_book答错则 wrong_count1答对则从错题本移除或标记已掌握。3.2 随机组卷的 SQL 与接口组卷要按分类、题型、难度、数量抽题。MySQL 里ORDER BY RAND()在几万题时性能很差常见做法是用主键范围随机或先查 id 列表再内存随机。public ListQuestion randomPaper(Long categoryId, Integer type, Integer difficulty, int count) { // 先查出符合条件的 id 集合数据量大时这一步走覆盖索引 ListLong ids questionMapper.selectIdsByCondition(categoryId, type, difficulty); if (ids.size() count) { return questionMapper.selectBatchIds(ids); } Collections.shuffle(ids); ListLong picked ids.subList(0, count); return questionMapper.selectBatchIds(picked); }selectIdsByCondition只查 id 列走idx_category或idx_type_difficulty比直接SELECT *快很多。Collections.shuffle在内存里打乱几万 id 的列表也就几毫秒。参数 count 建议设上限比如单次不超过 100防止有人构造请求一次抽全库。3.3 答题接口的幂等与防重复提交在线答题最怕用户狂点提交同一条记录写好几遍。我一般用「用户题目时间窗口」做去重或者前端提交时带一个 requestId后端用 Redis 存 5 秒。public AnswerResult submit(Long userId, Long questionId, String userAnswer, String requestId) { String key answer:lock: userId : requestId; Boolean ok redisTemplate.opsForValue().setIfAbsent(key, 1, 5, TimeUnit.SECONDS); if (Boolean.FALSE.equals(ok)) { throw new BizException(请勿重复提交); } // ... 判分、落库逻辑 }setIfAbsent就是 SETNX5 秒过期。requestId 由前端每次进入答题页生成一个 UUID。这个方案比数据库唯一索引轻也不会因为网络重试误伤正常提交。4. 避坑与排查那些让我熬夜的报错刷题系统跑起来容易跑稳难。下面几条是我和身边人真实踩过的按「现象 → 原因 → 解决」写清楚。4.1 多选题答案顺序导致误判现象用户明明选对了系统判错错题本里多了一堆「冤案」。 原因判分时直接字符串相等A,C和C,A不相等。 解决判分前对多选答案做排序归一化就是 3.1 里的sortLetters。上线前一定要用乱序答案跑一遍单元测试。4.2 组卷接口偶发返回重复题目现象同一份试卷里出现两道一模一样的题。 原因selectBatchIds传入的 id 列表本身有重复或者并发下随机种子相同。 解决selectIdsByCondition里加DISTINCT内存随机后用LinkedHashSet去重再截取。并发场景给组卷加个短锁或直接依赖数据库随机。4.3 答题记录时间字段时区错乱现象答题历史按时间排序顺序是乱的或者显示时间差 8 小时。 原因数据库CURRENT_TIMESTAMP用服务器时区Java 端new Date()用 JVM 时区两边不一致。 解决统一用 UTC 存储连接串加serverTimezoneUTC展示时再转本地时区。或者干脆全部用LocalDateTime并在配置里固定时区。4.4 题目列表分页越翻越慢现象前几页很快翻到几百页后响应好几秒。 原因LIMIT offset, size在 offset 很大时要扫描并丢弃前面所有行。 解决改用游标分页记住上一页最后一条的 id查询WHERE id lastId LIMIT size。刷题列表这种顺序浏览场景非常适合。4.5 错题本数据与答题记录对不上现象用户答对了题错题本里还在或者答错了没进错题本。 原因判分和错题本更新不在同一个事务里中间异常导致状态不一致。 解决把判分落库和错题本更新放进同一个Transactional方法或者用答题记录定时任务重算错题本保证最终一致。5. 让刷题系统从能跑到好用三个进阶技巧系统能跑通只是及格线下面这几个点能让它从「毕设水平」往「能给别人用」靠一靠。5.1 用缓存扛住高频的题目详情查询刷题时用户会反复看题目详情和解析这类数据读多写少非常适合缓存。我一般用 Spring Cache Redis在 Service 方法上加注解。Cacheable(value question, key #id, unless #result null) public Question getDetail(Long id) { Question q questionMapper.selectById(id); if (q ! null) { q.setOptions(optionMapper.selectByQuestionId(id)); } return q; } CacheEvict(value question, key #question.id) public void updateQuestion(Question question) { questionMapper.updateById(question); }Cacheable的unless防止空值被缓存导致缓存穿透。CacheEvict在更新时清掉对应 key。注意题目选项变了也要清缓存如果选项单独更新得手动cacheManager清别只依赖注解。缓存过期时间建议设 30 分钟到 1 小时题库变动不频繁。5.2 答题统计用定时任务预计算如果每次打开个人主页都实时COUNT答题记录用户一多数据库压力就上来了。常见做法是每天凌晨跑一个定时任务把统计结果写进汇总表。Scheduled(cron 0 0 3 * * ?) public void buildDailyStat() { ListUserStat stats answerRecordMapper.aggregateByUser(); for (UserStat s : stats) { userStatMapper.upsert(s); } }cron表达式0 0 3 * * ?表示每天凌晨 3 点执行。aggregateByUser用GROUP BY user_id一次算完所有用户比循环单查快得多。汇总表加唯一索引user_idupsert用INSERT ... ON DUPLICATE KEY UPDATE。这样个人主页只查一行汇总毫秒级返回。5.3 用接口测试固定判分行为判分逻辑一旦改动很容易悄悄改坏。我习惯给它写一组参数化测试把边界情况钉死。题型正确答案用户答案期望结果单选Aatrue单选ABfalse多选A,CC,Atrue多选A,CAfalse判断Ttrue需在归一化里处理最后一行提醒判断题如果前端传true而后端存T归一化里要加映射否则全判错。这种细节不写测试根本发现不了。我自己的习惯是每加一种题型或改一次判分规则先把这张表补一行再改代码。做刷题系统这几年最大的教训就是别信「这么简单的逻辑不会错」——判分错一次用户对整个系统的信任就没了。把归一化和测试做扎实比堆多少功能都值。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
vSphere 5.5上Opus编码性能异常根因与调优指南 1. 项目概述:Opus 5.5 基准测试结果为何“诡异”?这根本不是版本号问题你搜“Opus 5.5 基准测试结果诡异”,大概率是刚跑完一组音频编码压测,发现数据完全不按常理出牌——码率明明设在128 kbps,解码后文件大小却忽大忽… · 2026/9/26 23:16:55
Spring Boot JdbcTemplate No qualifying bean报错排查与解决 1. 先搞清楚报错在说什么:Spring容器里根本没有这个Bean一个很常见的场景:你在Service里写了一个Autowired的JdbcTemplate字段,项目一启动,Spring直接红屏甩给你一句——No qualifying bean of type org.springframework.jdbc.cor… · 2026/9/26 23:16:48
Win10/Win11远程桌面身份验证错误:CredSSP协议协商失败解决方案 1. 这个错误到底在说什么?——不是密码错了,是协议“说不上话”了你输入账号密码,点连接,弹窗直接甩你一句:“发生身份验证错误。要求的函数不受支持。”——这句微软式冷幽默,十次里有九次让人怀疑是不是自… · 2026/9/26 23:16:48
区块链驱动的物联网设备身份认证与访问控制:Fabric链码实践指南 简介:一套面向物联网安全领域的毕业设计与课程设计资料包,聚焦基于区块链的设备身份认证与敏感数据访问控制系统。系统利用区块链不可篡改与分布式共识特性,实现去中心化设备身份注册验证,并针对用户隐私、设备状态、控制指令等敏… · 2026/9/26 23:54:53
沟通即业务:DeskcommCRM如何重构客户管理与全渠道沟通闭环 1. 为什么DeskcommCRM瞄准的是"沟通即业务"这个空档1.1 传统CRM管得住数据,却管不住对话这些年我接触过不少做销售和客服的团队,大家嘴上说"上了CRM",实际用的方式却千差万别。最典型的一种状态是:系统里客户… · 2026/9/26 23:54:53
DeskcommCRM落地实战:解决客户分散、跟进失控与数据复盘难题 上个月的销售复盘会上,我盯着屏幕上的Excel透视表看了很久。表里登记了400多家客户,但真正能说清楚进展的不到三分之一。有个大客户,销售A说他两周前联系过,销售B说上周刚发过方案,两个人居然都不知道对方在跟进同一个… · 2026/9/26 23:54:53
通信型CRM的核心设计与落地实践:从软电话到坐席工作台 1. 名字拆解:DeskcommCRM 到底在解决什么问题先聊一个挺有意思的现象。很多人选 CRM,第一反应是看功能列表:有没有公海池、能不能做销售漏斗、报表长什么样。但真正有经验的人,第一件事是看产品的名字。名字往往比产品文档更诚实&… · 2026/9/26 23:54:53
AI-Native SDLC:从流水线到闭环的软件开发新范式 1. 从“流水线”到“闭环”:AI-Native SDLC到底改变了什么做软件开发的人对SDLC(Software Development Life Cycle,软件开发生命周期)这个词都不陌生。传统SDLC的标准形态是一条线性流水线:需求分析、架构设计、编码实… · 2026/9/26 23:54:47
开源舆情系统源码落地实战:数据库选型、采集入库与检索分析 简介:这是一套面向开发者、数据分析人员及中小企业技术团队的开源舆情监测系统源码与配套数据库,适合预算有限但需要本地化部署舆情分析能力的场景。系统覆盖数据采集、清洗处理、深度分析与可视化展示等完整模块,可对新闻、博客、社交媒体等… · 2026/9/26 23:54:47
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46