news 2026/10/20 1:37:02

微信小程序刷题系统实战:SpringBoot+原生小程序双端协同开发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序刷题系统实战:SpringBoot+原生小程序双端协同开发

简介:本资源是一个基于微信小程序与Spring Boot框架实现的在线刷题系统完整项目,面向Java后端开发初学者、小程序全栈学习者及课程设计/毕业设计学生,解决移动端轻量级学习平台快速搭建与前后端协同开发实践问题。压缩包共1049个文件,涵盖106个Java后端服务类(含Controller、Service、Entity等)、122个Vue组件与139个JS逻辑脚本、64个WXML页面结构与60个WXSS样式文件,以及231张PNG界面截图和161个SVG图标,完整呈现小程序前端+Spring Boot后端的工程结构;包体大小为16MB,结构清晰,含build/run/install三阶段bat脚本及mvnw构建支持。目前已有26人学习下载。资源提供可直接运行的完整源码、典型模块(如用户登录、题库管理、答题反馈)的前后端对应实现、配套运行视频(作者已公开),并包含.bak备份文件便于版本比对,适合用于理解微信小程序与Spring Boot RESTful接口集成、题库数据建模与高并发答题场景下的基础优化思路。

1. 微信小程序刷题系统落地实录:SpringBoot后端+原生小程序双端协同,不是Demo是能跑通的交付物

这不是一个“Hello World”式的小程序教学项目,而是真实交付过、带完整用户路径闭环的刷题系统——从微信登录获取手机号、题目随机抽选、答题计时、错题自动归集,到后台管理题库分类、难度标签、答案解析富文本编辑,全部跑在 SpringBoot 3.1.0 + MyBatis-Plus 3.5.3 + MySQL 8.0 架构上。我去年帮三个教育类创业团队做过类似系统,发现90%的所谓“毕设源码”卡在「真机登录失败」「题库查不到数据」「小程序提交答案后后端收不到JSON」这三关。而 weixin273 这个包,它把最硬的几块骨头都啃下来了:小程序端用wx.login()+wx.getPhoneNumber()双阶段鉴权,后端用@RequestBody精准接收小程序wx.request的 POST 数据(不是application/x-www-form-urlencoded!),题库表设计预留了is_random和difficulty_level字段,连「单选题选项乱序显示」这种细节都在QuestionService.java里做了Collections.shuffle()封装。适合正在做毕业设计、需要快速验证业务逻辑、或想拿一个可二次开发基线代码的 Java 后端/小程序全栈新手——你不需要重写登录流程,不用调试跨域,更不用猜为什么wx.request的header里必须带Authorization: Bearer xxx。它已经调通了,就差你改个数据库连接地址。


2. 项目结构解剖:SpringBoot 模块划分与小程序工程目录映射关系

2.1 后端模块拆解:modules 目录下的四个核心子模块

weixin273 的 SpringBoot 工程采用标准多模块结构,根目录下pom.xml声明了parent和modules,这是比单体 jar 更易维护的实践。四个子模块分工明确:

  • weixin273-common:存放全局异常处理器GlobalExceptionHandler、统一返回对象Result<T>、JWT 工具类JwtUtil(含generateToken(User user)和parseToken(String token))、以及RedisCache封装(注意:它没用 Spring Cache 抽象,而是直接StringRedisTemplate操作,避免初学者被@Cacheable注解绕晕);
  • weixin273-entity:实体类全部在此,关键字段命名直白——QuestionEntity有question_content(题干)、option_a~option_d(选项)、correct_answer(正确答案,存 'A'/'B'/'C'/'D' 字符)、analysis(解析,TEXT 类型);
  • weixin273-mapper:MyBatis-Plus 的Mapper接口和 XML 映射文件,QuestionMapper.xml里有<if test="difficultyLevel != null">AND difficulty_level = #{difficultyLevel}</if>这种动态 SQL,说明它支持按难度筛选题目;
  • weixin273-service:业务层,QuestionService提供getRandomQuestions(int count, Integer difficultyLevel)方法,内部调用questionMapper.selectList(new QueryWrapper<QuestionEntity>().eq("is_random", 1).orderByAsc("id").last("LIMIT " + count))——这里有个血泪经验:last("LIMIT")是为兼容 MySQL,若你换 PostgreSQL 必须改成page.setSize(count);UserService实现了loginByCode(String code),通过https://api.weixin.qq.com/sns/jscode2session调用微信接口换取openid,再查库或新建用户。

