ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

互助学习小程序毕设:环境搭建到接口联调实战

互助学习小程序毕设:环境搭建到接口联调实战 简介这是一套基于微信小程序与Java后端开发的互助学习毕业设计资料包面向高校计算机专业毕业生及课程设计学习者可作为选题开题、系统开发与答辩汇报的完整参考。项目基于微信小程序开发工具、Java后端和MySQL数据库构建包含管理员端的学生管理、课程分类、课程信息与评价、学习计划以及小程序用户的注册登录、课程浏览、评价留言等核心功能。资料包内共有1306个文件压缩后约25.37MB文件类型覆盖png界面素材、js与java前后端代码、json配置文件、wxss与wxml小程序页面、sql数据库脚本及mp4演示视频另含install/run/build操作脚本和Eclipse项目文件便于一键部署运行。目前已有127人学习下载适合需要快速跑通完整项目并理解前后端交互逻辑的学习者。1. 互助学习小程序毕设源码包先别急着点 2-run.bat从网盘拉下来的毕业设计源码包十有八九不是解压就能跑。“微信小程序 Java 后端 MySQL”这套互助学习平台压缩包里全是main.css.bak、update-password.vue.bak、3-build.bat这类文件说明作者在自己机器上从开发到演示调试通了但直接换一台电脑大概率会卡在环境上。整个项目覆盖管理员后台和小程序用户端两套界面涉及课程管理、课程评价、学习计划、留言板和学习论坛是一个典型的“小程序 后端管理平台”结构。建议先别双击那两个 bat先理清工程角色再把数据库建起来最后才轮到启动脚本。适合做课程设计、毕业设计的学生参考也适合刚接触前后端分离开发的工程师拿来拆解练习。2. 工程结构识别与本地环境搭建2.1 从压缩包文件名反推项目架构压缩包里的文件后缀信息量很大。.vue.bak文件是 Vue 单文件组件的备份main.css.bak是全局样式IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak这类命名对应的都是管理后台的布局组件。也就是说管理员端不是一个纯 HTML 页面而是基于 Vue 的 SPA 单页应用常见做法是套用 vue-element-admin 这类脚手架后改出来的。.classpath和org.eclipse.wst.common.component是 Eclipse 工程文件.classpath记录源码路径和依赖 jarorg.eclipse.wst.common.component描述 Web 模块发布信息这两者的存在说明 Java 后端最初是 Eclipse 工程可能是 SSM 或 Spring Boot 结构。三个 bat 文件是理解启动流程的关键。编号规律是1-install.bat、2-run.bat、3-build.bat这是典型的 npm 生命周期脚本先安装依赖再启动开发服务最后构建生产包。不过要注意这套脚本只覆盖了 Vue 管理后台部分Java 后端和小程序端都需要单独启动。整个项目拓扑可以画成一句话微信小程序负责用户端交互Java 后端暴露 HTTP 接口MySQL 存业务数据Vue 页面负责管理员操作。打开压缩包后先别急着跑脚本第一步是找init.sql或xxx.sql文件通常放在db、sql、database目录下。与此同时去找application.yml、application.properties或db.properties里面写的是后端连接数据库的账号密码。如果压缩包里既没有 SQL 文件也没有配置文件就只能通过后端实体类反推表结构这个流程放到第 3 章讲。2.2 开发环境选型与安装顺序这套项目同时涉及小程序、Java、Vue、MySQL 四个技术面环境装错的概率很高。按依赖关系整理了一份清单组件用途版本参考JDK编译并运行 Java 后端JDK 1.8 最佳老工程在 JDK 11 上经常出现兼容问题MySQL业务数据存储5.7 最稳8.0 注意时区配置Navicat 或 DataGrip导入 .sql 并检查表数据任意版本Maven后端依赖管理3.6工程带 mvnw 时可用自带的Node.js运行 Vue 管理后台14 或 16 LTS微信开发者工具导入小程序项目最新稳定版即可这套项目的正确启动顺序是先建数据库再启动 Java 后端然后启动 Vue 管理后台最后用微信开发者工具打开小程序。如果顺序反了比如后端没起来就打开小程序小程序端会立刻报request:fail看起来像前端代码问题实际是后端没有监听端口。出问题的时候按这个顺序倒着排查是效率最高的做法。JDK 安装完成后要确认环境变量。Windows 下需要设置JAVA_HOME指向 JDK 安装目录再把%JAVA_HOME%\bin追加到Path。命令行输入java -version能输出版本信息才算通过。MySQL 安装包在初始化时会让设置 root 密码这个密码必须跟后端配置文件里的username/password保持一致否则启动后端时会报Access denied for user。2.3 三个 bat 脚本是在做什么1-install.bat、2-run.bat、3-build.bat大概率是这样一段内容:: 1-install.bat npm install --registryhttps://registry.npmmirror.com:: 2-run.bat npm run dev:: 3-build.bat npm run build:prod--registryhttps://registry.npmmirror.com是 npm 镜像源参数因为原始仓库的依赖下载速度可能会很慢手动指定国内镜像源是为了一次性装完所有依赖。npm run dev启动的是开发服务器默认监听http://localhost:9528vue-element-admin 模板默认端口这个端口用来在浏览器里打开管理后台页面。npm run build:prod会把后台代码打包成静态文件一般是部署到 Nginx 时才需要执行本地调试时完全不需要跑这一步。有些压缩包里的 bat 脚本还加了一层判断比如先检测 npm 是否安装再检测 node_modules 目录是否存在。如果看到脚本里同时存在start.bat和stop.bat说明作者用批处理封装了 Windows 下的完整生命周期。总之这三个 bat 管的是 Vue 管理后台这一侧Java 后端和小程序端还是得手动处理。2.4 后端与小程序端的启动流程先执行 SQL 导入再启动后端最后拉起小程序。假设后端是 Spring Boot 工程且没有引入 Maven Wrapper命令如下# 1. 导入数据库表结构和初始数据 mysql -uroot -p init.sql # 2. 启动 Java 后端进入后端根目录后执行 mvn spring-boot:run # 3. 验证接口是否可用 curl http://localhost:8080/api/course/list?page1size10 # 4. 启动 Vue 管理后台进入 admin 或 web 目录后执行 npm run devmysql -uroot -p init.sql会读取init.sql文件并逐条执行其中的 SQL 语句完成建库建表。mvn spring-boot:run先拉取 pom 里声明的依赖再编译源码并启动内嵌 Tomcat 服务端口默认是8080。curl是验证接口最快的方式只要这个请求有 JSON 返回后端就说明没问题接下来才轮到小程序对接。小程序端不需要命令行启动。打开微信开发者工具点击导入项目选择压缩包里小程序对应的根目录目录下必须有app.js、app.json、pages目录。导入时的 AppID 建议先用测试号等联调完再替换成正式 AppID。真机预览需要小程序的 AppID 和后端接口域名配置本地调试阶段勾选开发者工具里的“不校验合法域名”即可。3. 数据库设计与课程/学习计划核心表拆解3.1 互助学习业务的数据表映射管理员端功能有个人中心、学生管理、课程分类管理、课程信息管理、课程评价管理、学习计划管理、留言板管理、学习论坛、系统管理等小程序用户端有注册登录、查看课程、课程评价、指定学习计划、留言。把这些业务动作翻译成数据表至少需要以下这些核心表表名业务含义代表字段sys_user 或 student学生/用户主体id、nickname、mobile、password、avatarcourse_category课程分类id、category_name、sort、statuscourse_info课程信息id、category_id、course_name、cover、introduction、teachercourse_comment课程评价id、course_id、user_id、content、score、create_timestudy_plan学习计划id、user_id、course_id、plan_date、today_task、statusmessage_board留言板id、user_id、content、reply、create_timeforum_post学习论坛帖子id、user_id、title、content、create_timeforum_reply帖子回复id、post_id、user_id、content、create_timesys_admin管理员id、username、password、role字段设计上有几个通用约定。主键全部用自增id可以省掉很多分页查询的复杂度密码字段不要存明文即使只是毕设也应该至少用 MD5 加盐create_time用datetime类型Java 侧用Date接收status字段统一用0/1表示禁用和启用不要用字符串。3.2 核心建表语句提取课程和学习计划这两条主线建表语句大致如下-- 课程分类表 CREATE TABLE course_category ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 分类ID, category_name VARCHAR(50) NOT NULL COMMENT 分类名称, sort INT DEFAULT 0 COMMENT 排序值越小越靠前, status TINYINT DEFAULT 1 COMMENT 状态1启用 0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程信息表 CREATE TABLE course_info ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 课程ID, category_id INT NOT NULL COMMENT 所属分类ID, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, cover VARCHAR(255) COMMENT 封面图, introduction TEXT COMMENT 课程简介, teacher VARCHAR(50) COMMENT 授课老师, click_count INT DEFAULT 0 COMMENT 浏览数, status TINYINT DEFAULT 1 COMMENT 状态1上架 0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程评价表 CREATE TABLE course_comment ( id INT PRIMARY KEY AUTO_INCREMENT, course_id INT NOT NULL COMMENT 课程ID, user_id INT NOT NULL COMMENT 评价人ID, content VARCHAR(500) COMMENT 评价内容, score TINYINT DEFAULT 5 COMMENT 评分1-5, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 学习计划表 CREATE TABLE study_plan ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL COMMENT 学生ID, course_id INT NOT NULL COMMENT 关联课程ID, plan_date DATE NOT NULL COMMENT 计划日期, today_task VARCHAR(500) COMMENT 今日任务, status TINYINT DEFAULT 0 COMMENT 状态0未完成 1已完成, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );AUTO_INCREMENT让数据库自动维护主键避免业务代码手动生成 ID 带来的冲突。DEFAULT CURRENT_TIMESTAMP在插入数据时自动填充当前时间少写一层 Java 代码。status字段在课程表里管上下架在学习计划表里管完成状态虽然都叫 status但含义随表不同而变化后端 VO 层需要单独翻译。score TINYINT DEFAULT 5限制了评分为 1 到 5 的整数数据库层面先挡住一部分非法数据。3.3 表关联关系与冗余字段取舍课程评价表course_comment通过course_id关联course_info通过user_id关联学生表这是一对多关系中的多表。学习计划表study_plan同时关联了user_id和course_id学生表与课程表之间通过这张表形成多对多关系。设计时比较关键的一个取舍是课程信息表里冗余一个category_name还是在查询时 join 分类表。毕设场景下我建议直接冗余。小程序首页要展示课程列表和分类名称如果每次查询都 joincourse_category代码写起来复杂分页 count 查询还要额外处理关联去重。直接在course_info里加category_name后台维护课程信息时同步写入虽然违反了严格意义上的第三范式但对这类业务量极小的项目来说查询性能和代码简洁度都是收益更大的这也是很多课程设计源码里实际采用的做法。论坛和留言板可看作同构的表结构一张主表记录内容一张子表记录回复。forum_post负责帖子主题forum_reply通过post_id关联回主表。留言板更简单一条记录就是一条留言管理员回复时更新reply字段即可不需要单独建回复表。3.4 从课程列表到评价的数据流整套系统最常被查询的链路是小程序用户进入首页 → 查看课程列表 → 点击课程详情 → 查看历史评价 → 发表新评价。这个过程涉及两次查询、一次插入。课程列表查询示例SELECT id, category_name, course_name, cover, introduction, teacher, click_count FROM course_info WHERE status 1 ORDER BY create_time DESC LIMIT #{offset}, #{pageSize};status 1表示只展示上架课程ORDER BY create_time DESC让最新课程排前面LIMIT分页。页面端传入页码和每页条数后端用offset (pageNum - 1) * pageSize换算偏移量。点击详情时执行UPDATE course_info SET click_count click_count 1 WHERE id #{id}浏览量直接用 SQL 自增避免先查后改的并发覆盖问题。评价插入走的是另一条链路前端校验打分和内容非空后端接收course_id、user_id、content、score后先检查课程是否存在、是否上架再执行 insert。这套流程能完整覆盖管理员端的课程评价管理和小程序端的课程评价展示两张表配合起来就是互助学习平台里最核心的课程模块数据流。4. 小程序与 Java 后端的接口对接与联调4.1 小程序端请求封装微信小程序原生环境没有 axios官方提供的wx.request每次写起来都比较啰嗦。常见做法是在utils/request.js里封装一个 Promise 风格的请求方法统一处理基础路径、token 和错误码const BASE_URL http://localhost:8080/api function request({ url, method GET, data {} }) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method, data, header: { Content-Type: application/json, token: wx.getStorageSync(token) || }, success(res) { // 后端统一返回 { code, message, data } if (res.data.code 200) { resolve(res.data.data) } else if (res.data.code 401) { // 登录过期跳转登录页 wx.removeStorageSync(token) wx.navigateTo({ url: /pages/login/login }) reject(res.data) } else { wx.showToast({ title: res.data.message, icon: none }) reject(res.data) } }, fail(err) { wx.showToast({ title: 网络请求失败, icon: none }) reject(err) } }) }) } module.exports { request }BASE_URL是后端接口的根路径本地调试时用localhost:8080真机预览时必须改成局域网 IP 或线上域名。header里塞 token 是前后端分离项目最常见的认证方式登录成功后后端返回 token小程序存进 storage后续每个请求自动带上。wx.request的success回调代表网络层已经拿到响应但业务上可能还是失败所以里层再根据code做二次判断fail回调则是网络层就挂了比如后端没启动、域名未配置、请求超时。4.2 后端接口的返回结构与跨域配置后端接口如果各自返回不同结构前端就没法统一处理。毕设项目里一般定义一个统一返回体Java 侧大致长这样RestController RequestMapping(/api/course) public class CourseController { GetMapping(/list) public ResultPageResultCourseVO list(RequestParam Integer page, RequestParam Integer size, RequestParam(required false) Long categoryId) { PageResultCourseVO pageResult courseService.queryPage(page, size, categoryId); return Result.ok(pageResult); } }public class ResultT { private Integer code; // 200 成功500 业务异常401 未登录 private String message; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.code 200; r.message success; r.data data; return r; } }RestController表示所有方法的返回值都会用 JSON 序列化后写回响应体不需要再手动转 JSON。RequestParam拿的是 URL 上的查询参数required false表示 categoryId 可以不传用于首页不分类筛选的场景。其实不管小程序端还是 Vue 管理后台都通过同一个Result结构通信前端只认code 200这一个成功标记。跨域问题在小程序端不严重因为小程序不是浏览器环境没有同源策略限制。但 Vue 管理后台跑在http://localhost:9528后端在8080浏览器会拦截跨域请求后端需要加跨域配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }addMapping(/**)匹配所有路径allowedOriginPatterns(*)允许任意来源访问allowCredentials(true)允许携带 Cookie 和认证信息。注意allowedOriginPatterns(*)与allowCredentials(true)必须搭配使用只写allowedOrigins(*)在较新版本 Spring Boot 里会报错。4.3 分页查询的前后端联调课程列表页是小程序最核心的页面用户下拉加载第二页前端这样调// 分页页码和每页条数 let page 1 let pageSize 5 async function loadCourseList(categoryId) { const data await request({ url: /course/list, method: GET, data: { page: page, size: pageSize, categoryId: categoryId } }) // data 为后端返回的 PageResult 对象 this.setData({ courseList: data.list, total: data.total, hasMore: data.total page * pageSize }) }page和size控制分页维度categoryId可选传了就筛选对应分类。后端返回的PageResult一般包含list当前页数据、total总条数、pageNum当前页码、pageSize每页条数四个字段前端拿到total之后就能计算hasMore决定是否继续触发加载下一页。Java 侧如果用 MyBatis PageHelper分页语句会被自动拦截改写Service 层代码很简洁Service public class CourseServiceImpl implements CourseService { Autowired private CourseMapper courseMapper; Override public PageResultCourseVO queryPage(int pageNum, int pageSize, Long categoryId) { PageHelper.startPage(pageNum, pageSize); Example example new Example(CourseInfo.class); if (categoryId ! null) { example.createCriteria().andEqualTo(categoryId, categoryId); } ListCourseInfo list courseMapper.selectByExample(example); PageInfoCourseInfo pageInfo new PageInfo(list); // 转换成 CourseVO 时补充分类名称等冗余字段 ListCourseVO voList list.stream().map(CourseVO::fromEntity).collect(Collectors.toList()); return new PageResult(voList, pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPageSize()); } }PageHelper.startPage(pageNum, pageSize)只对接下来执行的第一条 MyBatis 查询生效所以必须在selectByExample之前调用而且一个线程内不能连续 startPage 多次。PageInfo里封装好 total 总数分页 SQL 会被自动追加上LIMIT不需要手写。转换 VO 的目的一方面是过滤密码等敏感字段另一方面把数据库字段名翻译成前端想要的驼峰格式避免小程序里出现一堆下划线字段。4.4 后端接口与小程序渲染的对应关系联调阶段最容易踩的坑是数据结构不匹配。后端返回数据长这样{ code: 200, message: success, data: { list: [ { courseId: 1, courseName: Java 后端基础, categoryName: 后端开发, teacher: 王老师, cover: http://localhost:8080/upload/xxx.png } ], total: 23, pageNum: 1, pageSize: 5 } }小程序端使用wx:for渲染列表时data对象已经由封装层解包直接对list做循环封面图字段要和后端返回的cover一致不能写成coverUrl否则图片会一直加载失败。图片地址如果是完整 URL小程序端可以直接渲染如果是相对路径/upload/xxx.png前端拼接时必须以BASE_URL开头即http://localhost:8080/upload/xxx.png。联调过程中一旦发现字段对不上优先查后端 VO 的字段命名而不是在前端到处做映射因为每层映射都会增加后续维护成本。5. 本地复现排错与小程序接口调试技巧5.1 启动顺序与常见报错对照把这套源码包在原机器上能跑、换到另一台机器就报错的场景拆开几十个问题其实都能归到下面几类报错现象根因处理方式Access denied for user rootlocalhost后端配置的数据库密码和 MySQL 实际密码不一致修改application.yml中的 password 字段Unknown database study_help数据库没建或初始化脚本没执行用 Navicat 执行源码包里的 .sql 文件Port 8080 was already in use后端端口被其他进程占用改server.port或关掉占用进程java.sql.SQLException: The server time zone valueMySQL 8.x 时区问题连接串追加serverTimezoneAsia/Shanghai小程序请求一直request:fail后端没启动或域名不合法先 curl 后端接口再勾选“不校验合法域名”npm install卡住不动npm 官方镜像源慢执行安装时加--registryhttps://registry.npmmirror.comVue 后台请求 404后端接口路径和前端封装的 baseURL 不一致打开浏览器 Network 看实际请求路径这里最值得单独说的是“先 curl 再排错”。小程序端报网络错误时先用命令行验证后端接口是否真的通curl -X GET http://localhost:8080/api/course/list?page1size10如果这一步有 JSON 返回问题基本锁定在小程序侧重点检查“不校验合法域名”开关、BASE_URL 是否为http://localhost:8080。如果这一步就失败则检查后端控制台日志看是端口没监听、数据库连接失败还是接口路径匹配不到。5.2 小程序开发者工具的网络面板与过滤技巧微信开发者工具的 Network 面板能直接看到小程序的每个请求比用抓包工具更省事。打开后过滤条件选XHR查看某个请求的Status Code、Respinse里的 JSON 内容。联调时最常用的两个排查操作是修改BASE_URL后点工具栏的重置缓存避免旧的请求结果留在内存里打开详情页之前先Clear网络面板观察完整的请求链路。另一个实用技巧是条件编译不同环境。开发环境、测试环境、生产环境的接口域名不一样我在毕设项目中一般会在config/index.js里维护三个环境变量const ENV dev const BASE_URL_MAP { dev: http://localhost:8080/api, test: https://test.example.com/api, prod: https://api.example.com/api } module.exports { BASE_URL: BASE_URL_MAP[ENV] }小程序端所有wx.request的工具函数都从config里读BASE_URL换环境只改ENV一个变量即可。真机预览时切记不能再用localhost因为手机访问的是你电脑的局域网 IP改成http://192.168.x.x:8080/api同时后端要监听0.0.0.0而不是默认的127.0.0.1否则局域网其他设备无法访问。最后再用页面下拉刷新验证一次分页参数是否符合预期整个从源码包到本地可跑的流程就闭环了。本文还有配套的精品资源点击获取
返回列表