
每年到了毕设和课程设计季总有同学在“做什么题目”上反复纠结。功能太简单答辩时拿不出手技术太复杂自己又hold不住。如果你正准备做一个微信小程序方向的全栈项目不妨把“交通规则系统”这四个字放进备选名单。这个选题看起来朴素但它其实覆盖了微信小程序开发中最核心的“题库 考试 记录”业务链路从前端页面到后端接口、从数据库设计到权限校验都能完整练习到。本文不打算停留在“教你搭一个demo”的层面。我会把这个系统拆成几个真正需要想清楚的问题题库数据怎么设计才能支持顺序练习和随机抽题模拟考试的计时和自动交卷怎么实现才算可靠错题本应该放在前端还是后端微信登录在小程序端的完整流程是什么每个问题都会给出设计方案、核心代码和排错思路。如果你正在找毕设题目、课程设计题目或者想用一个小程序项目检验自己的全栈能力这篇文章可以作为你的起步参考。读完后你不仅能理清整个系统的架构还能直接照着代码跑通一个可演示的版本。1. 交通规则系统到底要解决什么问题先想一个问题一个“基于微信小程序的交通规则系统”它的用户是谁解决了什么痛点真实的驾考场景里学员刷题主要靠专门的驾考App。这些App功能很全但有两个问题一是要单独下载安装手机存储告急的用户会有顾虑二是广告和推销太多只是想刷个题的人体验并不好。小程序天然适合这种“低频、轻量、即用即走”的工具场景——微信里搜一下就能用刷完题关掉不用下载、不用注册、不会推送骚扰消息。从技术学习者的角度看这个系统要做的核心事情其实就三件提供一套题库支持分类浏览、顺序练习、随机练习。提供模拟考试能计时、能交卷、能自动判分。记录用户的学习行为包括错题、成绩、练习历史。这三件事听起来简单但每一件落到实现上都有讲究。题库的存储结构决定你能不能灵活地“随机抽题”和“按类型刷题”考试计时逻辑决定用户切出小程序再回来时计时是否还能保持正确错题数据放在本地存储还是后端决定了用户换一台手机后错题还在不在。所以这篇文章真正要讲的不是某个单一功能的实现而是如何用“微信小程序 后端接口 数据库”的组合把这些业务场景完整地落地。2. 总体架构与技术选型2.1 系统架构整个系统采用前后端分离的经典结构微信小程序前端 ↓ HTTPS / WebSocket 后端接口服务Spring Boot ↓ JDBC MySQL 数据库微信小程序作为客户端负责页面展示和用户交互后端提供RESTful API处理业务逻辑和数据存取MySQL存储用户、题库、考试记录等数据。小程序通过wx.request调用后端接口数据格式统一使用JSON。2.2 前端选型原生小程序还是 uni-app很多人在做毕设时会纠结这一点。原生微信小程序和 uni-app 都能实现同样的功能区别在于原生小程序使用 WXML、WXSS、JS微信开发者工具直接运行语法贴近小程序底层排查问题更直接。uni-app使用 Vue 语法一套代码可以编译到微信小程序、H5、App适合“以后还想做多端”的情况。从毕设和练手的角度看我的建议是如果你之前没接触过小程序选原生即可。原因很简单原生小程序的调试工具最成熟网上资料也最丰富遇到问题时更容易找到答案。uni-app 虽然能跨端但多一层编译出错时定位问题会绕一些。课程设计和毕设项目中80%的交通规则系统采用“Spring Boot 微信小程序原生 MySQL”的组合这个组合技术栈主流、参考案例多、答辩时也好解释。2.3 后端选型Spring Boot 为什么是默认选项后端选择 Spring Boot不是因为它是唯一选择而是因为它是目前中文互联网生态里资料最全的方案。无论是 GitHub 上的开源项目还是博客里的教程大多数毕设系统都基于 Spring Boot 实现。如果你在开发中遇到问题搜索“Spring Boot 微信登录”“MyBatis-Plus 分页查询”等关键词基本都能找到解决方案。技术栈清单层级技术选型作用前端微信小程序原生页面展示、用户交互、请求后端后端Spring Boot提供 RESTful API、业务处理ORMMyBatis-Plus 或 Spring Data JPA操作数据库简化CRUD数据库MySQL存储用户、题库、记录认证JWTJSON Web Token小程序登录后携带身份标识接口调试Apifox / Postman调试后端接口这几个技术之间是互补关系不是排他关系。JWT负责“你是谁”Spring Boot 负责“要做什么”MySQL负责“记下来”。3. 数据库设计与数据模型数据库设计是这类系统最容易出错的地方也是答辩时老师最喜欢问的地方。很多新手一上来就只建两张表用户表、题目表做到后面发现错题本没法实现、考试记录没地方存只能返工改表结构。3.1 核心表结构我推荐至少设计以下几张表数据表作用关键字段user存储小程序用户id, openid, nickname, avatar_url, create_timequestion存储题目id, type, title, options, answer, analysis, category_idcategory题目分类id, name, codeexam_record考试记录id, user_id, score, total, duration, create_timewrong_question错题记录id, user_id, question_id, wrong_time, create_time其中question表是核心。判断题、单选题、多选题可以用type字段区分。选项信息建议用 JSON 字符串存储避免为每个选项单建一张表。3.2 建表 SQL 示例以下SQL可以作为初始版本字段类型和长度请根据实际需求调整CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, openid varchar(64) NOT NULL COMMENT 微信openid, nickname varchar(64) DEFAULT COMMENT 昵称, avatar_url varchar(512) DEFAULT COMMENT 头像地址, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE category ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 分类ID, name varchar(32) NOT NULL COMMENT 分类名称, code varchar(32) NOT NULL COMMENT 分类编码, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目分类表; CREATE TABLE question ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 题目ID, category_id bigint(20) NOT NULL COMMENT 所属分类ID, type tinyint(4) NOT NULL COMMENT 题目类型1判断题 2单选题 3多选题, title text NOT NULL COMMENT 题干, options json DEFAULT NULL COMMENT 选项JSON格式, answer varchar(16) NOT NULL COMMENT 正确答案, analysis text COMMENT 题目解析, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题库表; CREATE TABLE exam_record ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 记录ID, user_id bigint(20) NOT NULL COMMENT 用户ID, score int(11) NOT NULL COMMENT 得分, total int(11) NOT NULL COMMENT 总题数, duration int(11) NOT NULL COMMENT 用时秒, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 考试时间, PRIMARY KEY (id), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT考试记录表; CREATE TABLE wrong_question ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, user_id bigint(20) NOT NULL COMMENT 用户ID, question_id bigint(20) NOT NULL COMMENT 题目ID, wrong_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 答错时间, PRIMARY KEY (id), UNIQUE KEY uk_user_question (user_id, question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT错题记录表;这套表结构有两个关键设计第一question表的options字段用 JSON 存储这样做的好处是数据结构灵活。单选题就存三个选项多选题存四个选项判断题可以只存“正确/错误”不需要为不同题型设计不同表。第二wrong_question表加了uk_user_question唯一索引。同一个用户对同一道题只会产生一条错题记录避免反复答错时错题本里出现重复题目。如果用户以后答对了就删除这条记录。从性能角度看question表需要经常按category_id查询所以建了索引exam_record表经常按user_id查询历史记录所以也建了索引。4. 后端接口设计与核心代码4.1 接口清单后端需要提供以下接口接口路径方法说明/api/auth/loginPOST微信登录返回 JWT/api/category/listGET获取题目分类/api/question/listGET分页获取题目支持按分类和题型筛选/api/question/randomGET获取指定数量的随机题目/api/exam/submitPOST提交考试答案自动判分/api/exam/recordsGET查询考试历史记录/api/wrong/listGET获取错题列表/api/wrong/removePOST将题目移出错题本登录接口是第一个要注意的地方。小程序端通过wx.login获取临时code后端拿着这个 code 调用微信的接口换openid。这个 exchange 过程在小程序端无法直接完成因为需要appid和appsecret而appsecret不能暴露在小程序代码里。4.2 微信登录接口实现// 文件路径src/main/java/com/example/traffic/controller/AuthController.java RestController RequestMapping(/api/auth) public class AuthController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody LoginRequest request) { // 1. 使用 code 调用微信接口获取 openid String openid userService.getOpenidByCode(request.getCode()); // 2. 查询用户是否存在不存在则自动注册 User user userService.findOrCreate(openid); // 3. 生成 JWT 返回给前端 String token JwtUtil.generateToken(user.getId()); return Result.success(token); } }这里有一个很多新手会忽略的点真实项目里获取 openid 需要后端发起 HTTP 请求调用微信接口// 文件路径src/main/java/com/example/traffic/service/UserService.java public String getOpenidByCode(String code) { // 这里使用 RestTemplate 或 Hutool 的 HttpUtil 请求微信接口 String url https://api.weixin.qq.com/sns/jscode2session ?appid appid secret secret js_code code grant_typeauthorization_code; String result httpUtil.get(url); JSONObject json JSONUtil.parseObj(result); return json.getStr(openid); }在微信官方接口jscode2session中如果请求成功返回的 JSON 里包含openid和session_key如果失败返回的 JSON 里会包含errcode和errmsg。调试接口时可以先在 Apifox 里模拟请求确认参数正确后再集成到代码中。注意appsecret绝对不能写在小程序前端代码里必须由后端持有。实际调试过程中如果返回errcode: 40029说明code已过期或appid与小程序不匹配如果返回errcode: 40163说明code已使用过。前者更常见多为测试时用了旧的 code。4.3 模拟考试自动判分逻辑提交考试答案时后端要做的不仅是“保存答案”还要根据正确答案自动判分。判分逻辑可以统一放在后端原因是如果前端判分用户可以通过修改页面代码篡改成绩而且多选、单选、判断题的判定规则不同后端集中处理更可控。// 文件路径src/main/java/com/example/traffic/service/ExamService.java public ExamResult submitExam(ExamSubmitRequest request) { // 1. 查询本次考试涉及的所有题目 ListQuestion questions questionMapper.selectBatchIds(request.getQuestionIds()); int score 0; for (Question question : questions) { String userAnswer request.getAnswerMap().get(question.getId().toString()); if (checkAnswer(question, userAnswer)) { score 1; } else { // 2. 答错的题目自动加入错题本 wrongQuestionMapper.insert(request.getUserId(), question.getId()); } } // 3. 保存考试记录 ExamRecord record new ExamRecord(); record.setUserId(request.getUserId()); record.setScore(score); record.setTotal(questions.size()); record.setDuration(request.getDuration()); examRecordMapper.insert(record); return new ExamResult(score, questions.size()); } private boolean checkAnswer(Question question, String userAnswer) { if (question.getType() 3) { // 多选题答案按固定顺序排列后比较 return sort(question.getAnswer()).equals(sort(userAnswer)); } return question.getAnswer().equals(userAnswer); }多选题的判定是一个经典细节。如果用户选中的选项顺序与标准答案不一致不能直接判错。例如标准答案是“A,B,C”用户选择了“C,B,A”两者应该是同一组答案。这里一个常见做法是将字符串拆分排序后比较这样才能保证多选判定不会出问题。4.4 随机抽题实现顺序练习用SELECT * FROM question WHERE category_id ?即可模拟考试则需要在指定分类下随机抽取题目// 文件路径src/main/java/com/example/traffic/service/QuestionService.java public ListQuestion getRandomQuestions(Long categoryId, Integer count) { // 使用 ORDER BY RAND() 实现随机抽取 return questionMapper.selectRandom(categoryId, count); }对应 SQLSELECT * FROM question WHERE category_id #{categoryId} ORDER BY RAND() LIMIT #{count}ORDER BY RAND()在题库数据量不大几千条到几万条时性能可以接受是毕设项目最常见也最简单的写法。只有当题库规模达到百万级时才需要换成“先查ID再联表查题目”的优化方案但在交通规则系统这个业务场景下基本不会遇到性能不是瓶颈。5. 微信小程序前端页面实现5.1 小程序目录结构一个典型的原生小程序项目目录结构可以这样组织pages/ ├── index/ # 首页 ├── practice/ # 练习页 ├── exam/ # 模拟考试页 ├── result/ # 考试结果页 ├── wrong/ # 错题本页 ├── record/ # 考试记录页 └── login/ # 登录页 utils/ ├── request.js # 封装 wx.request └── auth.js # 登录鉴权与 token 管理 app.js app.json这种“一个页面一个文件夹”的结构是微信小程序的标准写法每个文件夹内的.wxml负责结构、.wxss负责样式、.js负责逻辑、.json负责页面配置。首页的设计是用户对系统的第一印象设计上要以入口清晰为主不要在一屏内堆太多内容用户打开就能看到“顺序练习”“随机练习”“模拟考试”这几个核心入口再往下才是错题本和考试记录。这种设计在轻工具类小程序里非常常见核心原则是“三秒内让用户知道能干什么”而不是让用户去找功能。5.2 登录与 token 管理小程序端的登录逻辑并不复杂但很多新手会把它写乱。核心思路是调用wx.login获取临时code。把code发送到后端/api/auth/login。后端返回token前端存到wx.setStorageSync(token, token)。后续所有请求在 header 里带上Authorization: Bearer token。封装请求工具时需要统一处理“登录过期”的情况// 文件路径utils/request.js const request (url, method, data) { return new Promise((resolve, reject) { const token wx.getStorageSync(token); wx.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/json, Authorization: token ? Bearer token : }, success: (res) { if (res.statusCode 401) { // 登录过期跳转登录页 wx.redirectTo({ url: /pages/login/login }); reject(res); } else { resolve(res.data); } }, fail: (err) reject(err) }); }); }; module.exports { request };这里需要强调一个安全边界401状态码表示“未认证或登录过期”在公共 WiFi 或校园网络调试时不要因为看到401就认为代码写错了可以先用普通接口确认网络连通性再用带 token 的接口确认权限逻辑。5.3 模拟考试页面的倒计时实现考试页需要实现倒计时一个常见的误区是把倒计时塞进页面data里然后依赖setInterval每秒更新。这样做的问题是用户切到后台再回来时定时器可能会被微信冻结倒计时就不准了。更可靠的做法是记录“考试开始时间戳”每秒根据当前时间戳计算剩余时间而不是简单地做累减。// 文件路径pages/exam/exam.js Page({ data: { questions: [], answers: {}, remainTime: 0, // 剩余秒数 currentIndex: 0 }, onLoad(options) { this.startTime Date.now(); this.totalDuration 45 * 60; // 45分钟 this.startTimer(); }, startTimer() { this.timer setInterval(() { const elapsed Math.floor((Date.now() - this.startTime) / 1000); const remain this.totalDuration - elapsed; if (remain 0) { this.autoSubmit(); return; } this.setData({ remainTime: remain }); }, 1000); }, onUnload() { if (this.timer) { clearInterval(this.timer); } }, autoSubmit() { clearInterval(this.timer); // 提交答案 this.submitExam(); } });这个做法的核心价值在于即使用户切到后台 5 分钟回到页面时由于startTime没有变剩余时间依然准确。这比“每秒减一”的写法健壮得多也体现了开发者对小程序生命周期和定时器机制的掌握。另外需要注意onUnload里需要清理定时器否则页面销毁后定时器还在运行会造成资源泄漏。5.4 题库练习页面练习页的核心逻辑是逐题展示、选择答案后展示正确答案并给出解析。题目数据从后端接口获取一次请求一批比如 10 道答题过程中在本地记录答案不需要每答一题都请求后端这样能大幅减少网络请求次数。// 文件路径pages/practice/practice.js Page({ data: { questions: [], currentIndex: 0, selectedAnswer: , showAnalysis: false }, onLoad(options) { this.loadQuestions(options.categoryId); }, loadQuestions(categoryId) { request(/api/question/list, GET, { categoryId }) .then(res { this.setData({ questions: res.data.records }); }); }, chooseAnswer(e) { const answer e.currentTarget.dataset.answer; this.setData({ selectedAnswer: answer, showAnalysis: true }); // 记录答案供后续统计使用 const answers this.data.answers || {}; const q this.data.questions[this.data.currentIndex]; answers[q.id] answer; this.setData({ answers }); }, nextQuestion() { if (this.data.currentIndex this.data.questions.length - 1) { this.setData({ currentIndex: this.data.currentIndex 1, selectedAnswer: , showAnalysis: false }); } } });单元测试方面可以单独测试chooseAnswer是否能在选择答案后正确展示解析避免把答题逻辑和页面渲染耦合在一起难排查。5.5 WXML 页面结构示例以下是练习页的 WXML 片段注意选项的选中态处理view classquestion-card view classquestion-title{{questions[currentIndex].title}}/view view classoption-list view wx:for{{questions[currentIndex].options}} wx:keyindex classoption-item {{selectedAnswer item.key ? active : }} >mvn spring-boot:run启动成功后控制台会出现Started Application in xxx seconds的日志同时端口 8080 处于监听状态。可以用 Postman 或 Apifox 访问http://localhost:8080/api/category/list验证接口是否能正常返回。同时检查 MySQL 是否正常启动、数据库traffic是否已创建否则启动时可能会出现数据库连接异常。Spring Boot 默认会在启动时检查数据源所以这一步问题会最先暴露。6.2 小程序端运行用微信开发者工具导入小程序项目文件夹在app.js中把BASE_URL改为后端实际地址。需要注意一个关键点微信开发者工具中勾选“不校验合法域名”仅用于开发调试。真机预览时必须在微信公众平台配置服务器域名而且必须是 HTTPS。本地联调阶段推荐的开发模式是用微信开发者工具的“本地调试”功能同时后端用--spring.profiles.activedev指定开发环境配置。将小程序项目导入后点击“编译”如果首页能正常显示分类列表说明前后端已经连通。6.3 如何手动验证核心功能按下面顺序逐个验证每通过一项就在文档中记录验证项操作预期结果登录点击登录按钮获取用户身份请求能带上 token分类加载打开首页分类列表正常展示顺序练习进入某分类题目按顺序展示选择后有解析模拟考试进入考试直接交卷得分、用时、答题数正确显示错题收集故意做错一道题目错题本中新增该题考试记录提交考试后查看记录显示本次考试得分和时间如果某一项验证失败先看后端控制台日志有无异常再判断是接口问题还是前端渲染问题。一个通用排查顺序是先用 Apifox 调接口确认后端没问题再看小程序的 Network 面板确认请求是否发出最后才检查页面渲染逻辑。7. 高频问题与排查思路开发微信小程序交通规则系统时有下面几个高频问题。这些问题在热词列表里反复出现说明踩坑的人非常多。问题现象可能原因排查方式解决方案请求后端接口失败报url not in domain list没有配置合法域名在微信开发者工具中勾选“不校验合法域名”本地调试临时勾选正式发布必须配置 HTTPS 域名登录接口返回errcode: 40029code 已过期或 appid 不匹配检查 code 是否被重复使用检查 appid 是否一致每次登录都重新调用wx.login不要缓存 code模拟考试倒计时不准确使用累减方式计时切后台时定时器被冻结在onShow中打印剩余时间改为基于开始时间戳计算剩余时间真机上图片不显示图片链接未配置到 downloadFile 合法域名在微信公众平台添加 downloadFile 合法域名或者使用 base64 编码但不推荐会增加包体多选题目判分错误答案顺序不同直接字符串比较后端日志打印用户答案和标准答案将多选答案排序后再比较小程序页面顶部导航栏高度不一致不同机型状态栏高度不同真机预览查看多款设备使用wx.getWindowInfo动态计算顶部安全距离页面报Cannot read property xxxx of undefined后端返回的数据结构与前端预期不一致打印后端返回 JSON对比前端取值字段统一约定字段命名后端不要返回 null 字段或前端做空值兜底这里我想多解释一下“小程序自定义标题上边距怎么弄”的问题。很多新手在做自定义导航栏时直接把标题写死在页面顶部结果在 iPhone X 以上的全面屏机型上标题会顶到状态栏里。解决办法是获取系统状态栏高度然后把这个高度作为自定义导航栏的 padding-top// app.js 中获取全局窗口信息 App({ onLaunch() { const windowInfo wx.getWindowInfo(); this.globalData.statusBarHeight windowInfo.statusBarHeight; } });再在自定义导航栏的 WXSS 里设置.custom-nav { padding-top: {{statusBarHeight}}px; }这类细节在小程序开发的真实场景里非常常见也是评审老师比较看重的工程素养。8. 最佳实践与工程建议8.1 接口返回格式统一后端接口的返回格式建议统一定义不要一个接口返回{code: 0, data: ...}另一个接口又直接返回列表。{ code: 200, message: success, data: {} }这样小程序前端可以在request.js里统一处理响应遇到code ! 200时统一弹出提示而不是每个页面都写一套错误处理逻辑。8.2 小程序端 token 失效处理JWT 有过期时间。当后端返回 401 时小程序端应该清除本地 token 并引导用户重新登录否则会出现“接口报错但页面一直卡在加载中”的情况。实测中发现很多毕设项目根本没有处理 401用户一直看到空白页面体验很差。8.3 数据库操作的安全边界在真实的课程设计和毕设场景中数据库操作需要特别注意删除错题记录时WHERE条件必须同时包含user_id和question_id不能只按question_id删除否则一个用户删除错题会影响其他用户。涉及删除接口时先在测试环境验证确认影响范围后再操作。后端接口要对非法参数做校验比如count不能超过 100categoryId必须存在。做到这些边界处理不仅代码更健壮答辩时也会让老师觉得你有工程意识。8.4 配置管理建议appid、appsecret、数据库连接信息都属于敏感配置。不要把appsecret硬编码在 Java 代码里而是放到application.yml中并用环境变量或配置中心管理# 文件路径src/main/resources/application-dev.yml wechat: appid: ${WX_APPID} secret: ${WX_SECRET}这样即使代码上传到 GitHub敏感信息也不会泄露。如果项目要开源强烈建议做这一步。8.5 微信支付接入提示如果这个系统后续要扩展成“付费题库”或“会员课程”会涉及小程序微信支付 v3 对接。从热词反馈来看“小程序微信支付v3对接 无可用的平台证书”“请在商户平台-api安全申请使用微信支付公钥”这类报错是高频踩坑点。我的建议是毕设阶段先不要碰支付功能一是没有商户号无法真实联调二是支付逻辑会大幅增加复杂度。如果一定要做可以只写“支付流程设计说明”不实现真实支付。8.6 项目文档与答辩准备做毕设或课程设计时建议在项目根目录维护一份 README.md写清楚项目简介和功能列表。技术栈说明。数据库初始化步骤。项目启动步骤。核心接口列表。测试账号说明如果有。这份文档不仅能帮你在答辩时快速梳理思路也是实际工程中团队协作的基本要求。比起临时做 PPT一份清晰的项目文档更有说服力。9. 总结从选题到落地一个基于微信小程序的交通规则系统核心价值不在于功能堆得多满而在于它覆盖了微信小程序全栈开发中最重要的一条链路小程序端交互、后端接口设计、数据库建模、登录认证、业务逻辑实现、异常排查。本文把这套系统拆解成了几个关键模块数据库的表结构设计解决了“题库怎么存、错题怎么记”的问题后端的登录和判分逻辑解决了“用户是谁、成绩怎么算”的问题小程序端的倒计时实现解决了“考试计时准不准”的问题。每一条链路都能对应到真实开发中的常见场景。如果这是你的毕设题目下一步建议按这个顺序推进先建库建表再用 Apifox 调通所有后端接口然后写小程序首页和分类页再做练习功能最后做模拟考试和错题本。不要一上来就同时推进所有功能。如果这只是你的练手项目我特别建议你把“模拟考试计时”和“多选判分”这两个模块做扎实。它们看起来不起眼但最能体现你对细节的把控能力。这个系统做完之后值得继续深入的方向也很明确一是把顺序练习改成“智能推题”根据错题记录优先出题二是增加后台管理页面让管理员能维护题库三是把前端迁移到 uni-app体验一次跨端开发的差异。每个方向都够再写一篇技术博客了。