ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信小程序+Spring Boot打造在线刷题系统:全栈开发实战指南

2026/8/29 3:11:39 拓冰建站 浏览量
微信小程序+Spring Boot打造在线刷题系统:全栈开发实战指南 简介在线学习与测评系统是当前移动互联网和在线教育领域的热门应用其核心在于构建一个稳定、可扩展且用户体验良好的技术平台。从技术原理上看这类系统通常采用前后端分离架构前端负责交互与展示后端处理业务逻辑与数据持久化通过RESTful API进行通信。其技术价值在于能够高效地管理海量题库、实现智能组卷、实时判题与学习数据分析从而提升学习效率与个性化体验。在应用场景上广泛适用于高校教学、职业认证、企业内训等需要碎片化练习与即时反馈的场合。本文聚焦于使用微信小程序与Spring Boot框架实现一个完整的刷题系统详细阐述了从系统架构、数据库设计到前后端核心功能如用户登录、题库管理、智能组卷的实现过程并分享了在开发微信小程序和Spring Boot应用时关于性能优化、安全认证及常见问题排查的工程实践经验。1. 项目概述与核心价值最近几年无论是高校计算机专业的毕业设计还是企业内部的培训考核基于移动端的在线学习与测评系统需求都在激增。微信小程序凭借其无需安装、触手可及的便利性成为了承载这类轻量级应用的绝佳平台。而“基于微信小程序的刷题系统”这个项目恰好踩在了“移动学习”和“快速实践”两个痛点上。它不仅仅是一个简单的题目展示工具更是一个集用户管理、题库维护、智能组卷、实时练习与数据分析于一体的综合性学习平台。对于计算机相关专业的学生而言这个项目是一个极佳的毕业设计或课程设计选题。它技术栈清晰前端是当下热门的微信小程序后端是成熟稳健的Spring Boot框架数据库则通常选用MySQL。这个组合既能让你接触到前端移动开发、后端RESTful API设计、数据库建模等全栈技能又因为技术生态成熟社区资源丰富降低了学习和实现的难度。更重要的是它有一个非常明确的应用场景和价值输出——解决随时随地碎片化学习与练习的需求。无论是准备期末考试、职业资格认证还是公司内部的技能测评这样一个系统都能提供直接的支持。从实现角度看这个项目麻雀虽小五脏俱全。你需要考虑小程序的页面交互逻辑、与后端服务器的数据通信、用户登录态的维护、题库的增删改查、练习记录的持久化以及简单的数据统计。每一个环节都对应着软件开发中的核心知识点。完成这样一个项目不仅能让你对微信小程序和Spring Boot的开发流程有深刻理解更能锻炼你从需求分析、系统设计到编码实现、测试部署的完整项目能力。接下来我将以一个“过来人”的身份拆解这个项目的设计与实现全过程分享其中的技术选型思路、关键实现细节以及我踩过的那些“坑”。2. 系统整体架构与核心模块设计2.1 技术栈选型与架构思路为什么选择“微信小程序 Spring Boot MySQL”这个黄金组合这是经过实践检验的、最适合毕业设计或中小型项目的方案。微信小程序解决了跨平台iOS/Android和分发的问题用户扫码即用体验流畅。Spring Boot作为后端的“脚手架”极大地简化了Spring应用的初始搭建和开发过程通过自动配置和起步依赖让你能快速构建出可独立运行的、生产级的应用。MySQL则是关系型数据库中的老牌劲旅事务支持完善社区活跃对于题库、用户、记录这类结构化数据的管理游刃有余。整个系统采用经典的前后端分离架构。小程序端负责所有UI渲染和用户交互通过微信内置的wx.requestAPI调用后端提供的RESTful接口。后端Spring Boot应用则专注于业务逻辑处理、数据持久化和接口提供。两者之间通过JSON格式进行数据交换。这种架构的优点是职责清晰前后端可以并行开发且易于后续扩展和维护。注意在项目初期务必明确前后端的接口契约。我强烈建议使用Swagger或类似的API文档工具来自动生成和展示接口文档。在Spring Boot中集成springfox-swagger2和springfox-swagger-ui非常方便这能极大减少前后端联调时的沟通成本也是毕业答辩时展示项目规范性的一个亮点。2.2 核心功能模块拆解一个完整的刷题系统通常包含以下核心模块每个模块都对应着不同的技术实现重点用户模块这是系统的门户。除了基础的注册、登录通常与微信开放平台登录集成、个人信息管理外更重要的是用户角色划分。通常至少需要“普通用户学员”和“管理员”两种角色。学员可以看到题库、进行练习、查看历史管理员则拥有题库管理、用户管理、数据统计等权限。这里会涉及到Spring Security或Shiro这样的安全框架进行权限控制对于毕业设计使用简单的基于角色的拦截器也能满足需求。题库管理模块这是系统的核心数据层。题目本身的设计就很有讲究。一个题目实体Question通常包含题目ID、题干、题目类型单选、多选、判断、填空、简答等、选项集合对于选择题、正确答案、解析、所属分类/知识点、难度等级、创建时间等属性。特别是题目类型它直接影响前端如何渲染题目以及后端如何校验答案。设计数据库表时可以考虑将固定选项的题目单选、多选的选项存储为JSON字符串或者单独设计一个option表。对于填空题可能需要存储多个填空位的答案。练习与测评模块这是主要的业务逻辑层。功能包括随机组卷/按条件组卷根据知识点、难度、题型等条件从题库中随机抽取题目生成一套试卷。这里涉及到复杂的SQL查询或使用JPA的Specification动态构建查询条件。答题与交卷前端实时记录用户的答案选择提交时将所有题目ID和对应答案列表传到后端。后端需要逐题校验并计算得分。实时反馈与历史记录交卷后立即显示分数、正确答案和解析。同时将本次练习的试卷快照、用户答案、得分等详细信息持久化到“练习记录”表中供用户回顾。数据统计模块这个模块能为系统增添不少价值。可以统计用户的总体练习情况练习次数、平均分、总耗时、在各个知识点上的正确率分布、以及错题本自动收集所有答错的题目。这些数据不仅能帮助用户查漏补缺也是管理员评估题库质量和用户学习效果的重要依据。实现上主要依赖于对“练习记录”和“答题详情”表的聚合查询。3. 数据库设计与关键表结构解析数据库设计是系统的基石设计得好后续开发事半功倍。下面给出几个核心表的设计思路这并非唯一标准但经过了实践检验。用户表 (user)CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, openid varchar(100) DEFAULT NULL COMMENT 微信开放平台唯一标识, username varchar(50) DEFAULT NULL COMMENT 用户名, nickname varchar(100) DEFAULT NULL COMMENT 微信昵称, avatar_url varchar(500) DEFAULT NULL COMMENT 微信头像, role varchar(20) DEFAULT USER COMMENT 角色USER-学员 ADMIN-管理员, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) -- 确保一个微信用户对应一个系统账户 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;实操心得openid字段至关重要它是小程序用户登录你后台系统的唯一凭证。通过调用wx.login()获取code再传到你的后端服务器后端用code、appid和secret去微信接口服务端换取openid和session_key。这个过程一定要在后端完成appsecret绝不能泄露到小程序端。题目表 (question)CREATE TABLE question ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 题目ID, type tinyint(4) NOT NULL COMMENT 题型1-单选2-多选3-判断4-填空5-简答, content text NOT NULL COMMENT 题干, options json DEFAULT NULL COMMENT 选项JSON数组如[A.选项1,B.选项2]适用于单选/多选, answer text NOT NULL COMMENT 正确答案。单选/判断存选项键如“A”或答案如“对”多选存逗号分隔的选项键如“A,B,C”填空/简答存参考答案文本, analysis text DEFAULT NULL COMMENT 题目解析, category_id bigint(20) DEFAULT NULL COMMENT 所属分类/知识点ID, difficulty tinyint(4) DEFAULT 1 COMMENT 难度1-简单2-中等3-困难, creator_id bigint(20) DEFAULT NULL COMMENT 创建者ID, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_creator (creator_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目表;注意事项options字段使用JSON类型可以灵活存储不定长的选项方便前端直接解析渲染。但对于复杂查询如搜索包含某个文本的选项JSON查询效率可能不高。如果选项结构固定且查询需求简单也可以用多个varchar字段option_a,option_b...来存储。answer字段的设计是难点需要根据不同的type来设计存储格式和校验逻辑。练习记录表 (practice_record)CREATE TABLE practice_record ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 记录ID, user_id bigint(20) NOT NULL COMMENT 用户ID, total_questions int(11) NOT NULL COMMENT 题目总数, correct_count int(11) NOT NULL COMMENT 答对题数, total_score decimal(5,2) NOT NULL COMMENT 总得分, time_spent int(11) DEFAULT NULL COMMENT 耗时秒, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 练习时间, PRIMARY KEY (id), KEY idx_user (user_id), KEY idx_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT练习记录主表;答题详情表 (practice_detail)CREATE TABLE practice_record_detail ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 详情ID, record_id bigint(20) NOT NULL COMMENT 关联的练习记录ID, question_id bigint(20) NOT NULL COMMENT 题目ID, user_answer text DEFAULT NULL COMMENT 用户答案, is_correct tinyint(1) DEFAULT NULL COMMENT 是否正确0-错1-对, score decimal(5,2) DEFAULT NULL COMMENT 本题得分, PRIMARY KEY (id), KEY idx_record (record_id), KEY idx_question (question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT答题详情表;核心解析这里采用了主表-详情表的结构。practice_record存储一次练习的概要信息总分、耗时practice_record_detail则存储这次练习中每一道题的具体答题情况。这种设计避免了在practice_record中存储复杂的数组或JSON符合数据库第一范式也便于后续针对单题进行统计分析比如生成错题本SELECT DISTINCT question_id FROM practice_record_detail WHERE is_correct 0 AND user_id ?。4. Spring Boot后端核心实现详解4.1 项目初始化与基础配置使用Spring Initializr或IDE中的Spring Boot创建向导快速生成项目。依赖选择上核心需要Spring Web用于构建RESTful API、Spring Data JPA用于数据持久化简化数据库操作、MySQL Driver数据库连接。为了开发方便还可以加上Lombok减少样板代码、Spring Boot DevTools热部署。在application.yml或application.properties中配置数据库连接、JPA属性以及服务器端口server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/question_bank?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 项目初期使用update自动创建表生产环境务必改为validate或none show-sql: true # 开发时显示SQL便于调试 properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect format_sql: true4.2 实体类(Entity)与数据访问层(Repository)设计遵循JPA规范首先创建与数据库表对应的实体类。以Question实体为例Entity Table(name question) Data // Lombok注解自动生成getter, setter, toString等 NoArgsConstructor AllArgsConstructor public class Question { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Enumerated(EnumType.ORDINAL) // 将枚举类型存储为序号1,2,3... private QuestionType type; // 枚举类SINGLE_CHOICE, MULTI_CHOICE, TRUE_FALSE, FILL_BLANK, SHORT_ANSWER Column(columnDefinition TEXT) private String content; // 题干 Column(columnDefinition JSON) private String options; // JSON字符串如 [A.选项1, B.选项2] Column(columnDefinition TEXT) private String answer; // 正确答案 Column(columnDefinition TEXT) private String analysis; // 解析 private Integer difficulty; // 1-简单2-中等3-困难 ManyToOne JoinColumn(name category_id) private Category category; // 关联分类实体 ManyToOne JoinColumn(name creator_id) private User creator; // 关联用户实体创建者 private LocalDateTime createTime; }对应的Repository接口非常简单继承JpaRepository即可获得基础的CRUD方法Repository public interface QuestionRepository extends JpaRepositoryQuestion, Long { // 自定义查询根据分类查找题目 ListQuestion findByCategoryId(Long categoryId); // 自定义查询随机获取指定数量的题目用于组卷 Query(value SELECT * FROM question WHERE category_id :categoryId ORDER BY RAND() LIMIT :limit, nativeQuery true) ListQuestion findRandomQuestionsByCategory(Param(categoryId) Long categoryId, Param(limit) int limit); }踩坑记录ORDER BY RAND()在数据量大的时候性能极差因为它会对全表进行随机排序。对于生产环境如果题库很大需要考虑更高效的随机抽样算法比如先获取最大ID再在应用层生成随机ID范围进行查询。4.3 业务逻辑层(Service)与控制器层(Controller)实现Service层负责核心业务逻辑。例如组卷和判卷的逻辑就在这里。Service RequiredArgsConstructor // Lombok注解为final字段生成构造函数 public class PracticeService { private final QuestionRepository questionRepository; private final PracticeRecordRepository recordRepository; private final PracticeDetailRepository detailRepository; /** * 根据条件生成一套练习试卷 */ public ListQuestion generatePaper(Long categoryId, Integer difficulty, Integer count) { // 构建动态查询条件这里使用JPA Specification或QueryDSL更优雅 // 简化示例随机获取题目 return questionRepository.findRandomQuestionsByCategory(categoryId, count); } /** * 提交试卷并判分 */ public PracticeRecord submitPaper(Long userId, PaperSubmitDTO submitDTO) { // 1. 创建练习记录主表 PracticeRecord record new PracticeRecord(); record.setUserId(userId); record.setTotalQuestions(submitDTO.getQuestionAnswers().size()); record.setCreateTime(LocalDateTime.now()); int correctCount 0; BigDecimal totalScore BigDecimal.ZERO; ListPracticeDetail details new ArrayList(); // 2. 遍历用户提交的每一题答案 for (AnswerDTO answer : submitDTO.getQuestionAnswers()) { Question question questionRepository.findById(answer.getQuestionId()).orElseThrow(); PracticeDetail detail new PracticeDetail(); detail.setRecord(record); detail.setQuestion(question); detail.setUserAnswer(answer.getAnswer()); // 3. 调用判题逻辑 GradingResult result gradeAnswer(question, answer.getAnswer()); detail.setCorrect(result.isCorrect()); detail.setScore(result.getScore()); if (result.isCorrect()) { correctCount; } totalScore totalScore.add(result.getScore()); details.add(detail); } // 4. 更新主表统计信息并保存 record.setCorrectCount(correctCount); record.setTotalScore(totalScore); record.setTimeSpent(submitDTO.getTimeSpent()); recordRepository.save(record); // 先保存主表生成ID detailRepository.saveAll(details); // 再保存详情表关联主表ID return record; } private GradingResult gradeAnswer(Question question, String userAnswer) { // 根据question.getType()进行不同类型的判题 // 例如单选题直接比较字符串多选题需要分割字符串后比较集合填空题可能涉及模糊匹配等。 // 这是一个复杂的核心函数需要仔细实现。 // 返回一个包含是否正确、得分的对象。 return new GradingResult(); } }Controller层则负责接收HTTP请求调用Service并返回JSON响应。RestController RequestMapping(/api/practice) RequiredArgsConstructor public class PracticeController { private final PracticeService practiceService; PostMapping(/generate) public ResultListQuestionVO generatePaper(RequestBody PaperGenerateRequest request) { // 参数校验 // 调用Service生成试卷 ListQuestion questions practiceService.generatePaper(request.getCategoryId(), request.getDifficulty(), request.getCount()); // 转换为前端需要的VO对象可能隐藏答案等敏感信息 ListQuestionVO questionVOs questions.stream().map(QuestionVO::fromEntity).collect(Collectors.toList()); return Result.success(questionVOs); } PostMapping(/submit) public ResultPracticeRecordVO submitPaper(CurrentUser Long userId, RequestBody PaperSubmitDTO submitDTO) { PracticeRecord record practiceService.submitPaper(userId, submitDTO); return Result.success(PracticeRecordVO.fromEntity(record)); } }核心技巧使用CurrentUser这样的自定义注解配合Spring拦截器可以从请求头如Token中解析出当前登录用户的ID并自动注入到Controller方法参数中避免在每个方法里重复编写获取用户信息的代码。这是实现优雅权限控制的关键一步。5. 微信小程序前端核心功能实现5.1 项目初始化与页面规划使用微信开发者工具创建小程序项目。页面结构可以规划如下pages/index/index: 首页展示分类、热门题库、练习入口。pages/practice/practice: 练习/考试页面核心交互页面。pages/result/result: 练习结果展示页面。pages/me/me: 个人中心展示练习历史、错题本、个人信息。pages/admin/question-list: 管理员题目列表页需权限。pages/admin/question-edit: 管理员题目编辑/新增页。在app.json中配置这些页面路径和窗口样式。使用微信小程序的tabBar配置底部导航栏将首页和个人中心设为常驻Tab。5.2 用户登录与状态管理小程序端的用户登录流程是第一个关键点。在app.js的onLaunch中调用wx.login()获取临时登录凭证code。将code发送到你自己的Spring Boot后端登录接口。后端用code、appid、appsecret调用微信接口服务换取openid和session_key。后端根据openid判断用户是否存在若不存在则创建新用户。然后生成一个自定义的登录态令牌如JWT返回给小程序。小程序将令牌存储在本地如wx.setStorageSync(token, token)并在后续所有需要认证的请求的header中带上Authorization: Bearer token。// 在app.js中封装登录方法 login() { return new Promise((resolve, reject) { wx.login({ success: (res) { if (res.code) { wx.request({ url: https://your-domain.com/api/auth/login, method: POST, data: { code: res.code }, success: (resp) { if (resp.data.code 200) { const token resp.data.data.token; wx.setStorageSync(token, token); resolve(token); } else { reject(new Error(登录失败)); } }, fail: reject }); } else { reject(new Error(获取code失败)); } }, fail: reject }); }); }安全警告session_key是敏感信息必须保存在后端绝不能传到小程序端。小程序端只存储你自己服务器颁发的令牌Token。5.3 练习页面交互与数据绑定练习页面(practice.wxml)是交互最复杂的地方。需要动态渲染不同类型的题目。!-- practice.wxml -- view wx:for{{questionList}} wx:keyid>// practice.js Page({ data: { questionList: [], userAnswers: {}, // 格式{ questionId1: A, questionId2: [A,C], questionId3: 我的答案 } }, onLoad(options) { // 加载试卷 this.loadPaper(options.paperId); }, // 单选题选择 onRadioChange(e) { const qid e.currentTarget.dataset.qid; const value e.detail.value; this.setData({ [userAnswers.${qid}]: value }); }, // 多选题选择 onCheckboxChange(e) { const qid e.currentTarget.dataset.qid; const value e.detail.value.sort(); // 多选题答案排序后存储便于比较 this.setData({ [userAnswers.${qid}]: value }); }, // 填空题输入 onFillInput(e) { const qid e.currentTarget.dataset.qid; const value e.detail.value; this.setData({ [userAnswers.${qid}]: value }); }, // 提交试卷 submitPaper() { const answers []; for (const [qid, answer] of Object.entries(this.data.userAnswers)) { answers.push({ questionId: parseInt(qid), answer: answer }); } const submitData { questionAnswers: answers, timeSpent: this.calculateTimeSpent() // 计算耗时 }; // 调用后端提交接口 wx.request({ url: https://your-domain.com/api/practice/submit, method: POST, header: { Authorization: Bearer ${wx.getStorageSync(token)} }, data: submitData, success: (res) { wx.redirectTo({ url: /pages/result/result?recordId${res.data.data.id} }); } }); } });性能优化点在userAnswers中使用对象存储键是题目ID值是用户答案。这样在更新任意一题的答案时可以使用this.setData({ [userAnswers.${qid}]: value })进行精准更新避免因setData数据量过大导致页面渲染卡顿。这是小程序开发中一个重要的性能优化技巧。6. 项目部署、测试与常见问题排查6.1 后端项目打包与部署开发完成后需要将Spring Boot项目部署到服务器。首先进行打包# 在项目根目录下使用Maven打包跳过测试 mvn clean package -DskipTests打包后会生成一个target/your-project-name-0.0.1-SNAPSHOT.jar文件。这个jar包是可直接运行的“fat jar”包含了所有依赖。部署到Linux服务器以CentOS为例的常用方式使用scp命令或FTP工具将jar包上传到服务器。在服务器上运行java -jar your-project-name-0.0.1-SNAPSHOT.jar。但这样关闭终端后进程会结束。推荐使用nohup在后台运行nohup java -jar your-project-name.jar app.log 21 。这样日志会输出到app.log文件。更生产化的做法是将其配置为系统服务systemd service实现开机自启和便捷的启停管理。部署踩坑务必确保服务器上的Java版本与本地开发环境一致。检查java -version。另外如果后端需要连接本地数据库记得将application.yml中的数据库地址localhost改为服务器的内网IP或域名并确保服务器的防火墙开放了数据库端口通常是3306。6.2 微信小程序上线前配置小程序上线前需要在微信公众平台进行关键配置服务器域名配置在“开发管理” - “开发设置” - “服务器域名”中将你的Spring Boot后端API的域名如https://api.yourdomain.com添加到request合法域名列表中。必须是HTTPS协议。业务域名配置如果需要如果小程序中使用了web-view组件需要配置业务域名。体验版设置上传代码后可以设置为体验版供指定微信用户预览测试。提交审核与发布测试无误后提交微信官方审核审核通过后即可发布。6.3 开发与联调常见问题实录在开发过程中你几乎一定会遇到以下问题这里给出排查思路问题1小程序端请求后端接口失败报错“不在以下 request 合法域名列表中”原因未在微信公众平台配置服务器域名或配置的域名与请求的域名不完全一致包括协议、端口。解决开发阶段在微信开发者工具中点击右上角“详情” - “本地设置” - 勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”。但这只是临时方案。上线前必须去公众平台配置准确的HTTPS域名。确保后端服务支持HTTPS。问题2后端接口返回数据但小程序端无法解析或渲染异常原因可能是跨域问题虽然小程序不严格受浏览器同源策略限制但某些header缺失会导致问题或者返回的数据格式不是JSON。解决在后端Spring Boot的Controller上添加CrossOrigin注解或配置全局的CORS过滤器允许小程序域名访问。确保后端Controller返回的是application/json类型的数据。Spring Boot的RestController默认会处理。在小程序开发者工具的“网络”面板中查看请求和响应的具体内容对比数据格式。问题3用户登录态失效或混乱原因Token过期、存储异常或并发登录导致。解决后端生成Token时设置合理的过期时间如7天。小程序端在每次wx.request的fail回调或后端返回特定状态码如401时判断为登录失效重新执行登录流程。对于管理员等敏感操作可以考虑使用双Token机制Access Token Refresh Token。问题4小程序在真机上预览正常但在开发者工具上是白屏原因这是一个非常常见的问题通常与ES6语法兼容性或某些API在开发者工具中的模拟支持度有关。解决在project.config.json中检查libVersion是否设置得过高可以尝试降低基础库版本。在开发者工具中点击右上角“详情” - “本地设置” - 勾选“增强编译”它会将ES6/ES7语法进行转换。检查代码中是否使用了某些较新的API这些API可能在旧版开发者工具或低版本微信客户端中不支持。使用wx.canIUse()进行兼容性判断。问题5数据库连接池耗尽或慢查询原因随着用户量增加数据库连接不够用或者某些SQL查询未优化。解决在application.yml中调整Spring Boot数据源连接池如HikariCP的配置maximum-pool-size,connection-timeout等。为频繁查询的字段如question表的category_id,user_id建立索引。使用spring.jpa.show-sqltrue查看生成的SQL优化复杂的联表查询或N1查询问题可以使用EntityGraph注解或手动编写JOIN FETCH查询。完成这个项目你收获的将不仅仅是一份毕业设计的代码。你会对前后端协同开发、数据库设计、API设计、用户认证授权、以及微信小程序生态有了一次完整的、落地的实践。这套技术栈和架构思想完全可以复用到其他类似的移动端管理或学习类项目中。在编码之外更重要的是培养了你解决实际问题的能力从需求分析到线上部署这正是一名合格开发者所需要经历的全流程。本文还有配套的精品资源点击获取