news 2026/10/26 10:35:36

微信小程序商城源码实战:从跑通到二次开发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序商城源码实战:从跑通到二次开发

简介:这份微信小程序商城完整源码面向希望快速搭建微信电商应用的开发者与小型商家,提供从商品展示、购物车、订单管理到支付与用户互动的整套实现,适合具备一定前端基础、想通过真实项目理解小程序技术栈的学习者。压缩包共60个文件,约412KB,以24个png界面素材、9个wxss样式表、9个js逻辑脚本、7个json配置、7个wxml结构文件为主,另附说明文档,覆盖页面结构、样式、业务逻辑与全局配置等层次。资源已有7737人学习下载,热度较高。源码按pages页面目录、app全局配置、utils工具库、models数据模型与components自定义组件组织,读者可据此理清商城项目的目录划分与模块职责,对照商品详情、购物车、订单确认等页面理解数据绑定、网络请求与微信支付接口的调用方式,并借鉴全局样式与组件复用思路,为二次开发或独立搭建商城打下基础。

1. 从一份能跑通的微信商城源码说起

去年帮一个做社区团购的朋友看项目,他手里有一套“微信小程序商城完整源码”,解压后前后端加起来几十个目录,第一反应是“这玩意儿能直接上线吗”。答案是不能直接上线,但它能让你在一个下午把商品列表、购物车、下单、支付回调这条主链路跑通,这比从零搭架子省下的时间是以周计的。这套源码面向的是想快速拥有一个可运行商城骨架的开发者:前端是微信小程序原生语法,后端提供商品、订单、用户、支付四类接口,数据库用 MySQL,缓存用 Redis。你拿到它,改改配置、导一下 SQL、填上自己的小程序 AppID,就能在开发者工具里看到首页商品瀑布流。适合谁?适合接私活要快速出原型的人,也适合想研究小程序电商完整链路的学生和转行者。下面我按“它长什么样、怎么跑起来、哪里容易翻车、怎么改成自己的”这个顺序拆。

2. 项目结构与技术栈:先看清骨架再动手

2.1 目录分层与前后端职责边界

这套源码的目录划分比较典型,前端和后端是两个独立工程,靠 HTTP 接口通信,没有用云开发那套。前端目录大致是miniprogram/下分pages、components、utils、images,pages里按业务分index(首页)、category(分类)、cart(购物车)、order(订单)、user(个人中心)五个主包页面,components放商品卡片、数量步进器、空状态这类复用组件。后端目录是server/下分routes、controllers、models、middlewares、config,典型的 MVC 分层,入口是app.js。

我一般拿到这种项目先看两个文件:前端的app.json和后端的app.js。app.json里pages数组的顺序决定了小程序启动页,tabBar配置决定了底部导航有哪几个入口;app.js里能看到中间件注册顺序和路由挂载点。这两个文件看完,整个项目的请求流向就清楚了:小程序页面调utils/request.js封装的请求方法,带上 token 打到后端/api/xxx,后端路由分发到 controller,controller 调 model 查库,返回统一格式的 JSON。

提示:不要一上来就改业务代码,先把app.json的pages和tabBar对一遍,确认你要保留的页面和导航项,删页面时这两处要同步删,否则编译报错。

2.2 技术选型与依赖清单

前端没有用 uni-app 或 Taro 这类跨端框架,是微信原生小程序语法,好处是调试直接、没有编译层,坏处是不能一套代码多端跑。后端是 Node.js + Express,数据库 MySQL 5.7 以上,缓存 Redis,支付走微信支付 V2 或 V3 的统一下单接口(源码里通常封装在services/pay.js)。图片存储一般是本地uploads/目录或者对象存储,源码默认是本地目录,方便你本地跑通。

依赖清单大致如下,装依赖前先确认 Node 版本,Express 4.x 对 Node 14 以上都兼容:

层技术作用
小程序端原生 WXML/WXSS/JS页面渲染与交互
请求封装wx.request 二次封装统一带 token、统一错误处理
服务端Node.js + Express提供 REST 接口
数据库MySQL商品、订单、用户持久化
缓存Redis购物车、会话、库存扣减锁
支付微信支付接口下单与回调验签

这里有个选型细节值得说:购物车数据放 Redis 而不是直接放 MySQL,是因为购物车读写频繁且允许丢失,放缓存里响应快;订单和库存必须落库,库存扣减还要加锁防超卖。源码里如果购物车也走了 MySQL,你二次开发时可以考虑迁到 Redis,这是常见的优化点。

2.3 数据库表结构与接口约定

数据库一般有这几张核心表:goods(商品)、goods_category(分类)、cart(购物车,若走库)、orders(订单主表)、order_item(订单明细)、users(用户)、address(收货地址)。字段设计上,orders表通常有order_no(业务单号)、user_id、total_amount、status(待付款/待发货/已完成/已取消)、pay_time、created_at。order_item存商品快照,注意是快照——下单时把商品名、单价、图片复制一份进明细表,而不是只存goods_id,否则商品改价后历史订单金额就对不上了,这是电商项目里血泪经验级别的一条。

