1. 这个项目到底在做什么以及为什么值得参考1.1 先拆一下这个标题不要看到一堆字母就慌springboot基于微信小程序的四六级小助手系统x82rj4l9(源码LW部署讲解)——这个标题其实已经把项目最核心的三件事说清楚了后端是Spring Boot前端是微信小程序业务是四六级小助手。后面的“x82rj4l9”是项目唯一标识跟功能本身没关系很多同学第一次看到这种随机串会懵担心是不是什么特殊版本其实不用管它只是生成项目时用来区分文件的编号。“源码”很好理解就是完整的工程代码分后端和小程序两个目录。“LW”在毕设场景里指的是配套文档也就是项目说明书、需求分析、数据库设计、接口说明、答辩稿这一整套东西。对要拿这个项目去做毕设或者课设的人来说LW和源码同样重要因为答辩的时候老师先看文档再问代码文档写得不清楚代码再完整也很难拿高分。“部署讲解”也很关键光能跑起来不算完你还得能说清楚项目怎么从本地环境搬到云服务器上、小程序端怎么配置合法域名、后端怎么打包启动。这套技术组合在近几年的学生项目里非常常见Spring Boot负责提供接口和业务逻辑微信小程序负责界面展示和用户交互两个端通过HTTP接口通信。小程序天然免安装、扫码即用在校园场景下传播成本极低四六级又正好是大学生群体里的刚需话题所以选题方向本身没有问题。下面我结合自己实际带过的类似项目把这套东西从头到尾拆一遍。1.2 一个标准的四六级小助手核心功能应该有哪些很多人拿到需求后第一反应是“四六级小助手是不是就一个背单词的App”实际上完整的需求拆下来至少包含这几块单词学习、真题练习、错题整理、考试倒计时、个人中心如果还要考虑运营方就再加一个后台管理模块。落实到页面上小程序端大致是五个Tab首页、背单词、练习、错题本、我的。首页展示今日学习进度、考试日期倒计时、每日打卡状态顺便放一些四六级考试相关的小贴士背单词页是核心学习场景一次给一批单词支持卡片式浏览、标记“认识/不认识”、记录熟练度练习页按历年真题或者模拟题分类多半是选择题做完提交之后自动判分、展示答案解析错题本把做错的题按来源和知识点收纳起来方便后面集中复习我的页面展示用户的学习统计、打卡天数、单词总量有管理员角色的还可以从侧边栏进入管理入口维护词库和真题数据。后端这边Spring Boot项目里对应的模块也很清晰用户模块处理微信登录、token签发、个人信息单词模块负责词库的增删改查和按计划抽取单词练习模块管试卷、题目、答题记录和判分错题模块管错题上报和列表查询管理模块管公告发布、数据统计。整体下来就是一个小型前后端分离系统麻雀虽小五脏俱全。1.3 为什么偏偏选Spring Boot加微信小程序这套组合单纯从做毕设的角度讲这个组合胜在三个点。第一微信小程序把前端门槛压得非常低。小程序用的WXML和WXSS语法跟HTML和CSS很接近JavaScript部分也是标准的ES语法你哪怕之前只写过一点前端花一周时间就能把页面框架跑起来。不需要自己买域名搭H5不需要折腾跨浏览器兼容微信开发者工具里直接预览效果真机和模拟器同步调试比传统Web开发省心太多。第二Spring Boot把后端门槛也压下来了。内嵌Tomcat意味着你不需要单独装Web容器打成jar包直接java -jar就能跑自动装配省掉了SSM时代那一堆XML配置Spring Data JPA或者MyBatis的集成都有现成的starter写CRUD基本是模板化操作。对一个没有太多企业级开发经验的学生来说学习曲线比SSM平滑很多而且后期部署也不需要面对复杂的容器环境。第三前后端分离的结构天然适合毕设答辩。你可以把一次完整的用户请求讲清楚小程序发起wx.request后端Controller接收Service处理业务Mapper操作数据库数据再一层层返回去。整个调用链路清晰老师问问题也有抓手不至于你讲完PPT之后无话可说。顺带提一个容易犯的错有人觉得用小程序做前端就必须配一个“小程序后端”其实微信小程序没有自己的服务端所有的数据交互都是走你自建的HTTP接口微信官方只提供登录态校验code2session和文件存储之类的辅助能力。换句话说小程序和后端之间就是普通的前后端分离关系这一点先搞清楚后面设计接口的时候才不会走偏。2. 后端设计与核心实现先搞定数据库再谈业务逻辑2.1 数据库到底要建几张表字段怎么设计四六级小助手这种项目数据库不需要搞得很花哨核心表控制在七到八张就够了太多了自己维护累太少了功能撑不起来。我按实际项目整理了一张表清单表名职责关键字段关联关系user学生用户openid、nickname、avatar、study_days全局主表word单词库word、phonetic、translation、levellevel区分四级/六级user_word用户单词学习记录user_id、word_id、proficiency、review_count多对多中间表paper真题/模拟卷title、level、exam_date、duration练习模块主表question题目paper_id、content、option_a/b/c/d、answer、analysis关联试卷answer_record答题记录user_id、question_id、user_answer、is_correct判分和统计wrong_book错题本user_id、question_id、wrong_count去重记录notice公告title、content、create_time首页信息流用户表和单词表之间用user_word中间表连接这样每个学生看到的“待学单词”“已掌握单词”“复习单词”都是基于自己独立的学习记录而不是所有人共享一份进度。这里有两个设计细节值得注意第一个是单词表里加level字段比较好四级词库和六级词库不要分成两张表。分开建表看起来直观但代码里所有涉及单词的查询都要写两套逻辑后续如果加一个“高频核心词”分类又得再建表非常痛苦。用level字段区分一行SQL就能按难度过滤扩展性也更好。第二个是proficiency熟练度字段不要设计得太复杂。网上有些文章上来就推荐艾宾浩斯记忆曲线、间隔重复算法听起来高大上但学生项目的周期根本撑不起那么复杂的设计。我实际用的是一个简化模型0代表未学1代表学习中2代表已掌握3代表忘记。用户标记“认识”就把值往上涨一级标记“不认识”就降到3系统定时把熟练度为3的单词重新加入今日计划。简单粗暴但完全够用而且答辩时能讲清楚。DDL里有一个容易被坑的点MySQL的版本不同默认字符集可能不是utf8mb4。小程序端用户昵称很可能带emoji比如用这种表情做昵称头像如果表是utf8字符集insert的时候直接报错。建库的时候务必写成CREATE DATABASE cet_assistant DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;创建单词表的核心字段大致是这样CREATE TABLE word ( id BIGINT PRIMARY KEY AUTO_INCREMENT, word VARCHAR(64) NOT NULL COMMENT 单词本身, phonetic VARCHAR(128) COMMENT 音标, translation VARCHAR(512) NOT NULL COMMENT 中文释义, example_sentence TEXT COMMENT 例句, level TINYINT NOT NULL DEFAULT 1 COMMENT 1-四级 2-六级, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_word_level (word, level) ) ENGINEInnoDB COMMENT四六级词库;这里的唯一索引uk_word_level建议加上不然词库导入的时候重复执行脚本会插入一堆重复单词。后面你在管理后台做词库导入直接INSERT IGNORE就能跳过已有的记录省去手动去重。2.2 后端接口怎么规划统一返回体是必须的前后端分离的项目接口设计直接决定联调效率。我习惯把所有接口按业务模块拆分统一以/api开头大致这样分/api/auth/login微信登录/api/user/info获取个人信息/api/word/today获取今日学习计划/api/word/submit提交单词学习结果/api/paper/list获取试卷列表/api/paper/detail获取试卷详情/api/paper/submit提交试卷答案/api/wrong/list获取错题本/api/wrong/remove移除错题所有接口的返回值必须统一格式这一点看着简单但很多人一开始图方便某些接口返回成功对象某些接口只返回布尔值到小程序端写res.data判断逻辑的时候就会非常混乱。我用的统一返回体是这样public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMsg(ok); r.setData(data); return r; } public static T ResultT error(Integer code, String msg) { ResultT r new Result(); r.setCode(code); r.setMsg(msg); return r; } }小程序端拿到res.data之后只要判断code 200就进成功分支否则直接弹出msg提示。后面无论是接微信登录还是接单词接口这套模式一套到底前端代码的冗余量会小很多。业务模块之间要注意一个问题单词、试卷、错题这些接口都依赖登录态不可能裸奔。所以Controller层不要每个方法都手动写“从token里取userId”这种重复代码写多了既啰嗦又容易漏。正确做法是自定义一个拦截器在请求进入Controller之前统一完成鉴权把userId塞到request.getAttribute()里Controller直接取。2.3 微信登录的完整链路核心是openid不是用户密码很多刚接触小程序开发的同学被登录卡住因为小程序不像传统网站有账号密码体系它的登录建立在微信的code换openid机制上。完整的链路分成四步第一步小程序端调用wx.login()拿到一个一次性凭证code这个code有效期只有五分钟用完即废。第二步小程序把code通过POST /api/auth/login发送给后端。第三步后端拿这个code加上小程序的appid和appSecret向后端接口jscode2session发起请求换来openid和session_key。第四步后端用openid去用户表查记录查到就直接登录查不到就自动注册一个新用户然后用JWT给前端签发一个token。这段逻辑里有一个很多人会踩的坑jscode2session这个请求必须由后端发起不能在小程序端直接调。因为调这个接口需要appSecret它一旦暴露在小程序代码里任何人都能拿到你的密钥去伪造用户身份。小程序代码本质上是下发到用户手机里的没有任何保密性可言所以appSecret只能存在自己的服务器上。后端拿到openid后不要直接把openid当token返回给前端。openid相当于你在这个小程序里的身份证号它不应该暴露给客户端所有涉及身份的地方都应该用JWT。JWT的生成用现成的库比如jjwt核心代码很短String token Jwts.builder() .setSubject(userId.toString()) .setExpiration(new Date(System.currentTimeMillis() 7 * 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact();有效期我一般设七天小程序端超过七天token失效就重新走一次wx.login流程对用户来说无感刷新体验不会受到影响。这里再补充一个细节微信登录和传统账号体系并不是互斥的。你可以在用户表里既保留openid又允许管理员手动维护一个账号密码用于模拟登录。这样你在本地调试、或者在答辩现场演示的时候不需要依赖真机微信环境直接用测试账号就能跑通全流程。我当时的做法是在登录接口加一个loginType参数wx走微信登录admin走密码登录两个入口到最后都落到同一个用户体系上演示起来非常方便。2.4 今日学习计划和单词抽取逻辑怎么实现单词学习模块是整个系统的灵魂但实现逻辑其实不复杂。所谓“今日计划”核心是从单词库里挑出一批用户没学过的单词再加上一批需要复习的单词合在一起形成当天的学习队列。我用的是先查复习再补新词的策略先从user_word表里找proficiency 3忘记状态的记录同时满足next_review_date 今天把这一批单词拉出来数量不足目标数量时再从未学的单词里随机抽。每批目标单词数量我设为20个这个数字参考了市面主流背单词软件的新词档位再少没效果再多背不完。随机抽取用MySQL的ORDER BY RAND()就能实现数据量小的时候性能完全没问题SELECT w.* FROM word w WHERE w.level #{level} AND w.id NOT IN (SELECT word_id FROM user_word WHERE user_id #{userId}) ORDER BY RAND() LIMIT #{count}有同学担心ORDER BY RAND()在数据量大的时候会慢实际是四六级词库加起来也就一万多词条单用户查询带上user_word过滤之后扫描量根本不大。没必要为了这个上复杂的算法毕设项目讲究的是“能跑、能讲、能扩展”不是说一定要用多高深的技术。当用户点击“认识”或“不认识”之后后端更新user_word表认识就把熟练度加一不认识就把熟练度置为3。顺手再把last_review_date更新成今天这个字段既能用来画学习热力图也能在后期做“连续打卡”统计数据价值很高。3. 小程序端页面与交互不要只顾着写页面请求层先搞好3.1 小程序整体页面结构和TabBar配置小程序的页面结构其实很像一个简化版的前端路由系统每个页面由.wxml、.wxss、.js、.json四个文件组成在pages目录下注册。我当时的页面清单是下面这些页面路径对应功能说明pages/home/index首页学习概览、倒计时、公告pages/word/index背单词卡片翻页交互pages/exam/index真题练习试卷列表pages/exam/detail做题页答题交互pages/wrong/index错题本错题列表与移除pages/mine/index个人中心用户信息与统计pages/login/index登录页微信一键登录TabBar在app.json里配置我选了首页、背单词、练习、错题本、我的五个页面。这里有一个设计取舍问题做题页exam/detail不要在TabBar里因为它是二级页面用户点进试卷后应该进入一个专注的答题界面再挂TabBar会很干扰。非TabBar页面通过wx.navigateTo跳转返回的时候也能保留上一页的状态。页面的视觉风格我建议整体走简洁路线。四六级小助手的目标用户是在校大学生他们每天打开的学习类小程序可能不止一个界面太花哨反而显得廉价。主色调我用了藏蓝色加一点橙色点缀藏蓝色跟“学习、可靠”的气质相配橙色用于打卡按钮和“掌握”按键这类需要突出动作的元素整体看起来既有识别度又不会过于活泼。3.2 微信登录和用户信息在小程序端怎么落地小程序端登录的第一步永远是wx.login获取code然后调用后端接口换token。我把它封装在utils/auth.js里function wxLogin() { return new Promise((resolve, reject) { wx.login({ success: (res) { if (res.code) { request.post(/auth/login, { code: res.code }) .then((data) { wx.setStorageSync(token, data.token) wx.setStorageSync(userInfo, data.userInfo) resolve(data) }) .catch(reject) } else { reject(new Error(登录失败)) } } }) }) }登录成功之后token必须持久化存储在wx.setStorageSync里而不是只放在内存变量中。小程序随时可能因为切后台被系统回收内存数据丢得一干二净只有Storage里的数据还在。小程序里获取用户昵称和头像有个历史变数微信官方从某段时间开始wx.getUserProfile返回的头像变成灰色默认图昵称变成“微信用户”这是微信隐私政策调整的结果。不要在登录流程里卡这个字段更不要因为拿不到昵称就让用户卡在登录页出不来。正确做法是先进入系统用户可以在“个人中心”手动编辑昵称上传头像数据通过普通接口存到后端不依赖微信的授权开放能力。3.3 请求封装统一搞定Token、错误提示、加载状态小程序端请求封装是决定开发效率的关键一环很多人的代码里wx.request铺天盖地每个请求都重复设置header、处理错误、弹toast一旦后端改了返回结构前端要改的地方多到崩溃。我在项目里用了一个统一的request方法核心逻辑是const request (url, method, data) { const token wx.getStorageSync(token) return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}${url}, method: method || GET, data: data || {}, header: { Content-Type: application/json, Authorization: token ? Bearer ${token} : }, success: (res) { if (res.data.code 200) { resolve(res.data.data) } else if (res.data.code 401) { // token过期重新登录 wxLogin().then(() { wx.showToast({ title: 登录已过期请重试, icon: none }) }) } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }) reject(res.data) } }, fail: (err) { wx.showToast({ title: 网络异常请检查, icon: none }) reject(err) } }) }) }这个封装有几点细节很值得讲。第一是统一处理401token过期不应该让用户手动作任何操作自动重新走登录流程用户体验会自然很多。第二是resolve的时候直接返回data字段页面代码拿到的就是业务数据本身不需要每个页面再res.data.data这样解包代码干净很多。第三是失败情况统一弹toast页面里不需要再各自处理错误提示。还有一个坑必须提醒小程序请求的url必须填完整路径很多人把BASE_URL写成https://xxx.com就完了结果路径拼接出来是错的。建议BASE_URL不带尾部的斜杠接口url统一以斜杠开头例如BASE_URL https://api.example.com请求时拼/auth/login这样最不容易出问题。3.4 背单词页和做题页的核心交互实现背单词页的交互是典型的“卡片式学习”屏幕上一张卡片正面是英文单词和音标点击卡片翻转背面显示中文释义和例句卡片下方两个按钮左边“不认识”右边“认识”。点按钮之后卡片切换下一个同时把学习结果通过接口提交给后端。卡片翻转在原生小程序里用CSS动画实现核心是transform: rotateY(180deg)加transition过渡。这里要说一个实操经验一个小程序的.wxml里控制翻面状态通常是一个isFlipped的布尔变量样式切换用wx:if或者动态class动画写起来并不难难的是翻页时快速连续点击导致的动画错乱解决方案是做一个防抖setTimeout(() { this.setData({ isFlipped: !this.data.isFlipped }) }, 300)把isFlipped的变更包在300毫秒的延时里用户点完“认识”按钮之后再触发翻面切换可以从观感上避免按钮还没动画完、下一张卡已经闪出来的情况。做题页的交互就简单一些多数真题是选择题我的做法是题目区域展示题干和选项选项用列表渲染用户点击某个选项后高亮选中的项底部“上一题”“下一题”按钮切换题目全部做完点击“提交”按钮后端统一判分并返回正确答案和解析。做题页有一个细节要注意不要一次把整张试卷的所有题目一次性渲染出来选择题还好如果未来加阅读理解这种大段文本题目整个页面的渲染性能会明显下降。我的做法是只加载当前题目和下一题的数据切题时再向接口索要新的数据小程序端页面保持轻量。4. 实操中踩过的坑与问题排查实录4.1 真机调试连不上本地后端别把localhost当救命稻草我第一次用真机调试时信心满满地在小程序里填了http://localhost:8080结果手机一打开就报网络异常。原因很简单手机上的localhost指的手机自己不是你的电脑。真机要把后端地址改成电脑在局域网里的IP比如http://192.168.1.100:8080同时确保手机和电脑在同一个WiFi下。但这里又牵出一个更大的坑微信开发者工具默认强制HTTPS非HTTPS域名在真机上根本不发请求。本地调试的时候在开发者工具的“详情 - 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”正式发布是绝对不允许的调试完记得取消。每次项目重开这个选项都会重置属于正常现象不要以为是自己代码写错了。如果你已经部署到云服务器上后端接口必须配HTTPS证书可以用免费的证书服务配置好Nginx反向代理把https://你的域名/api转发到http://localhost:8080/api。小程序后台配置request合法域名的时候域名不能带端口号必须是标准443端口这个限制让很多刚接触的人头疼但这是平台的硬性规则只能去适配。4.2 JSON序列化循环引用导致接口直接报错做了一个“获取试卷详情”的接口本地用Postman测得好好的一到小程序端就报错后来看后端日志才发现是StackOverflowError。原因是实体类的关系设计Paper里面有一个ListQuestion而Question里面又回引了Paper对象Spring Boot默认的JSON序列化工具在序列化时看到这种循环引用直接爆栈。解决方案有两个写法上倾向于在回引字段上加JsonIgnore注解让序列化跳过这一端ManyToOne JoinColumn(name paper_id) JsonIgnore private Paper paper;需要注意加了JsonIgnore之后前端拿到的题目JSON里就不会包含paper字段了如果前端做“从题目反查试卷信息”的场景就需要额外再提供一个接口。另外一个方案是用JsonIdentityInfo处理循环引用让对象以ID代替实例序列化但这个方法对前端解析有一点要求复杂度更高学生项目完全没必要上。4.3 数据库里的时间字段格式化问题小程序端拿到的时间字符串默认是2025-01-01T12:00:00这种带T的格式直接显示在页面上很丑。问题根源是后端默认用的Jackson序列化格式不满足业务需求。解决办法是在application.yml里统一配置全局时间格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8这里time-zone一定要显式设置成GMT8否则服务器部署在别的时区或者MySQL连接参数里时区没配对用户看到的时间就会差八小时。我当时被这个问题坑了整整一个晚上最后发现是MySQL连接串里没加serverTimezoneAsia/Shanghai加上之后才恢复正常。4.4 单词进度统计不准确先确认是代码问题还是数据问题项目做完之后有同学反馈“今日计划里的复习单词跟实际学习记录对不上”排查了好久发现是user_word表里review_count字段更新逻辑有问题。我在提交学习结果时只更新了proficiency和last_review_date忘记了review_count的累加导致统计页面永远显示初始值。这类“统计不对”的问题排查思路有个优先级先看数据库里原始数据对不对再看接口返回的JSON对不对最后看前端渲染对不对。很多人上来就在小程序页面里翻代码翻半天发现是后端逻辑出了错。调试的正确路径应该是先用Postman直接请求接口确认接口返回的数据业务上没问题再去怀疑前端展示层效率会高很多。4.5 小程序端明明请求成功但页面不刷新这个问题非常典型多发于在success回调里修改了数据却忘了setData。小程序和Vue、React不一样它没有响应式绑定机制你在this.data里改了值页面不会自动更新必须调用this.setData({ key: value })才触发视图刷新。还有更隐蔽的一种情况你在回调函数里用了箭头函数this指向是对的然后调用了this.setData但数据是嵌套对象你直接改嵌套属性发现也没生效。比如this.setData({ wordList[0].proficiency: 2 })这种以路径字符串嵌套的写法在小程序里是支持的但很多人不清楚遇到复杂数据结构时直接在this.data里改页面毫无反应。遇到页面不刷新先把这两点自查一遍比重新编译项目十次都管用。5. 部署上线全流程从本地调试到服务器运行5.1 打包后端Maven构建是标准操作后端代码写完之后部署第一步是打包。在项目根目录执行mvn clean package -DskipTests这个命令会跳过单元测试直接打出一个可执行的jar包在target目录下生成类似cet-assistant-0.0.1-SNAPSHOT.jar的文件。-DskipTests这个参数建议本地打包时都加上因为有些项目里配置了测试类测试环境连不上数据库就会导致打包失败跳过测试能避免不必要的麻烦。如果服务器上不方便装Maven也可以在本地打包完把jar包上传到服务器这是大多数学生的操作方式。如果服务器上有Maven环境也可以把源码传上去直接构建但那个流程更长一般没必要。5.2 服务器环境的准备与项目启动我以一台最普通的云服务器为例操作系统是常见的Linux发行版部署需要准备JDK、MySQL和Nginx三个核心组件。JDK版本必须跟本地开发保持一致如果本地用的是JDK8服务器也装JDK8版本不一致会报ClassNotFoundException之类的诡异错误。MySQL的版本也尽量一致防止字符集或者SQL模式的差异导致线上出问题。上传jar包到服务器之后启动命令我用的是nohup java -jar cet-assistant-0.0.1-SNAPSHOT.jar --server.port8080 app.log 21 nohup保证SSH断开后进程继续运行 app.log 21把标准输出和错误日志都写进app.log方便事后排查。第一次启动之后别急着测试接口先看日志里有没有异常tail -f app.log正常启动的标志是日志里出现Started CetAssistantApplication in X seconds这样的内容。如果端口被占用日志里会明确报出Port 8080 was already in use改端口就行。服务器的安全组和防火墙要在控制台里放行8080端口不然本地访问不到接口。这一步很多人会忘接口从服务器本机curl一切正常但小程序端就是连不上十有八九是安全组没配。5.3 Nginx反向代理和HTTPS配置小程序正式环境强制要求域名HTTPS所以服务器上要部署Nginx做反向代理用户访问https://你的域名/api/xxxNginx把请求转给http://localhost:8080/api/xxx。Nginx配置的核心片段server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /etc/nginx/cert/yours.pem; ssl_certificate_key /etc/nginx/cert/yours.key; location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }证书申请好之后把pem和key文件放到服务器上改好Nginx配置执行nginx -t校验配置没有语法错误再nginx -s reload重新加载配置。有一个容易遗漏的细节如果域名备案还没有完成微信小程序后台会拒绝添加request合法域名这是硬性的平台合规要求。所以部署计划一定要把备案耗时算进去备案通常需要一到两周不要等答辩前一周才开始申请。5.4 部署完成后的小程序端配置后端服务跑起来之后小程序端要做两件事。第一件事把utils/config.js里的BASE_URL从开发环境的http://localhost:8080改成https://你的域名。第二件事登录微信公众平台在“开发管理 - 服务器域名”里添加request合法域名。然后重新编译小程序用手机扫码预览完整跑一遍登录、取单词、做题、提交答案的流程。这一步一定要在真机上跑因为开发者工具的模拟器环境和真机有差异真机跑通了才说明部署真正成功。部署和本地调试还有一个细微区别本地调试时后端日志直接打印在控制台出了问题一眼就能看到服务器上日志全在文件的海洋里排查问题的时候建议把日志加一些便于搜索的关键词比如[login] userId123、[submitWord] userId123 wordId456到时候用grep一查一个准比在没头没尾的大段日志里人工翻效率高出几个数量级。6. 想把这个项目做成高分毕设这几点经验值得记住我带过的学生里有些人在同一个项目上费了很大的劲但答辩分数平平有些人按部就班做完却能拿出漂亮的演示效果。差异往往不在代码本身而在于对整个系统的理解深度和演示细节。第一个建议是把用户身份体系讲透。微信登录的code换openid、JWT鉴权流程、session与token的区别这三个问题几乎是这类项目答辩必问的。不要只停留在“调用了接口、返回了token”这种层面要能讲清楚为什么小程序端不能直接调用微信接口为什么openid不能直接返回前端为什么选用JWT而不是传统Session。能把这三个“为什么”答清楚技术层面的分数基本就稳了。第二个建议是准备一套完整的测试数据。项目里默认词库只有几十个单词试卷只有一套演示的时候背单词页面没翻几页就到底了效果非常拉胯。提前导入几千个四六级核心词汇准备两到三套真题试卷演示时可以连续操作很久考官看到的是一个扎实、完整的系统而不是一个空架子。第三个建议是演示前先走一遍全流程。从登录开始到背单词、做练习、看错题再到管理员端发布公告、维护词库整个演示控制在五到八分钟节奏要稳。很多人平时测试都是断断续续点两下真到答辩现场手忙脚乱页面切错、接口报错、网络卡顿本来能拿高分的项目硬生生变成了翻车现场。提前排练的收益在答辩这种场合比再改一个月代码都大。最后再分享一个后续扩展方向做完四六级小助手这套基础功能之后可以顺手把“AI作文批改”或者“听力精听训练”作为一个亮点模块加进去哪怕只做一个简单的实现也能让项目从“普通管理系统”中跳出来。这类学习工具类的毕设一个独特的功能点远比堆十个平庸页面更有说服力。