☰
Spring Boot + 微信小程序考研复习平台:毕业设计选题与完整实现指南
2026/10/16 3:31:28 网站建设 项目流程

每年三四月,总有人来找我问毕业设计该做什么题目。聊了一圈下来,我发现今年最值得推荐的方向,就是基于 Spring Boot + 微信小程序的考研复习辅助平台。这个题目好在哪里?它不是一个空壳管理系统,而是真正围绕"考研人怎么规划每天的学习"去设计功能:复习计划、每日任务、打卡统计、题库刷题、错题整理、学习数据报表,全都对应真实使用场景。对做毕设的学生来说,它的技术栈足够完整——后端用 Spring Boot 接 MySQL,前端用微信小程序,至少覆盖了 Java Web、数据库设计、移动端开发、接口联调这些环节,论文能写的东西也多;对答辩老师来说,题目天然带"实际需求",比千篇一律的"XX管理系统"有说服力得多。这篇文章就按我实际做这一类项目的路子,把题目拆解、数据库设计、后端接口、小程序端实现、文档交付和踩坑经验完整地走一遍,写给正在准备毕设或者想快速搭一套同类系统的同学参考。

1. 题目先想清楚:这类"辅助平台"比"管理系统"好在哪

1.1 别再做"图书管理""会议室预约"了

很多同学挑毕设题目的时候有个误区:哪个框架熟就套哪个场景。于是每年能看到大量"基于 XX 的图书管理系统""基于 XX 的会议室预约系统",功能翻来覆去就是增删改查,答辩时自己都讲不出"为什么需要这个系统"。考研复习辅助平台不一样,它的用户是考研学生,需求非常明确:距离考试还有多少天、每周各科怎么安排、今天该完成哪些任务、刷过的题错了哪些、连续打卡了多少天。这些功能每一个都能说清楚"解决什么问题",也就让毕设从"技术演示"变成了"有实际价值的应用"。

我习惯把这个题目的功能拆成几个层次。底层是用户体系和基础数据:微信登录、个人信息、科目管理。中间层是复习核心流程:制定总体计划(比如距离考试 N 天)、按周/日拆分任务、完成任务打卡、记录学习时长。上层是提升体验的功能:题库刷题与错题自动收录、手动添加错题、每日学习统计、连续打卡日历、订阅消息提醒。实际做的时候,底层和中层是必须完成的,上层按时间取舍。这样一个题目可深可浅:基础版就是一个"计划+打卡"工具,进阶版可以做成"带刷题和错题分析的学习闭环",完全能撑起一篇合格的毕设论文。

1.2 技术选型为什么是 Spring Boot + 微信小程序

先说后端。Spring Boot 几乎是当前 Java 毕业设计的事实标准,理由很实际:内嵌 Tomcat 让部署变得极其简单,写好一个mvn package就能跑;Spring MVC 的注解式路由清晰,接口写起来顺手;配合 MyBatis-Plus 做数据访问,连单表 CRUD 的 SQL 都不用手写;再加上社区资料多,遇到问题搜索一下基本都能解决。同类的 SSM 框架不是不行,但配置繁琐,对毕设来说没必要。

前端选微信小程序而不是纯网页后台,关键原因是"使用场景"。考研学生用手机是常态,小程序扫码即用、不用装 App,而且微信生态自带的订阅消息正好用来发"今日任务提醒"。对毕设来说,小程序还多了一层能展示的技术含量:原生页面生命周期、组件通信、本地缓存、真机调试、域名配置,这些都是论文里可以详细展开的素材。如果你之前只做过 Web 前端,也不用慌,小程序的知识体系接近 Vue 的语法风格(数据绑定、列表渲染、事件绑定),上手成本不算高。

我用下面的表格给一个快速的技术栈清单,后面所有章节都按这套来:

模块技术选型说明
后端框架Spring Boot 2.7稳定版,资料多
数据访问MyBatis-Plus + MySQL 5.7/8.0单表 CRUD 少写 SQL
权限认证JWT + 拦截器小程序端 header 传 token
前端框架微信小程序原生避免额外编译链
UI 组件Vant Weapp表单、日历、按钮直接用
部署环境云服务器 + Nginx后端接口走 HTTPS 域名

这套组合还有一个隐形的优势:每一层都能单独写论文章节。比如数据库设计可以写 E-R 图和表结构优化;后端可以写 RESTful 接口设计和 JWT 拦截器实现;小程序端可以写页面交互与缓存策略;联调阶段可以写接口测试和部署环境搭建。论文根本不愁没内容。