接口约定上,后端统一返回结构一般是:

{ "code": 0, "msg": "ok", "data": {} }

code为 0 表示成功,非 0 表示业务错误,前端request.js里根据code做统一提示。你二次开发时如果要加新接口,务必沿用这个结构,否则前端封装层要改,牵一发动全身。

3. 本地跑通:从导库到首页出商品

3.1 环境准备与依赖安装

先把 Node、MySQL、Redis 装好。Node 建议 16 或 18,太新的版本某些老依赖会报ERR_OSSL_EVP_UNSUPPORTED,遇到就加环境变量NODE_OPTIONS=--openssl-legacy-provider。MySQL 建一个库叫mall,字符集utf8mb4,排序规则utf8mb4_general_ci。Redis 默认端口 6379 起起来就行。

后端进server/目录装依赖:

cd server npm install # 如果卡在某个包,换国内镜像源再试 npm config set registry https://registry.npmmirror.com npm install

前端不需要 npm 安装,直接用微信开发者工具“导入项目”,选miniprogram/目录,填上你自己的 AppID(没有就选测试号)。这一步常见翻车是选了整个仓库根目录而不是miniprogram/,导致工具找不到app.json,报“未找到入口文件”。

3.2 配置数据库与启动后端

后端配置一般在server/config/db.js或.env文件里,把数据库地址、用户名、密码、Redis 地址填成你本地的。然后导入 SQL:

# 假设源码里带了 mall.sql mysql -u root -p mall < mall.sql

启动后端:

cd server node app.js # 或者看 package.json 里有没有 dev 脚本 npm run dev

控制台打印“server running at 3000”之类就说明起来了。这时候用浏览器或 curl 访问http://localhost:3000/api/goods/list,能返回商品 JSON 就说明数据库连通、路由正常。如果返回 500,先看控制台报错,八成是数据库连接配置没对上或者表里没数据。

3.3 小程序端联调与请求域名配置

小程序端在开发者工具里要关掉“不校验合法域名”,因为本地是http://localhost:3000,不是 https。在“详情 → 本地设置”里勾上“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。然后改utils/request.js里的baseUrl:

// utils/request.js const baseUrl = 'http://localhost:3000/api'; // 本地联调改成这个 function request(options) { return new Promise((resolve, reject) => { wx.request({ url: baseUrl + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'content-type': 'application/json', 'Authorization': wx.getStorageSync('token') || '' // 登录后存的 token }, success(res) { if (res.data.code === 0) { 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); } }); }); }

这段封装做了三件事:拼完整 URL、自动带 token、按后端code统一处理成功和失败。baseUrl是唯一要改的地方,上线时换成你的 https 域名。Authorization头里放 token,后端中间件解析它拿到user_id,这是无状态鉴权的常见做法。改完保存,编译,首页应该能看到商品列表了。如果列表空白但接口有数据,检查 WXML 里wx:for的字段名和接口返回字段是否一致,字段名对不上是新手最常踩的坑。

4. 核心链路拆解:下单与支付回调怎么走

4.1 购物车到订单的接口串联

下单链路是整个商城最核心也最容易出 bug 的地方。前端在购物车页勾选商品,点“结算”,先调/api/cart/checkout拿到选中商品的金额汇总和可用地址,再进确认订单页,点“提交订单”调/api/order/create。后端order/create的 controller 大致逻辑是:校验参数 → 查商品实时价格和库存 → 扣库存 → 写orders和order_item→ 返回order_no。这里库存扣减必须用事务,且推荐用UPDATE goods SET stock = stock - ? WHERE id = ? AND stock >= ?这种带条件的更新,影响行数为 0 就说明库存不足,回滚。

// controllers/order.js 片段 async function createOrder(req, res) { const { goodsList, addressId } = req.body; const userId = req.user.id; const conn = await db.getConnection(); try { await conn.beginTransaction(); let total = 0; for (const item of goodsList) { // 条件更新防超卖,stock >= 购买数量才扣 const [result] = await conn.query( 'UPDATE goods SET stock = stock - ? WHERE id = ? AND stock >= ?', [item.count, item.goodsId, item.count] ); if (result.affectedRows === 0) { throw new Error(`商品${item.goodsId}库存不足`); } const [[goods]] = await conn.query('SELECT name, price, cover FROM goods WHERE id = ?', [item.goodsId]); total += goods.price * item.count; // 写订单明细,存商品快照 await conn.query( 'INSERT INTO order_item (order_no, goods_id, goods_name, price, count, cover) VALUES (?,?,?,?,?,?)', [orderNo, item.goodsId, goods.name, goods.price, item.count, goods.cover] ); } await conn.query('INSERT INTO orders (order_no, user_id, total_amount, status, address_id) VALUES (?,?,?,?,?)', [orderNo, userId, total, '待付款', addressId]); await conn.commit(); res.json({ code: 0, data: { orderNo } }); } catch (e) { await conn.rollback(); res.json({ code: 1, msg: e.message }); } finally { conn.release(); } }

