ARTICLE DETAIL

资讯详情

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

Spring Boot+微信小程序农产品销售系统:从业务拆解到支付对接全攻略

Spring Boot+微信小程序农产品销售系统:从业务拆解到支付对接全攻略 每年计算机毕业设计的选题季都有大量同学纠结要不要做农产品销售这类系统。我的看法是选题不怕老怕的是你只会照抄增删改查讲不出设计理由答不上来关键技术点的坑。springboot作为后端框架搭配微信小程序做前端再加上农产品这个非常垂直的业务场景是一个供应链条完整、角色分明、演示效果直观、答辩时有话可说的组合。这篇文章我就从实际能落地的角度把整个系统的业务拆解、技术设计、核心代码逻辑、以及那些文档里不会写的坑全部梳理一遍给准备做这个题目的同学一条可以直接走的路。1. 业务场景与需求拆解做系统之前先把人和事理清楚很多同学拿到这个题目第一反应是打开IDE建表写接口这是大忌。毕业设计和企业项目的最大区别在于毕业论文需要你讲清楚业务逻辑和技术方案之间的映射关系如果业务都拆不明白后面写再多代码也圆不回来。1.1 农产品销售系统里的三方角色与核心利益农产品销售不是普通的电商它有很强的时间性、地域性和信任成本。消费者会关心产地是不是真实的、蔬菜是不是新鲜的、价格是不是比菜市场便宜。所以系统里的角色不能只设计成用户和管理员两个就完事至少要拆出三个端、四种身份。消费者端微信小程序浏览时令农产品、按分类筛选、加入购物车、下单支付、查看订单状态、确认收货、发表评价。农户/商家端微信小程序或后台管理看时间决定上架商品、维护库存、处理订单、查看销售统计。平台管理端web管理后台或小程序内嵌管理页审核农户入驻、管理商品类目、处理售后纠纷、发布公告、查看全平台交易数据。三方角色的利益点完全不同。消费者要的是所见即所得农户要的是好货卖出好价管理员要的是平台规范有序。系统设计时要做的就是把这三个诉求落到具体的功能模块里去而不是机械地堆砌CRUD。这里有一个很容易被忽视的设计细节农产品有产地和季节属性这两项必须在上架时作为必填项处理。这不是为了表单好看而是在后续做推荐、搜索、筛选时产地和时令会是用户最在意的过滤条件。1.2 核心业务流程的全链路梳理整个系统的核心流程是用户登录 - 浏览商品 - 加购 - 下单 - 支付 - 商家接单 - 发货同城配送/快递 - 用户确认收货 - 评价。订单状态流转是这个项目最重要的状态机建议按下述方式设计答辩时直接把这套状态机画出来能比干讲代码多得5分。状态编码状态名称触发时机下一动作0待付款用户提交订单用户发起支付或订单自动取消1待发货支付回调成功商家在商家端点击发货2待收货商家录入物流单号用户确认收货或超时自动确认3已完成用户确认收货用户可发表评价4已取消用户取消或超时未支付流程结束5退款中用户申请退款商家/管理员审核6已退款商家同意退款流程结束前后端传递订单状态时不要直接传中文字符串传数字编码前端再根据编码映射展示。这样做的原因是第一数字编码更省流量第二状态扩展时不需要改动数据库结构第三后端判断状态流转用switch比equals字符串更清爽。1.3 非功能性需求毕设不能只做能跑除了功能上的需求网上评审老师还会看一些非功能性的东西。我建议在论文的可行性分析里至少提到以下几点这也是系统设计时的具体约束性能方面小程序首屏商品列表加载时间不超过2秒这个可以通过数据库索引和接口数据瘦身实现。安全方面用户密码不能明文存储需要BCrypt加密涉及金额的接口必须做参数校验不能只靠前端把关。兼容方面微信小程序基础库版本要向下兼容到2.14.0左右保证大部分用户手机能正常运行。可用性方面支付结果不能只靠前端跳转判断必须以后端收到微信支付回调为准。2. 技术选型与工程结构设计springboot这个版本该怎么定说完业务再谈技术。后端用springboot没什么悬念但版本选择是个容易被新手忽略的坑。现在springboot已经到了3.x但我不建议毕业设计直接用最新版。2.1 版本选型的依据与兼容性分析Spring Boot 3.0以上版本强制要求JDK 17而很多学校的实验环境和学生本机装的是JDK 8。如果你选了最新版最后部署到实验室服务器时发现环境不兼容那就很被动了。稳妥的做法是使用Spring Boot 2.7.x版本搭配JDK 8、MyBatis或MyBatis-Plus、MySQL 5.7或8.0。这个组合经过多年验证网上的资料最多报错也最好查。另外一个选型点是MyBatis建议直接上MyBatis-Plus。理由有三第一单表CRUD不用手写XML给你节省至少两天时间第二分页插件内置了不需要自己封装PageHelper第三字段自动填充、逻辑删除这些功能写论文时是亮眼的加分点。前端小程序不建议用uniapp直接使用原生微信小程序开发。理由很实际毕设的展示环节是现场演示原生小程序在微信开发者工具里的调试体验最直接出了问题也好查。uniapp虽然能一套代码多端发布但调试链路过长对新手不友好。2.2 后端工程目录结构分包方式决定代码质量后端工程的包结构我建议按模块分包而不是按层分包。很多教科书喜欢用controller、service、mapper三个包分到底这在单体小项目里问题不大但农产品销售系统功能模块多订单、商品、用户各自有独立的逻辑按层分包会导致包内文件越来越臃肿。推荐的做法是按业务域分包com.farm.market ├── common // 全局返回结果、异常处理、常量 ├── config // 配置类微信配置、MyBatis配置、拦截器配置 ├── controller // 各模块的controller按模块拆分子包 │ ├── user │ ├── product │ ├── order │ ├── cart │ └── payment ├── service // 接口 impl ├── mapper // MyBatis-Plus的mapper接口 ├── entity // 数据库实体类 ├── dto // 前端传参对象与entity分离 ├── vo // 返回给前端的视图对象 └── utils // JWT工具、日期工具等entity和vo一定要分开不要偷懒直接用实体类返回前端。原因很简单实体类里的字段和数据库一一对应直接暴露会把一些不该给前端看的字段比如密码哈希、逻辑删除标记传出去既不安全也不规范。数据库里可能有10个字段但前端只需要5个vo里只放需要的字段就行这样还能减小传输体积。2.3 数据库设计要点七张核心表撑起整个系统农产品销售系统的核心表可以控制在7张左右对毕设来说完全够用。users用户表、product商品表、category分类表、cart购物车表、orders订单表、order_item订单明细表、address收货地址表。如果要做评价加一张evaluation表要做农户管理加一张farmer表。如下是商品表和订单表的关键字段设计这两张表的设计质量直接决定后续开发效率。商品表product字段名类型说明idbigint主键namevarchar(64)商品名称category_idbigint分类idpricedecimal(10,2)单价stockint库存originvarchar(64)产地imagevarchar(255)主图URLdetailtext商品详情statustinyint上架/下架create_timedatetime创建时间订单表orders字段名类型说明idbigint主键order_novarchar(32)订单编号user_idbigint下单用户total_amountdecimal(10,2)订单总金额pay_amountdecimal(10,2)实付金额statustinyint订单状态address_infovarchar(255)收货信息快照pay_timedatetime支付时间refund_timedatetime退款时间订单编号别用数据库自增id直接展示一定要单独生成唯一字符串常见做法是yyyyMMddHHmmss 用户ID后四位 随机数这样看起来更真实也能避免订单号被遍历。3. 后端核心实现与接口设计从登录鉴权到支付对接后端开发的顺序建议是先搭公共模块再做登录鉴权再做商品浏览再做购物车和订单最后做支付。支付放最后是因为它最容易出问题需要单独留时间排查。3.1 统一返回结果与全局异常处理开发效率倍增器几乎所有课程设计都不会要求做统一返回但真实项目里这是必须的基础设施。统一返回结果的做法是封装一个Result类包含code、message、data三个字段。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }有了这个类所有controller返回的格式就统一了。前端小程序封装request时只需要判断code是否为200其他一律弹出message提示不用每个接口单独处理错误分支。全局异常处理要配合RestControllerAdvice使用把业务异常和系统异常分开捕获。业务异常指的是库存不足订单状态不允许取消这类可预期的错误自定义一个BizException抛出时返回对应的错误码系统异常统一返回500和兜底提示不要把堆栈信息暴露给前端。3.2 微信小程序登录与JWT鉴权流程微信小程序登录和Web端的账号密码登录不一样它的核心是基于微信的openid来识别用户。登录流程是这样的小程序端调用wx.login()拿到临时code。小程序把code传给后端接口/api/auth/login。后端用code调用微信的jscode2session接口换取openid和session_key。后端根据openid查用户表查不到就自动注册一个新用户。生成JWT token返回给小程序后续所有请求都在header中携带这个token。这一步有个需要注意的细节调用jscode2session时需要用到小程序的AppID和AppSecret这两个配置绝对不能硬编码在代码里更不能提交到Git仓库。正确的做法是放在application.yml中并在.gitignore里排除掉配置文件。后端拦截器里校验JWT的写法大致如下Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行预检请求 if (OPTIONS.equals(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (StringUtils.hasText(token) token.startsWith(Bearer )) { token token.substring(7); } try { Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.get(userId)); return true; } catch (Exception e) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\message\:\登录已失效请重新登录\}); return false; } } }拦截器注册时要注意放行哪些路径。/api/auth/login、/api/product/list、/api/product/detail这些接口应该允许匿名访问否则微信审核时测试人员拿不到token就进不去小程序。3.3 微信支付V3对接从下单到回调的完整链路微信支付是小程序商城绕不开的模块也是毕设里最容易翻车的部分。网上铺天盖地的教程大多还是V2版本的写法这两年微信支付全面推行V3接口API风格和签名算法都变了。热搜词里出现的小程序微信支付v3对接和由于小程序违规支付功能暂时无法使用说明大家确实在这一块踩了不少坑。支付V3的核心流程可以简化成四步后端调用微信支付V3的/v3/pay/transactions/jsapi接口传入用户openid、订单号、金额、回调地址拿到prepay_id。后端用prepay_id生成小程序端调起支付所需的paySign参数。小程序端调用wx.requestPayment发起支付。支付成功后微信服务器异步通知后端配置的回调地址后端在回调里校验签名、验密、更新订单状态。V3接口的签名方式和V2完全不同。V2用的是MD5签名V3用的是RSA非对称签名而且要求用SHA256withRSA算法请求头中还必须带上Authorization头。// 构造Authorization头简化版实际需要用微信商户API证书的私钥签名 String message POST \n /v3/pay/transactions/jsapi \n timestamp \n nonceStr \n body \n; String signature sign(message, merchantPrivateKey); String authHeader WECHATPAY2-SHA256-RSA2048 mchid\ mchId \,nonce_str\ nonceStr \,signature\ signature \,timestamp\ timestamp \,serial_no\ serialNo \;这块开发时强烈建议先走通沙箱环境再切正式环境否则真金白银试错成本太高。还有一个经常被忽略的问题回调地址必须是公网可访问的HTTPS地址本地开发时可以用内网穿透工具把本地服务暴露出去但要注意穿透工具的外网域名能不能配HTTPS证书不能的话回调就收不到。回调处理有个幂等性问题微信支付回调因为网络原因可能推送多次后端处理时必须先查订单状态如果已经是已支付就直接返回成功应答不再执行重复的业务逻辑。否则同一个订单入账两次金额就对不上了。3.4 MyBatis自动建表与初始化数据配置看到热搜词里有springboot mybatis 当表不存在自动建表这个需求在毕设里确实常见。写论文的时候数据库脚本是一回事但实际部署演示时每次手动执行SQL脚本很麻烦。Spring Boot可以通过配置spring.sql.init来在应用启动时自动执行SQL脚本。spring: sql: init: mode: always schema-locations: classpath:db/schema.sql >const request (url, method, data) { return new Promise((resolve, reject) { wx.request({ url: baseUrl url, method: method, data: data, header: { Content-Type: application/json, Authorization: Bearer wx.getStorageSync(token) }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail: (err) { wx.showToast({ title: 网络异常请稍后重试, icon: none }); reject(err); } }); }); };登录态的token存到wx.getStorageSync里请求时统一从storage取。如果后端返回401可以在success回调里跳转到登录页避免每个页面重复写失效处理。4.2 顶部导航栏高度适配与自定义导航栏热搜词微信小程序顶部导航栏高度是一个很小但特别坑的问题。默认情况下页面用的导航栏是系统自带的胶囊按钮右上角那个胶囊形状的按钮在不同机型上的位置和高度都不一样如果页面里要做自定义导航栏比如背景色是深色的商品详情页就需要动态计算出准确的导航栏高度。官方提供了一套方案用wx.getMenuButtonBoundingClientRect()获取胶囊按钮的位置和尺寸再结合wx.getSystemInfoSync()获取状态栏高度通过公式navigationBarHeight (胶囊顶部 - 状态栏高度) * 2 胶囊高度算出导航栏的自定义高度。业内把这个公式验证过很多次大部分机型都能正确适配。自定义导航栏需要把页面的navigationStyle配置为custom然后在页面顶部留出和导航栏等高的占位块。如果商品详情页要做沉浸式的大图头这个高度计算就必须做对否则返回按钮会被状态栏时间盖住。4.3 购物车与下单支付流程的边界情况购物车模块在这个项目里看起来简单但边界情况比想象中多。比如用户把商品加入购物车后商家在后台把价格改了加入购物车时是10元提交订单时可能变成12元。所以提交订单时不能直接用购物车里的价格必须让后端根据商品ID重新查库计算总价前端传过来的价格只能作为参考不能作为计费依据。下单接口需要开启事务涉及的操作包括创建订单主表记录、创建订单明细记录、扣减商品库存、清空购物车。任何一个环节失败整个事务必须回滚否则会出现订单生成了但库存没扣的脏数据。小程序端调起微信支付的代码相对简单const data await request(/api/payment/create, POST, { orderNo }); wx.requestPayment({ timeStamp: data.timeStamp, nonceStr: data.nonceStr, package: data.package, signType: RSA, paySign: data.paySign, success: () { // 支付成功跳转到订单详情页 wx.redirectTo({ url: /pages/order/detail?orderNo orderNo }); }, fail: (err) { // 用户取消支付或支付失败 wx.showToast({ title: 支付未完成, icon: none }); } });注意package这个字段在微信开发工具里是保留字传给wx.requestPayment时必须写成package但在变量命名时要用packageVal之类的名字否则会报语法错误。4.4 商品详情页与订单评价的实现细节商品详情页通常包含商品轮播图、价格和库存信息、规格选择斤/盒/个、产地介绍、用户评价列表。农产品类目的评价对转化率影响很大评价模块要包含评分、文字内容和图片。图片上传走wx.chooseMedia选图再调用wx.uploadFile传给后端。评价列表的展示要考虑图片九宫格布局不同数量的图片对应不同的样式。这里有个体验细节评价时间显示不要用原始的时间戳字符串建议在服务端格式化好返回或者前端写一个formatTime函数统一处理。5. 从开发到毕设答辩的避坑指南最后这部分我把开发过程中最常遇到的问题和答辩时老师最关心的点系统性地整理出来。这些内容不是教科书里写的那种面面俱到而是基于真实开发经验的高度浓缩。5.1 支付功能被限制之后的临时方案热搜词里有一句话特别真实由于小程序违规支付功能暂时无法使用。微信小程序在个人主体注册时无法开通微信支付即便是企业主体小程序上线后如果涉及虚拟支付或类目问题也可能被封禁支付权限。对于毕设系统这里有两条路可以走第一在项目演示阶段用模拟支付代替真实支付。后端预留一个mockPay接口演示时调用这个接口模拟支付成功接口里模拟微信回调的幂等逻辑照样把订单状态更新为待发货。论文里要说明这是为了演示方便生产环境会切换到真实支付。第二把微信支付渠道设计成接口适配器的模式。定义一个PaymentService接口真实支付和模拟支付分别实现通过配置项切换。这样论文里的系统设计能体现出扩展性答辩时也是一大亮点。5.2 开发调试中的抓包与远程调试技巧小程序开发时经常遇到后端接口没问题但小程序端表现异常的情况。这时候需要检查是不是缓存问题或者是不是开启了调试模式。微信开发者工具里的不校验合法域名选项在开发阶段一定要勾选否则本地请求到不了后端。如果你想看小程序实际发出去的请求数据可以用抓包工具比如热搜词里的burp suite代理查看。抓包的关键是把微信开发者工具的代理设置为抓包工具的监听端口同时安装对应的CA证书。要注意的是新版微信开发者工具支持直接在Network面板里查看请求大部分场景不需要额外抓包。真机调试如果出现问题优先用微信开发者工具的真机调试2.0它可以在手机端实时显示console日志和页面层级比截图反馈给队友再猜问题高效太多。5.3 微信小程序反编译与学习参考的边界热搜词里的微信小程序反编译需要说清楚边界。反编译线上小程序的包查看别人小程序的源码这个行为本身涉嫌违反微信平台规则和知识产权。毕设阶段不建议走这条路学习参考小程序设计应该通过正规渠道比如微信官方文档、开源社区里的示例项目、或者同学之间互相分享自己的代码。把反编译的时间花在读官方文档和看开源项目上收益其实更大。微信小程序的官方文档虽然啰嗦但每个API的边界条件和注意事项都写得很清楚比从反编译代码里猜逻辑靠谱得多。5.4 答辩时必问的技术问题演练毕设答辩时老师大概率会围绕系统设计和技术栈提问。根据经验下面这些问题出现频率最高为什么选择springboot而不是SSH或SSM——回答要点springboot简化了配置、内置服务器、生态成熟、适合快速开发重点强调自动配置原理。数据库表是怎么设计的有哪些字段——把核心表结构讲清楚重点说订单表和商品表之间的关联。JWT和Session有什么区别为什么选JWT——从无状态、跨端、扩展性角度回答。微信支付的回调如果失败怎么办——回答要包含幂等处理、状态机设计、人工补偿机制。系统的安全性怎么保障——从密码加密、SQL注入防护预编译、接口鉴权、参数校验四个维度答。这类问题建议写成一份完整的答辩准备文档把自己系统的每个技术决策都写成为什么这样做的说明答辩前过两遍基本就不会卡壳。5.5 关于代码规范与注释的一个实在建议很多同学认为代码规范是形式主义这个观点要纠正。毕设代码要交到学校有的学校会做查重和代码评审。如果代码里是一堆没有注释的魔法数字、命名全是a1、b2、c3评审老师一眼就会觉得是临时拼凑的哪怕功能全对分数也会打折。比较实际的规范做法是常量用枚举或静态final变量代替方法命名用动词开头业务逻辑复杂的地方写清楚注释DTO和VO字段加中文说明注解。这些不需要花太多时间但产生的效果立竿见影。我复核代码和调试时时间基本都花在那些没有注释的魔数上。
返回列表