2. 数据库设计:先想清楚"用户每天打开小程序会做什么"

2.1 核心实体拆分与关系

做数据库设计的时候,我不建议直接打开 Navicat 建表,而是先写用户故事。就拿这个平台来说,用户的故事线是:打开小程序登录 -> 查看距离考研还有多少天 -> 查看今天的复习任务 -> 完成英语阅读任务并打卡 -> 记一笔学习时长 -> 刷了几道数学题,错的进错题本 -> 睡前看一眼今天的统计数据和连续打卡天数。跟着这条线走,核心实体自然就出来了:

  • 用户表(user):openid、昵称、头像、目标院校/专业(选填)、考试日期
  • 科目表(subject):数学、英语、政治、专业课,可配置
  • 计划表(plan):用户创建的复习计划,时间段、总天数、每日学习时长目标
  • 任务表(task):从计划拆分出的每日任务,关联科目、计划日期、完成状态
  • 学习记录表(study_record):每次"开始学习/结束学习"或手动打卡的时长记录
  • 题库表(question_bank):题目内容、选项、答案、解析、科目、难度
  • 错题本表(wrong_question):用户、题目、错误答案、记录时间

这些表之间的关系不算复杂:一个用户有多条计划和多个错题记录,一个计划下面有多个任务,一个任务归属于某个科目。用 MySQL 外键也可以,但我通常只在逻辑上保留关联字段(比如 task 表里存 subject_id、plan_id),物理上不建外键约束,这样删除和改数据的时候灵活,接口性能也更好。答辩如果被问"为什么不用外键",可以回答:外键约束在高并发插入和批量删除时会带来额外开销,业务层面用代码保证数据一致性,这是互联网应用常见的做法。

2.2 几张关键表的字段设计与取舍

用户表除了基本字段,重点要考虑登录方式的兼容。小程序端用微信登录,首次登录时拿 openid 建用户;但答辩现场演示很可能没有小程序 appid 或者网络受限,所以我通常再加一套"手机号+密码"的账号登录,后端用 JWT 签发 token。这样即使微信登录走不通,演示也能正常继续。这个细节在答辩时是加分项。

CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `openid` varchar(64) DEFAULT NULL COMMENT '微信openid', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `password` varchar(128) DEFAULT NULL COMMENT 'BCrypt加密后的密码', `nickname` varchar(64) DEFAULT NULL, `avatar` varchar(255) DEFAULT NULL, `exam_date` date DEFAULT NULL COMMENT '考研日期', `target_school` varchar(128) DEFAULT NULL, `daily_target_minutes` int DEFAULT 180 COMMENT '每日学习目标(分钟)', `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

任务表是核心中的核心。我给它加的字段包括:计划 ID、科目 ID、任务日期、任务内容、预计时长、完成状态(0 未完成 1 已完成)、完成时间。这里有个容易踩的坑:不要把"任务内容"做成外键关联到另一个任务模板表,那样查询要多一次 JOIN,而且考研科目的每日任务本来就灵活——今天做英语阅读,明天可能是背作文模板,直接存文本字段最省事,也方便前端直接渲染展示。

CREATE TABLE `task` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) NOT NULL, `plan_id` bigint(20) DEFAULT NULL, `subject_id` bigint(20) NOT NULL, `task_date` date NOT NULL COMMENT '任务日期', `content` varchar(255) NOT NULL COMMENT '任务描述', `expect_minutes` int DEFAULT 0, `actual_minutes` int DEFAULT 0 COMMENT '实际花费分钟', `status` tinyint DEFAULT 0 COMMENT '0未完成 1已完成', `finish_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_user_date` (`user_id`, `task_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

错题本表采用的是"冗余题目快照"方案:除了 question_id,我还把题目的题干、选项、正确答案、解析都冗余了一份。原因是错题本应该是一个独立、稳定的记录,如果用户把某道题从题库删了(或者题库表被重置),错题本仍然要原样展示;另外错题列表页经常要做分页和条件过滤,冗余字段可以少 JOIN 一张大表,查询快得多。

3. Spring Boot 后端:把"复习逻辑"做成清晰可用的接口

3.1 接口风格与鉴权设计