这段代码的关键点:事务包裹、条件更新防超卖、明细存快照。orderNo一般用时间戳加随机数生成,保证唯一。status用中文字符串还是数字枚举看源码约定,二次开发时统一即可,但建议用数字枚举,方便后续状态机扩展。

4.2 微信支付统一下单与回调验签

支付这块源码通常封装在services/pay.js,流程是:后端调微信统一下单接口拿到prepay_id,返回给前端,前端调wx.requestPayment拉起支付,用户付完后微信异步回调后端notify_url,后端验签、更新订单状态为“待发货”。回调验签是重点,V2 用 MD5/HMAC-SHA256 签名,V3 用平台证书验签,源码里如果只做了下单没做回调验签,上线后会出现“用户付了钱订单还是待付款”的玄学问题。

// services/pay.js 回调处理片段(V2 思路) function handleNotify(body) { // 1. 验签,防止伪造回调 const sign = body.sign; delete body.sign; const checkSign = md5Sign(body, merchantKey); if (checkSign !== sign) { return { code: 'FAIL', message: '签名错误' }; } // 2. 判断支付结果 if (body.result_code === 'SUCCESS') { // 3. 幂等更新订单,已处理过的直接返回成功 return updateOrderPaid(body.out_trade_no, body.transaction_id) .then(() => ({ code: 'SUCCESS', message: 'OK' })); } return { code: 'FAIL', message: '支付失败' }; }

merchantKey是商户平台设置的 API 密钥,不能写死在代码里提交到仓库,放环境变量。out_trade_no就是你下单时的order_no,靠它关联订单。幂等很重要:微信会重复回调,updateOrderPaid里要先查订单状态,已经是“待发货”就直接返回成功,不要重复加库存或重复发货。

4.3 订单状态机与超时取消

订单状态流转要有明确的状态机:待付款 → 待发货 → 待收货 → 已完成,任意未付款状态可转已取消。超时取消一般用定时任务扫orders表里status='待付款'且created_at超过 30 分钟的记录,取消并回滚库存。源码里如果没有定时任务,你可以用node-cron加一个,每 5 分钟扫一次。回滚库存同样用UPDATE goods SET stock = stock + ?,并且要判断订单当前状态,避免重复回滚。

5. 避坑与排查:上线前必须过的几道坎

5.1 图片不显示与本地路径问题

现象:开发者工具里商品图裂开,控制台 404。原因:源码里图片存的是相对路径如/uploads/xxx.jpg,但小程序请求的是http://localhost:3000/uploads/xxx.jpg,而baseUrl只配了/api,图片路径没拼上域名。解决:在 WXML 里用image标签时,src要么存完整 URL,要么在utils里写个formatImg方法统一拼域名。上线后图片域名还要加到小程序后台的“downloadFile 合法域名”里,否则真机不显示。

5.2 登录态丢失与 token 过期

现象:用户操作一会儿就提示未登录,或者下单时提示 token 无效。原因:token 存了wx.setStorageSync,但后端 token 有效期短,过期后前端没做续期或重新登录。解决:request.js里判断后端返回code为未授权时,清掉本地 token 并跳登录页;或者做双 token(access + refresh)机制。源码如果只做了单 token,二次开发时建议补上刷新逻辑,否则用户体验很差。

5.3 支付回调收不到

现象:本地测试支付成功,但订单状态不变。原因:notify_url必须是公网可访问的 https 地址,本地localhost微信服务器回调不到。解决:本地联调阶段用内网穿透工具把本地端口映射出去(注意这里说的是开发调试用的端口映射,仅用于接收回调),把映射后的 https 地址填到支付配置里;上线后换成正式域名,并确保该地址没有被防火墙拦截、没有做登录鉴权(微信回调不带你的 token)。

5.4 库存超卖与并发下单

现象:压测或多人同时下单时,库存变成负数。原因:先查库存再扣库存,两步之间没有锁。解决:用 4.1 里的条件更新WHERE stock >= ?,或者用 Redis 分布式锁包住扣减逻辑。另外注意事务隔离级别,MySQL 默认可重复读,条件更新本身是原子操作,够用;如果业务复杂到要预占库存,再考虑单独的库存流水表。

5.5 小程序包体积超限