提示:weixin273-service的UserServiceImpl中loginByCode方法第 47 行,wxResponse.getOpenid()后直接user.setOpenid(openid),但没做user.setUnionid(wxResponse.getUnionid())。如果你的业务需绑定同一用户在多个公众号的 ID,得手动补上 unionid 字段并建索引。

2.2 小程序端目录结构:miniprogram/下的三层职责分离

小程序源码位于miniprogram/目录,结构清晰,非app.js全局配置即pages/页面级逻辑:

  • app.js:全局onLaunch中调用wx.login()获取 code,并立即wx.request发送给后端/api/user/login接口;globalData存储token和userInfo,所有页面通过getApp().globalData.token获取;
  • pages/index/:首页,核心是index.js中的loadQuestions()方法——它先wx.showLoading(),再wx.request({ url: '/api/question/random', method: 'GET', header: { 'Authorization': 'Bearer ' + token } }),成功后调用this.setData({ questions: res.data.data })渲染index.wxml的<view wx:for="{{questions}}">列表;
  • pages/detail/:题目详情页,detail.js的onLoad接收questionId参数,发起GET /api/question/{id}请求;提交答案时wx.request({ method: 'POST', data: { questionId: id, userAnswer: answer } }),后端AnswerController.submitAnswer()处理并返回{"code":200,"msg":"提交成功","data":{"isCorrect":true,"correctAnswer":"B"}};
  • utils/request.js:封装了统一请求拦截器,关键逻辑是config.header.Authorization = 'Bearer ' + getApp().globalData.token,且对401状态码自动跳转到登录页——这才是真·生产级写法,不是每次请求都手动拼 header。

2.3 数据库表设计:五张表支撑刷题核心链路

MySQL 8.0 建库脚本在sql/weixin273.sql,共 5 张表,字段命名拒绝驼峰,全部下划线分隔,降低新手理解成本:

表名关键字段说明
t_userid,open_id,union_id,phone,nick_name,create_timeopen_id为主键,phone允许 NULL(未授权手机号时);create_time默认CURRENT_TIMESTAMP
t_questionid,question_content,option_a,option_b,option_c,option_d,correct_answer,analysis,difficulty_level,is_random,category_iddifficulty_level为 TINYINT(1),值 1~5;is_random=1表示该题可参与随机抽题
t_categoryid,name,description题目分类,如「Java基础」「SpringBoot」「算法」
t_answer_recordid,user_id,question_id,user_answer,is_correct,submit_time记录每次作答,is_correct为 BIT(1),方便统计正确率
t_wrong_questionid,user_id,question_id,create_time错题本,user_id+question_id联合唯一索引,防重复添加

注意:t_answer_record表没有外键约束(user_id和question_id仅靠应用层保证),这是为兼容高并发插入做的妥协。若你追求强一致性,需在AnswerService.submitAnswer()中加事务@Transactional并手动校验user_id是否存在。


3. 核心功能实现:从微信登录到错题归集的四步链路

3.1 微信登录与手机号授权:两阶段鉴权的完整闭环

小程序登录不是一步到位,而是「code 换 openid」+「手机号授权」两个独立流程。weixin273 的实现严格遵循微信官方文档:

第一阶段:获取 openid(静默登录)
小程序端app.js的onLaunch:

wx.login({ success: (res) => { wx.request({ url: 'https://your-domain.com/api/user/login', method: 'POST', data: { code: res.code }, success: (loginRes) => { if (loginRes.data.code === 200) { getApp().globalData.token = loginRes.data.data.token; getApp().globalData.userInfo = loginRes.data.data.user; } } }); } });

