☰
微信小程序+Java后端:校园服务平台毕业设计实战指南
2026/10/8 11:59:49 网站建设 项目流程

简介:这是一套面向高校计算机相关专业学生的毕业设计/课程设计完整方案,主题为微信小程序结合Java后端的校园服务平台,适合正在准备毕设、需要可运行项目参考或想练习前后端分离开发的学习者。资源包共1112个文件,约27.76MB,涵盖小程序端wxml、wxss、js与vue页面文件,Java后端源码,以及sql数据库脚本、json配置、png与svg图片素材和mp4演示视频,结构完整、层次清晰。功能上区分管理员、卖家与用户三类角色:管理员负责用户、校园公告、卖家、二手商品、订单信息与发货等后台管理;卖家可发布维护二手商品并处理订单发货;用户可注册登录、浏览公告、查询购买商品并管理个人订单。随包附演示视频与说明文档,便于快速理解业务流程、对照源码梳理数据库设计与接口逻辑,也可作为二次开发或答辩演示的基础模板。目前已有140人学习下载。

1. 校园服务平台从零到一:为什么微信小程序加 Java 后端是毕业设计里最稳的那条路

每年到了三四月,实验室里总有一批人对着选题发愁。想做点看得见摸得着的东西,又怕技术栈太偏、资料太少、答辩时被老师问穿。我带过几届本科毕设,也帮不少同学救过火,最后发现一个规律:微信小程序 + Java 后端这套组合,是计算机毕业设计里容错率最高的方案,没有之一。原因很实在——小程序端有官方开发者工具兜底,Java 后端有 Spring Boot 生态托底,两边都有海量可查的公开资料,你踩的坑大概率别人已经踩过并写了博客。

这篇笔记要拆解的,就是标题里那个.rar压缩包背后代表的完整工程:一个校园服务平台,前端跑在微信小程序里,后端用 Java 写接口,中间连着 MySQL 数据库,最后交付源码、演示视频、说明文档和数据库脚本。我会按真实开发顺序,把环境搭建、接口设计、数据库建模、联调排错、部署验收这几个环节讲透。适合正在做毕设、需要一套能跑通、能答辩、能写进论文的系统的人。读完你至少知道每一步该敲什么命令、参数怎么配、哪里最容易翻车。

2. 技术选型与工程骨架:为什么是 Spring Boot 而不是 Servlet

2.1 小程序端与 Java 后端的职责边界

先把架构说清楚,不然后面写代码容易乱。校园服务平台通常包含这些模块:用户登录与身份认证、校园资讯浏览、失物招领发布、二手交易信息、活动报名、消息通知。这些功能里,小程序负责展示和交互,Java 后端负责业务逻辑和数据持久化,两边通过 HTTPS 接口通信。

小程序端的优势在于免安装、即用即走,学生用户不需要下载 App,扫码或搜索就能进入。但小程序有包体积限制,主包不能超过 2MB,所以图片、视频这类静态资源必须放后端或对象存储,不能塞进小程序工程里。Java 后端的优势在于生态成熟,Spring Boot 把 Tomcat 内嵌了,打成一个 jar 包就能跑,不需要单独装 Web 服务器,这对毕设部署来说省了太多事。

我一般会这样划分:小程序端只做三件事——收集用户输入、调用接口、渲染返回数据。所有校验逻辑、权限判断、数据组装都放后端。这样做的好处是,答辩时老师问“你的业务逻辑在哪”,你可以明确指向后端 Service 层,而不是含糊地说“前后端都有”。

2.2 用 Spring Initializr 生成后端骨架

不要手动建 Maven 工程,容易漏依赖。直接用 Spring Initializr 生成,选好依赖再下载。下面是核心依赖的pom.xml片段,我保留了毕设最常用的几个:

<!-- Spring Boot Web:提供 REST 接口能力 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis-Plus:简化数据库操作,毕设够用 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- JWT:做登录令牌,比 Session 更适合小程序 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency>

这里解释几个关键选择。MyBatis-Plus 而不是原生 MyBatis,是因为它自带BaseMapper,单表增删改查不用写 XML,毕设时间紧,能省一半代码量。JWT 而不是 Session,是因为小程序没有浏览器 Cookie 机制,用 Token 放在请求头里更自然,后端也不用维护会话状态,重启服务不影响已登录用户。

数据库连接配置写在application.yml里,几个参数必须改:

