尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot+微信小程序农产品售卖系统开发全流程解析

SpringBoot+微信小程序农产品售卖系统开发全流程解析 做毕设选了“农村农作物售卖微信小程序管理系统”这个题目的同学我先把话放在前面这个题目看着朴实但真做起来技术栈横跨小程序前端、SpringBoot后端、数据库设计、接口联调再加上农产品特有的业务逻辑多规格、按斤计价、物流配送、农户分账一个合格的系统下来工作量是实打实的。这篇文就围绕“Java SpringBoot 微信小程序”这个组合把整条链路怎么设计、怎么落地、哪些坑必须避开一次性讲清楚。1. 项目整体设计与技术选型拆解1.1 业务需求到底在说什么“农村农作物售卖”这个描述拆开看其实是两个角色、三条线游客/消费者逛商品、看分类、加购物车、下单买农产品、查订单状态这是小程序端的主线。管理员/平台运营维护商品上下架、库存、价格、规格、处理订单发货、退款、改状态、管理会员/用户、看销售数据这是管理后台的主线。农户/供货端可选但强烈建议加如果做成平台型系统还需要一个简易的供货商入驻和收益结算模块。对毕设而言加个“农户列表”和“商品归属农户”字段就够了做成真正多商户系统容易失控。所以这个系统本质上是一个单端用户 一个运营后台的双端应用。后端统一由SpringBoot提供RESTful接口小程序承担C端展示和交互管理后台可以复用Web页面或直接用Vue搭建。1.2 为什么是SpringBoot 微信小程序而不是其他组合先说SpringBoot。它简化了Spring的配置内嵌Tomcat配合MyBatis-Plus操作数据库开发效率极高。对毕设来讲你不需要花时间在XML配置或者Bean装配细节上而是把精力集中在业务逻辑——这非常关键因为农产品销售的订单状态流转待支付→已支付→已发货→已完成→退款本身就是一道隐含的业务设计题。再说微信小程序。选择它有三个实际理由零安装门槛用户扫一扫或搜一搜就能打开对农村长辈群体非常友好比下载App的获客成本低一个量级。微信生态天然信任链支付走微信支付、登录走微信授权省去自己做账号体系的时间和成本。毕设展示有天然优势答辩现场用真机演示扫码、下单、支付或模拟支付视觉效果和完整度远超纯Web项目。1.3 总体架构和模块划分我建议按“四层 双端”来规划小程序端用户交互层 ↓ HTTPS/JSON SpringBoot 控制层Controller层参数校验、鉴权 ↓ 业务服务层Service层订单、商品、用户、支付等逻辑 ↓ 数据访问层DAO层MyBatis-Plus操作MySQL功能模块上最小可行版本至少包含用户模块微信登录、个人信息维护、收货地址管理商品模块分类浏览、关键字搜索、商品详情、多规格展示购物车模块增删改查、勾选结算、库存预校验订单模块生成订单含地址快照、支付、取消、确认收货管理后台商品管理、订单管理、用户/农户管理、数据统计把上面几个模块做扎实答辩时老师问任何一个功能你都能说出接口、表结构和状态流转这就是高分底气。接下来按这个骨架逐一拆解实现细节。2. 数据库表设计与核心实体关系2.1 核心表到底怎么建才不返工表设计是整个系统的地基。我见过太多人先写代码后补表结果联调时发现自己跟自己打架。这里给出我实际使用过的最小表结构你在建库时直接参考即可表名核心字段说明userid, openid, nickname, avatar, phone, address_list小程序用户openid是唯一标识farmerid, name, introduce, picture, sales_volume农户/供货商信息挂在商品下面categoryid, name, sort_order商品分类如蔬菜、水果、粮油productid, category_id, farmer_id, name, main_image, price, stock, unit, detail, statusunit很重要农产品常按“斤/份/箱”计价product_skuid, product_id, spec_name, price, stock多规格表如“5斤装”“10斤装”可暂无cartid, user_id, product_id, sku_id, quantity, checked购物车orderid, order_no, user_id, total_price, status, address_snapshot, remark订单核心表地址直接冗余快照不关联地址表order_itemid, order_id, product_id, product_name, price, quantity, image订单商品明细价格商品名也要冗余addressid, user_id, receiver, phone, region, detail, is_default收货地址这里说三个容易踩的坑订单表一定要冗余字段比如商品名称、价格、收货人信息。订单生成后商品改了名字、价格、甚至被删除都不应该影响历史订单展示。关联查询虽然看起来“规范”但历史数据会飘。注意unit字段。农产品最常见的问题是“一份到底是多少斤”。你卖“有机番茄”价格是“12元/斤”还是“12元/份”必须用单位字段明确否则订单金额会产生歧义。答辩时拿出这个细节老师会觉得你考虑得周到。多规格要提前留位。农产品经常有“5斤装/10斤装/礼盒装”这种规格虽然第一版可以先不做SKU表但至少在product表留一个is_multi_spec字段和一个product_sku表的结构后续扩展不至于推倒重来。2.2 订单状态流转怎么设计才稳订单状态是整套系统最容易被问懵的部分。我的设计是0 待支付用户提交订单后尚未支付超时自动取消定时任务或延时消息。1 已支付待发货支付成功管理员后台可以看到并操作发货。2 已发货管理员填入物流单号农产品常用顺丰/京东或同城跑腿用户端可见物流信息。3 已完成用户确认收货或系统自动确认发货后7天自动确认。4 已取消用户主动取消或超时未支付。5 退款/售后整单退款或部分退款订单表加refund_status字段。每个状态变更建议都记录一条订单日志表order_log电商系统讲究可追溯这对毕设加分非常明显——老师如果问“你这个系统如何保证数据可追溯性”你直接甩出order_log表和状态流图印象分会完全不同。3. SpringBoot后端核心实现要点3.1 项目初始化与依赖选型SpringBoot版本我建议直接用2.7.x系列最新稳定版别追太高。等一下热搜词里有人问“springboot版本太高”这个我必须提醒3.x系列底层是Spring 6很多老教程的依赖坐标和配置方式会失效你接手前辈代码或者跟着网上博客走很容易卡版本兼容问题上。毕设追求的是稳定运行和顺利答辩不是Boot 3带来的性能提升。pom.xml核心依赖长这样parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies !-- web支持内嵌Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus减少SQL编写量 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- 微信支付/登录需要的HTTP客户端也常用hutool -- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.25/version /dependency !-- lombok省去getter/setter -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency /dependencies关于MyBatis-Plus我强烈建议选它做数据层框架。毕业设计场景下它的IService接口和LambdaQueryWrapper能让你少写至少30%的样板代码。比如查商品列表只需要ListProduct list this.list(new LambdaQueryWrapperProduct() .eq(Product::getStatus, 1) .orderByDesc(Product::getCreateTime));不要用原生MyBatis写大量手工XML除非你确实需要连表复杂查询。毕设的时间应该花在业务场景的完整度和答辩的流畅度上。3.2 微信登录与鉴权机制小程序端用户登录的流程是前端调用wx.login()拿到临时code后端拿着code去微信接口换openid和session_key。这里有个高频错误点很多人直接把session_key返回给前端存起来这是不安全的。session_key只能留在服务端用来解密用户手机号等敏感信息。我的做法是openid首次出现则创建用户然后用UUID生成一个token把userId存到Redis或内存Map中过期时间设7天。前端后续请求在Authorization请求头带上这个token后端用拦截器统一校验。核心代码示例PostMapping(/login) public Result login(RequestBody LoginRequest req) { // 1. 用code换openidhutool封装了请求也可以原生写法 String url https://api.weixin.qq.com/sns/jscode2session?appid appId secret appSecret js_code req.getCode() grant_typeauthorization_code; JSONObject obj HttpUtil.get(url).toJSONObject(); String openid obj.getStr(openid); // 2. 查库或创建新用户 User user getUserByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); user.setNickname(微信用户_ openid.substring(openid.length() - 4)); this.save(user); } // 3. 生成token存储映射关系 String token UUID.randomUUID().toString().replace(-, ); tokenManager.put(token, user.getId()); return Result.ok().put(token, token).put(userInfo, user); }3.3 商品模块与文件上传商品主图是农产品销售的命门——蔬菜水果的照片如果不吸引人再便宜也卖不动。文件上传这一块我建议本地存储不要一开始就上OSS和云存储理由有两个一是成本控制OSS要绑实名、付费用或申请免费额度时间成本高二是答辩演示环境是内网或本机本地图片访问最直接。SpringBoot上传文件的处理spring: mvc: static-path-pattern: /images/** web: resources: static-locations: file:D:/project/farm-images/然后拦截器放行/images/**上传接口把文件写到该目录返回路径/images/xxx.jpg。小程序端把host前缀拼上就能直接渲染图片。商品列表接口要考虑分页。热搜词里出现“微信小程序页面列表加载更多”这个点几乎必考滑动列表加载更多是电商小程序的刚需交互。后端用MyBatis-Plus的Page分页即可GetMapping(/list) public Result list(RequestParam Integer page, RequestParam Integer size, RequestParam(required false) String keyword, RequestParam(required false) Long categoryId) { PageProduct pg Page.of(page, size); LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); wrapper.eq(Product::getStatus, 1) .like(StrUtil.isNotBlank(keyword), Product::getName, keyword) .eq(categoryId ! null, Product::getCategoryId, categoryId); return Result.ok(this.page(pg, wrapper)); }前端在小程序onReachBottom里把page加1继续请求直到返回的列表长度小于size时停止加载。这个“触底加载”一定要做它比一次性返回全部数据优雅得多也是面试/答辩时的亮点。4. 核心功能模块的完整实现流程4.1 购物车与下单流程购物车这部分没什么高深的但要注意两个业务校验加入购物车时先校验商品状态是否为上架、库存是否足够不要让用户把下架商品留在购物车里结算。商品数量只能为正整数农产品按份数购买避免小数数量导致库存计算纷乱。点击购物车“结算”后进入订单确认页前端把勾选的购物车明细productId、skuId、quantityPOST给后端后端生成订单。这里有个场景化的技巧前端只传购物车记录ID、用户ID、收货地址ID后端重新从数据库查出最新单价和最新库存再计算总金额。绝对不能信任前端传入的价格数字这是电商开发的铁律也是答辩的高频追问点。订单生成的核心逻辑Transactional public Order createOrder(OrderCreateDTO dto) { // 1. 锁定用户购物车记录 ListCart carts cartService.listByIds(dto.getCartIds()); if (carts.isEmpty()) throw new BizException(购物车不能为空); // 2. 遍历检查商品状态计算总价用数据库价格不用前端传的价格 BigDecimal total BigDecimal.ZERO; ListOrderItem items new ArrayList(); for (Cart cart : carts) { Product product productService.getById(cart.getProductId()); if (product null || product.getStatus() ! 1) { throw new BizException(商品[ product.getName() ]已下架); } if (product.getStock() cart.getQuantity()) { throw new BizException(商品[ product.getName() ]库存不足); } // 扣减库存条件更新防止超卖 boolean success productService.update(new LambdaUpdateWrapperProduct() .setSql(stock stock - cart.getQuantity()) .eq(Product::getId, product.getId()) .ge(Product::getStock, cart.getQuantity())); if (!success) throw new BizException(商品库存不足); // 组装明细... } // 3. 创建订单主表含地址快照返回orderId // 4. 清空购物车已选记录 cartService.removeByIds(dto.getCartIds()); return order; }注意上面扣库存用的setSql(stock stock - n)加条件ge(Product::getStock, n)这是数据库层原子操作天然防超卖。如果你用“先查再减”的写法并发场景下就会超卖——一个是普通农户卖农产品库存就几十份超卖一单就是事故。4.2 支付流程怎么接或怎么模拟这是毕设里最难完整落地的一环。真实微信支付需要营业执照 商户号 支付证书个人开发者很难申请下来。所以毕设场景我推荐模拟支付通道具体做法是在订单生成后小程序端弹出一个支付确认对话框点“确认支付”后后端将订单状态从“待支付”改为“已支付”。这个方案的优点是省去商户资质和证书配置流程完全可跑通演示效果也完整。如果你有真实商户号要接那么流程是后端调用微信支付统一下单接口传订单号、金额单位是分、openid、回调地址。微信返回prepay_id。后端按微信规范用appId、timeStamp、nonceStr、packageprepay_idxxx、signTypeRSA生成二次签名返回给前端。小程序端wx.requestPayment发起调起支付。支付成功后微信回调你的接口你在回调里判断resultCode SUCCESS然后更新订单状态。注意回调必须做签名校验同时校验金额并保证幂等——因为微信可能重试回调。对毕设而言我的建议是把默认配置做成模拟支付同时保留真实支付的对接代码。答辩时演示模拟支付最稳定讲到技术深度时再提真实支付的流程设计和签名机制导师会对你整体水平有明显好感。4.3 管理后台的关键功能管理后台默认用SpringBoot Thymeleaf或Vue前后端分离都可以但考虑到毕设工作量我更推荐一个轻量方案后端纯接口 一个Vue3 Element Plus管理端。如果不想前后端分离直接用Thymeleaf渲染页面也行工作量小但页面比较老土。这里建议后台至少包含这几个页面仪表盘展示今日订单数、销售额、待发货订单用ECharts画一个近7天销售额折线图。这个图放上去整个项目的答辩观赏性立刻上升。商品管理增删改查、上下架、库存修改、多规格维护。订单管理列表 状态筛选 发货操作 订单详情查看。用户管理用户列表、用户状态禁用。数据统计按分类统计销量做成柱状图。4.4 小程序端五个页面的实操要点小程序的目录结构我这里直接给出来pages/ index/ # 首页轮播图、分类、推荐商品 category/ # 分类页左侧分类、右侧商品 cart/ # 购物车勾选、全选、结算 order/ # 订单状态tab切换、列表、确认收货 user/ # 我的用户信息、地址管理、订单入口首页轮播图放三张农产品banner图下面接分类导航九个或十个图标然后按销量或推荐展示商品卡片。这里有一个视觉要点农产品图片饱和度要高、背景干净小程序端卡片圆角和阴影要统一这个细节决定了评委第一印象分。搜索框位置放在导航栏下方固定输入关键词后跳转到商品列表页关键词传给后端做LIKE %keyword%匹配同时在SQL里拼一个排序按销量降序。这个“按销量排序”在农产品场景下非常重要——用户天然倾向买“卖得多”的农产品平台也愿意推供应链能力强的农户。分类页建议用左窄右宽布局左侧是分类栏宽度180rpx左右右侧是商品瀑布流列表。分类数据先一次性缓存到本地storage减少请求次数右侧列表仍是正常的分页加载。购物车注意两件事全选反选逻辑要联动底部结算栏金额实时更新数量增减时调用后端更新接口不要只在本地改数字否则结算时价格不一致。结算前的库存校验也需要在后端生成订单时执行。“我的”页面要展示用户头像昵称、我的订单入口用五个小图标待付款、待发货、待收货、已完成、售后、常用功能收货地址管理、联系客服。这里提醒一下2022年后微信调整了头像昵称填写能力不能直接wx.getUserProfile拿头像昵称需要用户手动点击授权或者在个人中心放一个自定义的“头像昵称填写”按钮让用户自主录入。很多旧教程还停留在直接获取的阶段踩这个坑会浪费不少时间。5. 接口设计规范与前后端联调经验5.1 统一返回结构与错误码接口设计必须有统一风格。我用的返回结构极其简单{ code: 200, message: 操作成功, data: { } }code 200正常code 400参数错误code 401未登录或token过期code 500服务器异常code 20001业务异常比如“库存不足”message里带可读信息后端写一个Result类包装所有返回值配合RestControllerAdvice全局异常处理器把业务异常和系统异常统一转换。这样controller层只需要关注业务返回统一格式前后端联调时大大降低理解成本。Controller示例RestController RequestMapping(/api/product) public class ProductController { GetMapping(/list) public Result list(RequestParam Integer page, RequestParam Integer size) { return Result.ok(productService.pageList(page, size)); } GetMapping(/{id}) public Result detail(PathVariable Long id) { return Result.ok(productService.getDetail(id)); } }5.2 小程序端封装网络请求小程序端我建议封装一个request.js统一处理baseUrl、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, Authorization: wx.getStorageSync(token) || }, success(res) { if (res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { // 重新登录 wx.navigateTo({ url: /pages/login/login }); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); } module.exports { request };如果开发阶段要真机预览baseUrl不能写成localhost要写电脑的局域网IP并且保证手机和电脑连同一个WiFi。小程序开发工具里记得勾选“不校验合法域名”否则本地调试全是域名报错。等到正式发布再去小程序后台配置request合法域名HTTPS必须。5.3 部署与演示环境的三个注意点后端部署SpringBoot项目可以直接打jar包放到云服务器上运行用nohup java -jar farm-0.0.1.jar log.txt 21 启动。如果只是本机演示IDEA里直接run就完事。MySQL连接配置连接字符串记得加useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai否则中文乱码和时区问题会连环爆炸。图片路径问题本地存储图片和SpringBoot的静态资源路径要统一。如果你把项目拷贝到别的电脑演示图片路径很容易失效一个稳妥的办法是把图片目录配置放到application.yml里启动时用参数覆盖。6. 常见问题与排查技巧实录6.1 登录态失效与系统错误问题小程序调用接口报401或“未登录”。排查思路依次检查——小程序请求头是否带上token、后端拦截器是否放行了登录接口和静态资源、token存储时间是否过期。我在项目里加了拦截器放行白名单/api/auth/login、/images/**。每次加新接口时觉得“为什么不放行”核查后发现是token过期这类问题很基础但也很磨人。6.2 订单超时未支付的自动关闭问题用户下单不支付订单永远卡在待支付状态后台库存也被扣掉了。解决思路合理的做法是用户下单时扣库存订单超时关闭后把库存归还。实现方式有三种定时任务Scheduled每5分钟扫一次待支付订单超过30分钟自动关闭并回滚库存。这个方案最简单毕设完全够用。延迟消息RabbitMQ/TDMQ延迟消息但需要额外组件毕设不用过度设计。懒关闭用户查询订单时发现超时立即关闭。这个可以作为辅助兜底。定时任务代码Scheduled(fixedDelay 300000) public void closeTimeoutOrders() { LocalDateTime deadline LocalDateTime.now().minusMinutes(30); ListOrder list orderService.list(new LambdaQueryWrapperOrder() .eq(Order::getStatus, 0) .lt(Order::getCreateTime, deadline)); for (Order order : list) { order.setStatus(4); // 已取消 orderService.updateById(order); // 归还库存遍历orderItem还原product.stock restoreStock(order); } }注意Scheduled默认是单线程执行毕设演示没问题但实际生产要控制并发锁否则数据一致性会有一定风险。答到这一步时提到JVM级锁不够、应该用数据库乐观锁或分布式锁可以展示出你对并发问题的理解深度。6.3 多规格SKU与购物车数据错乱问题同一个商品有“5斤装”和“10斤装”加入购物车后购物车记录没有区分sku_id导致结算成了原商品价格。根源cart表缺少sku_id字段。解决cart表增加sku_id购物车唯一性校验改为(user_id, product_id, sku_id)组合。这样能实现同一商品不同规格拆成多条购物车记录。如果你的第一版没有做多规格直接把sku_id设为null后续扩展SQL不用改表结构只需改默认值。6.4 微信小程序审核被拒的三个高频理由毕设如果不止演示、还想上线发布以下三个坑需要提前规避虚拟支付卖线上课程/会员属于虚拟支付苹果端会被拒农产品属于实物商品问题不大。类目不符果蔬生鲜需要选“食品”类目需要食品经营许可证。如果个人主体上架难改用企业主体注册小程序类目换“商家自营-食品”。隐私协议小程序发布前必须设置用户隐私保护指引尤其涉及用户手机号、位置信息时一定要在这里进行说明。如果只是本地预览演示就不需要走审核流程直接预览或在真机调试模式看效果即可。7. 个人实操体会与后续扩展建议花了两三周把这个系统完整跑通后我最大的感受是农产品销售看起来是个边缘选题但它把电商闭环、微信生态和移动端开发都串起来了。对一个做毕设的人来讲它比纯后台管理系统多一个前端交互展示层比纯小程序多一套完整的管理业务复杂度刻意控制在了“全能展示但不失控”的范围。有几个小技巧实操下来特别有用顺手分享给你图片全部用本地静态资源演示时避免外链图片挂掉的尴尬答辩当天最怕白屏。数据库导入脚本写全把建表语句、测试数据、管理员的初始密码放在一个sql/init.sql文件里别只存在自己电脑上放一份到Git仓库。答辩换机器演示时导入不到一分钟就能恢复环境这种“小动作”在答辩当天能救你一命。小程序端每次请求都带loading提示虽然只是交互细节但农产品图片一般比较大接口慢的时候用户会误以为卡死。后续如果想要加分我建议按这个优先级扩展接入ECharts大屏数据统计页面订单趋势、热门品类Top10、农户销量排行。增加农户入驻申请流程申请表单、后台审核、农户独立查看自己订单。加优惠券/秒杀模块复杂度不高但业务丰富度显著提升。部署到真实云服务器 HTTPS域名微信开发者工具里正常请求。这个题目最大的美妙之处在于它够真实。农村农作物售卖不是拍脑袋编出来的玩具系统而是真正连接了农户、平台、消费者三方角色让每个模块都有清晰的存在价值。把这个价值讲清楚答辩自然就稳了。
返回列表