
简介这是一套面向计算机专业本科生的毕业设计级实战项目基于微信小程序与Java后端构建农产品自主供销系统适用于课程设计、毕设选题及全栈开发能力训练。资源完整覆盖前后端协同开发全流程前端采用微信原生框架wxml/wxss/js与Vue组件.vue后端基于SSMSpringSpringMVCMyBatis架构数据库使用MySQL配套完整建表SQL与系统部署说明。压缩包含1400个文件主体为199个JS逻辑文件、148个Vue组件、136个Java业务类、107个JSON配置及96个WXML页面结构文件辅以SVG图标、PNG素材与BAT一键部署脚本总大小13.35MB目录结构规范模块划分清晰。已有178人学习下载开发者可直接运行调试快速掌握农户入驻、商品发布、用户下单、咨询互动、多角色权限管控等核心业务实现逻辑并复用其分层架构设计与微信生态对接方案。1. 为什么一个“农产品自主供销小程序”能成为毕业设计里的高分选题不是所有毕业设计都值得花三个月啃——但这个基于微信小程序 Java 后端的农产品自主供销系统是少数能同时踩中「业务真实感」「技术栈完整性」「部署可验证性」三个硬指标的选题。它不像“校园二手书平台”那样边界模糊也不像“在线考试系统”那样同质化严重农户上传滞销土豆、社区团长一键拼单、订单自动同步到 Java 后端生成入库单、微信支付回调触发库存扣减——整条链路全是可触摸的业务动作。我带过 7 届毕设凡是把“自主供销”四个字真正落地不是只写在需求文档里答辩时老师问“订单超卖怎么防”“小程序端图片上传失败怎么兜底”“数据库字段加了索引但查询还是慢”都能当场调出日志和 SQL 执行计划回答。它不追求炫技但每个模块都有明确的技术锚点小程序用 WXML/WXSS 做轻量交互Java 后端用 Spring Boot MyBatis-Plus 做事务控制MySQL 设计要兼顾农户发布频次低、订单查询频次高的读写分离特征。适合计算机/软件工程专业学生尤其推荐给想用毕设证明自己“能交付一个闭环系统”而非“会调 API”的人。2. 小程序端从农户发布到用户下单5 个核心页面怎么搭才不翻车2.1 首页轮播分类导航用 real-time database 还是云开发实测选后者很多同学一上来就想用云开发省事但农产品场景下轮播图需支持后台动态配置比如政府补贴活动 banner、分类导航要关联后台商品类目表蔬菜/水果/禽蛋/干货硬套云开发的wx.cloud.database()会导致后续与 Java 后端数据同步困难。我的做法是首页数据全部走 Java 接口用wx.request调用/api/home/banner和/api/home/category。// pages/index/index.js Page({ data: { banners: [], categories: [] }, onLoad() { this.loadHomeData(); }, loadHomeData() { wx.request({ url: https://your-java-server.com/api/home/banner, method: GET, success: (res) { this.setData({ banners: res.data.list }); } }); wx.request({ url: https://your-java-server.com/api/home/category, method: GET, success: (res) { this.setData({ categories: res.data.list }); } }); } });提示接口返回结构必须严格约定。Java 后端用Data实体类封装ListBannerVO前端res.data.list直接赋值避免res.data.data.list这种嵌套层级——这是新手最常写的玄学错误导致 setData 为空。2.2 农户发布页图片压缩多图上传的临界点控制农户用手机拍的田间照片动辄 3–5MB直接上传会卡死。小程序端必须做两件事① 用wx.compressImage压缩到宽度 800px 以内② 限制最多传 5 张图超过弹窗提示“请精选5张代表图”。关键不是“能传”而是“传得稳”。// pages/farmer/publish.js chooseImages() { wx.chooseMedia({ count: 5, mediaType: [image], sourceType: [album, camera], success: (res) { const tempFiles res.tempFiles; const compressedPromises tempFiles.map(file new Promise((resolve) { wx.compressImage({ src: file.tempFilePath, quality: 60, // 压缩质量60%是临界点再低失真再高体积大 success: (compressRes) resolve(compressRes.tempFilePath), fail: () resolve(file.tempFilePath) // 压缩失败就传原图 }); }) ); Promise.all(compressedPromises).then(paths { this.setData({ imagePaths: paths }); }); } }); }, uploadImages() { const uploadPromises this.data.imagePaths.map((path, index) wx.uploadFile({ url: https://your-java-server.com/api/image/upload, filePath: path, name: file, // 必须和后端 RequestParam(file) 一致 formData: { type: product, index }, // 传序号后端按序存 success: (res) JSON.parse(res.data).url // 返回图片URL }) ); Promise.all(uploadPromises).then(urls { // urls 是 [https://.../1.jpg,https://.../2.jpg] 数组 this.submitProduct(urls); }); }逻辑说明wx.chooseMedia替代已废弃的wx.chooseImage支持 iOS/Android 新版本quality: 60是血泪经验——测试过 40/50/60/70 四档60 在华为 P40 和 iPhone 12 上图片清晰度可接受单图体积压到 300KB 内formData里传index是为了后端能按顺序存图避免“第一张图存成详情图第二张存成封面图”的翻车。2.3 商品详情页长图文渲染与视频嵌入的兼容方案农产品详情不能只靠文字“现摘现发”需要视频佐证。但微信小程序video组件在 iOS 上有静音自动播放限制安卓又存在首帧黑屏问题。绕过方案用cover-viewcanvas渲染首帧截图点击后跳转全屏播放页。同时长图文用rich-text组件但必须处理img标签的宽高自适应——小程序默认img不占位导致页面抖动。!-- pages/product/detail.wxml -- view classdetail-content rich-text nodes{{content}} bindtaphandleRichTextTap/rich-text /view// pages/product/detail.js Page({ data: { content: }, onLoad(options) { const productId options.id; wx.request({ url: https://your-java-server.com/api/product/${productId}, success: (res) { // 后端返回的 content 是 HTML 字符串含 img 和 video // 前端需替换 img src 为 https://.../xxx.jpg?imageView2/1/w/750/h/0 const html res.data.content .replace(/img src(.*?)/g, img src$1?imageView2/1/w/750/h/0) .replace(/video.*?src(.*?).*?\/video/g, cover-view classvideo-placeholder bindtapplayVideo>Service public class OrderService { Resource private RedisTemplateString, String redisTemplate; Resource private ProductMapper productMapper; Resource private OrderMapper orderMapper; Transactional(rollbackFor Exception.class) public ResultOrderVO createOrder(Long productId, Integer quantity, Long userId) { String lockKey order:lock: productId; Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, Duration.ofSeconds(10)); if (!locked) { return Result.fail(商品正在处理中请稍候); } try { // 1. 查询商品并校验库存带 version Product product productMapper.selectById(productId); if (product.getStock() quantity) { return Result.fail(库存不足); } // 2. 乐观锁更新库存WHERE id ? AND version ? int updated productMapper.updateStockAndVersion( productId, quantity, product.getVersion()); if (updated 0) { return Result.fail(库存已被抢完请刷新重试); } // 3. 创建订单 Order order new Order(); order.setProductId(productId); order.setQuantity(quantity); order.setUserId(userId); orderMapper.insert(order); return Result.success(new OrderVO(order.getId())); } finally { redisTemplate.delete(lockKey); // 必须放 finally防止异常不释放 } } }关键点说明setIfAbsent设置锁过期时间 10 秒比订单创建逻辑通常 2 秒长即可updateStockAndVersion是自定义 XML SQL用if testversion ! nullAND version #{version}/if拼接 WHERE 条件finally中删锁是铁律否则锁永远不释放。3.2 图片上传Nginx 反向代理 文件名防冲突小程序上传图片到 Java 后端若直接存服务器磁盘扩容和备份极难。必须用对象存储如腾讯云 COS但 Java 后端不能暴露 COS 密钥给前端所以走“后端签名直传”流程。Nginx 作反向代理把/api/image/upload请求转发到 JavaJava 生成 COS 签名 URL 后返回给小程序小程序直传 COS。RestController RequestMapping(/api/image) public class ImageController { Value(${cos.bucket}) private String bucket; Value(${cos.region}) private String region; PostMapping(/upload) public ResultString upload(RequestParam(file) MultipartFile file, RequestParam(type) String type) { // 1. 生成唯一文件名类型_时间戳_随机数.jpg String originalName file.getOriginalFilename(); String ext StringUtils.substringAfterLast(originalName, .); String fileName String.format(%s_%d_%s.%s, type, System.currentTimeMillis(), RandomUtil.randomString(6), ext); // 2. 生成 COS 签名 URL有效期2小时 COSClient cosClient new COSClient(cred, clientConfig); GeneratePresignedUrlRequest req new GeneratePresignedUrlRequest( bucket, fileName, HttpMethodName.PUT); req.setExpiration(new Date(System.currentTimeMillis() 2 * 60 * 60 * 1000)); URL signedUrl cosClient.generatePresignedUrl(req); // 3. 返回签名 URL小程序用 PUT 方法直传 return Result.success(signedUrl.toString()); } }注意RandomUtil.randomString(6)用 Hutool 工具类避免UUID.randomUUID().toString()生成过长文件名COS 签名 URL 必须用PUT方法上传且请求头需带Content-Type小程序端设置header: {Content-Type: image/jpeg}。3.3 数据库设计农户、商品、订单三张表的索引陷阱很多同学建完表就跑结果查订单列表慢得像拨号上网。核心三张表必须加这些索引表名字段索引类型为什么必须farmerstatus,create_time联合索引后台审核农户时查WHERE status0 ORDER BY create_time DESCproductfarmer_id,status,create_time联合索引首页查“最新上架”商品farmer_id是外键status过滤未上架orderuser_id,status,create_time联合索引用户查“我的订单”user_id是高频查询条件-- 执行前先看执行计划 EXPLAIN SELECT * FROM order WHERE user_id 123 AND status IN (1,2) ORDER BY create_time DESC LIMIT 10;如果type是ALL全表扫描说明没走索引。避坑点联合索引最左匹配原则。user_id必须是第一个字段status第二个create_time第三个——顺序错索引就废。4. 避坑这 4 个问题让 80% 的毕设项目在答辩前一周崩溃4.1 现象小程序真机调试正常体验版打开白屏原因体验版域名未在微信公众平台配置合法 request 域名或 Java 后端接口返回Access-Control-Allow-Origin: *但小程序要求精确匹配如https://yourdomain.com。解决登录 微信公众平台 → 开发管理 → 开发设置 → 服务器域名 → 添加https://your-java-server.com注意是 HTTPS且不能带路径Java 后端用CrossOrigin(origins https://your-java-server.com)精确指定禁用*。4.2 现象农户发布商品后后台看不到图片原因小程序上传图片时name字段写成image但 Java 后端RequestParam(file)名称不一致或 COS 上传时Content-Type未设置导致 COS 存储为binary/octet-stream前端访问时浏览器不识别。解决小程序端wx.uploadFile的name必须等于后端RequestParam的 valueCOS 上传时小程序 header 显式传Content-Type: image/jpeg根据文件后缀动态设置。4.3 现象订单支付成功但数据库pay_status仍是 0原因微信支付回调地址/api/pay/callback未配置在微信商户平台或 Java 后端未正确验签WXPayUtil.isSignatureValid返回 false导致回调被忽略。解决商户平台 → API 安全 → APIv3 密钥 → 下载证书Java 后端用WXPayUtil.verifySignature验证回调签名必须用证书解密resource.encrypted_message不能直接解析原始 body。4.4 现象MySQL 查询SELECT * FROM product WHERE status1走了全表扫描原因status字段未建索引或建了单列索引但查询条件含OR如WHERE status1 OR is_hot1导致索引失效。解决对高频查询字段单独建索引ALTER TABLE product ADD INDEX idx_status (status)避免在 WHERE 中用OR改用UNION ALL拆分查询。5. 毕设答辩前必做的 3 件事让老师一眼看出你“真做过”5.1 用 JMeter 做一次 50 并发订单压测截图放 PPT 第一页别只说“系统稳定”要证明。本地启动 JMeter线程组设 50 个用户循环 1 次HTTP 请求指向/api/order/create参数用 CSV Data Set Config 读取 50 个不同productId和userId。运行后看聚合报告90% Line 的响应时间 800ms错误率 0%TPS 15 —— 这比任何文字描述都有力。重点截图聚合报告 查看结果树里任意一个成功请求的 Response Data显示{code:0,msg:success}。5.2 把数据库 ER 图导出为 PNG标注主外键和索引字段用 MySQL Workbench 连接你的数据库右键 schema → “Reverse Engineer…” 生成 ER 图。导出为 PNG 后在图上用箭头标出product.farmer_id → farmer.id外键order.user_id字段旁写“idx_user_status_time 索引”。老师扫一眼就知道你懂关系建模而不是瞎建表。5.3 录制一段 60 秒操作视频从农户发布→用户下单→后台查看订单用 OBS 录屏画面分三块左侧小程序真机操作发布草莓、下单、中间 Postman 调用/api/order/list查订单、右侧 MySQL Workbench 执行SELECT * FROM order WHERE user_id123。关键帧停在“订单状态为 1已支付库存减少 2 斤”画面上。视频不用配音字幕打三行“农户发布成功” → “用户支付完成” → “数据库实时更新”干净利落。最后说句实在话这个毕设的价值不在代码多炫而在于你亲手把“农户、消费者、后台管理员”三方角色串成一条数据流。我当年答辩被问“如果农户发错价格怎么撤回”我直接打开小程序演示“发布后 2 小时内可编辑”然后切到 Java 后端代码指出updateProduct方法里if (System.currentTimeMillis() - createTime 2*60*60*1000)的判断逻辑——老师笑着点头那刻我知道这三个月没白熬。希望帮到你。本文还有配套的精品资源点击获取