前后端分离之后,后端最重要的是接口边界清晰。我习惯按资源来划分子模块:auth负责登录和 token,plan负责计划管理,task负责每日任务,record负责学习记录和统计,question负责题库与错题。每个模块都遵循标准的 Controller-Service-Mapper 三层结构,Controller 只做参数接收和简易校验,业务逻辑全在 Service 层。这样答辩被问到"某一处逻辑怎么实现的",你直接说"在 Service 层里,逻辑是……",就能展开讲清楚。

鉴权这块,我用的是最经典的 JWT 方案:登录成功后后端生成一个 token,里面带上 userId 和过期时间,小程序端每次请求在 header 里带Authorization: Bearer xxx。后端用一个拦截器统一校验:从请求头解析 token,解析成功就把 userId 放入 ThreadLocal(或者 request attribute),Controller 层直接从上下文拿到当前用户。这样业务代码里不需要每个接口都重复一遍"取 userId"的逻辑,也避免前端伪造身份。

public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String auth = request.getHeader("Authorization"); if (auth == null || !auth.startsWith("Bearer ")) { throw new BusinessException(401, "未登录"); } String token = auth.substring(7); Long userId = JwtUtil.parseToken(token); if (userId == null) { throw new BusinessException(401, "登录已过期"); } request.setAttribute("currentUserId", userId); return true; } }

一个小细节:拦截器注册时要注意排除路径,比如/api/auth/login、/api/auth/wx-login不能拦截,否则用户根本没法登录。我经常看到有人把这个顺序写反,导致登录接口 401,排查半天才发现是拦截器配置问题。

3.2 复习计划生成:最简单的算法也能讲清楚

"根据考研倒计时自动生成每日任务"是这个项目最值得讲的功能。很多同学一听到"算法"两个字就紧张,其实这里的核心逻辑非常简单,但答辩时非常出彩。我的实现思路是:

  1. 用户设置考试日期和每天目标学习时长(比如 3 小时)。
  2. 后端计算从今天到考试前一天的总天数 N。
  3. 按科目权重分配各科的每天时长。比如数学 40%、英语 30%、政治 20%、专业课 10%,权重可以在计划设置里调整。
  4. 每天生成若干条任务,任务内容是科目+默认描述(如"数学:完成高数第三章习题"),也可以允许用户每天自己编辑。
  5. 批量写入 task 表,按 user_id + task_date 建索引,前端按日期查询。

生成计划的核心方法大致是这样:

