微信小程序商城源码实战:从部署到二次开发全指南 简介这份微信小程序商城完整源码面向希望快速搭建微信电商应用的开发者与小型商家提供从商品展示、购物车、订单管理到支付与用户互动的整套实现方案适合具备一定前端基础、想通过真实项目理解小程序技术栈的学习者。压缩包共60个文件约412KB以24个png界面素材、9个wxss样式表、9个js逻辑脚本、7个json配置、7个wxml页面结构为主另附说明文档覆盖页面目录、全局配置、工具库、数据模型与自定义组件等模块结构清晰便于按功能检索。目前已有7737人学习下载。读者可从中获得可直接运行的商城原型对照WXML、WXSS与JavaScript的分层组织理解数据绑定、事件监听、网络请求与微信支付接口调用并借鉴商品详情、购物车、订单确认等页面的设计思路快速复用到自己的小程序项目中。1. 从一份能跑通的微信商城源码说起去年帮一个做本地生鲜的朋友搭线上店预算有限外包报价看完直接劝退。后来翻到一份微信小程序商城的完整源码前后端加数据库脚本一应俱全部署完当天就能下单。这件事让我意识到对中小商家和独立开发者来说一套结构清晰、能直接跑起来的微信商城源码比从零搭架子省下的不只是钱更是反复踩坑的时间。这份资源适合三类人——想快速上线卖货的小商家、拿它当毕设或练手项目的学生、以及需要一套电商骨架做二次开发的工程师。它覆盖商品展示、购物车、订单流转、用户中心这些电商标配模块技术栈围绕微信小程序原生框架加后端接口服务展开下面按「能干什么、怎么跑、怎么改」的顺序拆开讲。2. 项目定位与技术栈这套商城源码到底由哪些部分组成拿到一份源码先别急着npm install花十分钟把目录和依赖看清楚后面能少走很多弯路。这套微信小程序商城的整体结构是典型的前后端分离小程序端负责页面渲染和交互后端提供 REST 接口和数据库读写两边通过 HTTPS 通信。理解这个分层是后面部署和改造的前提。2.1 前端小程序端原生框架 组件化页面小程序端用的是微信原生开发方式没有上 uni-app 或 Taro 这类跨端框架。好处是包体小、调试直接、和微信能力贴合度高代价是不能一套代码多端跑。对只做微信渠道的商城来说这个取舍是合理的。页面组织大致分四层pages放业务页面首页、分类、商品详情、购物车、订单、个人中心components放可复用组件商品卡片、数量选择器、价格栏、空状态utils放请求封装和格式化工具app.js/app.json管全局配置和生命周期。常见做法是把网络请求统一收口到一个request.js里统一加 token、统一处理错误码避免每个页面各写一套wx.request。// utils/request.js —— 统一请求封装 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, // 登录后把 token 存进 storage每次请求带上 Authorization: wx.getStorageSync(token) || }, success(res) { if (res.statusCode 200 res.data.code 0) { resolve(res.data.data); } else if (res.data.code 401) { // token 失效跳登录 wx.navigateTo({ url: /pages/login/login }); reject(res.data); } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }); reject(res.data); } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); } module.exports { request };这段封装的关键点有三个BASE_URL是唯一需要改的域名配置部署时换成自己的备案域名Authorization头从本地缓存取 token登录成功后写入即可code 0是约定的成功码后端返回结构要和它对齐。参数options.url是相对路径options.data在 GET 时会拼成查询串、POST 时作为 JSON body这个行为由wx.request决定不用额外处理。2.2 后端接口层REST 风格的路由与鉴权后端承担商品、订单、用户、支付回调这几块逻辑。从源码结构看路由按资源划分/api/goods、/api/cart、/api/order、/api/user每个资源下再分列表、详情、增删改。鉴权用 JWT 是这类项目的常见做法——登录接口签发 token其余接口在中间件里校验。// middleware/auth.js —— JWT 鉴权中间件Node/Express 风格 const jwt require(jsonwebtoken); const SECRET process.env.JWT_SECRET || change-me-in-prod; module.exports function auth(req, res, next) { const token (req.headers.authorization || ).replace(Bearer , ); if (!token) { return res.json({ code: 401, msg: 未登录 }); } try { // 解出用户 id 挂到 req 上后续接口直接用 req.user jwt.verify(token, SECRET); next(); } catch (e) { return res.json({ code: 401, msg: 登录已过期 }); } };SECRET一定要通过环境变量注入源码里如果写死了默认值上线前必须改掉这是血泪经验。jwt.verify失败会抛异常所以用 try/catch 包住统一返回 401 让前端跳登录。req.user里通常带userId和openid订单归属判断就靠它。2.3 数据层商品、订单、用户三张核心表数据库脚本一般随源码附带核心表就三张goods商品、orders订单、users用户。商品表存标题、价格、库存、封面图、详情富文本订单表存订单号、用户 id、商品快照、金额、状态用户表存 openid、昵称、头像。订单里存商品快照而不是只存商品 id是为了防止商品改价后历史订单金额对不上这个设计在电商里几乎是必须的。表名关键字段作用goodsid, title, price, stock, cover, detail商品信息与库存ordersorder_no, user_id, snapshot, amount, status订单主表与状态流转usersid, openid, nickname, avatar微信用户身份把这三层看清楚后面部署时就知道哪些配置必须改、哪些可以先用默认值跑通。3. 本地跑起来从环境准备到首单走通的完整步骤源码能不能跑通往往卡在几个不起眼的配置上。这一章按实际部署顺序走一遍先备环境再导数据库然后起后端、配小程序最后用开发者工具走一单验证链路。每一步都给出可抄的命令和要改的文件位置。3.1 环境准备与依赖安装需要的东西不多Node.js建议 16 以上、MySQL5.7 或 8.0、微信开发者工具。后端如果是 Node 项目进目录后先看package.json里的脚本通常npm install装依赖npm run dev起开发服务。# 进入后端目录 cd server # 安装依赖国内网络慢可以换镜像源 npm install # 复制环境变量模板按实际填写 cp .env.example .env # 启动开发服务 npm run dev.env里一般要填数据库连接、JWT 密钥、微信小程序的appid和secret。数据库连接格式是mysql://用户名:密码主机:端口/库名本地默认127.0.0.1:3306。微信的appid和secret在小程序后台「开发管理」里拿支付相关还要配商户号和密钥第一遍跑通可以先不接支付用模拟支付回调验证订单状态。3.2 数据库导入与初始数据源码里通常有个sql目录放着建表脚本和初始数据。用命令行导入最稳# 先建库 mysql -u root -p -e CREATE DATABASE mall DEFAULT CHARSET utf8mb4; # 导入结构和初始数据 mysql -u root -p mall sql/init.sql导入后连上去看一眼goods表里应该有测试商品users表可能是空的。如果脚本里没有初始商品手动插两条测试数据否则首页会是空白容易误以为接口挂了。字符集用utf8mb4不然商品标题里的表情符号会存不进去这个坑我踩过不止一次。3.3 小程序端配置与真机预览用微信开发者工具打开小程序目录第一件事是改project.config.json里的appid换成自己的否则没法真机预览。然后检查utils/request.js里的BASE_URL本地调试填http://127.0.0.1:3000/api但要注意开发者工具里需要勾选「不校验合法域名」真机调试则必须用 HTTPS 域名。// project.config.json 关键片段 { appid: wx你的appid, projectname: mall-miniprogram, setting: { urlCheck: false, es6: true } }urlCheck: false只在开发阶段用上线前必须打开并配置合法域名。es6: true让工具做语法转换源码里用了 async/await 的话这项要开。配置完点编译首页能出商品列表说明前后端已经通了。3.4 走通一单登录、加购、下单、支付回调验证链路完整性的最好方式是走一单。流程是进个人中心触发登录wx.login拿 code后端换 openid 并签发 token→ 商品详情点加购 → 购物车勾选结算 → 提交订单生成order_no→ 支付回调更新订单状态。// pages/cart/cart.js —— 结算提交核心逻辑 async submitOrder() { const checked this.data.cartList.filter(i i.checked); if (!checked.length) { return wx.showToast({ title: 请选择商品, icon: none }); } const res await request({ url: /order/create, method: POST, data: { items: checked.map(i ({ goodsId: i.goodsId, count: i.count })), addressId: this.data.addressId } }); // res 里返回 orderNo 和支付参数 wx.navigateTo({ url: /pages/pay/pay?orderNo${res.orderNo} }); }items只传商品 id 和数量价格和库存由后端重新计算前端传的价格一律不信这是防篡改的基本要求。addressId来自地址选择页没选地址时后端应返回明确错误码。支付页拿到orderNo后调起wx.requestPayment回调地址在后端配置收到通知后把订单状态从「待支付」改成「已支付」并扣库存。第一遍不接真实支付时可以在后端加一个测试接口手动改状态把整条链路验证完。4. 核心模块拆解商品、购物车、订单三条主线的代码实现跑通之后就该看代码了。电商的复杂度集中在三个地方商品数据的组织方式、购物车的状态管理、订单的状态机。这一章逐个拆重点讲清楚每个模块的接口约定和关键实现方便你判断哪些能直接复用、哪些要按业务改。4.1 商品模块列表分页与详情富文本渲染商品列表接口通常是分页的参数page和pageSize返回list和total。小程序端用onReachBottom触发加载更多把新数据 concat 到原列表。// pages/index/index.js —— 商品列表分页加载 data: { list: [], page: 1, hasMore: true }, async loadGoods() { if (!this.data.hasMore) return; const res await request({ url: /goods/list, data: { page: this.data.page, pageSize: 10 } }); this.setData({ list: this.data.list.concat(res.list), page: this.data.page 1, hasMore: this.data.list.length res.list.length res.total }); }, onReachBottom() { this.loadGoods(); }hasMore的判断用「已加载数量 总数」比单纯看返回条数是否小于pageSize更准因为最后一页可能刚好整除。setData里用 concat 而不是 push避免直接改原数组导致视图不更新。商品详情页的富文本用rich-text组件渲染后端存的 HTML 要先做一次白名单过滤防止脚本注入这个安全处理不能省。4.2 购物车模块本地与服务端双存储的取舍购物车有两种做法纯本地存wx.setStorageSync或者存服务端。这套源码用的是服务端存储好处是换设备不丢、能和库存联动代价是每次操作都要请求。加购接口要处理「已存在则数量累加」的逻辑。// 后端 cart 控制器 —— 加购存在则累加 async addToCart(req, res) { const { goodsId, count } req.body; const userId req.user.userId; const goods await Goods.findByPk(goodsId); if (!goods || goods.stock count) { return res.json({ code: 1, msg: 库存不足 }); } const [item, created] await Cart.findOrCreate({ where: { userId, goodsId }, defaults: { count } }); if (!created) { await item.update({ count: item.count count }); } res.json({ code: 0, data: item }); }findOrCreate是 Sequelize 的方法返回[实例, 是否新建]用它省掉先查后插的两次往返。库存校验放在加购时做一次、下单时再做一次因为加购到下单之间库存可能被抢。userId从 token 里取不从前端传否则谁都能改别人的购物车。4.3 订单模块状态机与库存扣减的时序订单状态一般有待支付、已支付、已发货、已完成、已取消。状态流转必须单向不能从「已完成」退回「待支付」。下单时的库存扣减有两种策略下单即扣可能被恶意占库存和支付成功才扣可能超卖。中小商城常见做法是下单时用数据库行锁扣减超时未支付再回补。// 下单核心事务内扣库存 建订单 async createOrder(req, res) { const t await sequelize.transaction(); try { const { items } req.body; let amount 0; const snapshot []; for (const it of items) { // 行锁防止并发超卖 const goods await Goods.findByPk(it.goodsId, { lock: t.LOCK.UPDATE, transaction: t }); if (!goods || goods.stock it.count) { throw new Error(库存不足); } await goods.decrement(stock, { by: it.count, transaction: t }); amount goods.price * it.count; snapshot.push({ goodsId: goods.id, title: goods.title, price: goods.price, count: it.count }); } const order await Order.create({ orderNo: genOrderNo(), userId: req.user.userId, snapshot: JSON.stringify(snapshot), amount, status: PENDING }, { transaction: t }); await t.commit(); res.json({ code: 0, data: { orderNo: order.orderNo, amount } }); } catch (e) { await t.rollback(); res.json({ code: 1, msg: e.message }); } }lock: t.LOCK.UPDATE是行级排他锁保证同一商品并发下单时库存不会扣成负数。整个循环在一个事务里任何一步失败全部回滚不会出现「扣了库存没建订单」的脏数据。snapshot存 JSON 字符串包含下单时的标题和价格后续商品改价不影响历史订单。genOrderNo一般用时间戳加随机数保证唯一且可读。5. 避坑与排查部署和联调阶段最容易翻车的五个点源码跑不起来八成不是代码本身的问题而是环境和配置。下面五条是这类项目里出现频率最高的每条按「现象 → 原因 → 解决」写照着排查能省不少时间。5.1 首页空白但接口返回 200现象开发者工具里首页一片空白Network 面板看接口状态码是 200。原因多半是返回结构和前端约定不一致比如后端返回{ code: 0, data: { list: [] } }前端却取res.list。解决在request.js里统一resolve(res.data.data)页面里按res.list取或者对照接口文档把字段名对齐。联调前先把一个接口的返回结构打印出来确认比猜快得多。5.2 真机预览请求全部失败现象开发者工具里正常扫码到手机上所有请求报错。原因有两个一是BASE_URL用了127.0.0.1手机访问不到你电脑的本地服务二是域名没在小程序后台配置为合法域名。解决本地真机调试用局域网 IP如http://192.168.x.x:3000并开启「不校验合法域名」正式环境必须用备案过的 HTTPS 域名并在后台「开发设置」里加到 request 合法域名列表。5.3 登录后接口仍返回 401现象登录成功但后续请求一直提示未登录。原因通常是 token 没存进去或者请求头字段名对不上。检查wx.setStorageSync(token, ...)是否执行、request.js里Authorization拼的是Bearer加 token 还是裸 token后端解析方式要和它一致。还有一种情况是 JWT 过期时间设得太短调试期间可以临时调长。5.4 订单金额和商品页显示不一致现象下单后订单金额比商品页看到的多几毛钱。原因一般是浮点数计算0.1 0.2这类问题在金额上很致命。解决金额统一用「分」为单位存整数展示时再除以 100或者后端用decimal类型并在计算时保留两位。前端传的价格一律不用全部后端重算这条前面提过但翻车的人依然很多。5.5 支付回调收不到现象调起支付成功但订单状态一直是待支付。原因回调地址没配、回调地址不是公网可达、或者回调里验签失败直接返回了错误。解决先用支付平台的回调模拟工具打一次自己的接口确认能进业务逻辑验签失败要打日志看具体字段回调处理必须幂等同一笔订单重复通知不能重复扣库存。6. 二次开发改造从能跑到能用的几个具体切入点源码跑通只是起点真正花时间的是按自己的业务改。这套商城的骨架比较干净改造空间主要在三个方向换主题和页面结构、加营销能力、接真实支付和物流。下面给几个具体切入点附上验证方法避免改完不知道对不对。6.1 换肤与页面结构调整小程序的主题色集中在app.wxss的 CSS 变量里改几个变量就能换整体色调。页面结构调整优先动components里的商品卡片因为它在首页、分类、搜索页复用改一处全局生效。改完用开发者工具的「真机调试」看一遍不同机型重点看rpx换算在窄屏上会不会挤。我一般会先复制一份components/goods-card做实验确认没问题再替换原组件留个后悔药。6.2 加优惠券与满减营销是商城二次开发的高频需求。最小实现是在orders表加coupon_id和discount字段下单时先算原价、再查可用券、最后算实付。优惠券表存面额、门槛、有效期、适用商品范围。验证方法造一张「满 100 减 10」的券下单金额 99 时不可用、101 时可用边界值必须测。注意优惠金额也要进订单快照否则退款时算不清。6.3 接真实支付与物流查询支付部分把测试回调换成真实商户配置重点在验签和幂等。物流查询一般接快递查询接口订单发货时存运单号详情页按需拉取轨迹。改造顺序建议先支付后物流因为支付不通整个交易链路都是断的。每次改完支付相关代码我都会用一笔一分钱的真实订单走一遍全流程确认回调、状态、库存三处都对得上这个习惯帮我挡掉过好几次线上事故。6.4 改造后的回归清单改完别急着上线按这张表过一遍检查项验证方式通过标准登录态清缓存后重新登录能拿到 token 并正常请求下单链路走一单完整流程订单状态、库存、金额一致支付回调模拟重复通知订单不重复扣库存多机型真机调试 2 到 3 款布局不溢出、按钮可点这份清单不长但每次改完核心链路都走一遍比事后查日志省心得多。希望帮到你。本文还有配套的精品资源点击获取