资讯动态

基于Spring Boot的在线考试系统设计:从题库管理到自动判分全程解析

发布时间:2026/10/17 12:34:48 来源:尧图企业网站定制
在线考试系统这类项目说起来并不算新鲜但每年总有一批人前赴后继地做。做课设的、参加毕设的、给学校或培训机构搭内部考试平台的需求从来没断过。我现在手头这套基于 Spring Boot 的在线考试系统算是我个人做过的比较完整的版本源码也整理过一遍这里把整个项目的设计思路、核心实现、踩过的坑一并写出来希望能帮到正在琢磨同类系统的人。这个系统能干什么简单说就是一套常规的在线考试流程管理员维护题库、创建试卷、发布考试考生登录后参加考试系统自动判客观题主观题可由教师打分最后汇总成成绩单。适合用来做课程设计、毕业设计也适合小范围内部考试场景使用。接下来我不会只贴代码而是把每个模块背后的逻辑和取舍讲清楚。1. 系统整体设计与技术选型思路1.1 为什么选 Spring Boot 这套技术栈在线考试系统的需求核心是“稳定、清晰、快”对并发的要求其实不高——大多数场景是几百人同时在线考试撑死几千人。相比搭建微服务架构单体应用更合适。Spring Boot 天然就是干这个的自带内嵌容器启动不需要额外配置 Tomcat打包成 Jar 一键运行也方便部署到云服务器。和传统 SSMSpring SpringMVC MyBatis相比Spring Boot 省掉了大量 XML 配置这对做课设和中小项目来说节省的时间不是一点半点。前端方面采用了 Thymeleaf 服务端渲染没有单独拆 Vue 或 React。有人可能觉得前端技术栈老旧但从实际场景看在线考试系统更重视功能完整性和后台管理的便利性Thymeleaf 可以直接在后端写好页面逻辑不用解决跨域问题也不需要额外构建前端工程。对个人开发或小团队来说这个选择效率更高。数据库用 MySQLORM 用 MyBatis-Plus。MyBatis-Plus 提供了现成的 CRUD 接口写简单的数据库操作几乎不需要手写 SQL可以把精力集中在业务逻辑上。如果你后续想扩展功能比如从 MySQL 迁移到 PostgreSQL改动量也不会太大MyBatis-Plus 对主流数据库的兼容性都做得不错。1.2 项目分层从 Controller 到 Service 再到 Mapper项目没有用什么复杂的架构就是经典的三层结构外加 Entity 和 DTOController 层接收前端请求校验参数返回视图或 JSON 数据。Service 层核心业务逻辑都在这一层比如组卷、交卷、判分、考试状态控制。Mapper 层基于 MyBatis-Plus 的 BaseMapper做基础的数据持久化操作。代码结构上按功能模块分包用户模块、题库模块、试卷模块、考试模块、成绩模块。每个模块下再分 Controller、Service、Mapper、Entity。这样做的好处是后期排查问题非常快哪块出问题直接定位到对应包不会像大杂烩工程一样翻半天找不到代码。1.3 环境要求与准备如果你打算跑这套源码先准备好基础环境。我这边开发时用的是 JDK 1.8虽然 Spring Boot 2.x 已经支持 JDK 11 甚至 17但 1.8 在兼容性上最稳妥尤其是一些老项目的依赖如果用到 JDK 8 特有的 API换新版本反而会有麻烦。Maven 建议 3.6 以上MySQL 用 5.7 或 8.0 都行如果使用 8.0 记得修改驱动配置。2. 数据库设计考试系统的数据根基2.1 核心表结构与关系拆解考试系统的数据表不算多但每一张表都承担了核心功能。我按功能域把表分组这样设计的时候思路更清晰用户相关sys_user用户表存储考生和教师账号包含用户名、密码、姓名、角色标识、状态字段。密码使用 MD5 加盐存储不存明文。sys_role角色表区分管理员、教师、考生三种角色。题库相关exam_question题目表题目正文、题型单选、多选、判断、简答、选项内容、正确答案、分值、所属课程 ID。选项我统一用 JSON 字符串存储减少表的数量。exam_question_category题目分类表对应课程或知识点分类比如“Java 基础”、“计算机网络”。试卷相关exam_paper试卷表试卷名称、总分、考试时长、试卷状态草稿、发布、结束、创建人 ID。exam_paper_question试卷题目关联表试卷和题目的多对多关联额外存储每题在试卷中的分值以及题目在该试卷中的顺序。考试相关exam_record(考试记录表记录某考生对某张试卷的作答状态包括开始时间、交卷时间、总得分、状态。exam_record_answer答题明细表考生对每一道题的具体作答内容、得分、判分状态。这组表设计基本覆盖了考试系统的所有数据交互场景。举几个关键交互例子考生点击“开始考试”时系统往 exam_record 插入一条记录提交答案时往 exam_record_answer 分批插入明细交卷时系统遍历已提交的答案做自动判分并回填得分到记录表中。2.2 JSON 存储选项而非多选题单独建表数据表设计的取舍考试题目的选项结构存在差异单选题有 A/B/C/D 四个选项判断题往往只有对/错两个选项多选题每个选项如何排列也不固定。最开始我考虑过把选项和题目拆成两张表但后来放弃了——查询要 JOIN插入要事务包裹两步代码量翻倍收益却不明显。在线考试系统的题目量通常不大不需要复杂的选项检索更多时候是把选项跟着题目一次性查出来JSON 字段存储反而更符合使用习惯。如果题目包含图片、音频等资源则需要考虑额外的附件表存储文件路径或者走对象存储服务。我在这个项目中暂时用最简单的本地路径存储方式题目的文本内容带一个图片链接字段指向服务器上的静态资源路径。2.3 数据库设计中的索引优化考试记录表和答题明细表是数据增长最快的表一个考生考一张卷子会产生一条记录外加几十条答题明细。上线一学期之后这两个表的数据量可能达到数万甚至数十万条。我在设计时给三张高频查询表加了索引exam_record 表的 user_id 字段加索引用于快速查询考生历史考试列表。exam_record 表的 paper_id 字段加索引用于统计某张试卷的参与人数和平均分。exam_record_answer 表的 record_id 字段加索引用于加载考生答卷详情。不加索引前考试记录表只有 1 万多条时候不太明显一旦到了四五万条查询个人历史考试列表的响应时间会明显变长。经验是考试系统这种读写比高的场景索引带来的收益远大于索引维护的成本。3. 核心功能模块拆解题库、组卷与考试流程3.1 题库管理不只是简单的增删改查题库管理表面上是对题目的增删改查实际使用中还有一个隐藏需求——批量导入。手动一道题一道题录入效率太低尤其一个学期要录入几百道题的时候人工录入会让人崩溃。我实现了基于 Excel 的批量导入功能模板格式固定每行一题包含题型、题干、选项 A 到 D、正确答案、分值、课程分类。导入过程中最容易出问题的点是中文字段编码和 Excel 日期格式。解决方式是统一读取模板文件按列索引取值不要按表头文字匹配避免不同版本的 Excel 表头格式存在细微差别。数据校验方面每行数据都要检查题目内容是否为空、答案是否在合法范围内非法数据逐行标记错误原因并跳过导入结束后返回一个包含失败原因的结果文件。这种处理方式比一次性全量导入安全得多也方便用户修正错误数据后重新导入。题型方面系统支持单选题、多选题、判断题、简答题。单选题和判断题走自动判分逻辑多选题需要精确匹配才得分——选多、选少、选错都不得分简答题则进入人工评判环节。这类评分规则是符合考试实际要求的开学时和老师确认过多选题漏选不给分防止考生通过部分选对的方式凑分。3.2 手动组卷和自动组卷的实现差异组卷方式我做成了两种手动组卷和自动随机组卷。手动组卷的逻辑简单从题库中按分类筛选题目勾选后添加进试卷。页面展示的是一个题目列表点击添加后通过 AJAX 请求把题目 ID 传给后端后端写入 exam_paper_question 关联表。试卷准备好后可以预览整张试卷确认题目顺序和分值设置是否合理。自动组卷相对复杂核心是一个按条件抽取题目的算法。规则配置上管理员需要指定试卷的总分和考试时长以及抽取题目的分类策略——比如“Java 基础”出单选题 10 道每道 3 分“计算机网路”出多选题 5 道每道 4 分判断题若干简答题若干。系统根据这些条件从题库中随机抽取题目如果题库题量不足需要在界面上给出明确提示而不是生成一张缺斤短两的试卷。随机抽取后续要做扩展可以考虑加入防重复规则比如在一场考试中同一知识点的题目不能出现太多雷同选项或者限制同一张试卷中不同题目的相似度。这一点做自动组卷时非常值得注意因为纯粹随机抽题容易出现知识点覆盖不均匀的情况。3.3 在线考试流程从开始考试到最终交卷的完整闭环在线考试需要处理的核心场景是考试过程中的状态控制这部分的逻辑比表面看起来要复杂。考生从考试列表中选择一张可参加的试卷点击“开始考试”系统先检查考生是否已有该试卷的考试记录。有记录且状态为“已交卷”则禁止再次参加有记录且状态为“考试中”则恢复上次的答题进度继续考试。这解决了一个实际痛点考生答题过程中不小心关闭了浏览器再次打开不应该重新作答而应该恢复到之前的答题状态。恢复进度的实现方式比较直接——考生每做一题就实时保存答案到 exam_record_answer 表或者按一定时间间隔自动保存。我实际采用的是前端定时保存策略JavaScript 每 30 秒自动把当前页面上的所有答案提交一次用户手动点击保存按钮也会立即提交。这样就保证了即使浏览器崩溃、网络中断考生的答题数据也不会丢失太多。交卷环节是考试系统最容易出 Bug 的部分。我在交卷接口设计中做了严格的权限校验和状态校验分三层保障第一层前端确认弹窗提醒考生交卷后不能再次作答。第二层后端检查当前时间是否超过考试截止时间超时的按自动交卷处理。第三层后端检查考试状态是否为“考试中”如果已经是“已交卷”则直接拒绝防止重复交卷导致成绩覆盖。如果交卷时网络中断前端会捕获异常并提示考生检查网络后重试同时保留本地答案副本不直接清空页面。这个设计我踩过坑早期版本是交卷直接清空页面结果有考生在交卷过程中断网刷新后已经“交卷”了但后台根本查不到提交记录成绩丢失后续处理起来很麻烦。3.4 自动判分与主观题评分的双重机制题目类型不同判分策略也不同。单选题和判断题逻辑最简单考生答案和标准答案一致即得分不一致不得分。多选题需要做数组比较标准答案和考生答案都存为 JSON 数组提交后先排序再逐项比较完全一致才判满分。这里有一个细节容易被忽略多选题的选项顺序。某道题的标准答案是 A、C考生作答顺序是 C、A如果直接用字符串比较两者不会相等就会误判为不得分。好在我在设计之初就把 JSON 数据解析为数组后排序再比较这个顺序问题就自动规避了。简答题的评分我实现了两种模式一种是纯人工评分即主观题不在自动判分阶段处理而是交卷后由教师在后台逐份批改另一种是关键词打分辅助模式——系统提取考生答案中的关键词与标准答案关键词做匹配给出一个参考分再由教师确认或者手动调整。关键词打分只是一个辅助工具不能全依赖它因为简答题的答案是开放式语言描述机器无法完全理解语义比如“请简述 TCP 三次握手过程”考生的描述可能跟标准表述有差异但核心意思正确关键词匹配就不一定准确。所以生产环境中必须保留人工评分入口。成绩统计模块实现了几个指标单场考试的最高分、最低分、平均分、及格率以及每个考生的成绩排名。这些统计信息在管理后台通过图表展示数据来源是 exam_record 表中的得分字段。查询时要注意把主观题和客观题分开统计客观题得分可以在交卷时立即算出主观题得分在教师批改完成后回填两者汇总得到最终成绩。4. 实操过程从零到一搭建考试系统的关键环节4.1 搭建项目骨架与基础配置先用 Spring Initializr 生成基础项目这是最省事的办法选择需要的依赖直接生成一个可运行的骨架工程。关键依赖有这么几个dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.4.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependencyThymeleaf 做服务端渲染MyBatis-Plus 做持久层Lombok 用来省略 getter/setter 模板代码。如果没有使用 MyBatis-Plus 的话这些基础 CRUD 代码需要手动写很多现在简单了不少。application.yml 中的关键配置项要注意两点一是数据库连接串要指定编码为 UTF-8 并设置时区否则运行时会报时区错误或出现中文乱码二是 MyBatis-Plus 的映射文件路径要正确同时开启下划线转驼峰的配置避免查询结果映射失败。spring: datasource: url: jdbc:mysql://localhost:3306/exam_system?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false mybatis-plus: configuration: map-underscore-to-camel-case: true mapper-locations: classpath*:mapper/**/*.xmlThymeleaf 的缓存建议在开发阶段关闭这样修改页面后刷新浏览器就能看到效果不用重启应用。4.2 实现考试防作弊随机选项与切屏检测的取舍在线考试系统和线下考试环境最大的差别在于无法监控考生行为完全靠自律。我在这套系统里做了两重防作弊设计第一重是选项随机排序。对选择题前端渲染时打乱选项顺序每次进入考试页面选项顺序都不同。这个功能说不上难但能有效杜绝“背选项位置”的作弊方式——比如提前把答案纪录为第 2 个选项是什么考试时直接选第 2 个。我在后端返回题目数据时随机打乱选项数组再渲染到页面上。第二重是切屏检测。利用浏览器的 visibilitychange 事件当用户切换到其他标签页或最小化窗口时记录一次切屏行为。超过设定次数后在后台标记警告并将该考生加入重点监控名单。这个方案实际效果有限毕竟考生可以用另一台手机作弊但至少可以起到一定的威慑作用。实现时要注意一个坑有些浏览器在页面加载时也会触发一次 visibilitychange 事件需要在启动时加上一个状态标记避免误记。是否开启摄像头监控、AI 人脸识别取决于项目的投入成本。我做的版本没有引入摄像头监控因为直播流的存储和带宽成本较高且涉及敏感数据合规问题至少需要评估实际考试严肃程度再决定是否启用。4.3 考试时间控制逻辑考试时间控制是在线考试系统的核心模块之一。每个试卷都有一个考试时长设定比如 90 分钟。考生开始考试后系统需要在这个时间范围内完成考试。这里有两个时间点需要区分考试开始时间考生点击“开始考试”按钮在后台记录 begin_time。考试结束时间考生点击交卷按钮或者系统自动交卷的时间。自动交卷逻辑在考生到达考试截止时间时触发。实现方式是后端在预检查时对比当前时间和 begin_time duration如果当前时间已经超过截止时间自动调用交卷逻辑保存当前已自动保存的答案计算成绩将考试状态更新为已交卷。这个逻辑需要特别注意一个问题倒计时显示时间的可信度。前端倒计时如果完全依赖 JavaScript 的 setTimeout 和 setInterval考生可以修改浏览器时间或者清掉定时器倒计时就会出问题。我处理的方式是前端只显示倒计时时间基准由后端在启动考试时计算并传给前端倒计时时间基于系统时间自动递减。但最终交卷时的截止时间判断以后端服务器时间为准并额外增加一个后端定时任务做兜底检查和自动交卷。这样即使考生没操作网页只要服务器认定考试已超时就会自动完成交卷。一个比较实用的优化是倒计时剩余最后 5 分钟前端弹窗提醒考生及时保存答案最后一分钟再做一次醒目变色提示。这些细节在真实考试场景中很影响用户体验——据我观察考生紧张起来根本注意不到页脚的时间显示没有醒目的警告很容易导致答案来不及保存。4.4 部署方案从开发环境到正式环境的迁移开发环境和正式环境的差异最容易踩坑的是静态资源路径和数据库配置。我建议用不同 profile 区分环境开发环境走 application-dev.yml正式环境走 application-prod.yml。数据库账号、密码、日志输出级别、静态资源路径这几个配置项必须分开管理。部署到服务器采用最简单的单机部署模式服务器安装 JDK 和 MySQL将项目打包为 Jar 文件直接 java -jar exam-system.jar 运行。如果服务器内存较小记得配置 JVM 参数限制堆内存大小比如java -jar -Xms256m -Xmx512m exam-system.jar默认最大堆内存为物理内存的四分之一如果服务器是 4G 内存JVM 可能配置到 1G 左右小规模使用场景用不了这么多反而可能因为占用过大影响其他服务。手动指定堆参数可以避免这种情况。生产环境启用 HTTPS 也是必要的考试系统会传输账号密码和答题数据明文传输太不安全。有域名的情况下用证书管理工具申请免费证书很方便然后在 Spring Boot 中配置好证书路径和端口即可。5. 高频问题排查与避坑经验5.1 并发交卷导致成绩丢失的问题我遇到的最大翻车现场是并发交卷导致成绩丢失。模拟场景是这样的某个班有 50 个学生同时参加考试考试结束时大量学生一起点击交卷。因为交卷接口做了成绩计算、答题明细批量写入、考试状态更新等多个数据库操作如果事务边界控制不好在高并发场景下会出现部分记录写入失败但事务已经提交的情况。排查过程基于错误日志推进交卷高峰期部分请求报数据库连接超时或事务回滚异常。根因有两个一是交卷接口内部逻辑过于耗时——逐题计算得分时会查询多张表导致数据库连接被长时间占用二是更新考试状态和写入答题明细的时序不对部分失败后没有完整回滚。修复方案分两步第一步把成绩计算逻辑优化为内存中批量处理避免交卷时逐题查询数据库。试卷题目数据在考生开考时已经加载到前端后端保存了拿到试卷时的题目快照交卷时直接用内存中的题目和答案数据计算成绩不额外查库。第二步调整事务边界。交卷操作整体包裹在一个事务中任何一步失败就整体回滚避免出现“考试状态更新了但答题明细没写进去”的孤儿数据。压测结果说明这套方案能支撑住日常使用场景。用 500 并发同时交卷没有出现成绩丢失或接口超时。真实考试场景很少有 500 人同时精确到同一秒交卷系统容量基本够用。5.2 考生答案自动保存与“继续考试”恢复不一致开发时遇到过另一个问题考生的答案前端自动保存到了本地存储但刷新页面后无法恢复。排查发现自动保存按钮把答案以 JSON 格式存储在 localStorage 中而页面在恢复时只从后端数据库读取数据不读本地缓存。如果自动保存的请求因为网络原因没有传到后端数据库中没有数据那就恢复不了。解决方案是将 localStorage 作为即时草稿箱后端作为持久存储同时并行使用。考生点击“开始考试”时后端生成一条考试记录前端立刻去后端拉取该记录下已有的答题数据没有则显示空白试卷有则直接渲染到页面上。自动保存的 Ajax 请求失败时前端保留本地草稿下次刷新优先从本地恢复再异步同步到后端。这样处理后极端情况下考生断网答题答案先存在本地网络恢复后再同步到后端不会丢题。5.3 时区问题导致开考时间异常数据库时区和应用时区如果不一致会出现一个非常隐蔽的 Bug考生端的页面显示考试时间是下午两点后台查询数据库中的开始时间是早上六点时间差正好 8 小时。排查后确认是 JDBC 连接串中没有设置 serverTimezone 参数MySQL 默认使用了服务器的系统时区而应用使用的是 JVM 默认时区不同地区调试时很容易出偏差。解决办法是在数据库连接串中显式指定时区serverTimezoneAsia/Shanghai。修改后重启应用验证开考时间和交卷时间都正常了。Java 8 的 LocalDateTime 类型本身不含时区信息处理时尤其要注意时区设置统一否则多个实例部署在不同时区服务器上时存储的时间数据会出现混乱。5.4 缓存穿透风险与应对在查询试卷详情时大量考生同时开考前请求同一张试卷的数据如果每次请求都从数据库查询数据库压力会比较大。我引入了一层简单的缓存机制使用本地缓存框架存储试卷题目快照设置过期时间为 5 分钟。开考前一段时间同一个试卷的题目数据从缓存返回不影响数据库。缓存框架的引入需要注意缓存更新策略的问题。如果管理员中途修改了试卷题目缓存中的旧数据不会立即可见。我的做法是管理员编辑试卷后主动清空对应试卷的缓存这个操作在试卷保存接口中连动完成。也可以设置更短的过期时间比如 3 分钟牺牲一点性能换取数据一致性。这个方案够用了。如果考试并发级别到了几千人同时访问就需要考虑引入分布式缓存组件了但那种场景已经超出这套系统的目标范围。6. 代码结构解析与二次开发建议6.1 源码目录结构与关键类说明拿到源码后先看整体目录结构不要直接进去翻页面代码。我建议按这个顺序逐步查看启动类程序入口包含 Spring Boot 启动注解和包扫描配置。配置类拦截器配置登录校验、静态资源映射配置。统一返回结果类接口返回 JSON 的统一格式。工具类JWT 工具、Excel 导入工具、日期处理工具。实体类对应数据库表结构。Mapper 接口继承 MyBatis-Plus 的 BaseMapper简单查询不需要自己写 SQL。Service 接口和实现类业务逻辑主体。Controller接收请求、参数校验、调用 Service。建议从 Service 层看起因为它包含了考试系统最重要的业务逻辑——开考检查、自动保存答案、交卷判分、成绩统计理解了 Service 层的业务流向整体项目就基本吃透了。6.2 如何修改判分规则假如你希望多选题改为“漏选可得分”选对但不全按比例给分只需修改评分方法。默认逻辑是“完全匹配才得分”可以改成// 多选题评分选对一部分也按比例给分 private double scoreMultipleChoice(String standardAnswer, String studentAnswer, double fullScore) { ListString standardList parseAnswer(standardAnswer); ListString studentList parseAnswer(studentAnswer); int correctCount 0; for (String answer : studentList) { if (standardList.contains(answer)) { correctCount; } } double ratio (double) correctCount / standardList.size(); // 如果选择了错误选项不给分或按一定比例扣分 for (String answer : studentList) { if (!standardList.contains(answer)) { ratio 0; break; } } return fullScore * ratio; }这种扩展点还有很多比如增加一种新题型需要在题目表中扩展题型字段、在前端渲染模板中增加对应的展示方式、在判分逻辑中增加对应的策略分支。6.3 从考试系统扩展到其他业务场景在线考试系统的核心业务模式是“题目—试卷—答题—判分—统计”这个模式可以复用得很好企业培训考核系统换一套登录方式加一个部门组织架构就能支撑企业内部培训认证。问卷调研系统把判分环节去掉增加统计图表分析功能就变成了问卷工具。竞赛答题平台加入计时排行榜和实时排名就能支撑知识竞赛场景。所以如果后续有类似需求可以参考这套系统的核心模块直接改造演进把所有业务逻辑集中在业务层扩展时非常灵活。7. 实操心得与项目复盘做完这个项目回头复盘有一些感受是写代码之前想不到的。在线考试系统看起来不算复杂至少比电商系统简单得多但真正做下来会发现它最麻烦的地方不是 CRUD而是各种异常场景的处理。交卷时网络断了怎么办考试途中页面不小心关了怎么办多选题答案顺序不一致怎么办时间到了考生没交卷怎么办这些问题没有哪一个需要高深的技术但每一个都要仔细思考和设计少一个都会在实际使用中带来麻烦。安全方面也值得再谈。密码存储用了加盐哈希方式登录校验用了拦截器加 Token 的方式角色权限控制上用了简单的注解拦截已经可以满足基础安全需求。但系统如果部署在公网环境还需要增加登录验证码、接口限流、操作日志等安全措施这些不在本次项目的源码范围内但我会在后续迭代中补上。如果你打算把这个项目作为毕业设计或者课程设计我建议在现有基础功能之上选择一个方向做深做透比如做一个考试数据分析模块通过图表可视化展示考生的知识点掌握情况分布或者做一个考试公告通知模块支持考试开始前自动提醒考生又或者做一个错题本功能让考生查看历史错题和答案解析。有了核心功能兜底再有一个亮点功能突出整套方案就会比千篇一律的“基础版考试系统”更有竞争力。最后想说的是源码只是一个起点。我看到很多同学拿到项目源码以后第一步是直接跑起来第二步是改数据库名称和密码第三步就不知道该干什么了。真正有价值的是读懂代码里的设计思路——为什么题库表要这样设计为什么交卷要加状态校验为什么多选题判分要排序比较搞清楚这些“为什么”以后遇到新项目才能举一反三。这套系统的源码我整理得很清晰注释也加了不少关键地方的说明建议你拿到手以后通读一遍核心 Service 层代码再动手修改自己想要增强或者自定义的功能。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