public Long createPlan(PlanCreateDTO dto, Long userId) { LocalDate start = LocalDate.now(); LocalDate end = dto.getExamDate(); long totalDays = ChronoUnit.DAYS.between(start, end); if (totalDays <= 0) { throw new BusinessException("考试日期必须晚于今天"); } // 保存计划主记录 Plan plan = new Plan(); plan.setUserId(userId); plan.setStartDate(start); plan.setEndDate(end); plan.setDailyMinutes(dto.getDailyMinutes()); planService.save(plan); // 按天生成任务 for (int i = 0; i < totalDays; i++) { LocalDate taskDate = start.plusDays(i); for (SubjectWeight sw : dto.getWeights()) { Task task = new Task(); task.setUserId(userId); task.setPlanId(plan.getId()); task.setSubjectId(sw.getSubjectId()); task.setTaskDate(taskDate); task.setContent(sw.getSubjectName() + ":今日复习任务"); task.setExpectMinutes(sw.getWeight() * dto.getDailyMinutes() / 100); task.setStatus(0); taskService.save(task); } } return plan.getId(); }

如果总天数太长,产生几千条任务,批量插入要改成saveBatch(MyBatis-Plus 自带),别一条条循环插入,否则接口响应会肉眼可见地变慢。这个优化细节论文里也可以写,属于"数据量大时的性能优化"。

3.3 学习统计接口:连续打卡与学习时长的计算

统计模块是这类平台最容易出效果的地方。我做了两个接口:一个返回用户某天/某段时间的总学习时长和任务完成数;另一个返回连续打卡天数。连续打卡天数的计算有一个常见的坑:不能只数 task 表里今天完成的任务数,还要判断"昨天是否打卡",否则周日断了、周一又打卡,连续天数会被错误延续。我的做法是先查出最近 30 天每天的打卡日期列表,然后从今天往前推,遇到断档就停止:

public int calcContinuousDays(Long userId, LocalDate today) { List<LocalDate> activeDates = studyRecordMapper.findDistinctDatesAfter(userId, today.minusDays(30)); Set<LocalDate> dateSet = new HashSet<>(activeDates); int count = 0; LocalDate cursor = today; // 今天没打卡,从昨天开始算 if (!dateSet.contains(cursor)) { cursor = cursor.minusDays(1); } while (dateSet.contains(cursor)) { count++; cursor = cursor.minusDays(1); } return count; }

这个算法不复杂,但很能体现"考虑边界情况"的工程思维,答辩时值得拿出来讲。

4. 微信小程序端:页面不多,但这几个交互细节必须做好

4.1 项目结构与请求封装

小程序部分我建议直接用原生框架,不要在这个阶段引入 uni-app 之类的高度封装方案,原因很简单:毕设更重要的是把逻辑讲清楚,原生框架的页面结构、生命周期、wx.request一目了然,遇到问题也好定位。如果对样式要求高,组件库接一个 Vant Weapp 就够了。

全局结构我通常这样组织:

miniprogram/ pages/ index/ # 首页:倒计时+今日任务 plan/ # 复习计划设置 task/ # 任务打卡 question/ # 题库/刷题 wrong/ # 错题本 stats/ # 统计图表 mine/ # 个人中心 utils/ request.js # 封装 wx.request,自动带 token auth.js # 登录态管理 app.js app.json

request.js是重中之重。它要做三件事:自动在 header 里塞 token;遇到 401 时清理本地登录态并跳转到登录页;后端返回的统一结构{ code, message, data }在这里解包,页面只管取data,不用每个页面都重复判断 code。

const request = (url, method, data) => { return new Promise((resolve, reject) => { wx.request({ url: getApp().globalData.baseUrl + url, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + wx.getStorageSync('token') }, success: (res) => { if (res.data.code === 0) { resolve(res.data.data); } else if (res.data.code === 401) { wx.removeStorageSync('token'); wx.reLaunch({ url: '/pages/login/login' }); reject(res.data); } else { wx.showToast({ title: res.data.message, icon: 'none' }); reject(res.data); } }, fail: (err) => reject(err) }); }); };

4.2 首页倒计时与今日任务列表

首页是用户打开小程序第一眼看到的东西,我设计的信息优先级是:考研倒计时(最大字号)-> 今日完成 X/Y 个任务 -> 连续打卡天数 -> 今天的任务清单。倒计时直接在后端计算,返回days字段,前端拿到后渲染即可;不要在端上自己算,因为客户端时间可能不准。

任务列表项上有一个"完成打卡"按钮,点击后调用后端接口更新任务状态,同时把实际学习分钟数一并提交。这里我建议加一个二次确认弹窗(Vant 的 Dialog),选"本次学习多少分钟",这样学习时长数据就不会靠用户脑补,统计报表因此更真实。

4.3 刷题与错题本:页面再简单也要注意状态同步

题库刷题页面本质上是一个"按科目加载题目 -> 单选/多选 -> 提交答案 -> 判对错 -> 错题自动进错题本"的流程。有两个容易忽略的同步问题。第一个是用户在刷题页面删除了一道错题,返回错题本列表时要刷新,否则列表里还残留;我的做法是错题本页面在onShow里重新拉数据,而不是只在onLoad里加载。第二个是"已做过的题"要有一个本地标记,否则用户反复刷同一套题,每次都像新题一样。用wx.setStorageSync('done_' + questionId, true)就能解决,简单且高效。

5. 容易被忽略的交付环节:文档、演示与远程调试

5.1 论文文档怎么才能写满,又写得真

很多学生代码写完了,发现论文憋不出来。这通常是因为开发的时候没有同步留素材。我在开发阶段就会随手做三件事:一是把每个接口的请求/响应示例存到一个 Markdown 文件里,后面写接口设计章节直接复制;二是把每次遇到 Bug 的现象和解决办法记在 Git 提交说明里,后面变成"系统测试与问题分析"章节的第一手资料;三是数据库设计阶段画 E-R 图,哪怕手画的都行,后面整理成论文配图。

一份合格的毕设文档至少要包含:需求分析(用例图、功能列表、非功能需求)、系统设计(总体架构图、功能模块图、数据库 E-R 图与表结构说明、接口设计)、系统实现(核心功能页面截图和关键代码说明)、系统测试(测试用例表、测试结果、问题修复记录)。这些内容只要开发过程真实做过,整理出来并不难。怕的是框架搭好了但没做细节,论文自然空洞。

5.2 远程调试环境:从 localhost 到手机上看效果

毕设验收和答辩最常见的要求是"能在手机/真机上运行给我看看"。这里必须提前解决两个问题:后端接口部署在哪里,小程序怎么访问到它。如果后端还在 localhost,小程序真机调试完全连不上(真机上没有 localhost 这个概念);如果用云服务器,就必须先把后端打成 jar 包部署到服务器上,再用 Nginx 反代一下,同时配上 HTTPS 证书。小程序 request 的合法域名要求 HTTPS,证书用免费的就行,没必要花钱。我习惯的部署路径是:

  1. 服务器安装 JDK 和 MySQL,导入 SQL 脚本。
  2. 后端配置application-prod.yml,数据库连接、日志级别改成生产环境。
  3. mvn clean package打出 jar,用nohup java -jar xxx.jar &启动,或者用 systemd 管理。
  4. Nginx 配置 HTTPS 证书,把/api/转发到本地的8080端口。
  5. 小程序开发者工具里把baseUrl改成自己的域名,打开"不校验合法域名"方便调试;真机预览时正式校验域名。

这个流程看起来长,但做一次之后就是机械操作。如果实在没有服务器,也可以用内网穿透工具把本地端口暴露成公网地址临时演示,但我还是建议至少把后端部署到服务器上,稳定性完全不同,而且"我部署到了云服务器"本身也是一条答辩加分项。

5.3 演示翻车点:提前排练,比开发更花时间

远程调试环境搭好以后,至少自己完整走三遍演示流程。我遇到的翻车情况大多是这些:演示时微信扫码登录,结果小程序是"测试版",后端返回的 openid 和上次缓存的用户对不上,导致数据错乱;演示现场网络慢,题库分页接口首屏加载转圈超过 3 秒,老师以为卡死了;演示中途 token 过期,接口 401 弹窗,然后跳登录页,观感非常差。这三个问题都可以提前规避:测试版/体验版的用户数据单独建一套演示数据;题库接口默认加载 10 条加缓存;token 有效期设成 7 天,答辩当天重新登录一次就行。

6. 开发调试记录:这几个坑我几乎每次都踩

6.1 小程序合法域名与本地联调的冲突

第一个坑发生在最开始的联调阶段。小程序开发者工具默认会校验 request 的域名必须是 HTTPS 且在小程序后台配过,如果你后端跑在本地http://localhost:8080,直接请求会报https://...不在合法域名列表。新手往往在这里卡很久。解决办法有两个:本地调试时在开发者工具右上角把"不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书"勾上;真机预览时再用正式域名。注意这只是调试设置,真机正式版一定要有备案域名和证书。

6.2 JWT 拦截器与跨域的叠加问题

第二个坑是跨域。小程序端发请求不算严格意义上的浏览器跨域,但如果你在答辩前临时加了一个 Web 管理后台(比如用 Vue 写的),同一个后端接口就得处理 CORS。我的建议是在后端写一个全局 CORS 过滤器,放行所有来源和方法,开发阶段省心;上线前再收窄到自己的域名。

还有一个更隐蔽的问题:拦截器里抛出业务异常时,如果异常没有被全局@RestControllerAdvice捕获,前端拿到的会是 Spring Boot 默认错误页,而不是统一的 JSON 结构。所以记得配一个全局异常处理器,把BusinessException转成{ code, message, data },前端request.js才能统一处理。

6.3 跨午夜打卡与"同日多段学习"的数据边界

第三个坑在学习时长统计。用户可能晚上 23:50 开始学习,学到第二天 00:20,这在业务上算"昨天的学习"还是"今天的"?我的处理方式是让前端在打卡时把开始时间和结束时间一起传上来,后端用开始时间归属日期,结束时间只用来算时长。如果同一天有多条学习记录,统计接口里按日期分组求和,不要简单 count 记录条数。另外有一类边界情况是用户手动补卡,我会在 study_record 表里加一个source字段(1 自动计时 2 手动补卡),统计连续打卡时允许手动补卡记录参与计算,但统计"专注时长"时手动补卡不计入,避免数据虚高。这类细节当时排查了很久,但理顺之后,"统计精确性"反而变成答辩中能讲的亮点。

最后一个想说的小技巧是:开发这类项目时,一定会出现"功能简单但细节繁杂"的阶段——比如日历翻页、打卡状态回显、错题删除后的列表刷新,它们都不难,但每一个都考验你有没有耐心把状态管理想清楚。我自己的习惯是每完成一个页面,就立刻在真机上把完整流程走一遍,不要攒到最后一起测试。一个页面十分钟,攒到最后可能变成三个小时。这个习惯比任何技术方案都更能保证毕设按时交付。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询