2026/10/18 10:41:12

基于微信小程序与SSM的社区垃圾回收管理系统实现

基于微信小程序与SSM的社区垃圾回收管理系统实现 简介基于微信小程序的社区垃圾回收管理系统毕业设计源码案例面向计算机专业正在做毕设的学生及需要项目实战练习的学习者后端采用SSMSpring、SpringMVC、MyBatis框架围绕社区垃圾回收场景实现用户管理、回收请求提交、垃圾分类指导、回收任务分配、进度跟踪和数据统计等功能并支持通过微信小程序便捷完成回收操作。压缩包共916个文件、约58.9MB主要包含Java后端源码、Vue管理端页面、JavaScript逻辑脚本、微信小程序WXML/WXSS/JSON页面文件、SQL数据库脚本、PNG/JPG界面截图以及MP4演示视频等类型覆盖从设计到部署的完整环节。目前已有53人学习下载。除源码与数据库外还配套演示视频、使用说明和环境安装说明便于快速运行和部署阅读源码可学习SSM框架下业务系统模块划分、接口设计与数据流转以及小程序端与后端服务之间的交互方式适合作为毕业设计、课程设计、期末大作业的参考也可在此基础上进行二次开发。1. 从“收垃圾”到“管垃圾”这套系统到底在做什么社区垃圾分类回收这件事真正动手做过的人都知道难的不是贴几张宣传标语而是“谁上门收、收了多少、怎么记账、怎么让居民愿意持续参与”。基于微信小程序的社区垃圾回收管理系统本质上是把线下回收流程搬到线上居民在小程序里提交回收请求、选择垃圾类别和上门时间回收员接单上门称重计价后系统自动给居民积分积分可以兑换小礼品或抵扣物业费。这套模式跑通后社区不用再靠人工登记本和微信群里吼来吼去后台还能看到每天的回收量、各类垃圾占比、哪个楼栋参与度最高。我拆过不少类似的毕业设计源码案例这类项目最容易被低估的不是页面数量而是小程序与 SSM 后端的登录态打通、订单状态流转、积分事务一致性这几块。新手往往把前端页面做得很漂亮一联调就翻车。这篇文章从技术选型、数据库设计、核心接口实现到本地复现的坑按实际开发顺序讲清楚适合正在做毕设、课设或者想给社区/物业快速搭一套轻量回收管理系统的开发者。2. 技术选型为什么是 SSM 微信小程序2.1 后端扎在 SSM 上不是守旧是课程体系与课题要求SSM 指的是 Spring、SpringMVC、MyBatis 三件套。Spring 管对象和事务SpringMVC 管 HTTP 请求的路由分发MyBatis 管 SQL 与 Java 对象之间的映射。很多高校的软件工程、计算机科学与技术专业课程设计大纲还明确写着“基于 SSM 框架”或“基于 Spring SpringMVC MyBatis”所以这套技术栈在毕业设计里不仅没过时反而是最容易匹配任务书要求的组合。如果你在纠结“为什么不直接用 Spring Boot”我的看法是Spring Boot 确实省去大量 XML 配置但 SSM 的手动配置过程本身就是课程考核点——Spring 容器怎么加载、DispatcherServlet 怎么注册、MyBatis 的 mapper 怎么扫描这些在 Spring Boot 里全被自动配置藏起来了。对于需要写“系统实现”章节、需要讲清楚每个配置文件作用的学生来说SSM 反而更好写。真到工作场景SSM 的理解也能平滑迁移到 Spring Boot底层的 IoC 和 AOP 思想完全一致。项目环境这块我给一个踩过坑后固定下来的版本组合组件推荐版本说明JDK1.8不要上 11/17部分老依赖和 Tomcat 版本兼容性会出问题Tomcat8.5与 JDK8 配合最稳部署 war 包方便MySQL5.78.0 也可以但连接驱动和时区配置有差异Maven3.6.x管理依赖镜像用阿里云微信开发者工具稳定版即可调试小程序端2.2 小程序端为什么用原生功能复杂度决定框架选择微信小程序端的实现常见方案有原生 WXML/WXSS/JavaScript、uni-app、Taro 等。这类社区回收管理系统页面量不大核心就四五个页面首页提交回收、订单列表、订单详情、个人中心积分与地址管理用原生小程序语法完全够而且不需要额外引入编译链遇到问题在微信开发者工具里直接断点调试对新手更友好。系统的角色权限闭环是这样的普通居民通过小程序提交回收请求回收员在小程序或后台管理系统里接单上门称重后标记完成系统按垃圾类别和重量计算积分并写入居民账户管理员在 Web 后台看统计报表和用户管理。整个闭环里订单状态是最核心的主线数据后面所有接口设计都围绕状态机展开。2.3 业务流程先画清楚代码才不会写乱我把这个项目的主流程归纳为一条单线加两条支线。单线是居民提交回收单 - 回收员接单 - 上门回收 - 称重计价 - 积分入账 - 订单完成。两条支线分别是居民端地址管理每次下单可以选择默认地址、后台端的公告发布与回收员管理。这条主流程决定了数据库表结构、接口数量和事务边界。比如“提交回收单”和“积分入账”不在同一次操作里而是在回收员点击“确认完成”时后端需要在一个事务里同时更新订单状态、写入积分流水、累加用户总积分。如果你在编码前没理清这条线很容易写着写着就把积分更新的逻辑散落在多个 Controller 里后面排错非常痛苦。3. 数据库设计先让业务有地方落脚3.1 核心表拆分不过度设计但这两张表不能省这类系统的数据库设计我建议控制在七八张表以内多了显得臃肿少了盖不住完整流程。最少要有的核心表是用户表、回收订单表、垃圾类别表、积分明细表、地址表、公告表。回收员和管理员可以复用用户表通过 role 字段区分不值得单独建表。订单表是最需要仔细设计的因为它承载了全流程的状态流转。我的建议是 state 字段用 tinyint 存数字状态码0 表示待接单1 表示已接单2 表示已上门3 表示已完成4 表示已取消。用数字而不是字符串的好处是前端枚举映射简单后端判断状态机流转时写起来也干净。另外两个容易忽略的字段是 delete_flag逻辑删除和 create_time / update_time时间戳前者避免误删数据后者是后台做回收量趋势分析的基础。积分明细表要记录每次积分变动字段包含用户 id、变动类型1 增加 / 0 扣减、变动分值、关联订单 id、备注。之所以强调关联订单 id是因为后期对账时经常要回答“这 50 积分是哪次回收产生的”没有订单关联就只能一笔笔查流水非常被动。3.2 建库建表脚本直接能跑的初始化版本以下建表脚本是我基于常见实现整理的初始版本包含了核心的用户表、回收订单表和积分明细表。垃圾类别表和地址表比较简单可以在脚本基础上自行补上。CREATE DATABASE IF NOT EXISTS community_recycle DEFAULT CHARACTER SET utf8mb4; USE community_recycle; -- 用户表居民、回收员、管理员统一存放用 role 区分 CREATE TABLE t_user ( id INT NOT NULL AUTO_INCREMENT COMMENT 主键, openid VARCHAR(64) DEFAULT NULL COMMENT 微信小程序openid登录凭证, username VARCHAR(50) NOT NULL COMMENT 用户名/登录账号, password VARCHAR(100) DEFAULT NULL COMMENT 密码仅后台管理员使用, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像URL, role TINYINT NOT NULL DEFAULT 0 COMMENT 角色0居民 1回收员 2管理员, total_points INT NOT NULL DEFAULT 0 COMMENT 累计积分, delete_flag TINYINT NOT NULL DEFAULT 0 COMMENT 逻辑删除0正常 1删除, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 回收订单表整个系统的状态机核心 CREATE TABLE t_recycle_order ( id INT NOT NULL AUTO_INCREMENT COMMENT 订单ID, order_no VARCHAR(32) NOT NULL COMMENT 订单编号如 RC20250101001, user_id INT NOT NULL COMMENT 提交订单的居民ID, worker_id INT DEFAULT NULL COMMENT 接单回收员ID未接单时为NULL, category_id INT NOT NULL COMMENT 垃圾类别ID, address_detail VARCHAR(255) NOT NULL COMMENT 上门回收地址, appointment_time VARCHAR(50) DEFAULT NULL COMMENT 预约上门时间段, weight DECIMAL(10,2) DEFAULT NULL COMMENT 实际回收重量(kg)称重后填写, points INT DEFAULT NULL COMMENT 本次获得积分完成时计算, state TINYINT NOT NULL DEFAULT 0 COMMENT 状态0待接单 1已接单 2已上门 3已完成 4已取消, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, delete_flag TINYINT NOT NULL DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_state (state) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT回收订单表; -- 积分明细表每次积分变动留痕 CREATE TABLE t_points_detail ( id INT NOT NULL AUTO_INCREMENT COMMENT 主键, user_id INT NOT NULL COMMENT 用户ID, order_id INT DEFAULT NULL COMMENT 关联订单ID可空, change_type TINYINT NOT NULL COMMENT 变动类型1增加 0扣减, change_points INT NOT NULL COMMENT 变动积分值, balance INT NOT NULL COMMENT 变动后余额, remark VARCHAR(255) DEFAULT NULL COMMENT 变动原因, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT积分明细表;注意几个参数设计点。openid 字段我留了 64 位长度实际微信返回的 openid 一般在 28 位左右但留足余量避免以后小程序绑定其他平台时不够用。order_no 用业务编号而不是自增主键直接展示给用户是为了避免订单号可被遍历猜测也算是一点安全习惯。DECIMAL(10,2) 用来存重量不要用 FLOAT否则累加统计时会出现 0.1 0.2 ! 0.3 这类精度玄学问题。delete_flag 每个核心表都带上社区运营中误操作删除订单是常事逻辑删除可以留一条后悔药。4. 后端与小程序端实现核心流程怎么跑通4.1 后端分层与登录态小程序登录不是传用户名密码SSM 后端的包结构我习惯按功能分层而不是按业务模块分层因为这个项目规模不大分层过高反而绕。controller 放接口入口service 放业务逻辑mapper 放 MyBatis 的接口和 XMLentity 放数据库映射实体common 放工具类和通用返回体。返回体统一用{ code, msg, data }结构前端判断 code 是否为 200 决定业务是否成功比裸返回数据要规范。小程序登录这块很多新手会踩一个认知上的坑小程序里没有用户名密码微信提供的登录方案是wx.login()获取临时 code后端拿 code 去微信接口换 openid再用 openid 去数据库匹配用户。首次登录时自动用 openid 创建一条用户记录之后每次请求通过 token 识别身份。我在 common 包下写了一个TokenUtil基于 UUID 生成 token存入内存 Map 或 Redis请求头里带token字段拦截器里做校验。下面是登录接口的核心代码在 SSM 下的 controller 和 service 实现Controller RequestMapping(/api/user) public class UserController { Autowired private UserService userService; /** * 小程序登录接收 wx.login 返回的 code换取 openid * 首次登录自动注册返回自定义 token */ RequestMapping(value /login, method RequestMethod.POST) ResponseBody public Result login(RequestBody JSONObject param) { String code param.getString(code); if (StringUtils.isEmpty(code)) { return Result.error(code不能为空); } return userService.login(code); } }Service public class UserServiceImpl implements UserService { Autowired private UserMapper userMapper; Override public Result login(String code) { // 1. 调用微信接口 code2Session 换取 openid String appid 你的小程序appid; String secret 你的小程序secret; String url https://api.weixin.qq.com/sns/jscode2session?appid appid secret secret js_code code grant_typeauthorization_code; String result HttpUtil.get(url); JSONObject json JSONObject.parseObject(result); String openid json.getString(openid); if (StringUtils.isEmpty(openid)) { return Result.error(openid获取失败: json.toJSONString()); } // 2. 根据 openid 查用户不存在则自动注册 User user userMapper.selectByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); user.setUsername(wx_ openid.substring(0, 8)); user.setNickname(微信用户); user.setRole(0); // 默认为居民 userMapper.insert(user); } // 3. 生成 token 并返回 String token TokenUtil.generateToken(user.getId()); JSONObject data new JSONObject(); data.put(token, token); data.put(userId, user.getId()); data.put(nickname, user.getNickname()); data.put(role, user.getRole()); return Result.success(data); } }这段代码里有两个关键参数。code 是wx.login()接口返回的临时登录凭证有效期只有 5 分钟且只能用一次所以前端必须在拿到 code 后立刻请求后端接口不能存起来过一会儿再用。appid 和 secret 在小程序后台的「开发管理 - 开发设置」里查看secret 一旦泄露别人就可以伪造你的小程序身份务必放在后端配置里不要写在小程序前端代码里否则等于把钥匙挂在门上。token 生成我这里用的是按用户 id 加时间戳加随机数做 MD5简单够用。如果想要更严谨可以引入 JWT但该项目没有重启后 session 失效的强要求内存 Map 方案足够。要注意的是如果做了多实例部署Map 方案会有登录态丢失问题毕设单实例部署可以忽略。4.2 订单状态机积分入账必须和完成动作在同一事务里订单创建相对简单居民提交表单后插入一条 state0 的记录。真正考验代码功底的是回收员点击“确认完成”这个动作。这个动作涉及三步更新订单状态为已完成、给居民加积分、写入积分流水。这三步必须在一个事务里完成否则会出现订单已完成但积分没到账或者流水重复记录的情况。我在 Service 层实现这个逻辑用 Spring 的Transactional注解保证原子性。关键代码如下Service public class RecycleOrderServiceImpl implements RecycleOrderService { Autowired private RecycleOrderMapper orderMapper; Autowired private UserMapper userMapper; Autowired private PointsDetailMapper pointsDetailMapper; /** * 回收员确认完成订单更新状态 发放积分 写流水 * 三个操作在同一事务中任何一步失败全部回滚 */ Override Transactional(rollbackFor Exception.class) public Result completeOrder(Integer orderId, BigDecimal weight) { // 1. 校验订单存在且状态为“已接单(1)”或“已上门(2)” RecycleOrder order orderMapper.selectById(orderId); if (order null) { return Result.error(订单不存在); } if (order.getState() ! 1 order.getState() ! 2) { return Result.error(当前订单状态不可完成); } // 2. 计算积分可按重量*系数也可按垃圾类别固定积分 int points weight.multiply(new BigDecimal(10)).intValue(); if (points 0) { points 10; // 保底积分 } // 3. 更新订单状态改为已完成(3)填入重量和积分 order.setState(3); order.setWeight(weight); order.setPoints(points); orderMapper.updateById(order); // 4. 累加用户总积分 User user userMapper.selectById(order.getUserId()); user.setTotalPoints(user.getTotalPoints() points); userMapper.updateById(user); // 5. 写入积分流水 PointsDetail detail new PointsDetail(); detail.setUserId(user.getId()); detail.setOrderId(order.getId()); detail.setChangeType(1); detail.setChangePoints(points); detail.setBalance(user.getTotalPoints()); detail.setRemark(回收订单完成重量: weight kg); pointsDetailMapper.insert(detail); return Result.success(points); } }这里我设置了rollbackFor Exception.class这是个容易被忽略的参数。Spring 的Transactional默认只在 RuntimeException 时回滚如果你在业务代码里手动 catch 了异常然后抛出一个普通的 Exception事务是不会回滚的。在毕设代码里最常见的翻车就是数据库写入成功了但后续积分计算抛了一个空指针或类型转换异常结果订单状态改了、积分没加数据不一致。状态码的流转校验也很重要。我在代码里限制了只有“已接单(1)”和“已上门(2)”状态的订单才能执行完成操作这能防止回收员手滑把“待接单(0)”的订单直接点成完成。更完整的做法是把状态机用枚举或常量类定义出来每个状态变更都校验当前状态是否合法否则后期加需求时到处是 if-else改一处漏三处。4.3 小程序端请求封装token 带上错误统一提示小程序端的核心不是页面样式而是请求层的封装。如果每个页面都单独写wx.request代码会非常冗余且难以维护。我在小程序根目录下建了utils/request.js统一封装请求方法自动附带 token统一处理 401 跳转登录。// utils/request.js const BASE_URL http://localhost:8080; // 后端接口地址 function request(url, method, data, header) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method || GET, data: data || {}, header: Object.assign({ Content-Type: application/json, token: wx.getStorageSync(token) || }, header), success(res) { // 后端统一返回 {code, msg, data}code200 为业务成功 if (res.data res.data.code 200) { resolve(res.data.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 };这里的关键参数和习惯做几点说明。BASE_URL 在开发阶段填http://localhost:8080但微信开发者工具里请求本机地址需要勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”否则会被拦截。上线时这里要换成配置了 HTTPS 的正式域名且该域名必须在小程序后台的 request 合法域名列表里。token 从 storage 里读取每次请求自动带上后端拦截器从请求头里取出 token 校验用户身份。然后是页面调用以提交回收订单为例// pages/recycle/submit.js const { request } require(../../utils/request); Page({ data: { categoryList: [], selectedCategory: , appointmentTime: 上午 9:00-12:00, addressDetail: , remark: }, onLoad() { this.loadCategories(); }, // 加载垃圾类别下拉选择 async loadCategories() { try { const list await request(/api/category/list, GET); this.setData({ categoryList: list }); } catch (e) {} }, // 提交回收订单 async onSubmit() { if (!this.data.selectedCategory) { wx.showToast({ title: 请选择垃圾类别, icon: none }); return; } if (!this.data.addressDetail) { wx.showToast({ title: 请填写上门地址, icon: none }); return; } try { await request(/api/order/create, POST, { categoryId: this.data.selectedCategory, addressDetail: this.data.addressDetail, appointmentTime: this.data.appointmentTime, remark: this.data.remark }); wx.showToast({ title: 提交成功, icon: success }); setTimeout(() { wx.navigateTo({ url: /pages/order/list }); }, 1000); } catch (e) {} } });提交订单的关键是前端传入 categoryId 而不是类别名称字符串。因为后端在计算积分、统计分类回收量时需要的是稳定的类别主键如果前端传中文名称一旦名称修改、或者前后端编码不一致数据就对不上了。地址和时间段这类信息属于展示性数据前端校验必填后传字符串即可后端不要二次解析。5. 本地复现避坑5 个高频问题与排查思路5.1 微信开发者工具请求直接报 404 或无法连接现象小程序页面能打开但点击登录或加载列表时控制台一片红提示request:fail或GET http://localhost:8080/xxx 404 (Not Found)。原因多数情况是开发工具默认开启了域名校验localhost不在合法域名列表里。还有一部分原因是后端 Tomcat 的 context-path 和请求路径拼接错了比如后端部署在/recycle路径下但前端 BASE_URL 还是http://localhost:8080导致接口全路径变成/api/user/login而不是/recycle/api/user/login。解决微信开发者工具右上角 -「详情」-「本地设置」- 勾选「不校验合法域名...」。后端如果是 Maven 多模块项目确认 Tomcat 部署的 context path 是什么BASE_URL 里补齐例如http://localhost:8080/recycle。改完记得在开发者工具里清缓存重启有时改了配置不重启不生效。5.2 数据库时间字段差 8 小时现象插入订单后MySQL 里的 create_time 比本地时间少了 8 小时。原因JDBC 连接串没有指定时区MySQL 5.7 默认时区是系统时区而驱动和系统时区对美国时间做了换算导致时间偏移。这是这类管理系统的经典玄学问题几乎每个做毕设的都会踩一次。解决在 JDBC 连接串末尾加上serverTimezoneAsia/ShanghaiuseSSLfalse。完整示例jdbc.urljdbc:mysql://localhost:3306/community_recycle?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse如果改完还是差时间检查一下服务器系统时区Linux 执行date -R看时区是不是 0800不是的话用timedatectl set-timezone Asia/Shanghai修改。改完重启 Tomcat 和 MySQL 才会彻底生效。5.3 小程序登录 code 换 openid 失败现象后端日志显示调用微信接口返回errcode: 40029或40163。原因40029是 code 无效一般是 appid 和 secret 不匹配或者小程序不是同一主体。40163是 code 已被使用过说明前端在同一个登录流程里调了两次wx.login()或者后端接收了重复请求。解决先核对小程序后台的 appid 和 secret 是否复制完整注意不要混淆测试号和正式小程序的 appid。前端代码里确保wx.login()只在需要登录时调用一次拿到 code 后立即传给后端中间不要有异步等待。如果后端临时调试需要手动模拟登录可以在代码里写一个测试分支绕过微信接口直接创建测试用户但上线前务必删掉。5.4 上传的图片回显失败路径打不开现象居民头像或垃圾图片上传成功后数据库中存的是/upload/xxx.jpg但小程序里image标签加载时提示图片不存在。原因Tomcat 默认只映射部署目录下的静态资源上传到自定义磁盘目录比如D:/upload或/var/upload的图片不在 Web 应用的访问路径内所以直接通过/upload/xxx.jpg访问不到。解决在 SpringMVC 配置里增加资源映射把文件系统的上传目录映射为 URL 路径。在 spring-mvc.xml 中配置mvc:resources mapping/upload/** locationfile:D:/upload//这样访问http://localhost:8080/upload/xxx.jpg时Tomcat 会去D:/upload/目录下找文件。另一个注意点是 Windows 和 Linux 的路径写法不同Linux 下写成file:/var/upload/。如果用了 Tomcat 的虚拟目录也可以但配置资源映射更适合开发调试。5.5 MyBatis 传入多个参数报错 BindingException现象Mapper 接口方法定义了两个以上参数运行时报Parameter xxx not found. Available parameters are [arg0, arg1, param1, param2]。原因MyBatis 在多个参数时不会自动把参数名绑定到 SQL 里的#{userId}需要显式加Param注解否则只能用arg0, arg1这种下标访问非常容易写错。解决在 Mapper 接口方法参数前加注解例如ListRecycleOrder selectOrdersByUserAndState( Param(userId) Integer userId, Param(state) Integer state);对应 XML 里写WHERE user_id #{userId} AND state #{state}。这个报错在单参数时不会出现所以很多人写第一个查询没问题扩展成多条件查询时突然翻车。建议从一开始就约定所有 Mapper 方法参数超过一个一律加Param不要依赖 MyBatis 的参数名推断。6. 从能跑到能用联调验证与下一步改进方向项目在本地完整跑通后我习惯先做一套最小验证而不是直接开始补功能。验证的核心是走完一条居民 回收员的完整业务链路确认订单状态和积分在每个节点的变化都符合预期。下面的表格是我针对这类订单系统整理的验证用例照着过一遍能覆盖绝大多数接口 bug操作步骤操作角色预期结果微信开发者工具打开小程序点击登录居民数据库 user 表新增一条 openid 记录返回 token选择垃圾类别填写地址提交回收单居民订单表新增记录state0订单号生成正确在订单列表刷新查看居民订单显示“待接单”状态回收员登录后台接单回收员订单 state 变为 1更新 worker_id回收员点击“确认完成”填写重量回收员订单 state 变为 3weight 和 points 回填查看居民积分居民total_points 增加积分明细表多一条记录管理员后台查看回收统计管理员今日回收量、分类占比连续且汇总一致验证过程中一旦发现数据对不上优先查事务边界。比如积分没加查completeOrder方法是不是抛了异常但被 Controller 层吞掉了积分加了两次查是不是前端重复点击“确认完成”而后端状态机校验没拦住。跑通之后如果要让这个系统从“毕设作品”变成“能真正给社区用”我建议优先补两块一是定时统计任务每天凌晨通过 Spring 的Scheduled注解跑一个汇总按垃圾类别统计昨日各楼栋回收量数据落到一张统计表里后台报表直接查表而不是实时聚合大表二是小程序订阅消息居民提交订单后订阅“回收完成通知”回收员标记完成时后端调用微信订阅消息接口居民微信里就能收到提醒这比居民反复刷新订单列表体验好很多。我自己的一个习惯是所有接口写完后先不用小程序测而是用 Postman 把后端接口按业务流程顺序调一遍确认每一步的参数和返回结构都正确。这样能快速定位是前端问题还是后端问题。小程序端的报错信息有时会被 wx.request 的 fail 回调吞掉直接调后端能少走很多弯路。最后讲一个我见过很多次的翻车现场数据库、后端、小程序三端字符集不一致导致中文乱码。MySQL 建库时指定utf8mb4JDBC 连接串里带characterEncodingutf8后端web.xml里配置 Spring 的CharacterEncodingFilter小程序端请求头里Content-Type带charsetutf-8这四处有一个漏了中文就会出现问号。遇到乱码不要到处改代码先按这条链路排查基本问题都出在漏了CharacterEncodingFilter。这个方向做到“能用”并不难核心就是把订单状态机和积分事务这两块咬死剩下的页面和字段都是体力活。希望这篇拆解能帮你少踩几个坑把精力花在真正能写进论文和答辩的项目亮点上。本文还有配套的精品资源点击获取