spring: datasource: url: jdbc:mysql://localhost:3306/campus_service?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver

serverTimezone必须设成Asia/Shanghai,否则插入时间会差 8 小时,这个坑我见过太多人踩。characterEncoding=utf8保证中文不乱码,少了这个参数,失物招领里的物品描述可能变成问号。

2.3 小程序工程的目录结构与请求封装

小程序端用原生开发就行,不需要上 uni-app 或 Taro,毕设答辩时原生代码更直观。目录结构建议这样组织:

miniprogram/ ├── pages/ # 页面目录 │ ├── index/ # 首页 │ ├── login/ # 登录页 │ └── publish/ # 发布页 ├── utils/ │ └── request.js # 请求封装 ├── app.js └── app.json

请求封装是必须做的,不然每个页面都写一遍wx.request,改接口地址时会疯掉。下面是一个带 Token 自动携带和错误统一处理的封装:

// utils/request.js const BASE_URL = 'http://localhost:8080/api'; function request(options) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', // 从本地缓存取 Token,登录后存入 'Authorization': wx.getStorageSync('token') || '' }, success(res) { if (res.statusCode === 200) { resolve(res.data); } else if (res.statusCode === 401) { // Token 过期,跳回登录页 wx.navigateTo({ url: '/pages/login/login' }); reject(res.data); } else { wx.showToast({ title: res.data.msg || '请求失败', icon: 'none' }); reject(res.data); } }, fail(err) { wx.showToast({ title: '网络异常', icon: 'none' }); reject(err); } }); }); } module.exports = { request };

这段代码的逻辑是:所有请求走同一个函数,自动拼接基础地址、自动带 Token、自动处理 401 和网络错误。参数说明——BASE_URL在开发阶段指向本机 8080 端口,上线后改成服务器域名;Authorization头里放 JWT,后端拦截器会校验。注意小程序正式发布要求接口必须是 HTTPS 且域名已备案,但毕设演示阶段可以在开发者工具里勾选“不校验合法域名”来用 HTTP 调试。

3. 数据库建模与接口设计:把 ER 图变成能跑的 SQL

3.1 校园服务平台的核心表结构

数据库设计是毕设的重头戏,答辩老师最爱问“你这几张表什么关系”。校园服务平台至少需要这几张表:用户表、资讯表、失物招领表、二手物品表、活动表、报名记录表。下面给出用户表和失物招领表的建表 SQL,其他表结构类似:

