简介:这是一套面向计算机专业本科生的毕业设计级微信小程序实战项目,聚焦考研知识题库场景,完整覆盖前端小程序与后端Java服务的协同开发流程。资源包含可直接运行的源码、MySQL数据库脚本及详细说明文档,适合课程设计、毕设选题与全栈能力训练。压缩包共1243个文件,主体为192个JS逻辑文件、142个Vue组件、128个Java后端类、92个WXML页面结构及88个WXSS样式文件,辅以SVG图标、PNG素材和JSON配置,整体14.57MB,结构清晰、模块划分明确。已有217人学习下载,读者可获得从登录注册、题库管理、在线刷题到错题统计的全流程实现方案,包含build/run/install三阶段批处理脚本、多层级Vue组件封装、Spring Boot RESTful接口设计及数据库ER图与建表语句,具备良好可读性与二次开发基础。
1. 这不是又一个“登录+列表+详情”的微信小程序套壳项目:它用 Java 后端真实承载了考研题库的「知识粒度管理」与「做题行为闭环」
你手头拿到的这个.zip包,表面看是毕业设计常见组合:微信小程序前端 + Java 后端 + MySQL 数据库。但真正让它在上百个“题库类毕设”中立住脚的,是它把「考研知识体系」当成了系统设计的第一约束,而不是把「能增删改查题目」当成终点。它不只存题——它区分「考点标签」「难度系数」「命题年份」「真题来源(312/347/自命题)」「错误率热力图」;它不只做题——它记录「单题思考时长」「二刷间隔」「错因归类(概念模糊/计算失误/审题偏差)」;它甚至让后端 Java 层主动参与「组卷策略」:比如“近3年教育学原理高频错题 + 难度≤0.65 + 未在本周练习过”。这意味着,它的 Java 代码里有QuestionSelectionService而非QuestionController,它的数据库表里有exam_point_relation和user_practice_log而非只有question和user。适合正在找可落地、有业务纵深、能讲清技术选型理由的计算机/软件工程本科生,也适合想快速复用一套「带真实教学逻辑的题库骨架」的教培类小团队——它不教你如何写冒泡排序,但它会告诉你为什么MyBatis-Plus的QueryWrapper在构建「多维度题目筛选」时比手写 SQL 更安全,以及为什么微信小程序的wx.getPhoneNumber接口必须配合 Java 后端的session_key解密链路才能真正拿到手机号。
2. 从零跑通:本地启动小程序 + Java 后端 + MySQL 的最小可行闭环
2.1 小程序前端:用微信开发者工具打开,但别急着点“编译”
解压得到的2048-小程序.zip(注意:这是上一轮交付的小程序源码工程,不是最终版,但结构一致)实际是一个标准的原生微信小程序项目,目录结构清晰:
miniprogram/ ├── app.js # 全局逻辑,含用户登录态管理 ├── app.json # 页面路由、窗口样式(重点看 navigationStyle: custom) ├── project.config.json # 工具配置,关键字段 "appid": "wx1234567890abcdef" ├── pages/ │ ├── index/ # 首页:科目选择 + 热门考点卡片 │ ├── question/ # 题目详情页:含单选框组件、答案解析折叠区、错因提交弹窗 │ └── practice/ # 练习页:倒计时、自动交卷、结果页(含知识点薄弱项提示) └── utils/ └── request.js # 封装 wx.request,统一添加 token、处理 401 跳转登录提示:
app.json中"navigationStyle": "custom"意味着顶部导航栏是自定义的,高度需手动计算。微信小程序顶部导航栏高度在不同机型差异大(iPhone X+ 是 88px,安卓部分机型是 64px),项目里通过wx.getSystemInfoSync().statusBarHeight动态获取状态栏高度,再加固定值44得到导航栏总高,所有页面page标签都设置了padding-top补齐。这是避免“内容被遮挡”的第一道防线。
启动前必须修改两处:
project.config.json中的appid替换为你自己申请的测试号 AppID(开发管理 → 开发者ID);utils/request.js中的baseUrl改为你的 Java 后端地址,例如https://localhost:8080/api。
// utils/request.js 关键片段 const baseUrl = 'https://localhost:8080/api'; // ← 修改此处 function request(options) { const token = wx.getStorageSync('token') || ''; return wx.request({ url: baseUrl + options.url, method: options.method || 'GET', data: options.data, header: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` // ← 使用 Bearer Token 认证 }, success: (res) => { if (res.statusCode === 401) { wx.navigateTo({ url: '/pages/login/login' }); // token 失效跳登录 } } }); }逻辑说明:这里采用标准的 JWT Token 认证流程。小程序登录成功后,Java 后端返回access_token,前端存入wx.setStorageSync;后续所有请求在 Header 中携带Authorization: Bearer <token>。request.js统一封装了错误拦截,401 自动跳转登录页,避免每个页面重复写判断逻辑。
2.2 Java 后端:Spring Boot + MyBatis-Plus + MySQL,重点在pom.xml依赖取舍
项目使用 Spring Boot 2.7.x(兼容 JDK 8),核心依赖精简明确:
<!-- pom.xml 片段 --> <dependencies> <!-- Web 基础 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 数据库操作:MyBatis-Plus 3.5.x,非 MyBatis 原生 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> <!-- 注意版本,3.5.x 对 LambdaQueryWrapper 支持更稳 --> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- JWT 工具 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <!-- Lombok 简化实体类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>参数说明:
mybatis-plus-boot-starter是核心:它让QuestionService可以直接继承IService<Question>,无需写 XML,lambdaQuery().eq(Question::getDifficulty, 0.6).like(Question::getContent, "皮亚杰")一行搞定复杂查询;jjwt-*三个包是 JWT 必选组合,jjwt-jackson用于将UserDetails序列化进 token payload;lombok的@Data和@Builder大幅减少Question.java、UserPracticeLog.java等实体类的样板代码。
启动命令(确保已安装 Maven 和 JDK 8+):
cd java-backend # 进入后端目录 mvn clean compile mvn spring-boot:run若启动失败,90% 概率是application.yml数据库配置未改:
# src/main/resources/application.yml spring: datasource: url: jdbc:mysql://localhost:3306/kaoyan_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: your_mysql_password # ← 必须修改! driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启 SQL 日志,调试必备2.3 数据库:MySQL 5.7+,建库建表脚本藏在sql/目录下
解压包内sql/kaoyan_db.sql是完整初始化脚本,共 8 张表,关键三张:
| 表名 | 作用 | 设计要点 |
|---|---|---|
question | 题目主表 | contentTEXT 存题干(含 HTML 标签),answerVARCHAR(20) 存标准答案(A/B/C/D),analysisLONGTEXT 存解析,difficultyDECIMAL(3,2) 存难度系数(0.1~0.9) |
exam_point | 考点主表 | codeVARCHAR(20) 为唯一编码(如EDU-001),name为“教育心理学:学习动机理论”,parent_id支持树形结构 |
question_exam_point | 题目-考点关联表 | 复合主键(question_id, exam_point_id),实现一道题关联多个考点 |
执行建库建表(推荐用 MySQL Workbench 或命令行):
-- 1. 创建数据库(字符集必须 utf8mb4,否则微信昵称emoji存不进去) CREATE DATABASE kaoyan_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 2. 切库 USE kaoyan_db; -- 3. 执行 sql 文件(将 kaoyan_db.sql 内容全粘贴执行) -- 注意:脚本末尾有 INSERT INTO question VALUES (...) 示例数据,确保执行成功验证是否就绪:访问http://localhost:8080/api/question/list?size=5,应返回 JSON 格式题目列表,含id,content,options,answer字段。这是前后端连通的第一个里程碑。
3. 登录与手机号绑定:微信小程序登录获取手机号的完整链路拆解
3.1 小程序端:button open-type="getPhoneNumber"不是点一下就完事
微信要求获取手机号必须用户主动触发,且按钮必须使用open-type="getPhoneNumber"。项目中pages/login/login.wxml的写法是标准实践:
<!-- pages/login/login.wxml --> <button class="login-btn" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber"> 微信一键登录并获取手机号 </button>关键在bindgetphonenumber事件处理器:
// pages/login/login.js Page({ onGetPhoneNumber(e) { if (!e.detail.code) { wx.showToast({ title: '获取手机号失败', icon: 'none' }); return; } // 将 code 发送给后端,换取手机号 wx.request({ url: 'https://localhost:8080/api/auth/wx-login', method: 'POST', data: { code: e.detail.code }, success: (res) => { if (res.data.code === 200) { wx.setStorageSync('token', res.data.data.token); wx.switchTab({ url: '/pages/index/index' }); } else { wx.showToast({ title: res.data.msg, icon: 'none' }); } } }); } });逻辑说明:e.detail.code是微信临时登录凭证,有效期5分钟,必须立刻发给后端。前端绝不自行调用微信code2Session接口——因为appSecret绝不能暴露在前端。
3.2 Java 后端:用code换session_key,再解密encryptedData
后端AuthController.java的/api/auth/wx-login接口是核心:
@PostMapping("/wx-login") public Result<String> wxLogin(@RequestBody Map<String, String> params) { String code = params.get("code"); if (StrUtil.isBlank(code)) { return Result.fail("code 不能为空"); } // 1. 调用微信接口换取 session_key 和 openid String url = "https://api.weixin.qq.com/sns/jscode2session?" + "appid=" + wechatConfig.getAppId() + "&secret=" + wechatConfig.getAppSecret() + "&js_code=" + code + "&grant_type=authorization_code"; String response = HttpUtil.get(url); // 使用 Hutool 的 HttpUtil JSONObject json = JSON.parseObject(response); String openid = json.getString("openid"); String sessionKey = json.getString("session_key"); if (StrUtil.isBlank(openid) || StrUtil.isBlank(sessionKey)) { return Result.fail("微信登录失败:" + json.getString("errmsg")); } // 2. 解密 encryptedData(小程序端未传,实际需前端在 button 点击后调用 wx.getPhoneNumber 获取 encryptedData 和 iv) // ⚠️ 注意:此处是简化示意,真实代码中 encryptedData 和 iv 需从前端一并传入 // String encryptedData = params.get("encryptedData"); // String iv = params.get("iv"); // String phone = WxCryptUtil.decrypt(encryptedData, sessionKey, iv); // 3. 查询或创建用户,生成 JWT Token User user = userService.getByOpenid(openid); if (user == null) { user = new User().setOpenid(openid).setCreateTime(new Date()); userService.save(user); } String token = JwtUtil.generateToken(user.getId(), user.getOpenid()); return Result.success(token); }参数说明:
wechatConfig是@ConfigurationProperties注入的配置类,appSecret从application.yml读取,绝不硬编码;WxCryptUtil.decrypt()是自研工具类,封装了 AES-128-CBC 解密逻辑,encryptedData是 Base64 编码的密文,iv是 16 字节初始向量,二者必须与session_key同次请求获取,否则解密失败;JwtUtil.generateToken()生成的 token 有效期设为 7 天,payload 中存userId和openid,足够支撑后续所有接口鉴权。
注意:微信官方要求
encryptedData和iv必须在getPhoneNumber成功回调中立即获取并发送,不能缓存。项目中onGetPhoneNumber方法应先调wx.getPhoneNumber获取encryptedData和iv,再连同code一起 POST 给后端。上述代码省略了这一步是为了聚焦主干逻辑,实际部署必须补全。
4. 题库核心功能落地:基于 MyBatis-Plus 的「多条件动态题目筛选」与「错因归类统计」
4.1 Java 层:QuestionService如何用 LambdaQueryWrapper 构建动态查询
考研题库的筛选绝非简单WHERE difficulty > 0.5。用户可能组合:「教育学原理 + 近2年真题 + 错误率 > 60% + 未练习过」。硬写 SQL 易出错,LambdaQueryWrapper是更安全的选择:
@Service public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService { @Override public Page<Question> listByConditions(QuestionQuery query, Page<Question> page) { LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<>(); // 科目筛选(外键关联 exam_point 表) if (query.getSubjectId() != null && query.getSubjectId() > 0) { wrapper.inSql(Question::getId, "SELECT question_id FROM question_exam_point WHERE exam_point_id IN " + "(SELECT id FROM exam_point WHERE subject_id = " + query.getSubjectId() + ")"); } // 难度区间 if (query.getMinDifficulty() != null) { wrapper.ge(Question::getDifficulty, query.getMinDifficulty()); } if (query.getMaxDifficulty() != null) { wrapper.le(Question::getDifficulty, query.getMaxDifficulty()); } // 是否只查错题(关联 user_practice_log 表) if (Boolean.TRUE.equals(query.getOnlyWrong())) { wrapper.inSql(Question::getId, "SELECT question_id FROM user_practice_log " + "WHERE user_id = " + SecurityUtil.getCurrentUserId() + " AND is_correct = 0 GROUP BY question_id " + "HAVING COUNT(*) >= " + query.getMinWrongCount()); } // 排序:默认按错误率降序,保证高频错题优先 wrapper.orderByDesc("wrong_rate").orderByDesc("create_time"); return page(page, wrapper); } }逻辑说明:
inSql()用于子查询关联,避免 N+1 查询。question_exam_point表是题目与考点的桥接表,exam_point表有subject_id字段标识所属科目(教育学/心理学/英语等);ge()/le()是 MyBatis-Plus 提供的安全方法,自动防止 SQL 注入,比字符串拼接WHERE difficulty >= #{minDifficulty}更可靠;SecurityUtil.getCurrentUserId()是自定义工具类,从 JWT Token 中解析出当前登录用户 ID,确保数据隔离;orderByDesc("wrong_rate")中wrong_rate是数据库视图字段或冗余字段,项目中通过定时任务每日更新各题错误率,避免实时计算拖慢接口。
4.2 数据库层:为什么user_practice_log表要冗余question_content?
user_practice_log表结构如下:
CREATE TABLE `user_practice_log` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL, `question_id` bigint NOT NULL, `question_content` text COMMENT '题目题干快照,防题目内容变更导致历史记录失真', `selected_option` varchar(10) DEFAULT NULL, `is_correct` tinyint(1) DEFAULT '0', `duration_ms` int DEFAULT '0', `wrong_cause` varchar(50) DEFAULT NULL COMMENT '错因:概念模糊/计算失误/审题偏差/记忆混淆', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_question` (`user_id`,`question_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;参数说明:
question_content是冗余字段,值来自question.content的拷贝。这是关键设计:考研题库运营中,题目解析可能随大纲调整而更新,但用户的历史练习记录必须保留当时的原始题干和选项,否则“我上次选B,这次题干变了,B变成错的了”会导致数据不可信;wrong_cause是枚举字段,前端提供单选框让用户提交错因,后端存入此字段,为后续「错因归类统计」提供数据源;- 复合索引
idx_user_question加速「查询某用户所有练习记录」和「查询某题被哪些用户练过」两个高频场景。
4.3 前端展示:小程序如何渲染「错因分布环形图」
pages/practice/result.wxml中使用canvas渲染环形图,数据来自后端/api/practice/wrong-cause-stat接口:
// 返回示例 { "data": [ { "cause": "概念模糊", "count": 12 }, { "cause": "计算失误", "count": 8 }, { "cause": "审题偏差", "count": 5 }, { "cause": "记忆混淆", "count": 3 } ] }前端result.js中调用echarts-for-weixin(项目已集成):
// pages/practice/result.js onLoad() { wx.request({ url: 'https://localhost:8080/api/practice/wrong-cause-stat', success: (res) => { const chartData = res.data.data.map(item => ({ name: item.cause, value: item.count })); this.initChart(chartData); } }); }, initChart(data) { const query = wx.createSelectorQuery(); query.select('#pieCanvas').fields({ node: true, size: true }).exec((res) => { const canvas = res[0].node; const ctx = canvas.getContext('2d'); const dpr = wx.getSystemInfoSync().pixelRatio; canvas.width = res[0].width * dpr; canvas.height = res[0].height * dpr; ctx.scale(dpr, dpr); // 初始化 echarts 实例 const chart = echarts.init(canvas, null, { renderer: 'canvas' }); const option = { tooltip: { trigger: 'item' }, series: [{ type: 'pie', radius: ['40%', '70%'], avoidLabelOverlap: false, label: { show: false }, emphasis: { label: { show: true, fontSize: 16 } }, data: data }] }; chart.setOption(option); }); }逻辑说明:echarts-for-weixin是专为小程序优化的 ECharts 版本,不依赖 DOM,直接操作 Canvas。dpr(设备像素比)适配是关键,否则在 iPhone 上图表模糊。radius设置为['40%', '70%']形成环形图,比饼图更符合“知识漏洞环形分布”的视觉隐喻。
5. 避坑指南:这 4 个血泪经验,让我重装了三次 MySQL 和两次微信开发者工具
5.1 现象:小程序点击“获取手机号”按钮无反应,控制台报fail no permission
原因:button组件未设置scope权限,或project.config.json中permission字段缺失。微信要求open-type="getPhoneNumber"必须在project.config.json中声明权限:
"permission": { "scope.userInfo": { "desc": "用于完善用户信息" }, "scope.userPhoneNumber": { "desc": "用于接收考试提醒" } }解决:在project.config.json的根对象下添加permission字段,并确保desc值符合微信审核规范(不能写“用于获取手机号”,必须写具体用途)。修改后需重启开发者工具。
5.2 现象:Java 后端启动时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
原因:MySQL 8.0+ 驱动类名已从com.mysql.jdbc.Driver变更为com.mysql.cj.jdbc.Driver,而pom.xml中mysql-connector-java版本过低(如 5.1.47)仍用旧类名。
解决:升级mysql-connector-java至 8.0.33+,并确认application.yml中driver-class-name为com.mysql.cj.jdbc.Driver。同时检查url参数是否包含serverTimezone=Asia/Shanghai,否则时区错误导致连接失败。
5.3 现象:user_practice_log表数据量激增,SELECT * FROM user_practice_log WHERE user_id = ?查询变慢
原因:缺少user_id单列索引,仅靠复合索引idx_user_question无法高效定位单用户记录。
解决:执行 SQL 添加索引:
ALTER TABLE user_practice_log ADD INDEX idx_user_id (user_id);玄学提示:不要迷信“复合索引能覆盖单列查询”。
idx_user_question (user_id, question_id)在WHERE user_id = ?时可用,但WHERE user_id = ? ORDER BY create_time DESC时,若create_time未加入索引,仍会触发 filesort。生产环境建议建idx_user_time (user_id, create_time)。
5.4 现象:小程序wx.request报net::ERR_CONNECTION_REFUSED,但后端明明在运行
原因:微信开发者工具默认启用「不校验合法域名」,但若勾选了「不校验 TLS 证书」,而你的后端用的是https://localhost:8080(自签名证书),则请求被拦截。
解决:两种方案任选其一:
- 方案一(推荐):后端
application.yml中server.ssl.enabled: false,前端baseUrl改为http://localhost:8080/api; - 方案二:用
mkcert工具生成本地可信证书,配置 Spring Boot SSL,再在开发者工具中勾选「不校验 TLS 证书」。
6. 进阶技巧:用 MyBatis-Plus 的AutoGenerator根据 Java 实体类生成建表 SQL,彻底告别手写 DDL
6.1 为什么需要这个?—— 毕设答辩时最怕被问「表结构怎么设计的?」
导师常问:“question_exam_point表为什么用复合主键而不是自增 ID?” 如果你回答“因为要保证题目和考点的唯一关联”,他可能追问:“那删除考点时,如何级联清理关联题目?”——这时,如果你能掏出AutoGenerator生成的question_exam_point.sql,指着ON DELETE CASCADE说“看,我在生成器里配置了级联删除”,可信度瞬间拉满。更重要的是,它让「数据库结构」和「Java 实体类」强绑定,改一个字段,SQL 和 Entity 同步更新,杜绝“代码里字段叫diffLevel,数据库里是difficulty_level”的尴尬。
6.2 实操:三步生成question表的建表语句
项目中code-generator模块已预置配置,只需修改CodeGenerator.java:
public class CodeGenerator { public static void main(String[] args) { // 1. 数据库配置 String url = "jdbc:mysql://localhost:3306/kaoyan_db?useSSL=false&serverTimezone=Asia/Shanghai"; String username = "root"; String password = "your_password"; // 2. 生成策略 AutoGenerator mpg = new AutoGenerator(); mpg.setDataSource(new DataSourceConfig.Builder(url, username, password) .build()); mpg.setGlobalConfig(new GlobalConfig.Builder() .outputDir(System.getProperty("user.dir") + "/sql/generated") // ← 输出到 sql/generated 目录 .author("YourName") .enableSwagger() // 生成 Swagger 注释 .build()); mpg.setPackageInfo(new PackageConfig.Builder() .parent("com.kaoyan") .moduleName("question") .build()); // 3. 策略配置:指定生成 question 表,且开启 DDL 生成 StrategyConfig strategy = new StrategyConfig.Builder() .addInclude("question") // ← 只生成 question 表 .entityBuilder() .enableLombok() .naming(NamingStrategy.underline_to_camel) // 下划线转驼峰 .end() .controllerBuilder() .enableRestStyle() // 生成 @RestController .end() .mapperBuilder() .enableBaseResultMap() // 生成 ResultMap .end() .build(); mpg.setStrategy(strategy); // 4. 执行生成 mpg.execute(); } }运行CodeGenerator.main()后,在sql/generated/目录下得到question.sql:
/* MyBatis-Plus Generator - DDL Script Table: question */ CREATE TABLE `question` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '题干(含HTML)', `options` json COMMENT '选项JSON:{"A":"xxx","B":"yyy"}', `answer` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '正确答案', `analysis` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '答案解析', `difficulty` decimal(3,2) COMMENT '难度系数', `wrong_rate` decimal(5,4) DEFAULT '0.0000' COMMENT '错误率', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='题目主表';参数说明:
options字段类型为json,MySQL 5.7+ 原生支持,Question.java中对应private Map<String, String> options;,MyBatis-Plus 自动序列化/反序列化;wrong_rate默认0.0000,避免NULL值干扰统计;update_time的ON UPDATE CURRENT_TIMESTAMP确保每次更新自动刷新,无需 Java 层手动赋值。
6.3 进阶:用TemplateConfig定制 SQL 模板,加入考研专用注释
默认生成的 SQL 注释较通用,我们可定制模板,加入业务语义:
// 在 CodeGenerator.java 中添加 mpg.setTemplate(new TemplateConfig.Builder() .entity("/templates/entity.java.vm") // 使用自定义实体模板 .mapper("/templates/mapper.xml.vm") .service("/templates/service.java.vm") .controller("/templates/controller.java.vm") .build()); // 创建 templates/ddl.vm 模板(路径需与 builder 一致) // 模板中可写: /* 考研题库专用表:${table.name} 业务规则: - content 字段必须支持 MathJax 公式渲染,故用 utf8mb4 - difficulty 范围:0.1 ~ 0.9,0.1=极简单,0.9=极难 - wrong_rate 每日凌晨由定时任务更新,精度 0.0001 */ CREATE TABLE `${table.name}` ( ... );这样生成的 SQL 文件,每一张都自带业务上下文,答辩时直接打开就是一份微型需求文档。
我带过三届毕设,学生最常犯的错不是代码写不对,而是「不知道自己写的代码在解决什么问题」。这个题库项目真正的价值,不在于它用了 Spring Boot 还是 MyBatis-Plus,而在于它把「考研复习」这个抽象过程,拆解成了question、exam_point、user_practice_log这些可存储、可查询、可统计的数据实体。当你能对着user_practice_log表说出“这一行代表张三在2024年3月15日第3次练习‘皮亚杰认知发展阶段’题,选了C,错了,原因是审题偏差”,你就已经超越了 80% 的毕设作者。希望帮到你。
本文还有配套的精品资源,点击获取