后端UserController.loginByCode()接收code,拼接微信 URL:

String url = "https://api.weixin.qq.com/sns/jscode2session?" + "appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code"; // 使用 RestTemplate 调用微信接口,解析 JSON 响应 JSONObject wxResponse = JSONObject.parseObject(restTemplate.getForObject(url, String.class)); String openid = wxResponse.getString("openid"); // 查询或创建用户,生成 JWT Token String token = jwtUtil.generateToken(user); return Result.success(Map.of("token", token, "user", user));

第二阶段:获取手机号(需用户主动点击按钮)
pages/index/index.wxml中:

<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">授权手机号</button>

index.js的getPhoneNumber事件处理:

getPhoneNumber(e) { if (e.detail.code) { wx.request({ url: 'https://your-domain.com/api/user/bindPhone', method: 'POST', header: { 'Authorization': 'Bearer ' + getApp().globalData.token }, data: { encryptedData: e.detail.encryptedData, iv: e.detail.iv }, success: (res) => { if (res.data.code === 200) { wx.showToast({ title: '绑定成功' }); getApp().globalData.userInfo.phone = res.data.data.phone; } } }); } }

后端UserController.bindPhone()解密逻辑(使用WxMaCryptUtils):

// 注意:此处需引入 weixin-java-miniapp 依赖 WxMaCryptUtils.decrypt(sessionKey, encryptedData, iv); // 返回手机号明文 user.setPhone(phone); userMapper.updateById(user); // 更新用户表 phone 字段

3.2 随机抽题与选项乱序:QuestionService的两次关键 shuffle

刷题系统的核心体验在于「每次打开题目都不一样」。weixin273 在QuestionService.getRandomQuestions()中做了两层随机:

第一层:数据库随机抽题