-- 用户表:存微信 openid 和基本信息 CREATE TABLE `user` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `openid` VARCHAR(64) NOT NULL COMMENT '微信唯一标识', `nickname` VARCHAR(64) DEFAULT NULL COMMENT '昵称', `avatar` VARCHAR(255) DEFAULT NULL COMMENT '头像地址', `phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号', `role` TINYINT DEFAULT 0 COMMENT '0学生 1管理员', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 失物招领表:type 区分失物和招领 CREATE TABLE `lost_found` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `user_id` BIGINT NOT NULL COMMENT '发布人', `type` TINYINT NOT NULL COMMENT '0失物 1招领', `title` VARCHAR(100) NOT NULL, `description` TEXT, `images` VARCHAR(1000) COMMENT '图片URL,逗号分隔', `status` TINYINT DEFAULT 0 COMMENT '0进行中 1已完成', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='失物招领表';

几个设计要点。openid加了唯一索引,因为一个微信用户只能对应一条记录,重复插入会报错,这比在代码里查重更可靠。images字段用逗号分隔存多个图片 URL,毕设阶段够用,不用单独建图片表,省事。role字段区分学生和管理员,管理员可以删除违规帖子,这个权限控制在后端拦截器里做。

3.2 微信登录接口的实现与手机号获取

微信小程序的登录流程是毕设里最容易卡住的地方。核心逻辑是:小程序调wx.login拿到临时 code,传给后端,后端拿 code 去微信服务器换 openid,再生成 JWT 返回给小程序。下面是后端换 openid 的代码:

// 调用微信接口换取 openid public String getOpenid(String code) { String url = "https://api.weixin.qq.com/sns/jscode2session" + "?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code"; // 用 RestTemplate 发 GET 请求 ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); JSONObject json = JSON.parseObject(response.getBody()); // 返回 openid,如果失败会返回 errcode return json.getString("openid"); }

参数说明——appId和appSecret在小程序管理后台的“开发设置”里拿,不要硬编码在代码里,建议放配置文件。code只能用一次,五分钟内有效,所以不能缓存复用。如果返回的openid为空,先检查errcode,常见的是 40029(code 无效)和 45011(频率限制)。

获取手机号是另一个高频需求。小程序端用<button open-type="getPhoneNumber">触发,回调里拿到加密的encryptedData和iv,传给后端解密。后端用 AES 解密,密钥是session_key,这个值在换 openid 时一起返回,必须存在服务端,不能下发到小程序。解密代码网上很多,核心是AES/CBC/PKCS5Padding模式,这里不展开,但提醒一句:session_key会过期,解密失败先重新登录。

3.3 接口规范与统一返回格式

后端接口返回格式必须统一,不然小程序端处理起来很乱。我一般用这个结构:

{ "code": 200, "msg": "success", "data": {} }

code为 200 表示成功,401 表示未登录,500 表示服务器错误。data里放具体业务数据,列表接口就放数组,详情接口就放对象。后端用一个Result类包装:

public class Result<T> { private Integer code; private String msg; private T data; // 静态工厂方法,方便调用 public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.code = 200; r.msg = "success"; r.data = data; return r; } public static <T> Result<T> error(Integer code, String msg) { Result<T> r = new Result<>(); r.code = code; r.msg = msg; return r; } }

这样 Controller 里只需要return Result.success(list),代码干净。小程序端request.js里判断res.data.code === 200再取data,逻辑一致。

4. 前后端联调与部署:从本机跑通到服务器上线

4.1 本地联调的三个关键配置

本地联调阶段,小程序开发者工具和后端 IDEA 同时开着,最容易出的问题是请求发不出去。三个配置必须检查:

第一,小程序开发者工具右上角“详情”里,勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。不勾这个,http://localhost:8080的请求会被拦截。

第二,后端application.yml里配置跨域。虽然小程序请求不受浏览器同源策略限制,但如果你用浏览器测试接口,跨域会报错。加一个配置类:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true) .maxAge(3600); } }

第三,后端端口别用 80,本地容易冲突。8080 是默认值,如果被占用,在application.yml里改server.port。

4.2 用宝塔面板部署 Spring Boot 项目

毕设演示通常需要外网访问,把后端部署到云服务器是常见做法。用宝塔面板可以少敲很多命令。步骤是:先在服务器上装宝塔,然后在软件商店装 MySQL 和 Java 项目管理器。接着把本地打好的 jar 包上传到服务器,在 Java 项目管理器里添加项目,选 jar 包路径,填启动端口,点启动。

数据库导入用宝塔的 phpMyAdmin,新建一个库,把本地导出的.sql文件导入。注意本地和服务器上的数据库名、用户名、密码要一致,不一致就改application.yml再重新打包。

小程序端的BASE_URL改成服务器地址,比如https://你的域名/api。域名需要在小程序管理后台的“开发设置”里配置为 request 合法域名,且必须是 HTTPS。毕设阶段如果没有域名,可以用服务器 IP 加端口,但小程序正式版不支持 IP,所以演示时用开发者工具的“不校验域名”选项。

4.3 演示视频的录制与说明文档的整理

演示视频不是随便录屏就行。答辩老师看视频的时间有限,你要在 3 到 5 分钟内展示完整流程:登录、浏览资讯、发布失物招领、查看报名记录、管理员删除帖子。录制前把数据库清空,用真实数据走一遍,避免出现“暂无数据”的尴尬。

说明文档一般包括:项目简介、技术栈、环境要求、部署步骤、接口清单、数据库表说明。接口清单用表格列出来,比大段文字清楚。数据库表说明把每张表的字段、类型、含义列出来,答辩时老师翻文档就能看懂。

5. 避坑与排查:那些让我熬夜的常见问题

5.1 小程序请求返回 400 或 401

现象:小程序端调用接口,返回 400 Bad Request 或 401 Unauthorized。

原因:400 通常是请求参数格式不对,比如后端要 JSON,你传了表单;或者日期格式不匹配。401 是 Token 没带或已过期。

解决:先看后端控制台有没有异常堆栈,400 多半是HttpMessageNotReadableException。检查wx.request的header里Content-Type是不是application/json,data是不是对象而不是字符串。401 就检查Authorization头有没有取到 Token,Token 是不是在登录后存进了wx.setStorageSync。

5.2 数据库插入中文变成问号

现象:失物招领的标题在数据库里显示为???。

原因:数据库、表、连接三处的字符集不一致。常见的是建库时用了latin1,或者 JDBC URL 里没写characterEncoding=utf8。

解决:建库语句用CREATE DATABASE campus_service DEFAULT CHARSET utf8mb4;,建表也用utf8mb4。JDBC URL 加上useUnicode=true&characterEncoding=utf8。已经建好的表用ALTER TABLE lost_found CONVERT TO CHARACTER SET utf8mb4;转换。

5.3 时间字段差 8 小时

现象:小程序端显示发布时间是 8 小时前。

原因:MySQL 时区是 UTC,Java 取出来按 UTC 解析,但实际是北京时间。

解决:JDBC URL 加serverTimezone=Asia/Shanghai。如果还不对,在实体类的日期字段上加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")。

5.4 图片上传后小程序显示不出来

现象:发布失物招领时上传了图片,列表里图片裂开。

原因:图片存在服务器本地磁盘,但小程序访问的 URL 是http://localhost:8080/upload/xxx.jpg,真机上 localhost 指向手机自己。

解决:后端把图片存到服务器某个目录,通过静态资源映射暴露出去,返回的 URL 用服务器公网地址。或者用对象存储,毕设阶段用服务器本地存储加 Nginx 静态代理就行。

5.5 打包后 jar 运行报数据库连接失败

现象:本地 IDEA 跑得好好的,打成 jar 放到服务器上启动就报Access denied或Communications link failure。

原因:服务器上的 MySQL 没启动,或者用户名密码不对,或者防火墙没放行 3306 端口。

解决:先在服务器上用mysql -u root -p能登录,确认密码。然后检查application.yml里的密码和服务器一致。最后确认宝塔安全组和服务器防火墙都放行了 3306。如果 MySQL 只允许 localhost 连接,要改用户权限host为%。

6. 进阶技巧:让毕设从及格变成优秀

6.1 用拦截器做统一权限校验

基础版是在每个 Controller 方法里判断 Token,代码重复且容易漏。进阶做法是写一个HandlerInterceptor,在preHandle里统一校验 Token,把用户信息塞进ThreadLocal,Controller 里直接取。这样管理员接口只需要加一个@AdminOnly注解,拦截器里判断角色即可。代码量减少,答辩时也显得有设计感。

6.2 分页查询用 MyBatis-Plus 的 Page 对象

列表接口一定要分页,不然数据多了小程序卡死。MyBatis-Plus 的分页很简单:

Page<LostFound> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<LostFound> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(LostFound::getType, type) .orderByDesc(LostFound::getCreateTime); Page<LostFound> result = lostFoundMapper.selectPage(page, wrapper);

pageNum从 1 开始,pageSize建议 10 到 20。返回的result.getRecords()是当前页数据,result.getTotal()是总条数,小程序端做上拉加载时用得到。

6.3 用定时任务清理过期数据

失物招领和二手物品有“已完成”状态,但时间久了数据会堆积。加一个@Scheduled定时任务,每天凌晨把 30 天前已完成的数据归档或删除。这个功能写进论文的“系统优化”章节,比单纯增删改查有亮点。

@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行 public void cleanExpiredData() { LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30); lostFoundMapper.delete(new LambdaQueryWrapper<LostFound>() .eq(LostFound::getStatus, 1) .lt(LostFound::getCreateTime, thirtyDaysAgo)); }

cron表达式六位分别代表秒、分、时、日、月、周。0 0 3 * * ?就是每天 3 点整。注意启动类上要加@EnableScheduling注解,否则定时任务不生效。

6.4 答辩前必做的三项检查

第一,把数据库里的测试数据清掉,重新录入一批看起来真实的校园数据,比如“图书馆三楼捡到黑色钱包”“周六下午篮球赛报名”。第二,把小程序开发者工具里的“不校验域名”取消勾选,确认正式域名下所有接口都能通。第三,录一遍完整演示视频,自己看一遍,把卡顿和报错的地方修掉。

我自己的习惯是,答辩前一天晚上把整个流程走三遍,每遍都换不同角色登录,学生发帖、管理员删帖、游客浏览,确保没有遗漏。这个方案从技术栈到部署都不算难,难的是细节的耐心。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询