简介萤火商城 v2.0.8 多端版是一套面向中小型电商创业者、独立开发者与二次开发学习者的轻量级前后端分离电商系统源码支持微信小程序、H5、公众号与 APP 多端运行前后端代码 100% 开源所见即所得便于快速搭建个性化独立商城。技术栈采用 PHP7.4 ThinkPHP6.0 Uni-APP Ant Design Vue兼顾轻量、高性能与可持续稳定可学习亦可商用。资源包共约 2000 个文件压缩后 17.34MB以 1175 个 PHP 后端逻辑文件、155 个 JS 脚本、46 个 CSS 样式、48 个 JSON 配置、32 个 HTML 页面及 136 个 PNG、45 个 GIF 等图片资源为主另含 SQL 建表脚本、Markdown 说明文档与字体文件目录结构完整清晰。目前已有 267 人学习下载适合希望研究多端商城架构、进行二次开发或搭建独立商城的开发者参考可从中掌握前后端分离项目的组织方式与核心业务实现思路。1. 萤火商城 v2.0.8 多端版商业落地一套代码如何同时喂饱小程序、H5 和 App如果你手里正捏着一个「一套后端要同时撑起微信小程序、H5、App 三端」的商城需求又不想为每个端各养一个前端团队那萤火商城 v2.0.8 多端版这个方向大概率会被你翻出来。它本质上是把「多端复用」和「商业闭环」这两件事绑在一起做后端一套 API前端按端拆包营销、订单、支付、分销这些商业模块共用同一套领域模型。我最早接触这类多端商城是在一个虚拟项目里当时团队只有三个人却要交付小程序加 H5 两个入口最后就是靠这种「后端统一、前端分端」的结构把工期压下来的。这篇不聊虚的只讲这套东西怎么跑起来、参数怎么配、哪些地方最容易翻车。2. 多端版的分层结构为什么不是「一套代码编译到所有端」2.1 后端统一 API 与前端分端打包的边界多端版最容易让人误解的一点是以为「多端」等于「一份前端代码编译出所有端」。真做过就知道小程序和 H5 在登录态、支付、路由、甚至图片懒加载上的差异硬用条件编译抹平后期维护成本会指数级上升。萤火商城 v2.0.8 这类方案通常采用的分层是后端提供一套 RESTful 或类 RESTful 的 API前端按端维护各自的页面壳和交互但共享请求层、状态管理和业务工具函数。具体到目录结构常见做法是后端一个服务前端一个仓库里按platform分目录或者用 monorepo 管理多个包。下面是一个典型的后端 API 分层示意用 Python 伪代码表达重点看它怎么把「商业逻辑」和「端差异」隔开# 后端 API 分层示意商业逻辑与端适配解耦 class OrderService: 订单领域服务所有端共用同一套下单、拆单、计价逻辑 def create_order(self, user_id, cart_items, platform): # 1. 统一校验库存与价格不区分端 self._check_stock(cart_items) total self._calc_total(cart_items) # 2. 平台差异只影响「支付方式」和「回调地址」这类外围参数 pay_channel self._resolve_pay_channel(platform) order Order(user_iduser_id, totaltotal, platformplatform) # 3. 落库后返回统一订单号由前端各自决定跳转 return self.repo.save(order), pay_channel def _resolve_pay_channel(self, platform): # 小程序走微信支付H5 走聚合支付App 走原生 SDK 回调 mapping {mini: wxpay, h5: h5pay, app: apppay} return mapping.get(platform, h5pay)这段代码的关键不在语法而在platform这个参数只用来决定支付通道和回调不参与价格计算和库存扣减。很多团队翻车就翻在这里把「小程序专享价」写进订单服务结果 H5 端下单时价格对不上排查半天发现是平台判断写反了。参数上platform建议用枚举而不是字符串避免mini和miniprogram混用导致支付通道取不到值。2.2 商业模块的领域模型怎么切「商业」这个词在标题里不是装饰。萤火商城 v2.0.8 多端版要撑起的商业能力通常包括商品、订单、支付、退款、优惠券、分销、拼团、秒杀这几块。切领域模型时我一般会按「交易主链路」和「营销旁路」分开交易主链路商品→购物车→订单→支付→售后必须强一致营销旁路优惠券、拼团、分销允许最终一致。下面这张表是我在虚拟项目里整理的核心表职责划分可以直接对照建表表名职责是否分端关键字段goods商品 SPU 与 SKU否spu_id, sku_id, price, stockorder订单主表否order_no, user_id, total, statusorder_item订单明细否order_no, sku_id, num, pricecoupon优惠券模板否coupon_id, type, threshold, amountuser_coupon用户领券记录否user_id, coupon_id, statusdistribution分销关系否user_id, parent_id, level注意所有表都没有platform字段作为业务主键的一部分。平台信息只落在订单的扩展字段或日志里用于统计和风控不参与业务唯一性判断。这是多端版能保持数据一致的前提。2.3 环境准备与最小启动步骤假设你已经拿到一份可运行的后端代码和前端分端工程最小启动路径通常是装依赖、配数据库、导初始数据、起后端、起前端。以常见的 Node 后端加 Vue 前端为例命令如下# 1. 后端依赖与配置 cd server npm install cp .env.example .env # 编辑 .env填入数据库连接、Redis、支付商户号等 npm run migrate # 执行数据库迁移 npm run seed # 导入初始商品与管理员账号 npm run dev # 启动后端默认监听 3000 # 2. 小程序端 cd ../client-mini npm install npm run dev:mp-weixin # 生成小程序包用开发者工具打开 dist 目录 # 3. H5 端 cd ../client-h5 npm install npm run dev # 本地起 H5默认 8080参数说明.env里最不能错的是数据库连接和 Redis 前缀。多端版通常用 Redis 存购物车和登录态如果多个端共用一个 Redis 实例务必给每个端加不同的 key 前缀否则会出现「小程序登录后 H5 也变成已登录」的玄学问题。npm run seed导入的管理员账号一般写在脚本注释里第一次登录后立刻改密码。3. 多端登录态与支付回调最容易出错的三个环节3.1 小程序、H5、App 的登录态怎么统一多端版最烦人的不是业务逻辑是登录态。小程序用code换openidH5 用手机号加验证码或公众号授权App 用账号密码或一键登录。三套入口但后端要发同一种 token。我一般会做一个AuthAdapter把不同端的凭证统一换成内部user_id再签发 JWT。// 登录适配层不同端凭证统一换 token async function loginByPlatform(platform, payload) { let userId; switch (platform) { case mini: // payload.code 来自 wx.login const session await wxCode2Session(payload.code); userId await findOrCreateUserByOpenid(session.openid); break; case h5: // payload.phone payload.smsCode await verifySmsCode(payload.phone, payload.smsCode); userId await findOrCreateUserByPhone(payload.phone); break; case app: userId await verifyPassword(payload.account, payload.password); break; default: throw new Error(unsupported platform); } // 统一签发 JWT过期时间按端可配 return signJwt({ userId, platform }, { expiresIn: platform mini ? 7d : 2h }); }逻辑说明findOrCreateUserByOpenid和findOrCreateUserByPhone内部要做用户合并否则同一个人在小程序和 H5 会变成两个账号订单对不上。参数上JWT 过期时间建议小程序长一点7 天H5 短一点2 小时因为 H5 更容易被分享出去。signJwt的密钥必须放在环境变量里不要硬编码。3.2 支付回调的幂等与验签支付回调是多端版另一个血泪重灾区。微信支付、支付宝、聚合支付的回调格式不同但共同要求是必须幂等、必须验签、必须返回成功标识。我见过最典型的翻车是回调处理超时支付平台重试三次结果订单被重复发货。# 支付回调处理幂等 验签 快速返回 def handle_pay_callback(platform, raw_body, headers): # 1. 验签失败直接拒绝 if not verify_sign(platform, raw_body, headers): return {code: FAIL, msg: sign error} data parse_callback(platform, raw_body) order_no data[out_trade_no] # 2. 幂等用 Redis 锁 订单状态双重判断 lock_key fpay_callback:{order_no} if not redis.set(lock_key, 1, nxTrue, ex30): return {code: SUCCESS, msg: duplicate} order order_repo.find_by_no(order_no) if order.status paid: return {code: SUCCESS, msg: already paid} # 3. 更新订单并触发后续发货、分销佣金 order_repo.mark_paid(order_no, data[transaction_id]) trigger_post_pay(order) return {code: SUCCESS, msg: ok}参数说明nxTrue, ex30表示锁 30 秒足够处理一次回调。trigger_post_pay里如果有耗时操作比如发短信、算分销建议丢进队列异步做不要在回调里同步跑否则支付平台等不到响应会一直重试。验签函数verify_sign每个平台实现不同但一定要用官方 SDK 或官方文档给的算法不要自己造。3.3 多端商品详情与库存扣减的并发处理商品详情页在三端展示逻辑不同但库存扣减必须走同一套。常见做法是下单时预扣库存支付成功后实扣超时未支付回滚。这里用 Redis 做预扣数据库做最终一致。-- 库存预扣先查再扣用版本号防并发 UPDATE goods_sku SET stock stock - #{num}, version version 1 WHERE sku_id #{skuId} AND stock #{num} AND version #{version}; -- 影响行数为 0 说明库存不足或并发冲突需重试或报错这条 SQL 的关键是stock #{num}和version两个条件同时成立才更新。如果只用stock num高并发下会出现超卖。version从查询时带过来更新失败就重查重试一般重试 3 次还失败就提示「手慢了」。注意 Redis 预扣和这条 SQL 要配合使用Redis 挡掉大部分无效请求数据库做最终扣减。4. 避坑与排查多端版上线前必须过的五道坎4.1 坑一小程序包体积超限主包塞了太多商业模块现象微信开发者工具提示主包超过 2MB上传失败。原因把商品、订单、分销、拼团所有页面都放主包没有做分包。解决按业务域拆分包商品和订单放主包分销、拼团、秒杀放子包用subPackages配置。分包预下载在首页onLoad里触发用户点进子包时基本已经下载完。4.2 坑二H5 端支付回调地址配错支付成功但订单未更新现象用户 H5 支付扣款成功但订单状态还是「待支付」。原因.env里H5_PAY_NOTIFY_URL填的是内网地址或带端口的本地地址支付平台回调不到。解决回调地址必须是公网可访问的 HTTPS 域名且不能带端口。排查时先看支付平台后台的回调日志再查后端访问日志有没有收到请求。4.3 坑三多端共用 Redis 导致登录态串端现象小程序登录后H5 刷新也变成已登录但用户信息不对。原因JWT 存在 Redis 时 key 没加端前缀或者前端存 token 的 localStorage key 相同。解决Redis key 用token:{platform}:{userId}前端存储 key 用token_mini、token_h5区分。这个坑很隐蔽因为本地开发时往往只跑一个端发现不了。4.4 坑四分销佣金在小程序和 H5 重复计算现象同一用户在小程序下单H5 端也收到佣金通知。原因佣金计算逻辑写在了前端或回调里没有做订单维度的幂等。解决佣金计算必须放在订单支付成功后的异步任务里用order_no做唯一键计算前先查commission_log表是否已存在记录。4.5 坑五数据库迁移脚本在多个环境执行顺序不一致现象测试环境正常生产环境启动报字段不存在。原因迁移脚本没有版本号管理或者多人同时改了迁移文件。解决用带时间戳的迁移文件名如20240101120000_add_coupon_table.js并且迁移记录表migrations要纳入版本控制。每次上线前在预发环境完整跑一遍migrate。5. 商业数据分析的接入点从订单表到看板的最小闭环多端版跑起来之后下一步一定是看数据。数字商业生态这个词听着大落到代码里其实就是「订单、用户、商品」三张表加几个聚合查询。我一般会先做一个最小看板今日 GMV、今日订单数、新增用户数、各端占比。不急着上 BI 工具先用一个定时任务把数据算好写进统计表。-- 每日统计表按端和日期聚合 INSERT INTO daily_stats (stat_date, platform, gmv, order_count, new_user_count) SELECT DATE(created_at) AS stat_date, platform, SUM(total) AS gmv, COUNT(*) AS order_count, 0 AS new_user_count FROM order WHERE status paid AND created_at CURDATE() - INTERVAL 1 DAY AND created_at CURDATE() GROUP BY DATE(created_at), platform ON DUPLICATE KEY UPDATE gmv VALUES(gmv), order_count VALUES(order_count);这段 SQL 每天凌晨跑一次把前一天各端的 GMV 和订单数写进daily_stats。ON DUPLICATE KEY UPDATE保证重跑不会重复插入。新增用户数可以另起一条 SQL 从user表按created_at聚合再更新到同一张表。看板前端直接查这张统计表不要实时查订单表否则数据量上来后页面会卡。进阶一点的做法是把统计任务拆成「实时」和「离线」两层实时层用 Redis 计数器记录当天的 GMV 和订单数离线层用上面的 SQL 做 T1 校准。实时层在支付回调里INCR对应 key离线层跑完覆盖。这样看板既有实时感又不会因为 Redis 丢数据而长期偏差。我自己的习惯是任何多端项目上线前先把「支付回调幂等」和「库存扣减并发」这两个测试用例跑通再谈营销功能。这两个地方翻车后面所有商业数据都是假的。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?