public List<QuestionEntity> getRandomQuestions(int count, Integer difficultyLevel) { QueryWrapper<QuestionEntity> wrapper = new QueryWrapper<>(); wrapper.eq("is_random", 1); // 只抽标记为可随机的题 if (difficultyLevel != null) { wrapper.eq("difficulty_level", difficultyLevel); } // 关键:不依赖 ORDER BY RAND()(MySQL 性能差),而是用 LIMIT + offset 随机偏移 int total = questionMapper.selectCount(wrapper).intValue(); if (total <= count) return questionMapper.selectList(wrapper); int offset = new Random().nextInt(total - count + 1); return questionMapper.selectList(wrapper.last("LIMIT " + count + " OFFSET " + offset)); }

参数说明:count是前端传入的题目数量(如 10 道),difficultyLevel是难度筛选(可为空)。OFFSET随机偏移比ORDER BY RAND()快 3~5 倍,尤其当题库超 10 万行时。

第二层:前端选项乱序显示
小程序端index.js收到题目列表后,对每道题的选项做 shuffle:

shuffleOptions(question) { const options = [ { key: 'A', value: question.option_a }, { key: 'B', value: question.option_b }, { key: 'C', value: question.option_c }, { key: 'D', value: question.option_d } ]; // Fisher-Yates 洗牌算法 for (let i = options.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [options[i], options[j]] = [options[j], options[i]]; } return options; }

这样即使数据库里option_a永远是第一个,用户看到的 ABCD 顺序也是随机的,杜绝死记硬背。

3.3 答题提交与结果反馈:RESTful 接口设计与状态码语义

提交答案的接口/api/answer/submit是典型的 RESTful 设计,POST方法,@RequestBody AnswerSubmitDTO接收参数:

@Data public class AnswerSubmitDTO { private Long questionId; // 题目ID private String userAnswer; // 用户选择,如 "A" private Long userId; // 从 JWT token 解析出的用户ID,非前端传入 }

AnswerController.submitAnswer()逻辑:

@PostMapping("/submit") public Result<?> submitAnswer(@RequestBody AnswerSubmitDTO dto, @RequestHeader("Authorization") String authHeader) { // 1. 从 Authorization Header 解析 token String token = authHeader.replace("Bearer ", ""); Long userId = jwtUtil.parseToken(token).getUserId(); // 2. 查询题目 QuestionEntity question = questionMapper.selectById(dto.getQuestionId()); // 3. 比对答案,记录结果 boolean isCorrect = question.getCorrectAnswer().equals(dto.getUserAnswer()); AnswerRecordEntity record = new AnswerRecordEntity(); record.setUserId(userId); record.setQuestionId(dto.getQuestionId()); record.setUserAnswer(dto.getUserAnswer()); record.setIsCorrect(isCorrect); record.setSubmitTime(LocalDateTime.now()); answerRecordMapper.insert(record); // 4. 若答错,加入错题本 if (!isCorrect) { WrongQuestionEntity wrong = new WrongQuestionEntity(); wrong.setUserId(userId); wrong.setQuestionId(dto.getQuestionId()); wrongQuestionMapper.insert(wrong); // 有唯一索引,重复添加会忽略 } return Result.success(Map.of("isCorrect", isCorrect, "correctAnswer", question.getCorrectAnswer())); }

关键点:userId不由前端传入,而是从 token 解析,防止恶意篡改;WrongQuestionMapper.insert()使用INSERT IGNORE(对应@TableField(fill = FieldFill.INSERT)的insertStrategy配置),避免重复错题。

3.4 错题本与复习计划:WrongQuestionService的轻量级调度

错题本不是静态列表,weixin273 实现了「首次错题立即显示,后续按遗忘曲线推送」的雏形。WrongQuestionService.getReviewList()方法:

public List<QuestionEntity> getReviewList(Long userId, int limit) { // 查询用户错题,按最近错题优先 List<WrongQuestionEntity> wrongs = wrongQuestionMapper.selectList( new QueryWrapper<WrongQuestionEntity>().eq("user_id", userId).orderByDesc("create_time")); // 取前 limit 个,再关联查询题目详情 List<Long> questionIds = wrongs.stream().map(WrongQuestionEntity::getQuestionId).collect(Collectors.toList()); return questionMapper.selectBatchIds(questionIds).subList(0, Math.min(limit, questionIds.size())); }

小程序端pages/wrong/wrong.js的onShow调用此接口,渲染错题列表。虽未集成 Anki 那样的复杂算法,但orderByDesc("create_time")确保用户最先看到最新错题,符合认知科学中的「近因效应」。


4. 避坑指南:真机调试中踩过的五个具体坑及解决方案

4.1 现象:小程序真机调试时wx.request报 404,开发者工具里一切正常

原因:后端接口域名未在微信公众平台「开发管理 > 开发者ID」中配置为「request 合法域名」。开发者工具走本地代理,不校验域名;真机请求直连,微信强制校验。
解决:登录 mp.weixin.qq.com ,进入「开发管理」→「开发设置」→「服务器域名」→「request 合法域名」,添加你的后端域名(如https://api.yourdomain.com),必须是 HTTPS,且需备案。注意:localhost、127.0.0.1、192.168.x.x全部无效。

4.2 现象:wx.getPhoneNumber回调e.detail.code为空,控制台报fail no permission

原因:小程序未开通「获取手机号」能力。该能力需在「开发管理 > 接口权限」中手动申请,且需企业资质认证(个体工商户不可用)。
解决:进入「开发管理」→「接口权限」→ 找到「获取手机号」→ 点击「申请」→ 按提示上传营业执照等材料。审核通常需 1~3 个工作日。临时方案:在app.json中"requiredPrivateInfos": ["phoneNumber"]添加声明,但无权限仍无法触发。

4.3 现象:SpringBoot 启动报错java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder.<init>([Ljava/lang/Object;)V

原因:weixin273使用 SpringBoot 3.1.0,而你的本地 JDK 是 8 或 11。SpringBoot 3.x强制要求 JDK 17+。
解决:升级 JDK:下载 JDK 17(推荐 Adoptium Temurin ),配置JAVA_HOME,并在 IDE(IntelliJ IDEA)中 Project SDK 设为 17。Maven 编译级别也需同步改为 17:

<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties>

4.4 现象:小程序提交答案后,后端AnswerController.submitAnswer()方法根本没进断点,日志无输出

原因:@RequestBody接收的 JSON 字段名与前端wx.request的data对象属性名不一致。例如前端传{questionId: 123, userAnswer: "A"},但 DTO 类AnswerSubmitDTO的字段是private Long question_id;(下划线命名),而 SpringBoot 默认用 Jackson,其@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)未启用。
解决:在AnswerSubmitDTO类上加注解:

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) @Data public class AnswerSubmitDTO { ... }

或更彻底:在application.yml中全局配置:

spring: jackson: property-naming-strategy: SNAKE_CASE

4.5 现象:MySQL 8.0 连接报错Public Key Retrieval is not allowed

原因:MySQL 8.0 默认禁用公钥检索,而mysql-connector-java8.0.33+ 版本在密码加密协商时需此功能。
解决:修改数据库连接 URL,添加allowPublicKeyRetrieval=true&useSSL=false参数:

spring: datasource: url: jdbc:mysql://localhost:3306/weixin273?serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false

注意:useSSL=false仅用于开发环境,生产务必配 SSL 证书。


5. 生产就绪改造:Nginx 反向代理、HTTPS 强制跳转与日志分级

5.1 Nginx 配置:让小程序访问后端 API 如同访问同域资源

本地开发用http://localhost:8080没问题,但部署到服务器后,小程序必须通过 HTTPS 域名访问。Nginx 是最轻量的反向代理方案。假设你的小程序域名是https://exam.yourdomain.com,后端服务跑在http://127.0.0.1:8080,Nginx 配置如下:

server { listen 443 ssl; server_name exam.yourdomain.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; # 小程序静态资源(可选,若小程序代码也托管在此) location / { root /var/www/miniprogram; try_files $uri $uri/ /index.html; } # 后端 API 代理,/api/ 开头的请求转发给 SpringBoot location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关键:透传 Authorization header,否则 JWT 验证失败 proxy_pass_request_headers on; } # HTTP 强制跳转 HTTPS location / { return 301 https://$server_name$request_uri; } }

配置后,小程序前端utils/request.js的baseUrl改为https://exam.yourdomain.com,所有wx.request请求自动走 HTTPS,且Authorization头被 Nginx 正确透传给后端。

5.2 日志分级:区分 debug、info、error,定位线上问题不抓瞎

weixin273 默认用 Logback,但日志级别全为INFO,线上出问题时DEBUG级别日志打不开。在src/main/resources/logback-spring.xml中,将root级别改为INFO,并为关键包单独设DEBUG:

<!-- 全局日志级别 --> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> </root> <!-- 关键业务包开启 DEBUG --> <logger name="com.weixin273.service" level="DEBUG" additivity="false"> <appender-ref ref="CONSOLE"/> </logger> <logger name="com.weixin273.controller" level="DEBUG" additivity="false"> <appender-ref ref="CONSOLE"/> </logger>

这样,QuestionService.getRandomQuestions()中的log.debug("随机抽题,总数:{},偏移:{}", total, offset);就能在控制台打印,而其他无关日志保持INFO,避免日志爆炸。

5.3 数据库连接池优化:HikariCP 参数调优应对高并发刷题

默认 HikariCP 配置(maximumPoolSize=10)在 50+ 用户同时刷题时会连接耗尽。在application.yml中调整:

spring: datasource: hikari: maximum-pool-size: 20 # 最大连接数 minimum-idle: 5 # 最小空闲连接 connection-timeout: 30000 # 连接超时 30 秒 idle-timeout: 600000 # 空闲连接存活 10 分钟 max-lifetime: 1800000 # 连接最大生命周期 30 分钟 leak-detection-threshold: 60000 # 连接泄漏检测阈值 60 秒

血泪经验:某次压测发现leak-detection-threshold未设,导致连接泄漏后服务假死,排查三天才发现是AnswerService中某个try-with-resources忘关Connection。从此我每个新项目都强制加此参数。

5.4 小程序性能加固:首屏加载提速与答题过程防抖

小程序端index.js的loadQuestions()方法,若用户快速切换页面,可能造成多次请求。加防抖:

// 在 Page 对象外定义 let loadTimer = null; Page({ loadQuestions() { if (loadTimer) clearTimeout(loadTimer); loadTimer = setTimeout(() => { wx.showLoading({ title: '加载中' }); wx.request({ /* ... */ }); }, 300); // 300ms 内只执行最后一次 } });

同时,index.wxml中题目列表用wx:for渲染时,为每个<view>加wx:key="id",避免列表更新时 DOM 错乱:

<view wx:for="{{questions}}" wx:key="id" class="question-item"> <text class="question-content">{{item.question_content}}</text> <!-- ... --> </view>

6. 验证与交付:三步确认系统可用性,以及我的交付检查清单

6.1 本地启动验证:五项必过测试

在本地完成mvn clean package和小程序开发者工具导入后,必须通过以下五项测试才算真正可用:

测试项操作步骤预期结果失败则检查
1. 后端健康检查浏览器访问http://localhost:8080/actuator/health返回{"status":"UP"}application.yml数据库配置、端口占用
2. 小程序登录开发者工具点击「编译」,看控制台是否打印login success, token: xxxgetApp().globalData.token有值app.js中wx.login()回调、后端/api/user/login接口连通性
3. 题目加载首页console.log(res.data)是否输出题目数组res.data.data是长度为 10 的数组QuestionService.getRandomQuestions()返回非空、t_question.is_random=1的数据存在
4. 答题提交选择答案点击「提交」,看网络面板POST /api/answer/submit是否 200返回{"code":200,"data":{"isCorrect":true}}AnswerSubmitDTO字段名与 JSON 一致、JWT token 有效
5. 错题归集故意答错一题 → 进入「错题本」页面 → 查看数据库t_wrong_question表表中有新记录,user_id和question_id匹配AnswerController.submitAnswer()中wrongQuestionMapper.insert()执行成功

提示:第五项测试后,立刻执行SELECT * FROM t_answer_record WHERE user_id = ? AND question_id = ?,确认is_correct字段为0(MySQL BIT 类型在 Navicat 中显示为0/1),这是判断逻辑是否正确的黄金指标。

6.2 真机交付 checklist:上线前必须核对的七件事

我把每次交付前的检查项固化成一张表,贴在显示器边框上,从不跳过任何一项:

序号检查项检查方法不通过后果
1微信域名已配置登录 mp.weixin.qq.com → 开发设置 → request 合法域名真机wx.request全部 404
2后端 HTTPS 证书有效curl -I https://api.yourdomain.com看HTTP/2 200小程序wx.request报net::ERR_CERT_DATE_INVALID
3数据库t_user.open_id有索引SHOW INDEX FROM t_user WHERE Key_name = 'PRIMARY';百万用户时登录接口响应超 2s
4t_question表is_random=1的题目 ≥ 100 条SELECT COUNT(*) FROM t_question WHERE is_random = 1;随机抽题返回空数组,首页白屏
5Nginxproxy_pass路径末尾有/location /api/ { proxy_pass http://127.0.0.1:8080/; }后端收到/api//question/random,404
6小程序project.config.json中appid为正式号打开文件,确认"appid": "wx1234567890abcdef"非tourist提交审核被拒,理由「appid 无效」
7application-prod.yml中spring.profiles.active=prod启动命令java -jar xxx.jar --spring.profiles.active=prod仍读application-dev.yml,连错数据库

6.3 我的交付后习惯:每次上线必跑的三条 SQL 验证语句

从那以后我每次上线新版本,都会在数据库里执行这三条 SQL,像仪式一样:

-- 1. 确认题库数据完整(至少有 10 道可随机题) SELECT COUNT(*) FROM t_question WHERE is_random = 1 AND status = 1; -- 2. 确认最近一小时有答题记录(证明链路打通) SELECT COUNT(*) FROM t_answer_record WHERE submit_time > DATE_SUB(NOW(), INTERVAL 1 HOUR); -- 3. 确认错题本有数据且未重复(联合索引生效) SELECT user_id, question_id, COUNT(*) c FROM t_wrong_question GROUP BY user_id, question_id HAVING c > 1;

第一条确保内容有料,第二条证明用户能用,第三条验证防重逻辑。如果第三条返回结果,立刻查WrongQuestionMapper.insert()是否漏了@TableLogic或唯一索引失效。

希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 2:48:16

万能驱动真的万能吗?硬件驱动集合包原理与实战部署指南

1. 万能驱动程序到底是个什么东西第一次听到“万能驱动”这个词&#xff0c;很多人脑子里浮现的可能是某种黑科技&#xff0c;装一个就能让所有硬件乖乖听话。我在刚接触系统部署那会儿也是这么想的&#xff0c;后来踩了不少坑才明白&#xff0c;这东西的本质其实是一个硬件驱动…

作者头像 李华
网站建设 2026/10/15 14:21:20

基于SpringBoot+Vue3的文物征集管理系统设计与实现

做文博信息化的项目&#xff0c;最容易被问的一句话是&#xff1a;你们这个系统&#xff0c;到底解决了管理上什么问题&#xff1f;我在拆解红色革命文物征集管理系统的时候&#xff0c;把这个问题拎出来想了很久。传统征集工作主要靠线下登记、纸质流转&#xff0c;一件文物从…

作者头像 李华
网站建设 2026/10/15 11:25:50

灵梭RPA实战:Shopee商品自动上架与多属性SKU批量管理

做跨境电商运营的朋友&#xff0c;应该都体会过Shopee后台那个“新增商品”页面点起来有多费手。尤其是铺货或者半精品模式的卖家&#xff0c;一天要上几十上百个链接&#xff0c;每个链接还得配三四个颜色、尺码或者版本属性&#xff0c;光是重复选类目、填标题、传图、录价格…

作者头像 李华
网站建设 2026/10/15 17:35:49

Python+Django自主在线学习系统:从项目拆解到部署上线全解析

如果你在课程设计季或者毕业季搜过项目资源&#xff0c;大概率见过这类标题&#xff1a;“基于PythonDjango的自主在线学习系统”&#xff0c;后面跟着“源码lw部署文档讲解”几个后缀。lw就是论文&#xff0c;整套东西指的是一份可以直接运行、可以直接写进设计文档的项目包。…

作者头像 李华
网站建设 2026/10/16 2:19:02

大规模单仓库多旅行商问题求解:鲸鱼迁徙算法与Matlab实现

1. 为什么死磕LS-SDMTSP&#xff1a;问题拆解与典型场景1.1 单仓库多旅行商问题的核心矛盾单仓库多旅行商问题&#xff08;SDMTSP&#xff09;字面上是经典TSP的"多玩家版本"&#xff1a;一个配送中心&#xff08;仓库&#xff09;&#xff0c;N个客户&#xff0c;M个…

作者头像 李华
网站建设 2026/10/16 0:41:23

SocketTool 调试实战:TCP/UDP 连接、Hex 收发与粘包处理避坑指南

简介&#xff1a;SocketTool是一款面向网络工程师、系统管理员及软件开发者的TCP通信测试工具&#xff0c;主要用于网络编程调试与服务器性能评估。它提供TCP连接与断开、自定义数据包收发、流量控制、通信日志记录以及多线程并发处理等能力&#xff0c;并配有图形化界面&#…

作者头像 李华