ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

微信小程序交通规则考试系统开发实战:题库、考试与上线全解析

微信小程序交通规则考试系统开发实战:题库、考试与上线全解析 微信小程序交通规则系统核心目标是让驾校学员或准备科目一、科目四的用户在小程序里完成交通法规学习、模拟考试、错题回顾和成绩统计。很多开发者第一次做这类系统时容易把页面设计、动画效果和扩展功能想得太多反而忽略了最关键的刷题链路。我建议把精力放在三件事上题库怎么组织、考试流程怎么闭环、用户数据怎么记录。如果你正在做基于微信小程序的驾校模拟考试、交通规则学习平台或者刚拿到一个毕设、外包项目这篇文章的路线会比较实用从小程序端、Spring Boot 后端、MySQL 数据表到订阅消息、微信支付、上线审核和常见问题排查都会按落地顺序拆开讲。最值得关注的不是某个单独功能而是整套系统在普通配置下能不能稳定跑起来。1. 做这类交规小程序先想清楚核心模块和用户主流程交规系统最忌讳一上来就堆功能。用户打开小程序不是为了看花哨首页而是为了刷题、模考、看错题。先把主链路理清后面所有页面和接口都好设计。1.1 核心用户是谁他们最重视什么核心用户是驾校学员和准备科目一、科目四的考生。这类用户有两个典型使用场景一是碎片时间刷题比如地铁上、排队时二是考试前做整套模拟检验自己能不能在限定时间内及格。所以他们最关心的不是页面好看不好看而是题目加载快不快、交卷稳不稳、错题能不能方便回顾。驾校管理员或教练也需要一个管理后台用来维护题库、查看学员通过率。这里要注意管理后台不一定非要放在小程序里用 Web 管理端会更合适。小程序端页面栈和适配成本有限不要把复杂表单、图表统计全部塞进去。1.2 角色和权限怎么划分第一版可以只做两级角色普通用户和管理员。普通用户对应小程序里的学员可以练习、模拟考试、查看错题和成绩。管理员在后台维护题库、导入题目、查看考试统计。用户表里加一个 role 字段后端在管理接口上做简单拦截就可以。细粒度的权限、菜单权限、角色权限等业务真正需要时再加。不要一上来把 Spring Security 的复杂权限模型设计进去交规系统早期最重的数据是题目不是用户权限。1.3 功能优先级先把考试闭环跑通第一版建议只做这几个模块题库列表按分类或随机显示题目。练习模式逐题作答实时判断对错。模拟考试有限时间、固定题量、交卷评分。错题本自动收集答错的题目支持继续练习。成绩记录查看最近考试分数、正确率、用时。第二版再考虑收藏、每日一题、学习进度、排行榜、订阅消息、微信支付、蓝牙打印。为什么这个顺序很重要因为交规系统最核心的价值是帮助用户通过考试考试链路不稳定其他功能做得再多也是空转。我见过有的项目先做了一堆动画和抽奖页面结果交卷时请求参数对不上成绩一直存不进去这就是功能优先级没理清。1.4 页面主流程和页面跳转关系页面不用设计得太深建议保持这样的关系pages/index/index 首页 - pages/exam/exam 练习或考试页 - pages/result/result 成绩页 - pages/wrong/wrong 错题页首页可以通过模式参数区分练习和考试进入 exam 页面时传入 modepractice 或 modeexam。考试交卷后跳转 result 页result 页显示成绩和错题入口。错题页可以单独从首页进入考试结果页也可以带 examId 进入。小程序页面栈层级太深会出现返回逻辑混乱所以页面层级尽量控制在三层以内。2. 技术选型小程序 Spring Boot MySQL 为什么比较稳选型不用追新关键是团队能维护、部署简单、跑得稳。微信小程序作为前端展示和交互层Spring Boot 负责接口MySQL 存题库和用户数据是目前这类系统比较常见的组合。2.1 为什么不能只用本地缓存有些同学觉得几千道题可以直接放到小程序本地缓存里不需要后端。这个想法在很小规模时可行但交通规则题目通常包含文字、图片、解析还会定期更新。如果只放本地题库更新需要发版用户考试记录、错题同步、多设备登录都无法处理。小程序本地缓存更适合存用户最近一次答题状态、登录 token 这类轻量数据不适合做题库主存储。所以服务端接口是必须的。2.2 前端选择原生小程序还是 uni-app做微信小程序交通规则系统优先推荐原生微信小程序。原因很简单官方文档和工具链最直接真机调试、上传发布、组件兼容都少一层中间转换。如果你团队熟悉 Vue并且确定以后要同时发布支付宝小程序、抖音小程序那用 uni-app 也可以。但要注意uni-app 编译后部分原生组件表现不同比如 video、canvas、地图组件都需要额外测试。如果只是做一个微信小程序没必要为了跨端而跨端。2.3 Spring Boot 后端结构后端不需要复杂微服务一个单体 Spring Boot 应用足够。建议按职责分包com.example.traffic ├── controller // 接收请求返回统一结果 ├── service // 业务逻辑 ├── mapper // 数据库访问 ├── entity // 数据库实体 ├── common // 统一返回、异常处理、工具类 └── config // 配置类如微信配置、拦截器不要把所有代码都写在 controller 里也不要过度分层。简单项目 controller 调用 serviceservice 调用 mapper基本上够用。如果后面题库量变大再把缓存、消息队列加进来。2.4 环境准备清单不用一次性准备太多先满足最小开发环境即可。下面是我常用的起步配置项目建议微信开发者工具下载稳定版申请小程序 AppID基础库版本以微信开发者工具当前稳定版为准设置一个最低兼容版本JDKJDK 8 或 17取决于 Spring Boot 版本Maven3.6 以上后端框架Spring Boot 2.7 或 3.x根据 JDK 选择数据库MySQL 5.7 或 8.0服务器学习阶段本机即可上线建议 2 核 4G 起步域名需要 HTTPS 域名且域名备案用于小程序 request 合法域名这里的版本没有绝对标准以你当前下载到的稳定版本为准。如果后端是 Spring Boot 3.xJDK 必须是 17 以上如果还用 JDK 8就选 Spring Boot 2.7。这个问题在项目初始化时最容易踩坑。2.5 最小化依赖后端一开始不需要引入太多依赖。Spring Boot Web、MyBatis-Plus 或 Spring Data JPA、MySQL Connector、Lombok 就够了。Redis、RabbitMQ、定时任务这些都先不放等真的需要再引入。依赖越多环境兼容问题和部署问题越多。3. 最小可运行 Demo先让一条题目从后端跑到小程序里有没有一个最小闭环有后端提供一条交通规则题目的接口小程序拉取后展示用户点击某个选项后给出对错反馈。只要这个闭环跑通后面所有功能都是在这个基础上扩展。3.1 创建后端项目并配置端口用 Spring Initializr 创建一个项目引入 web、mysql 相关依赖。在 application.yml 里做基础配置server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/traffic_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Drivercontext-path 用 /api 可以让接口路径更清晰后续小程序请求统一带 /api 前缀。端口不要和小程序开发者工具其他服务冲突8080 比较常见。3.2 建一张最简单的题目表第一版不用把选项拆到多张表先用单表把题目、选项、答案、解析放在一起CREATE TABLE traffic_question ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type TINYINT NOT NULL COMMENT 1-判断 2-单选 3-多选, title VARCHAR(500) NOT NULL, option_a VARCHAR(200), option_b VARCHAR(200), option_c VARCHAR(200), option_d VARCHAR(200), answer VARCHAR(20) NOT NULL, analysis VARCHAR(1000), category_id BIGINT, status TINYINT DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;插入两条样例数据一条判断题一条单选题。排序字段不需要一开始就做先按 id 排序。如果后面要支持章节、分类、按难度出题再加字段和索引。3.3 编写查询接口用 MyBatis-Plus 写一个最简单的随机题目接口RestController RequestMapping(/question) public class QuestionController { Autowired private QuestionService questionService; GetMapping(/random) public ResultListQuestion random(RequestParam(defaultValue 10) Integer limit) { return Result.success(questionService.getRandomQuestions(limit)); } }这里要注意一个问题练习模式虽然需要即时判题但考试模式不能把正确答案下发到小程序端否则用户通过接口直接看到 answer 字段考试就没意义了。建议返回给前端时把 answer、analysis 字段过滤掉或使用单独 VO 对象。判题放在服务端完成。3.4 小程序端请求题目在页面 onLoad 里发起 wx.requestwx.request({ url: https://your-domain.com/api/question/random, data: { limit: 10 }, success: (res) { if (res.data.code 0) { this.setData({ questions: res.data.data }); } else { console.log(接口返回错误, res.data); } }, fail: (err) { console.log(请求失败, err); } });开发阶段如果还没有正式域名可以在微信开发者工具右上角“详情 - 本地设置”里勾选“不校验合法域名”。但这只能用于本地开发真机预览和正式上线必须配置合法域名。3.5 怎么判断 Demo 跑通判断标准很简单小程序冷启动后能看到题目列表。点击选项页面能根据服务端返回或本地逻辑判断对错。后端控制台能看到请求日志。修改数据库题目内容后小程序重新进入能拿到新数据。如果题目加载不出来不要急着加倒计时、答题卡、交卷逻辑。先看网络面板里的请求状态再看后端日志有没有堆栈。很多问题不是代码逻辑问题而是数据库没连上、接口路径写错、返回字段名对不上。4. 交通规则系统的数据模型题目、考试记录和错题表怎么设计数据模型决定了后续功能好不好扩展。交通规则系统的核心数据不是用户而是题目和用户答题行为。把这部分设计清楚后续功能会顺利很多。4.1 题库表单表冗余字段还是选项子表题库表有两种方案。第一种是把选项 A/B/C/D 直接放在 traffic_question 表里适合判断、单选、固定四项多选题。优点是简单查询快写代码直观。缺点是如果某道题有五个选项或者选项带图片扩展起来麻烦。第二种是把选项拆成 traffic_option 子表每个选项一行关联 question_id。适合选项数量不固定、选项有图片、需要单独维护选项顺序的场景。缺点是查询时需要多表关联或多次查询开发量稍大。我建议第一版先用单表冗余字段。等遇到选项需要加图片、数量不固定时再考虑拆表。很多毕设和内部项目用单表就够。不要为了“标准化”过早增加复杂度。4.2 用户学习行为表除了题目表至少还需要几张行为表表名用途关键字段traffic_user用户信息openid, unionid, nickname, avatar, roletraffic_answer_record每次答题明细user_id, question_id, user_answer, is_correct, source_typetraffic_exam_record模拟考试主记录user_id, score, total_count, correct_count, duration, statustraffic_wrong_book错题本user_id, question_id, wrong_count, last_wrong_time, is_masteredtraffic_favorite收藏表user_id, question_id, created_time其中 traffic_answer_record 可以记录用户每次答题结果source_type 用来区分是练习还是考试。traffic_exam_record 保存每次模拟考试的整体情况。traffic_wrong_book 不是把所有错误记录都存一次而是保存一个“错题状态”同一个用户同一道题可以累计错题次数这样错题本里不会出现重复题目。4.3 为什么 openid 要做唯一索引用户登录后后端通过 wx.login 的 code 调用微信接口换取 openid。openid 是用户在当前小程序里的唯一身份标识设计时一定要加唯一索引。否则同一个用户重复登录会产生多条记录错题和成绩无法关联。昵称和头像的处理要注意。微信已经调整了用户信息接口不能像以前那样直接弹出授权获取昵称头像。更稳妥的做法是先用 openid 建立用户记录默认头像和昵称为空等用户主动填写或通过头像昵称填写能力补充。不要在登录流程里强制要求头像昵称否则会卡住一批用户。4.4 判断题、单选题、多选题的答案约定答案字段的格式要统一否则判题逻辑会很乱。我建议这样约定判断题answer 存 0 或 10 表示错误1 表示正确。单选题answer 存 A、B、C、D。多选题answer 存 A,B,C 或 A,C,D统一用逗号分隔。多选题判题时最怕用户选择顺序不一样。用户选 A,D,C正确答案是 C,A,D如果直接用字符串比较会判定错误。所以服务端判题前要先把选项排序再比较public boolean checkAnswer(String correctAnswer, String userAnswer) { String correct normalize(correctAnswer); String user normalize(userAnswer); return correct.equals(user); } private String normalize(String answer) { return Arrays.stream(answer.split(,)) .map(String::trim) .map(String::toUpperCase) .sorted() .collect(Collectors.joining(,)); }这样多选题不管用户点选顺序如何判断结果都正确。4.5 考试模式下答案安全考试模式必须注意不要在小程序端下发正确答案。很多新手把题库接口写成一个万能列表接口练习和考试都用同一个接口返回内容又把 answer 字段带出去了。用户只要在网络面板里看一次请求就能拿到所有题目答案。正确做法是练习接口可以返回解析考试接口只返回题目、选项、类型不返回答案。交卷时服务端根据 answer 字段判分再把成绩和解析统一返回。这样既保证考试公平也减少不必要的业务风险。5. 小程序端核心页面和交互答题页是重头戏小程序端虽然有多个页面但真正复杂的是答题页。考试倒计时、答题卡、多选题切换、交卷确认这些交互都要在答题页里处理。5.1 页面目录和跳转关系推荐页面目录如下pages/index/index 首页 pages/exam/exam 答题页练习和考试共用 pages/result/result 成绩页 pages/wrong/wrong 错题页 pages/favorite/favorite 收藏页可选 pages/profile/profile 个人中心页index 页面放两个入口练习模式和模拟考试。点击对应入口后跳转到 exam 页面并带上 mode 参数。exam 页面根据 mode 决定是否显示倒计时、交卷按钮和即时解析。result 页面展示考试成绩可以从 exam 页面带 examId 跳转也可以从历史记录进入。5.2 答题页的数据结构答题页不要直接修改 questionList 里的数据维护一份独立的答案映射表会更方便。推荐在 data 中这样设计data: { mode: practice, // practice 或 exam questionList: [], currentIndex: 0, selected: [], // 当前题已选答案 answerMap: {}, // 题号 - 已选答案 remainSeconds: 600, // 考试剩余秒数 examId: null }questionList 只负责渲染。当前题选中状态单独用 selected。用户切换题目时从 answerMap 中恢复 selected这样不会出现切出去再切回来选项丢失的问题。5.3 单选/判断题的点击逻辑单选题和判断题点击选项后是直接替换 selected 里的值handleTapOption(e) { const { value } e.currentTarget.dataset; this.setData({ selected: [value] }); }多选题要支持选中和取消使用 toggle 逻辑handleTapOption(e) { const { value } e.currentTarget.dataset; let selected [...this.data.selected]; const index selected.indexOf(value); if (index -1) { selected.splice(index, 1); } else { selected.push(value); } this.setData({ selected }); }每次点击选项后同步更新 answerMapconst { answerMap, currentIndex, selected } this.data; answerMap[currentIndex] selected; this.setData({ answerMap, selected });这样交卷时只需要遍历 answerMap就能拼出完整的答案列表。5.4 上一题下一题和答题卡答题卡组件可以显示题目序号和答题状态。已答的题号用实心颜色未答的用灰色。点击题号直接跳转到对应题目。切换题目时要做两件事先保存当前题答案到 answerMap再从 answerMap 里恢复新题目的 selected。顶部可以显示整体进度比如“第 5/100 题”。如果要做自定义导航栏还需要获取状态栏高度和胶囊按钮位置使用 wx.getMenuButtonBoundingClientRect 拿到胶囊信息否则自定义标题栏在全面屏手机上会顶到状态栏。这个细节看起来小但真机适配时经常出问题。5.5 倒计时和交卷模拟考试需要倒计时。最简单的方式是 setInterval 每秒减少 remainSeconds。但用户可能锁屏、切后台setInterval 不一定准。更稳妥的方案是记录考试开始时间 timeStamp页面每次切到前台时用当前时间减去开始时间再重新计算剩余秒数const now Date.now(); const elapsed Math.floor((now - this.data.startTime) / 1000); const remain Math.max(0, this.data.totalSeconds - elapsed); this.setData({ remainSeconds: remain });倒计时归零后自动交卷。交卷前要弹窗提示未答题目数避免用户误点。页面 onUnload 时要清理定时器否则会内存泄漏。5.6 交卷和结果页交卷时需要把 answerMap 转换成一个数组按题目顺序提交const submitData []; const { questionList, answerMap, examId } this.data; questionList.forEach((item, index) { const userAnswer answerMap[index] || []; submitData.push({ questionId: item.id, userAnswer: userAnswer.join(,) }); }); wx.request({ url: https://your-domain.com/api/exam/submit, method: POST, data: { examId: examId, answers: submitData }, success: (res) { const data res.data.data; wx.redirectTo({ url: /pages/result/result?examId${data.examId} }); } });成绩页显示分数、正确率、用时还可以显示本次考试所有错题的解析。练习模式交卷后可以即时显示解析考试模式要等交卷时统一返回避免提前泄露答案。6. 后端接口设计返回格式、分页、随机出题和幂等交卷后端接口设计要和前端页面对应好。接口路径、参数、返回结构都要提前约定否则前后端联调时会反复改。6.1 统一返回格式建议所有接口都返回同一结构{ code: 0, message: success, data: {} }code 是业务状态码0 表示成功。登录过期、参数错误、业务异常使用非 0。前端可以封装一个 request 方法统一判断 code。不要出现一个接口返回对象一个接口返回数组另一个接口直接返回字符串否则前端很难处理。6.2 核心接口列表第一版的核心接口可以按这张表来接口方法说明/api/user/loginPOST接收 wx.login 的 code换 openid返回 token 和用户信息/api/question/randomGET随机返回练习题支持按分类和数量过滤/api/exam/startPOST创建一次模拟考试返回 examId 和题目列表不含答案/api/exam/submitPOST提交考试答案返回成绩、正确率、错题列表/api/wrong/listGET分页查询错题列表/api/wrong/removePOST移出错题或标记为已掌握/api/study/progressGET返回用户学习进度、答题总数、正确率考试开始和提交是独立接口。start 接口生成一条 exam_record状态为进行中submit 接口通过 examId 定位记录计算成绩。这样即使前端网络抖动用户重新进入考试也能找到未完成的记录。6.3 随机出题的实现方式练习模式要随机抽题。最直接的 SQL 是SELECT * FROM traffic_question ORDER BY RAND() LIMIT 10;数据量小的时候没问题但题目上万之后ORDER BY RAND() 会对全表排序性能下降明显。如果只是毕设或几十人的内部系统可以继续用。如果是准备上线的真实项目建议先把题目 id 列表缓存起来比如放在 Redis 或内存里每次随机从 id 列表里抽取再按 id 查询题目。这样可以避免大表的随机排序。考试模式还要考虑每次考试的题目不重复、同一用户短时间内不能抽到完全一样的试卷。可以在 start 接口生成试卷时保存快照或者只保存题目 id 列表。简单做法是生成一个随机种子然后把题目按固定算法排序保证考试题目稳定。6.4 提交答案的幂等性考试提交接口必须考虑重复提交。用户可能网络超时后点了两次交卷或者页面重复跳转导致请求重发。如果后端不做幂等会产生两次考试记录成绩也会变乱。简单方案是start 接口在 exam_record 里生成一个唯一 examId初始 status0进行中。submit 接口先查这个 examIdif (examRecord.getStatus() 2) { return Result.success(examRecord); } examRecord.setStatus(2); examRecord.setScore(score); examRecord.setCorrectCount(correctCount); examRecordService.updateById(examRecord);已经交过卷的记录重复提交时直接返回第一次的成绩。这样能避免重复计分也能避免前端重试后表现不一致。6.5 分页和列表过滤错题列表、收藏列表、成绩历史这些列表接口都要做分页。分页参数统一用 page 和 sizeGET /api/wrong/list?page1size20返回结构里除了 records还要有 total。前端可以通过 total 判断是否还有下一页配合 onReachBottom 做上拉加载。分类查询可以通过 type 参数过滤判断题、单选题、多选题也可以通过 categoryId 按章节筛选。字段命名建议后端统一使用驼峰返回比如 correctCount 而不是 correct_count小程序端取值时可以少一层转换。7. 订阅消息、微信支付和蓝牙打印扩展功能要单独设计核心刷题功能稳定后很多需求方会加扩展功能。扩展功能不是不能用但一定要和核心功能解耦否则一个扩展功能出问题会影响整个小程序。7.1 订阅消息推送订阅消息适合做学习提醒、考试结果通知、新题库上线提醒。小程序端需要先调用 wx.requestSubscribeMessage 让用户授权拿到一次性订阅的授权后才能给用户发一次消息。同一个模板最多弹出一次授权框不能每次都弹。服务端发送订阅消息前需要先获取 access_token调用 subscribeMessage.send 接口带上用户的 openid、模板 ID、页面路径和消息数据。access_token 有有效期建议缓存起来避免每次都重新获取。开发时要注意用户不授权服务端就不能发送用户授权次数用完再发送会报错。不要把消息推送设计成关键通知比如交卷成绩不能只靠订阅消息展示否则用户不授权就看不到成绩。7.2 微信支付 V3先确认资质再动手如果要做付费题库、VIP 会员就需要接入微信支付。微信支付 V3 涉及商户号、API 证书、API v3 密钥、平台证书、回调地址、订单号等很多要素。开发时要特别注意小程序类目必须合规支付资质要提前申请。不要拿正式 API 密钥反复测试先用测试环境或模拟数据验证流程。支付成功后依赖回调通知更新订单状态不能只靠前端返回结果。一旦小程序违规支付功能会被限制所以接入前一定要确认业务资质和平台规则。我的建议是支付功能等核心刷题和考试体系稳定后再接。不要让题库接口依赖支付模块的配置否则支付配置缺一个参数整个小程序都可能起不来。7.3 蓝牙打印线下驾校场景可以后置有些驾校希望学员考完试直接通过蓝牙打印成绩单。这个需求在线下场景真实存在但蓝牙打印的坑很多。小程序需要处理蓝牙适配器初始化、设备搜索、连接、服务发现、写入指令不同打印机的指令格式还不同。这个功能适合放到二期并单独抽出一个工具模块来写不要和考试提交、成绩展示混合在一起。如果没有明确的线下打印需求不建议放到第一版。7.4 通过配置开关控制扩展功能为了降低扩展功能带来的风险可以在后端配置里增加开关feature: pay-enabled: false push-enabled: false print-enabled: false代码中通过 ConfigurationProperties 或 Value 读取配置。接口根据开关返回不同结果。这样做的好处是某个扩展功能没配好核心刷题接口仍然可以正常使用。灰度发布时也可以先开一部分用户观察稳定性后再全量放开。8. 从开发到上线域名、HTTPS、版本审核和常见坑小程序开发完成不等于上线完成。域名、HTTPS、版本审核、类目资质这些环节反而最容易卡人。建议按流程走别跳步。8.1 上线发布流程正式发布流程是在微信开发者工具中上传代码填写版本号和备注然后在微信公众平台提交审核。审核通过后再点击发布。如果是自己学习或测试可以先使用体验版不需要提交审核。但体验版只能添加体验成员不能面向所有用户。正式发布前要确认小程序类目。交通规则学习类可以选择教育或工具相关类目具体以微信公众平台当前要求为准。个人主体很多权限受限如果要做支付、订阅消息、用户信息扩展建议使用企业主体。8.2 request 合法域名和 HTTPS小程序 wx.request 请求的接口域名必须在小程序管理后台配置为 request 合法域名。域名必须支持 HTTPS且需要备案。开发时可以在本地设置里勾选“不校验合法域名”但真机预览、体验版、正式版都不行。常见问题是服务器 80 端口能访问443 端口没放行SSL 证书只配置了主域名没配置 www接口路径里带了下划线请求 URL 里出现 http 而不是 https。遇到接口请求失败先看微信开发者工具 Network 面板里的具体报错再用浏览器或 curl 直接请求接口确认后端本身是否正常。如果后端正常但小程序请求失败大概率是域名和证书的配置问题。8.3 常见错误排查顺序遇到问题先不要乱改代码按这个顺序排查现象优先排查点常见处理小程序请求报 url not in domain list合法域名配置在公众平台添加 request 合法域名并下载校验文件接口超时或 504后端服务、数据库连接、接口耗时看后端日志确认数据库连接池是否正常页面空白接口返回格式、setData key 名在 Network 面板看返回 JSON确认字段名一致考试无法交卷是否缺少 examId、answerMap 是否为空检查请求参数和后端校验逻辑自定义导航栏错位状态栏高度、胶囊位置用 wx.getMenuButtonBoundingClientRect 获取实际高度视频播放失败视频域名、视频格式、组件层级确认视频文件在合法域名下或使用官方视频组件兼容方案有个典型场景如果用 uni-app 开发从 HBuilderX 运行到微信开发者工具时提示“不是开发者”。这个问题通常不是代码问题而是微信开发者工具没有开启服务端口。需要在微信开发者工具“设置 - 安全设置”里打开服务端口并确认 HBuilderX 里配置的微信开发者工具路径正确。8.4 低配服务器上的性能和并发边界交规系统的特点是读多写少大多数请求是拉取题目和提交答案。如果只有 2 核 4G 的轻量服务器日常几十人、上百人在线问题不大。但模拟考试会集中在某个时间段可能产生并发高峰。上线前期不要盲目调高并发参数。Tomcat 默认线程数可以参考但不建议一上来就调到 1000低配服务器很可能会 OOM。更稳妥的做法是先用脚本模拟 50 个用户同时提交答案观察接口平均响应时间和错误率。如果响应时间没有明显劣化再逐步增加并发。同时给 MySQL 连接池设置合理上限比如 20 到 50避免连接被占满导致后端假死。8.5 上线后的数据监控和备份上线后至少要保留后端日志和慢 SQL 日志。每次发版前先备份数据库尤其是题库表、用户表、考试记录表。题目更新不要直接改生产库建议通过管理后台导入 Excel导入时校验题号、答案、重复内容。上线后重点看几个指标日活、模拟考试完成率、平均成绩、错题集中分布。错题排行能反哺题库内容比如某些题目错误率过高可能是题目表述有问题也可能是讲解不够清楚这时可以针对性地补充解析。其实这类小程序真正落地后最影响体验的往往不是某个炫技功能而是题库结构是否清晰、考试提交是否可靠、域名和证书是否配置正确。先跑通最小闭环再补扩展功能会省掉很多返工。我个人的建议是第一版只做练习、模拟考试、错题和成绩四个模块等这些稳定了再慢慢加订阅消息、支付和蓝牙打印。
返回列表