微信小程序+SpringBoot刷题系统毕业源码 简介本资源是一套完整的微信小程序SpringBoot双端刷题系统毕业设计源码面向计算机专业本科生、Java与前端初学者及毕设开发者解决在线学习平台从0到1的全栈开发实践问题。资源包共773个文件涵盖101个Java后端业务逻辑与实体类、93个Vue组件与22个WXML/WXSS小程序页面、89个JS交互脚本、175个PNG/SVG图标资源及25MB左右的项目配置与静态资源整体压缩包25.76MB结构清晰前后端分离明确。已有118人下载学习适合用于课程设计、毕业设计复现或技术栈整合训练。读者可直接导入IDE运行完整系统获得含用户登录、动态题库加载、实时答题评分、错题自动归集、成绩统计报表等核心功能的可执行工程并参考配套的bat启动脚本、YML配置、SQL建表语句及模块化Vue组件结构深入理解小程序与SpringBoot协同开发的关键实现路径。1. 微信小程序刷题系统一个能跑通登录、做题、判分、错题回看的 SpringBoot 全栈毕业源码不是 Demo是真实可部署的最小可行产品你手头正赶着毕设开题导师说“别整虚的得能点开、能注册、能做三道题、能看分数”但翻遍 GitHub 和 CSDN要么是只有前端页面的“假刷题”点击提交没响应要么是后端空有 Controller 却缺数据库表结构和业务逻辑的“半成品”。这个基于微信小程序 SpringBoot 的刷题系统源码就是为这种场景生的——它不是教学 Demo而是一个从微信授权登录、题目随机抽取、单选/多选实时判分、错题自动归集、到管理员后台批量导入题库全部走通的完整闭环。它用的是 SpringBoot 2.7.x非最新但稳定兼容 JDK8、MySQL 5.7、MyBatis-Plus前端完全基于原生微信小程序不依赖 uni-app 或 Taro所有接口都经过真机调试iOS/Android 微信客户端实测。适合计算机/软件工程专业本科生直接拿去改题库、换 UI、加功能而不是花三天配环境、两天调跨域、一天查 session 失效原因。如果你需要的不是一个“看起来像系统”的截图集合而是一份能让你在答辩现场打开手机、扫码、注册、做题、当场展示分数和错题本的源码那它就是你现在该停下来的那个。2. 项目结构与核心模块拆解SpringBoot 后端怎么组织小程序前端如何对接为什么这样设计2.1 后端目录结构按业务域划分不是按技术层堆砌这个 SpringBoot 项目没有照搬教科书式的controller/service/mapper三层平铺而是以“用户行为流”为线索组织包结构com.example.quiz ├── config // WebMvcConfigurer、RedisConfig、Swagger 配置 ├── controller │ ├── api // 小程序端调用的 REST 接口/api/user, /api/question │ └── admin // 管理员后台接口/admin/question, /admin/report带 PreAuthorize(hasRole(ADMIN)) ├── entity // JPA 实体类User, Question, UserAnswer, WrongQuestionRecord ├── dto // 数据传输对象LoginDTO, SubmitAnswerDTO, QuestionPageDTO ├── vo // 视图对象用于返回给前端的精简字段如 QuestionVO 只含 id/title/options/correctAnswer ├── service │ ├── impl // 业务实现UserServiceImp、QuestionServiceImp、AnswerServiceImp │ └── strategy // 判分策略接口 多选/单选/判断题不同实现类Strategy Pattern 落地 ├── mapper // MyBatis-Plus Mapper 接口继承 BaseMapper └── util // 工具类WxMaCode2SessionUtil、IdWorker 雪花ID生成器、JSONResult 封装类提示strategy包的存在是它区别于大多数“毕设级”源码的关键。单选题判分是多选题是Collection.equals()判断题是布尔值比对——这些逻辑被抽成独立策略类后续加“填空题”只需新增一个FillInBlankStrategy并注册进 Spring 容器不用动AnswerService主干代码。2.2 小程序前端关键页面与数据流小程序共 6 个核心页面全部使用wx.request直连 SpringBoot 后端无 Nginx 代理层简化部署页面路径核心功能关键 API 调用数据缓存策略/pages/login/login微信一键授权登录POST /api/user/login传code登录成功后将token存入wx.setStorageSync(token)全局拦截器校验/pages/index/index首页科目选择 最近练习记录GET /api/question/subject-list,GET /api/user/recent-practice科目列表本地缓存 10 分钟避免重复请求/pages/exam/exam刷题页倒计时 题目渲染 答案选择GET /api/question/random?subjectId1count5,POST /api/answer/submit题目数据内存暂存防止切后台再切回时重新拉取/pages/result/result提交结果页得分 正确率 错题跳转按钮GET /api/answer/result?recordIdxxx结果页不缓存确保分数实时性/pages/wrong/wrong错题本按科目/日期筛选 重做按钮GET /api/wrong/list?subjectId1page1size10错题列表下拉刷新强制更新/pages/admin/login管理员登录账号密码非微信授权POST /admin/logintoken 存入wx.setStorageSync(adminToken)与用户 token 隔离注意所有wx.request均封装在utils/request.js中统一处理 loading、token 自动注入、401 跳转登录页。这是小程序端最值得复用的基建代码——它把“每次请求都要写 header、catch error、showToast”这种重复劳动压缩成一行request.get(/api/user/info)。2.3 为什么选 MyBatis-Plus 而不是 JPA为什么不用 Redis 缓存题目MyBatis-Plus 选型理由毕设场景下题库表question常需复杂查询如“查某科目下难度为‘中’且未被该用户做过的题目”JPA 的 JPQL 写起来冗长且难调试而 MyBatis-Plus 的QueryWrapper链式调用.eq(subject_id, 1).eq(level, MEDIUM).notInSql(id, SELECT question_id FROM user_answer WHERE user_id ?)更贴近 SQL 思维学生 debug 时直接复制 SQL 到 Navicat 执行验证效率高。不用 Redis 缓存题目的真实原因不是技术不能而是没必要。题库总量通常 5000 题MySQL 单表查询毫秒级而引入 Redis 带来额外运维成本本地要装 Redis、Docker Compose 编排、缓存穿透/雪崩处理对毕设而言属于“过度设计”。真正用 Redis 的地方只有两个① 用户登录态 token 存储SETEX token:abc123 3600 {userId:123}② 管理员后台的题库导入任务状态HSET import:task:789 status PROCESSING progress 65。3. 从零部署数据库建表、后端启动、小程序配置三步落地3.1 MySQL 数据库初始化5 张表 初始化脚本说明项目附带src/main/resources/sql/quiz_init.sql执行前请确认MySQL 版本 ≥ 5.7因用到了JSON类型字段存储选项字符集为utf8mb4支持 emoji微信昵称可能含建表语句核心片段已去除非关键约束-- 用户表微信 openid 是主键避免重复注册 CREATE TABLE user ( openid varchar(64) NOT NULL COMMENT 微信唯一标识, nick_name varchar(50) DEFAULT NULL COMMENT 微信昵称, avatar_url varchar(255) DEFAULT NULL COMMENT 头像URL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 题目表options 字段存 JSON 数组如 [A. Java, B. Python, C. Go] CREATE TABLE question ( id bigint NOT NULL AUTO_INCREMENT, subject_id int NOT NULL COMMENT 科目ID, title text NOT NULL COMMENT 题干, options json NOT NULL COMMENT 选项JSON数组, correct_answer varchar(10) NOT NULL COMMENT 正确答案如 A 或 AB, level enum(EASY,MEDIUM,HARD) DEFAULT MEDIUM, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 用户答题记录表一次练习生成一条 record关联多道题 CREATE TABLE user_answer_record ( id bigint NOT NULL AUTO_INCREMENT, user_openid varchar(64) NOT NULL, subject_id int NOT NULL, score int NOT NULL DEFAULT 0, total_count int NOT NULL DEFAULT 0, correct_count int NOT NULL DEFAULT 0, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_openid (user_openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 用户每道题作答详情record_id 关联上表便于查某次练习的所有题 CREATE TABLE user_answer_detail ( id bigint NOT NULL AUTO_INCREMENT, record_id bigint NOT NULL, question_id bigint NOT NULL, user_answer varchar(10) DEFAULT NULL COMMENT 用户选择如 A 或 BC, is_correct tinyint(1) NOT NULL DEFAULT 0, answer_time_ms int NOT NULL DEFAULT 0 COMMENT 答题耗时毫秒, PRIMARY KEY (id), KEY idx_record_id (record_id), KEY idx_question_id (question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 错题本用户做错的题按 (user_openid, question_id) 联合唯一避免重复插入 CREATE TABLE wrong_question_record ( id bigint NOT NULL AUTO_INCREMENT, user_openid varchar(64) NOT NULL, question_id bigint NOT NULL, first_wrong_time datetime DEFAULT CURRENT_TIMESTAMP, wrong_count int NOT NULL DEFAULT 1, PRIMARY KEY (id), UNIQUE KEY uk_user_question (user_openid,question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明user_answer_record和user_answer_detail的一对多关系是支撑“查看某次练习详情”的基础。很多毕设源码把所有答案塞进一个大 JSON 字段导致无法按“某题被多少人答错”统计这里用规范关系型设计为后续扩展留出空间。3.2 SpringBoot 后端启动三处必须修改的配置项目使用application.yml以下三项必须人工确认spring: datasource: url: jdbc:mysql://localhost:3306/quiz_db?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai username: root # 改为你本地 MySQL 账号 password: 123456 # 改为你本地 MySQL 密码 redis: host: localhost # 若本地没装 Redis注释掉或改为 127.0.0.1 port: 6379 database: 0 # 微信小程序配置从微信公众平台获取 wechat: miniapp: appid: wx1234567890abcdef # 必填替换成你的小程序 AppID secret: your_miniapp_secret # 必填替换成你的小程序 AppSecret mch-id: # 无需支付留空启动方式IDEA 或命令行# 方式1IDEA 直接运行 QuizApplication.java # 方式2Maven 打包后运行 mvn clean package -Dmaven.test.skiptrue java -jar target/quiz-server-1.0.jar启动成功标志控制台输出Started QuizApplication in X seconds且访问http://localhost:8080/swagger-ui.html能打开 Swagger 文档管理员接口/admin/**需登录后才可见。3.3 小程序开发者工具配置三个关键文件修改打开project.config.json确认appid为你自己的小程序 ID{ description: 刷题系统, packOptions: {}, setting: { urlCheck: false, // 必须关掉否则本地调试时 wx.request 会被拦截 es6: true, enhance: true, postcss: true, preloadBackgroundData: false, minified: false, newFeature: true }, compileType: miniprogram, libVersion: 2.28.2, appid: wx1234567890abcdef, // 替换此处 projectname: quiz-miniprogram, debugOptions: { hidedInDevtools: [] }, isGameTourist: false, simulatorType: wechat, simulatorPluginLibVersion: {}, condition: {} }然后修改utils/config.js中的后端地址// utils/config.js const BASE_URL http://localhost:8080; // 开发时指向本地 SpringBoot // const BASE_URL https://your-domain.com; // 上线时替换为 Nginx 反向代理地址 module.exports { BASE_URL, LOGIN_URL: ${BASE_URL}/api/user/login, QUESTION_RANDOM_URL: ${BASE_URL}/api/question/random, ANSWER_SUBMIT_URL: ${BASE_URL}/api/answer/submit, // ...其他接口 };提示微信开发者工具默认开启“不校验合法域名”但urlCheck: true会强制校验request域名。务必在project.config.json中设为false否则所有请求 404。这是新手最常卡住的点——不是代码错是工具配置没关。4. 关键业务逻辑实现从微信登录到自动判分代码级还原4.1 微信小程序登录全流程code 换 session_key 自定义登录态小程序端调用wx.login()获取code传给后端/api/user/login// 小程序 login.js wx.login({ success: res { if (res.code) { wx.request({ url: config.LOGIN_URL, method: POST, data: { code: res.code }, success: res2 { if (res2.data.code 200) { wx.setStorageSync(token, res2.data.data.token); wx.switchTab({ url: /pages/index/index }); } } }); } } });后端LoginController处理逻辑关键在WxMaCode2SessionUtilPostMapping(/login) public ResultString login(RequestBody LoginDTO dto) { // 1. 调用微信接口换取 openid/session_key WxMaJscode2SessionResult sessionResult this.wxMaService.getUserService().getSessionInfo(dto.getCode()); String openid sessionResult.getOpenid(); if (StrUtil.isBlank(openid)) { return Result.fail(微信登录失败请重试); } // 2. 查询用户是否存在不存在则创建 User user userMapper.selectById(openid); if (user null) { user new User(); user.setOpenid(openid); user.setCreateTime(new Date()); userMapper.insert(user); } // 3. 生成自定义 tokenJWT有效期 7 天 String token JwtUtil.generateToken(openid, 7 * 24 * 3600); // 4. 将 token 存入 Redis设置过期时间 redisTemplate.opsForValue().set(token: token, openid, 7, TimeUnit.DAYS); return Result.success(token); }参数说明JwtUtil.generateToken()使用 HS256 算法密钥硬编码在JwtUtil.java中private static final String SECRET quiz-secret-key-2024;实际部署需改为环境变量读取。redisTemplate存 token 是为了登出时可主动失效DELETE token:xxx比 JWT 黑名单更轻量。4.2 随机抽题逻辑保证同一用户两次练习不重复QuestionService.randomQuestions()方法核心逻辑public ListQuestionVO randomQuestions(Long userId, Integer subjectId, Integer count) { // 1. 查出该用户已做过的所有题目ID避免重复 ListLong doneIds userAnswerDetailMapper.selectQuestionIdsByUser(userId); // 2. 构建 QueryWrapper排除已做题 按科目筛选 QueryWrapperQuestion wrapper new QueryWrapper(); wrapper.eq(subject_id, subjectId) .last(AND id NOT IN ( buildInSql(doneIds) )) // 动态拼 IN 子句 .orderByAsc(RAND()) // MySQL 随机排序 .last(LIMIT count); // 限制条数 ListQuestion questions questionMapper.selectList(wrapper); // 3. 转 VO过滤敏感字段如 correct_answer 不返回 return questions.stream() .map(q - { QuestionVO vo new QuestionVO(); vo.setId(q.getId()); vo.setTitle(q.getTitle()); vo.setOptions(q.getOptions()); // JSON 字符串前端解析 vo.setLevel(q.getLevel()); return vo; }) .collect(Collectors.toList()); }注意buildInSql(doneIds)是一个防 SQL 注入的工具方法当doneIds为空时返回0使NOT IN (0)恒真避免NOT IN ()语法错误。这是生产级写法比网上常见的if (doneIds.isEmpty()) { ... } else { ... }更简洁安全。4.3 多选题自动判分策略Strategy Pattern 的真实落地判分入口在AnswerService.submitAnswer()根据题目类型委派给不同策略Service public class AnswerService { Resource private MapString, AnswerStrategy strategyMap; // Spring 自动注入所有 Strategy 实现 public AnswerResult submit(UserAnswerDTO dto) { Question question questionMapper.selectById(dto.getQuestionId()); AnswerStrategy strategy strategyMap.get(question.getType()); // type SINGLE/MULTI/JUDGE boolean isCorrect strategy.judge(dto.getUserAnswer(), question.getCorrectAnswer()); // 保存答题详情... return new AnswerResult(isCorrect, calculateScore(isCorrect, question.getLevel())); } } // 多选题策略实现 Component(MULTI) public class MultiChoiceStrategy implements AnswerStrategy { Override public boolean judge(String userAnswer, String correctAnswer) { // userAnswerAB, correctAnswerAB → true // userAnswerA, correctAnswerAB → false少选错 // userAnswerABC, correctAnswerAB → false多选错 return StrUtil.equals(userAnswer, correctAnswer); } }逻辑说明多选题判分严格遵循“完全匹配”不支持“少选得部分分”。若需扩展只需新增PartialMultiChoiceStrategy并在Component(MULTI_PARTIAL)再改strategyMap的 key 映射即可主流程代码零修改。5. 避坑指南那些让毕设答辩前夜崩溃的 4 个真实问题5.1 现象小程序真机调试时wx.request报request:fail net::ERR_CONNECTION_REFUSED原因开发者工具勾选了“不校验合法域名”但真机调试时微信客户端仍强制校验request的域名是否在小程序后台配置的“服务器域名”中。localhost不被允许。解决方案 A推荐用ngrok或localtunnel将本地8080端口映射为公网 HTTPS 地址如https://abc123.ngrok.io填入小程序后台「开发管理 开发者工具 服务器域名」方案 B在手机上安装 Charles 或 Fiddler设置手机代理到电脑抓包确认请求是否发出方案 C仅测试在小程序后台临时添加127.0.0.1到 request 合法域名微信不支持此路不通纯属常见误操作。5.2 现象SpringBoot 启动报Caused by: java.lang.ClassNotFoundException: org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration原因pom.xml中spring-boot-starter-web依赖被错误排除或 Maven 仓库损坏导致 jar 包不全。常见于从 GitHub 下载源码后未执行mvn dependency:resolve。解决删除项目根目录下target文件夹和~/.m2/repository/org/springframework/boot/下对应版本文件夹在 IDEA 中右键项目 →Maven → Reload project终端执行mvn clean compile -U-U强制更新快照检查pom.xml是否存在exclusions误删了 web 依赖。5.3 现象管理员后台登录成功但访问/admin/question/list返回 403 Forbidden原因Spring Security 配置中/admin/**路径要求ROLE_ADMIN但管理员用户在数据库user表中role字段为空或不是ADMIN。解决连接 MySQL执行UPDATE user SET role ADMIN WHERE openid admin123;假设管理员 openid 为admin123或在AdminLoginController登录逻辑中手动设置角色UsernamePasswordAuthenticationToken auth new UsernamePasswordAuthenticationToken(username, password, Arrays.asList(new SimpleGrantedAuthority(ROLE_ADMIN)));5.4 现象小程序提交答案后/api/answer/submit接口返回{code:500,msg:空指针异常}日志显示questionIdnull原因小程序端exam.wxml中form提交时button form-typesubmit的name属性未与form的bindsubmit事件中e.detail.value的 key 对应。例如!-- 错误写法name 是 qid但后端 DTO 期望 questionId -- input nameqid value{{question.id}} hidden/ !-- 正确写法name 必须与 DTO 字段名一致 -- input namequestionId value{{question.id}} hidden/解决在exam.js的formSubmit(e)方法中console.log(e.detail.value)确认字段名检查SubmitAnswerDTO的DataLombok 注解是否生效IDEA 需安装 Lombok 插件并启用 annotation processing在 Controller 方法参数加Valid和RequestBody让 Spring Boot 自动校验必填字段。6. 进阶技巧如何快速定制题库、更换主题色、添加新题型6.1 题库批量导入Excel 解析 事务回滚保障数据一致性管理员后台/pages/admin/import/import页面提供 Excel 上传功能。后端AdminQuestionController.importExcel()使用 Apache POIPostMapping(/import) public ResultString importExcel(RequestParam(file) MultipartFile file) { try (Workbook workbook WorkbookFactory.create(file.getInputStream())) { Sheet sheet workbook.getSheetAt(0); ListQuestion questions new ArrayList(); for (int i 1; i sheet.getLastRowNum(); i) { // 跳过标题行 Row row sheet.getRow(i); if (row null) continue; Question q new Question(); q.setSubjectId(getIntCellValue(row.getCell(0))); // 科目ID q.setTitle(getStringCellValue(row.getCell(1))); // 题干 q.setOptions(JSON.toJSONString(Arrays.asList( getStringCellValue(row.getCell(2)), getStringCellValue(row.getCell(3)), getStringCellValue(row.getCell(4)), getStringCellValue(row.getCell(5)) ))); // 选项转 JSON q.setCorrectAnswer(getStringCellValue(row.getCell(6))); // 正确答案 q.setLevel(QuestionLevel.valueOf(getStringCellValue(row.getCell(7)).toUpperCase())); // 难度 questions.add(q); } // 事务内批量插入任一失败则全部回滚 questionService.saveBatch(questions); return Result.success(导入成功共 questions.size() 题); } catch (Exception e) { log.error(Excel 导入失败, e); return Result.fail(导入失败 e.getMessage()); } }提示Excel 模板由后台/admin/download-template接口生成返回预设格式的.xlsx文件避免学生自己瞎填列。模板包含数据验证下拉选科目、难度降低出错率。6.2 小程序主题色一键切换CSS 变量 全局样式覆盖所有颜色定义在app.wxss根样式中/* app.wxss */ page { --primary-color: #07c160; /* 主题绿可全局替换 */ --bg-color: #f8f8f8; --border-color: #e0e0e0; --text-color: #333; } .question-item { border-left: 4px solid var(--primary-color); } .submit-btn { background-color: var(--primary-color); }如需换为蓝色主题只需改一行--primary-color: #1890ff;注意微信小程序不支持 CSS Custom Properties 在style属性中动态绑定如stylecolor: {{themeColor}}所以主题色必须编译时确定。若需运行时切换需用wx.setStorageSync(theme, blue) 多套 wxss 文件theme-blue.wxss/theme-green.wxss在app.js中动态require。6.3 添加新题型填空题三步扩展法填空题与选择题核心差异① 题干含{{blank}}占位符② 用户输入文本而非选择③ 判分需模糊匹配如忽略空格、大小写。扩展步骤Step 1数据库加字段在question表新增blank_answerTEXT 字段存标准答案如Javacorrect_answer字段保留给选择题。Step 2后端加策略Component(FILL_IN) public class FillInBlankStrategy implements AnswerStrategy { Override public boolean judge(String userAnswer, String correctAnswer) { // 忽略首尾空格、转换为小写、去除所有空格后比较 String cleanUser userAnswer.trim().toLowerCase().replaceAll(\\s, ); String cleanCorrect correctAnswer.trim().toLowerCase().replaceAll(\\s, ); return cleanUser.equals(cleanCorrect); } }Step 3小程序端适配在exam.wxml中根据question.type FILL_IN渲染input组件而非radio-group并调整提交逻辑view wx:elif{{question.type FILL_IN}} input bindinputonFillInput placeholder请输入答案 value{{fillAnswer}} / /view// exam.js onFillInput(e) { this.setData({ fillAnswer: e.detail.value }); }, formSubmit(e) { const answer this.data.question.type FILL_IN ? this.data.fillAnswer : e.detail.value.answer; // 选择题走原有逻辑 // ...提交 }从那以后我每次接手毕设源码第一件事就是打开pom.xml看 SpringBoot 版本和application.yml里的数据库配置第二件事是找sql/目录下的初始化脚本第三件事是运行mvn clean compile确认依赖无误——这三步走完90% 的“跑不起来”问题就消失了。剩下 10%基本都在小程序project.config.json的urlCheck和后端application.yml的wechat.miniapp.appid里。希望帮到你。本文还有配套的精品资源点击获取