微信考试小程序源码解析:前后端分离、题库设计与部署实践

发布时间:2026/9/17 5:09:02

微信考试小程序源码解析:前后端分离、题库设计与部署实践 简介这套微信考试答题小程序是一份适合毕业设计、也可用于实际场景的完整前后端源码包包含数据库文件覆盖小程序端、后台管理与接口服务。功能涉及单题/列表答题模式、分数查看、错题记录、历史记录、图片题库以及海报生成等可适配企业招聘笔试、知识竞赛、培训认证等不同考试场景。压缩包共2030个文件总大小约10MB其中以1545个js文件为主承载前后端逻辑286个md文档提供说明其余为json配置、html页面、xml及css等资源结构清晰便于学习与二次开发。目前已有716人浏览学习适合计算机相关专业学生作为毕业设计参考也适合想快速搭建答题小程序的技术开发者用于改造和部署。1. 拿到“微信考试小程序”源码包之后先想清楚要改什么拿到标题里这个.zip的时候我通常不会急着解压而是先把它当成一个“前后端分离的课程设计遗留物”来看微信小程序端负责答题交互后端接管出题、判分和登录态数据库存题目、用户和成绩。这类项目最常见的交付形态就是这样一个压缩包把三样东西打包在一起但真正决定它能不能跑起来的不是代码多少而是数据库脚本和接口地址有没有对齐。你可能是要拿它做毕业设计答辩也可能是想改造成考试系统或者培训机构的刷题小程序。无论哪种第一步都别停在“把 hello world 在微信开发者工具里跑起来”。更值得花时间的是把前端的请求地址、后端的配置文件、数据库的初始脚本这三个交接点理清否则换个电脑就起不来。下面按我自己会操作的顺序把这套链路盘一遍。2. 拆开前后端完整源码前端、后端与数据库三层该看哪里既然是完整源码目录结构通常不会只有小程序页面。zip包解压后建议先按层归类一般会看到小程序端、服务端、SQL 脚本三块。常见命名习惯是client、server、sql也有直接叫wxxcx、backend、database的按代码语言和构建文件去识别比按文件夹名更靠谱。先落一个通用结构你照着找就行exam-wx/ ├── client/ # 微信小程序端 │ ├── pages/ │ │ ├── index/ # 首页课程与试卷列表 │ │ ├── exam/ # 答题页刷题和考试共用 │ │ ├── result/ # 交卷后成绩页 │ │ └── mine/ # 个人中心与错题记录 │ ├── app.js # 小程序入口全局登录态 │ ├── app.json # 页面注册与 tabBar │ └── project.config.json # 开发者工具项目配置 ├── server/ # 后端服务 │ ├── pom.xml # Maven 依赖管理Spring Boot 常见 │ └── src/main/resources/ │ └── application.yml # 数据源、端口、微信参数 └── sql/ └── exam.sql # 建库建表语句和演示数据如果你拿到的包里没有单独sql目录多半是把建表语句写在README.md里或者后端resources目录下带了schema.sql。找到它之后先看表数量再决定从哪一层开始启动。2.1 用于毕业设计的项目结构原生小程序还是 uniapp先要判断前端是原生微信小程序还是 uniapp 工程。原生项目的pages下面全是.js/.wxml/.wxss文件入口是app.jsuniapp 工程则常看到src/pages、src/App.vue、manifest.json。两者最终都能用微信开发者工具运行但改代码的方式完全不同。如果拿到的是 uniapp我一般建议直接用 HBuilderX 打开源码包根目录而不是强行导入微信开发者工具。先确认manifest.json里的mp-weixin配置有没有填 appid然后在 HBuilderX 里运行到微信开发者工具。反过来原生小程序就直接用微信开发者工具“导入项目”选到client目录即可。一个容易忽略的点是 project.config.json 里的miniprogramRoot。有些源码把前端代码嵌在client/dist/wx这类编译产物目录中如果你直接导入整个client开发者工具会提示找不到 app.json。这时要检查miniprogramRoot是否指向了编译输出目录是的话按原样保留别自己乱改路径。2.2 后端接口设计如何配合小程序端免鉴权毕业设计级别的考试小程序前后端交互不会太复杂但接口路径一般能看出套路。小程序端通过wx.request调用后端后端返回 JSON。重点看三个接口登录、试卷列表、提交试卷。常见设计是这样POST /api/wx/login 请求体: {code: wx.login 获取的临时凭证} 响应: {token: 服务端签发的 JWT, openid: oXXXX} GET /api/exam/list 请求头: Authorization: Bearer token 响应: {code: 0, data: {records: [{id:1,title:科目一模拟卷}], total: 10}} POST /api/answer/submit 请求体: {paperId: 1, answers: {12: A, 25: B}, duration: 584} 响应: {code: 0, data: {score: 82, correct: 18, wrong: 2}}后端通常不会为每个小程序页面单独写接口而是按“考试”“题库”“错题”三个资源去组织。如果你要改成刷题类应用重点就是让GET /api/exam/list支持分页和科目筛选而不是每次把整张卷子所有题目都拉出来。这个后面会说怎么改。2.3 数据库表设计决定刷题功能的上限拿到sql/exam.sql后不要直接从头执行到尾先打开看建表语句。考试和刷题类型的数据库核心表一般是这几张表名作用关键字段sys_user用户表openid, nickname, avatarexam_course科目/课程name, remarkexam_paper试卷course_id, title, duration_minutes, total_scoreexam_question题目paper_id, question_type, content, options, answer, analysisexam_record考试记录user_id, paper_id, score, submit_timewrong_book错题本user_id, question_id, wrong_answer其中exam_question表决定题目能不能被复用。如果表的试卷关联字段直接写在paper_id上改成刷题模式时一道题就只能挂在某张卷子下灵活性很差。我自己的习惯是一开始就按“科目 题目”解耦不让题目和试卷强绑定。简化建表可以这样看CREATE TABLE exam_question ( id bigint PRIMARY KEY AUTO_INCREMENT, course_id bigint NOT NULL COMMENT 科目ID代替paper_id做解耦, question_type tinyint NOT NULL COMMENT 1单选 2多选 3判断, content text NOT NULL COMMENT 题干, options_json text COMMENT 选项JSON数组多选题也适用, answer varchar(10) NOT NULL COMMENT 标准答案如 A 或 A,C, analysis text COMMENT 答案解析, difficulty tinyint DEFAULT 1 COMMENT 难度1简单 2中等 3难, create_time datetime DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目表;这个设计的好处是同一个题库既能组考试卷也能支撑按科目顺序刷题difficulty字段还能做随机抽题的权重。很多毕业设计源码为了省事把options和answer都存成逗号分隔的字符串这在小规模演示下没问题但你要是打算自己扩展尽量在开始时就用options_json存结构化数据省得后面返工。3. 用微信开发者工具把答题小程序跑通的最短路径本地跑通这条链路顺序只有一条先数据库再后端最后前端。后端没起来之前小程序页面能打开但登录接口一定是报错的。很多人卡在这里是因为在好几个终端窗口里同时操作不知道先看哪个日志。按着数据库、Maven、小程序这个顺序来最少十分钟能见到首页数据。3.1 微信开发者工具导入前端前的三个准备动作微信开发者工具本身不是难点但我见过比较多的问题是版本不匹配。现在 stable 版本是几就用几不用刻意追 nightly。导入前先确认三件事第一要有一个小程序 AppID。自己做演示可以用测试号但测试号不支持某些能力例如不校验合法域名时调本地接口没问题真机预览就要有真实 AppID。第二开发者工具里要打开“详情 - 本地设置 - 不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”这个开关因为本地调试时后端是http://localhost:8080不打开开关请求会被拦。第三确认项目根目录选对如前面说的指向miniprogramRoot对应的那个目录。导入之后先在app.js里看有没有写死baseUrl。很多源码喜欢把地址直接写死// client/config/env.js module.exports { baseUrl: http://localhost:8080, timeout: 10000 }如果代码里到处是wx.request({ url: https://xxx.com/api/... })我建议你统一改成引用env.js里的baseUrl后面切测试服、正式服只改一个文件。3.2 先把数据库建好再让后端连上它找到sql/exam.sql后在 MySQL 里执行。如果脚本里没有建库语句就手动建库然后选中库再执行mysql -u root -p -e CREATE DATABASE IF NOT EXISTS exam_wx DEFAULT CHARACTER SET utf8mb4; mysql -u root -p exam_wx sql/exam.sql执行完成后用一句 SQL 验证数据有没有进去SELECT COUNT(*) FROM exam_question;很多源码提供的演示数据不多你能看到exam_question里至少十来条记录就够了。之后打开后端项目的application.yml重点看数据源配置、端口、微信小程序配置三块。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/exam_wx?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver wechat: appid: your_appid secret: your_secret exam: # 试卷最多允许提前交卷或自动交卷的误差时间单位秒 submit-grace-seconds: 5 # 刷题模式一页返回多少题 page-size: 20这几个参数里url最后的serverTimezone必须和服务器时区一致。如果你在本地跑MySQL 版本是 8.x驱动类名要用com.mysql.cj.jdbc.Driver老项目里的com.mysql.jdbc.Driver新版已经弃用。改完这些在server目录执行cd server mvn spring-boot:run看到Started Application或Tomcat started on port(s): 8080字样后端就算活了。3.3 改前端请求地址并验证第一个接口后端起来后不要急着在小程序里点按钮。先手动请求一下接口确认后端和数据库是通的。可以这样curl -X POST http://localhost:8080/api/wx/login \ -H Content-Type: application/json \ -d {code:debug_code}如果数据库和微信配置没接好这里会直接暴露错误比在小程序页面里调试快得多。注意code这里传的是测试值后端如果调用微信code2Session会返回异常但很多毕业设计源码的登录接口为了便于演示会用一个本地假 code 就生成 openid这种设计虽然不符合生产标准却方便本地联调。接口通了之后回到微信开发者工具把env.js里的baseUrl确认成http://localhost:8080。点击首页登录控制台 Network 面板里应该能看到/api/wx/login和/api/exam/list两个请求返回码都是 200数据里有records字段。看到这里前后端链路就通了。4. 刷题与考试核心逻辑题库、计时和判分的必改参数很多毕业设计里的“考试”和“刷题”是同一个页面只靠一个mode参数区分。这个思路没错但要注意二者在题目加载方式、交卷策略、判分逻辑上差别很大。如果只把前端mode从exam改成practice就完事你会发现刷题模式下也强制计时、也限制只能答一次体验很怪。下面把三个最容易改出问题的地方拆开讲。4.1 抽题与乱序题目列表和考试试卷不能一样实现考试模式要求每次进入试卷题目顺序可能不同刷题模式要求按科目顺序稳定往下做不能每次刷新都换一批。所以后端要区分两种取题逻辑。常见做法是考试模式随机抽样刷题模式分页返回-- 考试模式按科目随机抽 20 题 SELECT id, content, options_json, question_type FROM exam_question WHERE course_id 1 ORDER BY RAND() LIMIT 20; -- 刷题模式按科目分页题目顺序稳定 SELECT id, content, options_json, question_type FROM exam_question WHERE course_id 1 ORDER BY id ASC LIMIT 0, 20;如果题目量上千ORDER BY RAND()会有性能问题因为 MySQL 会全表扫描生成随机排序。比较稳的替代方案是先用COUNT(*)拿到总量在服务端生成随机偏移量再LIMIT offset, 1分多次取或者干脆在应用层洗牌。这里牵扯到两个必调参数。一是考试抽取题数建议在application.yml里配exam.paper-size: 20不要写死在 SQL 里。二是乱序范围是整份卷子乱序还是单选题内部乱序、多选题内部乱序。很多答题小程序的实现是整个题目数组乱序选项不重排这么做好做但考试时很容易出现所有题目的正确答案都集中在某一两个选项上答辩评委一眼就能看出问题。4.2 小程序端答题状态答案数组、剩余时间与自动交卷考试页面的核心是保证用户答到一半切后台回来计时还在继续并且要把答案保存在内存中不要每次setData都重刷整个题目列表。我用原生小程序写的话会这样管理答案和计时Page({ data: { paperId: null, questionList: [], currentIndex: 0, answers: {}, // 用 { questionId: A } 结构保存 remainSeconds: 600 // 从后端返回的考试时长 }, onLoad(options) { this.paperId Number(options.paperId) this.loadPaper(this.paperId) this.startTimer() }, startTimer() { this.timer setInterval(() { if (this.data.remainSeconds 0) { clearInterval(this.timer) this.autoSubmit() return } this.setData({ remainSeconds: this.data.remainSeconds - 1 }) }, 1000) }, chooseAnswer(e) { const { id, value } e.currentTarget.dataset this.setData({ [answers.${id}]: value }) }, onUnload() { if (this.timer) { clearInterval(this.timer) } } })关键点有三个。第一[answers.${id}]这种动态路径写法是 setData 的合法用法只更新当前那道题的答题值性能比整个赋值answers好得多。第二计时器在onUnload里必须清掉否则用户退出页面后定时器还在跑容易触发setData报错。第三自动交卷和手动交卷要调用同一个提交函数不要在自动交卷逻辑里再写一份判分逻辑。如果你拿到的是 uniapp 写的同样逻辑要放到onUnload或beforeDestroy生命周期里清定时器。uniapp 编译到微信小程序后生命周期映射略有差异但思路一致。4.3 判分与错题入库事务和幂等是唯一的坑判分逻辑放在后端不要信前端传过来的 score。前端可以传答案数组和答题用时但正确答案和分值只能在服务端算否则接口被刷人人都是满分。在 Spring Boot 里判分加错题本写入必须放在同一事务里否则会出现成绩落库但错题没写进去的脏状态。示意代码Transactional(rollbackFor Exception.class) public SubmitResult submit(SubmitRequest request) { Paper paper paperMapper.selectById(request.getPaperId()); ListQuestion questions questionMapper.selectByPaperId(paper.getId()); int score 0; int correct 0; for (Question q : questions) { String userAnswer request.getAnswers().get(q.getId().toString()); if (Objects.equals(normalize(userAnswer), normalize(q.getAnswer()))) { score q.getScore(); correct; } else { wrongBookMapper.insert(userId, q.getId(), userAnswer); } } ExamRecord record new ExamRecord(); record.setUserId(userId); record.setPaperId(paper.getId()); record.setScore(score); record.setCorrectCount(correct); record.setDurationSeconds(request.getDurationSeconds()); recordMapper.insert(record); return new SubmitResult(score, correct, questions.size() - correct); }这个写法里多选A,C和用户提交的C,A要当成同一个答案所以比较前要做一个 normalize去掉空格、转大写、按字母排序。这是判分最容易出 bug 的地方尤其当answer字段是A,C这种字符串时。还需要处理重复提交。前端防抖只能拦住手滑拦不住网络重试。后端给exam_record表加一个唯一索引(user_id, paper_id)或者在上面的insert前先select一次发现已有记录就直接返回第一次的分数保证接口幂等。5. 交付前必须处理的三个细节和一条验证路径走到这一步本地已经能答题、提交、看成绩。但如果这是毕业设计答辩要演示或者想部署到服务器上供别人访问还有三个细节绕不开全部处理完再谈上线的事。5.1 微信登录、合法域名与 HTTPS 三个必填项小程序从开发者工具换到真机预览第一个跳出来的问题就是“不在合法域名列表”。本地调试可以不校验域名但真机和上传体验版必须把接口域名配置到微信公众平台“开发管理 - 服务器域名”里而且只能是 HTTPS 地址。后端要做的配合是把wechat.appid和wechat.secret换成真实值。还有一点secret不要写在前端的任何配置文件里它如果出现在小程序代码包中会被看成泄露风险。正确做法是后端调用微信接口换 openid前端只能拿到后端签发的 token。做好 HTTPS 反代是最省事的可以用 nginx 把 443 端口代理到本地 8080证书申请放到服务器厂商的免费证书里配置即可。5.2 用一条 curl 命令验证全链路是否真正跑通上线后别急着拿手机打开小程序先用一条命令验证接口链路curl -s -X POST https://api.example.com/api/wx/login \ -H Content-Type: application/json \ -d {code:debug_code} \ | head -c 500这条命令能看到后端返回的 token 或错误信息。确认返回结构正确后再带上 token 请求试卷列表看题目数据是否从数据库正常查出来。前后端服务和数据库任一环节有问题这条链路的响应码或数据体都会中断比在小程序里反复点“重新进入”高效得多。最后留一个我自己判断项目是否闭环的办法在同一账号下连续提交三次答卷再登录 MySQL 查exam_record表看记录数是否严格等于三次。如果一致说明登录、取题、判分、落库、幂等这五件事都站住了这套微信考试小程序源码才算真正变成你可以接手维护的项目。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/17 5:09:02

