多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

易客云会员卡系统源码多开版:多门店独立部署与二次开发全解析

易客云会员卡系统源码多开版:多门店独立部署与二次开发全解析 简介易客云会员微信小程序多开版会员卡系统1.0.33源码包面向需要搭建微信小程序会员体系的商家、开发者和运营人员聚焦多开管理、会员档案、积分累积、优惠券与满减活动、支付对接等核心场景。资源共1285个文件压缩包仅4.91MB其中png图片与dat数据文件用于界面资源和业务缓存js、wxss、wxml、json构成小程序前端逻辑与页面结构php文件提供后端服务接口txt及readme为安装配置与使用说明。目前已有507人学习/下载。包内除完整源码外还附带界面截图、开发日志和部署说明可帮助快速搭建会员卡系统并进行二次开发适合中小商户、连锁门店或跨行业多门店统一管理会员资产提升用户复购与忠诚度。1. 易客云会员卡系统源码多开版是什么解决谁的什么问题搜「易客云会员卡系统源码」的人多半是手上有两三家店、几个小程序代运营单子或者刚接了一套二手源码不知道怎么落地。这套 1.0.33 多开版会员卡系统的核心卖点不在会员开卡、储值、次卡核销这些常规功能上——这些东西市面上随便一套都能做——而在于「一套源码、多个独立小程序实例」的部署方式。每个门店可以有自己独立的小程序、独立的后台、独立的会员库但代码只维护一份。后端是 PHP MySQL小程序端跑在微信里完整覆盖手机号登录、开卡、充值、消费、余额变动通知和微信支付回调这一条业务链。适合三类人想自己运营会员体系的门店老板需要一份能快速交付的源码打底的外包开发者以及想从表结构层面拆解会员卡业务的技术人员。需要先说明白的是这是源码包不是云账号拿到手第一件事是部署而不是登录某个后台开始建卡。2. 多开的核心原理一套源码怎么拆出相互独立的 N 个微信小程序2.1 多开到底开了什么站点配置、AppID 与数据库三者分离不少人第一次听到「多开版」会以为是把源码复制好几份各自装一遍。复制代码是最容易走的一条弯路等后面修 bug、升级版本的时候改一个文件要同步到 N 个目录几乎等于给自己埋雷。1.0.33 这个多开版的常见做法是把每个实例的差异收敛到三个地方小程序端绑定的 AppID、后端识别的站点标识 site_id、以及数据库表前缀或独立库。后端运行时拿到请求域名或 site_id就知道当前该用哪套配置、连哪个库、调哪组支付参数。这种设计的优势在于代码本体只有一份升级时只需要替换公共代码目录。下面是一份典型的多站点配置结构PHP 侧通常长这样return [ site_id 2, appid wx9c8a..., appsecret d3f1..., mch_id 152xxxxxxx, api_key ABCDEF..., db_prefix yky_site2_, base_url https://shop-b.example.com, ];重点是site_id和db_prefix这两个字段。site_id是后端区分「现在在处理哪一家店」的钥匙支付回调、订单日志、短信通知都会带着它db_prefix决定 SQL 语句最终落到yky_site2_member还是yky_site1_member。用表前缀而不是硬编码多个库名的好处是一个 MySQL 实例可以同时放多家店的数据备份、迁移都按同一组前缀的表走逻辑清晰。什么时候该上独立库两个店的会员量都不小或者你对数据隔离有更严格的要求就每个实例建独立库只是想验证流程、做演示表前缀隔离足够。还有一个细节容易被忽略base_url不要填localhost小程序端请求必须走 HTTPS 域名本地地址在真机上永远调不通。2.2 会员卡业务闭环与核心表结构在动手改代码之前先把一笔「开卡充值」请求在后端走的路理清楚。完整流程是用户进入小程序 → 手机号快捷登录 → 选择卡类型 → 提交订单并拉起微信支付 → 支付回调写入充值订单 → 后台给用户绑定一张卡并更新余额 → 消费时写消耗流水。支撑这套闭环的表把名字和关键字段列出来表名关键字段作用yky_site2_membermember_id, mobile, nickname, balance, total_recharge会员档案存余额和累计充值yky_site2_card_typetype_id, name, type, price, valid_days, gift_value卡类型定义储值/次卡/折扣yky_site2_member_cardid, member_id, type_id, remain_count, expire_time用户持有的卡次卡核销在这里扣yky_site2_recharge_orderorder_no, member_id, amount, pay_status, pay_time充值订单回调成功才置为已支付yky_site2_consume_loglog_id, card_id, member_id, amount, create_time每次消费的流水注意member_card和recharge_order是一对多关系一张卡可以有多笔充值订单。同时balance字段出现在member表里表示账户余额也出现在卡维度上表示卡内余额这套系统是两种口径并存的。做多店会员合并时最容易翻车的地方就是把这两个字段搞混后面第 6 章会说具体处理方式。pay_status值得多说一句。很多对账异常都是订单在pay_status0状态下就被业务逻辑读走了。规范顺序一定是支付回调先更新订单状态、再写卡和余额两步要么放在同一个事务里要么用订单号做幂等。我一般会用order_no作为幂等键回调进来先查recharge_order如果pay_status已经是 1直接返回成功避免微信重试回调导致重复发卡。2.3 为什么用多开而不是「一个后台开多门店」有些系统支持「一个后台建多个门店」看起来也能满足多店需求但是和「多开」完全两种设计。这里列一张对比表维度多开版每店一个实例单后台多门店模式数据隔离库/表前缀隔离天然互不可见靠 store_id 过滤漏写条件就串数据品牌 UI每家可独立改标题、Logo、主题色改一处影响全局小程序审核每家独立 AppID类目和名称独立提交共用一套代码审核粒度粗报表口径各算各的汇总需额外开发天然汇总拆分明细要另做支付商户号可同可不同回调地址相互独立回调要自己区分门店从这个角度说多开版更适合「店与店之间品牌、定价、活动都不一样」的场景如果是大连锁里的不同分店反而单后台多门店更省事。选型想清楚这一点后面能少改一半需求。小程序端的技术选型也要提前定。1.0.33 的小程序端如果是原版通常用微信原生语法写的想同时跑 App 和 H5常见做法是用 uniapp 重写前端层后端接口不动只把wx.request换成uni.request登录态和支付参数重新统一封装一遍。改动量不大但「请求封装」必须在动手前做好否则每个页面各写一套请求逻辑后续替换会非常痛苦。3. 从压缩包到能跑通开卡充值部署与微信支付配置实战3.1 环境参数清单PHP 版本、Nginx 伪静态与 HTTPS部署前先把环境对齐这是返工率最高的环节。1.0.33 后端常见要求是 PHP 7.4 以上8.0/8.1 能跑但要看扩展是否齐全、MySQL 5.7 以上、Nginx 或 Apache 并开启伪静态、HTTPS 证书必须完整。小程序端只认 HTTPS 的合法域名证书链缺中间证书也会导致真机请求失败这一步提前测好。检查项要求说明PHP7.4需要 pdo_mysql、openssl、curl、fileinfo 扩展MySQL5.7建库时选择 utf8mb4避免表情符号乱码Nginx任意稳定版必须配置伪静态且 root 指向 public 目录HTTPS全链证书小程序合法域名强制 HTTPS证书要完整微信公众平台小程序已注册拿到 AppID 和 AppSecret用于后端配置Nginx 伪静态配置贴一份ThinkPHP 系路由的常见写法server { listen 443 ssl; server_name api.example.com; ssl_certificate /etc/nginx/cert/api.pem; ssl_certificate_key /etc/nginx/cert/api.key; root /www/wwwroot/yky/public; index index.php index.html; if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } location ~ \.php$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/tmp/php-cgi-74.sock; } }root指向public目录是关键源码里的.env、配置文件、证书文件都放在public上层如果 root 指到项目根目录这些敏感文件可能被直接访问。重写规则把/api/pay/notify这类路径转成index.php?s/api/pay/notify如果你的系统还带index.php则说明伪静态未生效回调地址要按带index.php的形式写。3.2 后端安装三步走建库、改配置、进后台解压压缩包后先看目录结构一般会区分后端目录和小程序端目录。后端的部署分三步每一步做完再进下一步别跳。第一步建库并导入 SQL。先用命令行或面板创建一个数据库例如yky_site1再把压缩包里的.sql文件导入mysql -u root -p yky_site1 yky_site1.sql导入成功后去确认一下表前缀。如果是yky_开头说明是默认安装包如果导入时改过前缀后面配置里要对应写。第二步改数据库配置。以 ThinkPHP 系的.env或database.php为例return [ type mysql, hostname 127.0.0.1, database yky_site1, username yky_user, password 换成你的密码, hostport 3306, prefix yky_site1_, ];prefix要和第一步导入 SQL 时保持一致这是多开版区分实例的根基。很多人直接把默认配置复制成第二份忘记改database和prefix结果两个实例指向同一个库后台一登录就看到了对方的会员数据这个问题在第 5 章排查部分还会详细说。第三步访问后台。浏览器打开https://你的域名/admin或对应入口用安装说明里给出的初始账号登录。登录之后第一件事是改密码、改后台路径同时把站点名称、小程序 AppID/AppSecret 填进去。这两个值填错前端登录和支付都会失败而且报错信息往往不直观。3.3 小程序端编译与请求封装从开发者工具到真机小程序端用微信开发者工具导入先改config.js里的接口域名和 AppID// config.js module.exports { BASE_URL: https://api.example.com, APP_ID: wx9c8a..., REQUEST_TIMEOUT: 10000 };BASE_URL必须是 HTTPS 且已经配置到小程序后台的 request 合法域名里。REQUEST_TIMEOUT按业务调整会员卡场景常见 10 秒支付回调类接口不在这里配。接着看请求封装。这类系统的接口一般要求带 token、时间戳、随机串和签名封装一个统一的 request 方法能省掉后面大量重复代码// utils/request.js const config require(../config.js); function buildSign(params, secret) { let keys Object.keys(params).sort(); let str ; keys.forEach(k str ${k}${params[k]}); str key secret; return md5(str).toUpperCase(); } function request(url, method, data) { const timestamp Math.floor(Date.now() / 1000); const nonce Math.random().toString(36).slice(-8); const params Object.assign({ timestamp: timestamp, nonce: nonce, token: wx.getStorageSync(token) || }, data); return new Promise((resolve, reject) { wx.request({ url: config.BASE_URL url, method: method, data: params, header: { content-type: application/json }, success: res resolve(res.data), fail: err reject(err) }); }); }时间戳、随机串、token 是这套验签的基本三件套。buildSign里的签名算法要以源码里的签名类为准常见是参数名排序后拼接密钥做 MD5。token 为空时请求会带一个空字符串过去后端如果直接校验会报未登录正确做法是请求前先检查登录态没登录就跳授权页。开发者工具里可以勾选「不校验合法域名」来联调但这个选项只对工具生效真机预览和线上版本一律按合法域名走。3.4 微信支付与退款参数四个位置核对清楚支付配置集中在四个位置任何一个错了都会导致用户付了钱、订单不更新。先给一张配置对应表配置项从哪里拿常见填错点appid微信公众平台「开发-开发管理」填成公众号的 AppIDmch_id微信支付商户平台商户号和 appid 必须匹配API 密钥商户平台「API 安全」复制时带回车符或空格证书路径商户平台下载路径写错或权限不足导致退款失败回调地址在商户平台配置时填线上域名加支付通知路由。1.0.33 的常见路径是https://api.example.com/api/pay/notify。如果伪静态没开就要写成https://api.example.com/index.php?s/api/pay/notify这点最容易漏。退款还需要 API 证书把apiclient_cert.pem和apiclient_key.pem放到后端配置指定的目录并确保该目录不能被外部直接通过 URL 访问。4. 二开改造卡类型、消息通知与品牌化的完整替换路径4.1 卡类型与开卡赠送规则后台表单对应哪张表后台「卡类型管理」里新建一张卡最终落的就是card_type表。一套会员卡系统核心的卡类型一般有三类储值卡充多少送多少、次卡固定次数用完即止、折扣卡消费打折。新增一张储值卡的 SQL 如下INSERT INTO yky_site2_card_type (name, type, price, valid_days, gift_value, status) VALUES (半年储值卡, storage, 1000.00, 180, 100.00, 1);字段含义type用storage表示储值卡count表示次卡discount表示折扣卡price是用户实付金额valid_days是开卡后的有效天数0 表示永久gift_value是赠送余额计营收时要注意实收是price但卡余额是price gift_value报表对账按实收口径走。次卡还要关注remain_count每次核销减一在member_card表上更新。后台表单和表字段基本一一对应改价格区间、赠送比例这类运营配置不需要动代码。但如果要加「第二张卡打折」「老会员续费额外送次数」这类规则就得在开卡逻辑里补判断常见做法是给card_type加一个renew_type字段再在开卡接口里根据现有卡的情况计算优惠。4.2 订阅消息通知模板 ID 与一次性订阅的边界余额变动、开卡成功、消费提醒这些通知走的是微信小程序订阅消息。前端在用户完成开卡后拉起授权wx.requestSubscribeMessage({ tmplIds: [模板ID1, 模板ID2], success(res) { if (res.errMsg requestSubscribeMessage:ok) { // 用户同意后后端才能推送一条 } }, fail() { // 用户拒绝不能强制再次拉起 } });模板 ID 需要在微信公众平台「订阅消息」里申请类目审核通过后才有不是随便填的。一个关键限制是「一次性订阅」用户点一次授权后端只能推一条。所以这类系统的常见做法是在充值、开卡、消费三个关键动作时分别请求订阅授权而不是进入小程序时就一把梭全要那样用户大概率会拒绝。后端配置模板 ID 的位置通常在站点配置里每个模板对应一个消息类型。上线前用开发板测试一遍推送内容确认模板字段变量对得上否则会推送失败并提示parameter mismatch。4.3 品牌化替换标题栏、Logo 与自定义顶部每个店一套品牌小程序端要改的地方主要是标题栏和视觉素材。原生微信小程序改全局标题在app.json的window里{ pages: [pages/index/index, pages/card/card], window: { navigationBarTitleText: XX美容会员卡, navigationBarBackgroundColor: #333333, navigationBarTextStyle: white } }navigationBarTitleText会同时影响任务列表显示务必改掉默认的项目名。背景色建议用品牌主色深色背景配白色文字浅色背景配黑色文字对比度不对时小程序的返回箭头会看不清。如果要做沉浸式头部把navigationStyle设为custom这时状态栏高度要自己适配const sys wx.getSystemInfoSync(); Component({ data: { statusBarHeight: sys.statusBarHeight, navBarHeight: sys.statusBarHeight 44 } });statusBarHeight在不同机型上不一样iPhone 的刘海屏和 Android 的挖孔屏数值都不同。默认导航栏已经足够多数门店使用自定义顶部属于「好看但费工时」的改造建议放在功能稳定之后再做。Logo、分享图、小程序码这些素材在后端设置里替换对应的图片路径即可。4.4 报表口径一个实例内的数据统计怎么算后台统计页的数据口径核心就一条只统计已支付订单。以日报为例SELECT DATE_FORMAT(create_time, %Y-%m-%d) AS day, COUNT(DISTINCT member_id) AS new_members, SUM(amount) AS recharge_total FROM yky_site2_recharge_order WHERE pay_status 1 AND create_time 2025-01-01 00:00:00 GROUP BY day;pay_status 1这个条件是底线。很多新手直接SUM(amount)不过滤状态未支付订单全被算进营收数字虚高。COUNT(DISTINCT member_id)是为了防止同一用户当天开多张卡被重复计为新增。多开版每个实例的报表各自独立想要跨店合计需要额外做汇总方法在第 6 章。5. 部署避坑五个最常见的会员卡小程序问题与排查记录5.1 真机请求全部失败合法域名和证书链现象开发者工具里一切正常一扫码真机预览所有接口请求都报request:fail。原因分两种一是小程序后台的 request 合法域名没配二是服务器 HTTPS 证书链不完整。很多人只上传了域名证书没把中间证书一起配置电脑浏览器能打开但小程序客户端校验证书链时直接拒绝。解决登录微信公众平台在「开发管理-开发设置-服务器域名」里把接口域名加入 request 合法域名注意不要带路径只要域名然后用在线证书检测工具检查证书链是否完整。Nginx 配置里证书文件要包含完整链常见做法是把中间证书和域名证书合并成一个.pem文件。5.2 支付成功但余额没变回调地址和幂等现象用户微信支付弹窗显示扣款成功但后台订单还是未支付卡余额也没增加。原因支付回调没有正确通知到后端或者回调处理函数验签失败后没有返回成功标识。微信会重试回调若干次但每次都被同一原因挡掉结果就是钱到了商户号会员卡系统毫无感知。解决先用日志确认回调是否进来。看payment_log或运行日志里有没有微信的 POST 请求如果没有检查商户平台「支付回调地址」配置以及这个地址在服务器上是否能直接访问。如果回调进来了但验签失败多半是 API 密钥填错或签名算法不匹配。处理函数必须返回微信要求的成功应答否则视为失败会持续重试。同时保证接口幂等同一笔订单重复回调时不重复加余额。5.3 多开后台串数据数据库和前缀没隔离现象两个门店的小程序后台登录后会员列表出现重复或混着另一家店的数据。原因多开实例安装时把第一个实例的数据库直接复制给了第二个实例前缀也是同一个yky_等于两个实例共用一套表。看起来各自装了实际上读写的是同一份数据。解决新实例必须用独立数据库或者至少换一套完全不同的表前缀。我一般把第一个实例命名为yky_site1、前缀yky_site1_第二个实例命名为yky_site2、前缀yky_site2_配置文件里数据库名和前缀一起核对。上线前再登录两个后台各建一个测试会员确认互相看不到才算隔离通过。5.4 审核被拒类目、名称与虚拟支付现象小程序提审被拒理由涉及「虚拟支付」「类目与名称不符」或「含诱导性文案」。原因会员储值本身容易被判定为虚拟支付尤其当小程序名称、简介里出现「全网最低」「限时免费」这类词时审核更严格。类目选错也是高频原因。解决类目按实际行业选美发美容选「生活服务-丽人」健身房选「生活服务-运动健身」门店名称、服务描述要和主体业务一致。储值规则里避免出现「买会员返现金」「投资返利」这类描述。文案检查一遍去掉绝对化用语。一次订阅消息的模板也提前在公众平台申请不要等审核被拒了再补材料。5.5 接口报 need sign info验签参数和本地时间现象特定接口返回need sign info或者同一套代码在 A 手机上正常、B 手机上签名失败。原因公共参数没传全常见是timestamp、nonce、sign缺失或者时间戳差异超过后端设定的窗口。手机本地时间被手动改过、时区不对都会导致这个结果。解决先看请求体里参数是否齐全再核对时间窗口。以常见的 MD5 验签为例public function checkSign($params, $secret) { if (abs(time() - intval($params[timestamp])) 300) { return false; // 超过 5 分钟视为过期 } $sign $params[sign]; unset($params[sign]); ksort($params); $str urldecode(http_build_query($params)) . key . $secret; return strtoupper(md5($str)) strtoupper($sign); }ksort是必须的签名要求参数按 ASCII 排序后再拼接漏掉这一步客户端和后端永远对不上。http_build_query之后的urldecode是为了处理中文和特殊字符会员备注、地址这类字段很容易在这里出问题。排查时用微信开发者工具的 Network 面板看请求体和后端日志对照真机复现问题再用抓包工具看实际发出去的内容优先用工具面板能少走弯路。6. 进阶技巧多店数据汇总与上线前检查清单6.1 多实例汇总定时任务把各店订单拉进一张总表多开版每个实例各算各的老板要看全部门店的充值总额就得做汇总。常见做法是单独建一个汇总库用定时任务在每天凌晨拉取各实例的已支付订单。这里给一个脚本骨架$sites [ [db yky_site1, prefix yky_site1_], [db yky_site2, prefix yky_site2_], ]; foreach ($sites as $site) { $pdo new PDO( mysql:dbname{$site[db]};host127.0.0.1;charsetutf8mb4, read_user, read_pass ); $rows $pdo-query( SELECT order_no, member_id, amount, create_time FROM {$site[prefix]}recharge_order WHERE pay_status 1 AND create_time DATE_SUB(NOW(), INTERVAL 1 DAY) )-fetchAll(); // 写入汇总库按 order_no 做唯一索引 foreach ($rows as $row) { // INSERT INTO summary_recharge_order ... ON DUPLICATE KEY UPDATE } }汇总库连接用只读账号避免脚本误操作数据。order_no在各实例里可能重复汇总结论要加上site_id字段做复合唯一索引。各实例服务器的系统时间要一致最好统一用 NTP 同步否则按天汇总会出现数据落到前一天的情况。6.2 会员通要谨慎按手机号合并余额不直接相加两个门店想做会员互通时最容易踩的坑是「把 A 店的余额和 B 店的余额直接加总」。如果 A 店充值送 100、B 店充值送 200两边的赠送金额口径不同加总后的余额虚高。我一般在合并明细里保留「各店余额」和「共享余额」两个字段跨店消费时按共享余额扣各店的赠送余额不能互用。会员的唯一标识建议用手机号用户在小程序端授权手机号后自动匹配没匹配上的引导到线下门店核验身份后手动合并。6.3 上线前十分钟检查清单最后分享一套我每次部署多开实例前强制走一遍的流程。顺序很重要按这个顺序能保证在最快时间内定位问题新实例建独立库database和prefix和已有实例确认不一致后台配置里的小程序 AppID、AppSecret 与当前实例一一对应微信公众平台的合法域名已配置且证书链完整用工具实测一次商户平台回调地址能通过浏览器直接访问确认不跳登录真机跑一遍「开卡 → 支付一分钱 → 确认余额增加 → 退款测试」删除测试订单和测试会员把后台统计归零。从那以后我每次上线新实例都强制走一遍这套流程一分钱测试能挡掉大部分支付和回调问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表