现象:上传代码时提示主包超过 2MB。原因:图片、字体、第三方库都塞在主包。解决:图片放 CDN 或对象存储,不打包进代码;非首屏页面做分包,app.json里配subpackages;删掉没用到的组件和工具库。这套源码如果图片都在images/目录,先统计一下大小,往往删掉几张 banner 图就能过。

6. 二次开发:把它改成你自己的商城

改造的第一步不是写代码,是定边界。我一般会先列一张“保留 / 替换 / 删除”清单:保留用户、订单、支付三条主链路;替换商品分类和首页布局,因为这两个最影响观感;删除用不上的模块比如积分、优惠券(如果源码带了但你的业务不需要),删的时候连带路由、controller、前端页面一起删,别只删页面留一堆死接口。

第二步是抽配置。把baseUrl、支付商户号、数据库连接、图片域名这些全部收到一个配置文件或环境变量里,源码里如果散落在多个文件,先集中。这样你换环境部署时只改一处,不会漏。

第三步是加一个自己的业务模块练手,比如“限时折扣”。做法是:goods表加discount_price和discount_end字段,后端goods/list返回时判断是否在活动期,前端商品卡片显示划线价。这个改动小、链路完整,能让你把“改表 → 改接口 → 改页面”走一遍,走完你对这套源码的掌握程度就够接真实需求了。

验证方法上,我习惯用 Postman 或 curl 把每个接口单独打一遍,确认参数和返回;再用开发者工具的真机调试跑一遍下单支付(测试环境用 1 分钱商品);最后看后端日志有没有未捕获异常。改造过程中最容易忽略的是数据库字段类型和前端展示的匹配,比如金额用decimal(10,2)存,前端toFixed(2)展示,别用浮点数算钱。

从那以后我每次拿到这类商城源码,都强制先跑通“首页出商品 → 加购 → 下单 → 支付回调改状态”这一条最小闭环,再动任何业务代码。这条链路不通,后面改再多都是空中楼阁。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 10:35:21

VxWorks下GPIO模拟MDIO时序驱动PHY寄存器读写实战

简介&#xff1a;面向VxWorks嵌入式开发者的GPIO模拟MDC/MDIO驱动代码&#xff0c;用于在缺乏专用SMI硬件接口的平台上&#xff0c;通过通用GPIO引脚完成对以太网PHY芯片的配置与寄存器读写。资源聚焦MDC时钟生成、MDIO双向数据收发、命令与地址帧时序控制及错误检测等关键环节…

作者头像 李华
网站建设 2026/10/26 10:34:23

扩展MFC DLL开发实战:工程结构、接口设计与状态切换避坑

简介&#xff1a;这是一份面向C与MFC初学者及需要补齐动态链接库基础的开发者的入门示例包&#xff0c;围绕MFC DLL的创建、导出与在客户端程序中调用展开&#xff0c;适合课程实验、自学练手或作为小型项目的参考模板。压缩包共61个文件&#xff0c;约4.79MB&#xff0c;包含h…

作者头像 李华
网站建设 2026/10/26 10:34:22

IM即时通讯私有化部署实战:环境准备到接口联调完整路径

IM 即时通讯私有化部署实战&#xff1a;从环境准备到接口联调的完整落地路径IM 即时通讯私有化部署这两年成了很多技术团队的刚需话题。原因很直接&#xff1a;业务方希望聊天能力长在自己的系统里&#xff0c;账号体系、组织架构、消息记录、文件存储都放在自有服务器上&#…

作者头像 李华
网站建设 2026/10/26 10:34:17

64位OpenGL+GLUT环境配置与链接排错指南

简介&#xff1a;面向计算机图形学课程实验与OpenGL入门开发的64位GLUT库包&#xff0c;主要解决64位工程缺少匹配GLUT头文件与链接库、易出现32/64位不兼容的问题。包内共4个文件&#xff0c;以h头文件、dll动态库、lib导入库及keep占位文件为主&#xff0c;压缩包约127KB&…

作者头像 李华
网站建设 2026/10/26 10:33:57

2022美赛C题F奖复现:黄金比特币日频回测与避坑

简介&#xff1a;这份2022年美赛C题F奖项目资料面向备战美赛、关注金融时间序列建模的学习者&#xff0c;围绕比特币交易策略给出论文、代码与数据的一体化参考。其核心内容包括小波降噪预处理、GRU神经网络价格预测&#xff0c;以及结合动态规划求解买卖时机的策略设计&#x…

作者头像 李华
网站建设 2026/10/26 10:33:57

线性代数 [ 矩阵乘法与线性变换复合的联系 ]

线性代数 两个矩阵相乘&#xff0c;到底把什么乘在了一起&#xff1f; 我们前面已经把矩阵和线性变换联系起来了。一个二维线性变换&#xff0c;只要知道两个标准基向量分别去了哪里&#xff0c;就能够知道整个平面怎样变化。把这两个结果依次放进矩阵的两列&#xff0c;这个…

作者头像 李华