ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

农产品自主供销小程序源码与数据库设计:订单闭环与库存管理

农产品自主供销小程序源码与数据库设计:订单闭环与库存管理 简介这是一套面向计算机相关专业学生与项目实战学习者的微信小程序农产品自主供销系统可作为毕业设计、课程设计或期末大作业的完整参考方案。资源包共1399个文件约14.69MB涵盖前端小程序页面wxml、wxss、js、json、后端Java源码、Vue管理端组件、SQL数据库脚本以及png、svg、jpg等界面素材与图标资源并附带bat启动脚本、配置文件与说明文档目录结构清晰便于按模块查阅与二次开发。系统围绕农产品供销场景包含商品展示、订单管理、用户权限等典型业务模块适合需要快速搭建小程序全栈项目、理解前后端交互流程的学习者。目前已有123人学习下载可帮助读者掌握小程序页面布局、接口调用与数据库设计思路并对照源码完成功能调试与论文撰写节省从零搭建项目的时间成本。1. 农产品自主供销小程序从田间到餐桌的订单闭环怎么搭农产品自主供销小程序解决的是农户、合作社与消费者之间信息不对称、中间环节多、订单散乱的问题。它和普通电商小程序最大的区别在于商品是季节性、非标品库存随采摘波动下单后往往需要人工确认再发货。源码和数据库是这类项目的核心资产——源码决定业务逻辑能不能改数据库决定订单、库存、用户数据怎么存、怎么查、怎么同步。适合想接农业数字化项目的开发者、需要给合作社做自建渠道的运营者以及拿它当微信小程序全栈练手项目的学生。常见做法是前端用微信小程序原生或 uniapp后端用 Node.js 或 Java数据库用 MySQL 或 SQLite。下面按落地顺序拆开讲。2. 技术选型与数据库设计先定骨架再写页面2.1 前端选原生还是 uniapp农产品供销小程序的功能不复杂商品列表、详情、购物车、下单、订单查询、农户入驻。原生微信小程序开发工具链成熟调试方便适合只发微信端的场景。uniapp 的优势是一套代码可以同时出微信小程序和 App如果合作社后续想做个安卓端给收购商用的选 uniapp 更省事。我一般会先问一句只发微信还是多端都要只发微信就原生多端就 uniapp。原生小程序的目录结构很固定pages放页面utils放请求封装components放复用组件。农产品项目里商品卡片、订单状态标签、数量选择器这三个组件复用率最高建议一开始就抽出来。// utils/request.js 请求封装统一处理 baseURL 和 token const BASE_URL https://your-domain.com/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, 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 { reject(res.data); } }, fail(err) { reject(err); } }); }); } module.exports { request };这段封装做了三件事拼 baseURL、带 token、统一处理 401。参数说明BASE_URL换成你自己的后端地址Authorization头里的 token 从本地缓存取登录成功后写入。注意微信小程序要求所有请求域名在后台配置白名单本地调试可以在开发者工具里勾选“不校验合法域名”。2.2 数据库表怎么设计才扛得住订单农产品订单有个特点同一批货可能来自多个农户下单后需要合并发货或分单。数据库设计要提前考虑这个。核心表至少五张用户表、商品表、订单表、订单明细表、农户表。-- 商品表农产品是非标品规格用 JSON 存 CREATE TABLE product ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, farmer_id INT UNSIGNED NOT NULL COMMENT 所属农户, name VARCHAR(100) NOT NULL COMMENT 品名, cover VARCHAR(255) DEFAULT COMMENT 封面图, price DECIMAL(10,2) NOT NULL COMMENT 单价, unit VARCHAR(20) DEFAULT 斤 COMMENT 单位, stock INT NOT NULL DEFAULT 0 COMMENT 库存, spec JSON DEFAULT NULL COMMENT 规格如{重量:5斤,产地:山东}, status TINYINT DEFAULT 1 COMMENT 1上架 0下架, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_farmer (farmer_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 订单表状态机是关键 CREATE TABLE order ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单号, user_id INT UNSIGNED NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0待确认 1已确认 2已发货 3已完成 4已取消, address VARCHAR(255) NOT NULL, remark VARCHAR(255) DEFAULT , created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user (user_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明spec用 JSON 类型存非标规格比开一堆字段灵活status用 TINYINT 做状态机0 到 4 分别对应待确认、已确认、已发货、已完成、已取消。注意订单号order_no要加唯一索引防止重复提交。库存字段stock在高并发下要用UPDATE product SET stock stock - ? WHERE id ? AND stock ?这种带条件的更新避免超卖。2.3 订单状态流转与库存扣减农产品订单不能像普通电商那样下单即扣库存因为农户可能同时在线下卖货。常见做法是下单时冻结库存确认后再实扣。简单点也可以下单直接扣取消时回滚。关键是状态流转要写清楚不然后面查订单全是玄学。// 下单接口核心逻辑Node.js MySQL 示例 async function createOrder(userId, items, address) { const conn await pool.getConnection(); try { await conn.beginTransaction(); let totalAmount 0; const orderNo AG Date.now() Math.floor(Math.random() * 1000); // 1. 校验库存并扣减 for (const item of items) { const [result] await conn.execute( UPDATE product SET stock stock - ? WHERE id ? AND stock ?, [item.count, item.productId, item.count] ); if (result.affectedRows 0) { throw new Error(商品 ${item.productId} 库存不足); } totalAmount item.price * item.count; } // 2. 写订单主表 const [orderResult] await conn.execute( INSERT INTO order (order_no, user_id, total_amount, address, status) VALUES (?, ?, ?, ?, 0), [orderNo, userId, totalAmount, address] ); // 3. 写订单明细 for (const item of items) { await conn.execute( INSERT INTO order_item (order_id, product_id, count, price) VALUES (?, ?, ?, ?), [orderResult.insertId, item.productId, item.count, item.price] ); } await conn.commit(); return { orderNo, orderId: orderResult.insertId }; } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); } }逻辑说明整个下单过程放在一个事务里库存扣减用带条件的 UPDATE只有stock count时才扣成功否则抛错回滚。参数说明items是前端传来的商品数组每项包含productId、count、priceaddress是收货地址字符串。注意价格要在后端重新查一遍不能信前端传的price否则会被改价。3. 小程序端核心页面与接口联调3.1 商品列表与详情页的数据绑定商品列表页要处理分页加载和下拉刷新。农产品图片多建议列表只传缩略图详情页再加载大图。微信小程序的onReachBottom和onPullDownRefresh是两个关键生命周期。// pages/goods/list.js const { request } require(../../utils/request); Page({ data: { list: [], page: 1, pageSize: 10, hasMore: true, loading: false }, onLoad() { this.loadList(); }, onPullDownRefresh() { this.setData({ page: 1, list: [], hasMore: true }); this.loadList().then(() wx.stopPullDownRefresh()); }, onReachBottom() { if (this.data.hasMore !this.data.loading) { this.loadList(); } }, async loadList() { if (this.data.loading) return; this.setData({ loading: true }); try { const res await request({ url: /product/list, data: { page: this.data.page, pageSize: this.data.pageSize } }); const newList this.data.list.concat(res.data.list); this.setData({ list: newList, page: this.data.page 1, hasMore: res.data.list.length this.data.pageSize, loading: false }); } catch (err) { this.setData({ loading: false }); wx.showToast({ title: 加载失败, icon: none }); } } });参数说明page从 1 开始每次加载后加 1hasMore根据返回条数是否等于pageSize判断。注意onReachBottom里要加loading锁否则快速滑动会重复请求。下拉刷新时重置page和list并调用wx.stopPullDownRefresh()停止动画。3.2 购物车与下单页的本地存储购物车数据建议存在本地缓存下单时再提交到后端。微信小程序的wx.setStorageSync同步存储适合购物车这种小数据量场景。// 加入购物车 function addToCart(product, count) { let cart wx.getStorageSync(cart) || []; const index cart.findIndex(item item.productId product.id); if (index -1) { cart[index].count count; } else { cart.push({ productId: product.id, name: product.name, price: product.price, cover: product.cover, count: count }); } wx.setStorageSync(cart, cart); wx.showToast({ title: 已加入购物车, icon: success }); } // 下单页读取购物车 Page({ data: { cart: [], total: 0 }, onShow() { const cart wx.getStorageSync(cart) || []; const total cart.reduce((sum, item) sum item.price * item.count, 0); this.setData({ cart, total: total.toFixed(2) }); } });逻辑说明购物车用productId去重已存在就加数量。参数说明cart数组每项包含商品基本信息和数量total用reduce累加保留两位小数。注意下单成功后要清空购物车wx.removeStorageSync(cart)否则会重复下单。3.3 订单列表与状态筛选订单列表要按状态筛选微信小程序的picker或自定义 tab 都可以。接口传status参数后端按状态查。// pages/order/list.js Page({ data: { tabs: [全部, 待确认, 已确认, 已发货, 已完成], activeTab: 0, orders: [] }, onTabChange(e) { const index e.currentTarget.dataset.index; this.setData({ activeTab: index }); this.loadOrders(index 0 ? null : index - 1); }, async loadOrders(status) { const res await request({ url: /order/list, data: { status: status } }); this.setData({ orders: res.data.list }); } });参数说明status为null时查全部否则按 0 到 3 筛选。注意后端要校验user_id只能查自己的订单不能查别人的。4. 避坑与排查农产品小程序最容易翻车的 5 个点4.1 库存超卖并发下单时 stock 扣成负数现象两个用户同时下单同一商品库存只剩 1结果两个人都下单成功库存变成 -1。原因先查库存再更新中间有时间差。解决用UPDATE product SET stock stock - ? WHERE id ? AND stock ?根据affectedRows判断是否扣成功失败就回滚事务。4.2 订单号重复高并发下 Date.now() 不够用现象同一毫秒内多个订单生成相同订单号插入报唯一索引冲突。原因Date.now()毫秒级精度不够。解决订单号加随机数或用户 ID 后缀或者用数据库自增 ID 拼接。更稳的做法是用 Redis 的INCR生成序列号。4.3 图片加载慢农产品大图没压缩现象商品列表页图片加载慢用户滑半天出不来。原因农户上传的原图几 MB 一张直接返回给小程序。解决后端上传时用sharp或jimp压缩生成缩略图和大图两个版本列表用缩略图详情用大图。微信小程序本身也有image组件的lazy-load属性记得打开。4.4 数据库连接池耗尽请求多了就卡死现象小程序用一会儿就报 500后端日志显示Too many connections。原因每次请求都新建数据库连接没释放。解决用连接池Node.js 里mysql2的createPoolJava 里用 HikariCP。连接数根据并发量设一般 10 到 20 够用。注意conn.release()要放在finally里确保异常时也释放。4.5 微信登录态过期token 失效没跳转现象用户用了一段时间后下单提示未登录但页面没跳登录页。原因token 过期后接口返回 401前端没统一处理。解决在request封装里判断 401清除本地 token 并跳转登录页。注意登录页要能回跳到原页面用wx.navigateTo的url带参数或者用getCurrentPages拿当前路径。5. 源码二次开发与数据库同步的进阶技巧拿到源码后第一件事不是改页面而是把数据库跑起来。常见做法是导入sql文件改config里的数据库连接信息然后跑一遍下单流程看哪张表报错。农产品项目的数据库同步如果只是本地开发和线上环境用mysqldump导出导入就行。命令是mysqldump -u root -p dbname backup.sql恢复用mysql -u root -p dbname backup.sql。注意字符集要统一utf8mb4否则农户名字里的生僻字会乱码。二次开发时我一般会先加一个admin后台用来管理农户和商品。后台不用太复杂能用就行。接口复用小程序端的加个role字段判断权限。数据库层面给farmer表加个status字段审核通过才能上架商品。验证方法本地跑通后用微信开发者工具的“真机调试”扫一遍重点看下单、支付、订单查询三个流程。支付需要商户号没有的话可以先模拟把wx.requestPayment换成wx.showToast提示成功先把业务逻辑跑通。最后说个血泪经验农产品小程序最怕的是农户不会用。后台要做得极简最好一个页面搞定上架和改价。数据库里product表的spec字段虽然灵活但农户填不明白不如直接开几个固定字段重量、产地、保质期。源码是死的业务是活的别为了技术优雅牺牲可用性。希望帮到你。本文还有配套的精品资源点击获取
返回列表