WS2812驱动实战:时序精度、信号完整性与工业级避坑指南

1. 为什么WS2812不是“普通LED”,而是一套精密的微型嵌入式系统?WS2812这三个字母,对刚接触智能灯带的朋友来说,可能只是淘宝搜索框里敲出的几个字符;但对做过三年以上嵌入式开发、亲手焊过几十块PCB、在凌晨三点调试过…

2026/9/17 5:09:02

Mask2Former实战:一套架构统一语义/实例/全景分割

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 6:09:04

Python爬取Boss直聘大数据岗位并进行数据清洗与可视化分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 6:04:04

Linux运维必备:构建可复用的Shell脚本库与自动化巡检体系

简介:面向Linux运维工程师及shell初学者,这份PDF系统整理了日常高频使用的自动化脚本,覆盖日志错误过滤、服务连通性检查、旧文件清理、目录备份压缩、批量Ping多主机、SCP远程传输、用户home目录校验、日志实时监控等典型场景,并…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/17 0:03:13

WiFi密码安全测试:从原理到实战的字典暴力破解指南

1. 写在前面:我为什么要研究WiFi密码这件事先交代一下背景。我身边有不少朋友,家里的WiFi密码常年是"12345678"或者"88888888",问就是"好记"。直到有一次,隔壁邻居蹭网蹭到我家路由器后台都进不去&…

2026/9/17 0:03:13

redis-py服务控制与监控函数实战:从ping到slowlog的巡检指南

我用 redis-py 写了快五年的业务代码,坦白说,真正让我觉得这个客户端“像一个成熟工具箱”的,不是 get/set 那套基本操作,而是它那批专门做服务控制与状态监控的辅助函数。日常开发里,大家把redis.Redis(host..., deco…

2026/9/17 0:03:13

SpringBoot+Vue3实现中小企业设备管理系统开发实践

1. 项目概述与核心价值中小企业设备管理系统是制造业、服务业等领域的基础信息化工具。传统设备管理往往依赖Excel表格或纸质记录,存在数据孤岛、流程混乱、维护成本高等痛点。这套基于Java SpringBootVue3MyBatis的技术方案,通过前后端分离架构实现了设…

2026/9/16 22:55:57

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/16 22:56:09

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/16 22:56:16

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码