ARTICLE DETAIL

资讯详情

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

美容院会员系统源码二开:从表结构到微信端联调的避坑指南

美容院会员系统源码二开:从表结构到微信端联调的避坑指南 简介面向中小型美容院及美业门店的会员管理系统源码采用PHP后端配合HTML、JavaScript、CSS构建前端界面并内置微信端入口适用于需要会员档案、消费记录、权限分配等场景的开发者进行二次开发或学习整套增删改查业务流程。压缩包共1601个文件约23.4MB以662个PHP业务脚本、272个HTML页面、148个JavaScript脚本为主另有大量GIF、PNG、JPG图片素材、CSS样式、字体文件以及config、functions等配置和公共函数文件目录划分清晰便于部署替换同时配有说明文档、安装脚本与数据库SQL文件可降低环境配置门槛。系统权限可细分到每个功能节点基础增删改查一应俱全并整合Excel导出能力方便门店后台按需导出会员与消费数据微信端可支撑移动场景下的会员查询与操作。目前已有3256人学习下载适合具备一定PHP基础、希望快速搭建美业会员系统或作为毕设/课设参考的开发者。1. 美容院会员管理系统源码含微信端不等于上线就能用二开深度才是分水岭一套美容院会员管理系统源码标题里写着“含微信端”完整形态通常是管理后台加微信小程序端再配一份 MySQL 初始化脚本。它能解决储值、次卡、积分、预约这些美容院最核心的日常经营问题适合小店老板买回去就让员工用起来也适合接外包的团队拿来做二次开发的地基。但反直觉的是跑通源码并不难真正让项目翻车的是那些看不见的业务规则余额没有独立流水、openid 和会员身份只靠一张表硬绑这类隐患会在上线第三周集中爆发。这篇文章把从部署、联调到避坑的完整路径讲清楚既给新手一条能跟着走的路线也给熟手一份可以对照检查的清单。这类源码的另一个特点是“能跑”和“能用”之间隔着很长的二开距离。微信端不是简单套一个 wx.login 就完事网页端还涉及到跟小程序身份怎么同步管理后台也不是有张会员列表就行对账、补卡、挂失、换绑手机号都要有据可查。下面从表结构开始一步步把这条链路拆开。2. 从需求到表结构会员储值、次卡、积分怎么建模才能少返工拿到源码先别急着部署先打开 db 目录下的初始化脚本把会员相关的表读一遍。这一步花半小时能省掉后面改表结构的几天返工。美容院会员系统的核心资产就三样储值余额、次卡次数、积分。建模方式直接决定你后面做对账、做补卡、做微信端展示时的改动量。常见交付包在这块质量参差不齐有的把余额直接 update有的把次卡物理删除都属于上线即埋雷的设计。2.1 会员主表与储值流水分离余额为什么不能只当字段存最简单版本是 member 表放一个 balance 字段消费时执行 update member set balance balance - 金额。看起来没问题但一旦出现退款、手工调账、支付回调重复你根本说不清余额是怎么变成当前这个数的。到月底对账时财务问你“这个月充值的钱和实际到账差两百”你没有任何中间数据可以回溯整张表成了一个黑匣子。我见过的美容院源码里凡是直接改余额不出流水的后面百分之百要重构。常见做法是允许余额冗余在 member 表但所有余额变动必须走独立流水表。也就是说member.balance 只做展示和快速判断真正的账本在 member_balance_log 里。这样设计后每一次充值、消费、退款、管理员手动调整都能留下一行记录对账时直接把流水表按时间区间汇总就能和微信支付账单核对。CREATE TABLE member_balance_log ( id int(11) NOT NULL AUTO_INCREMENT, member_id int(11) NOT NULL COMMENT 会员ID, biz_type varchar(20) NOT NULL COMMENT recharge充值 consume消费 refund退款 adjust手工调整, amount decimal(10,2) NOT NULL COMMENT 变动金额正负按业务实际方向记录, balance_after decimal(10,2) NOT NULL COMMENT 本次变动后的余额快照, biz_no varchar(64) NOT NULL COMMENT 业务单号支付单号或订单号, remark varchar(255) DEFAULT NULL COMMENT 备注写清操作人和原因, created_at datetime NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_biz_type_no (biz_type,biz_no), KEY idx_member_id (member_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT储值流水表;这里值得重点说的是 uk_biz_type_no 这个唯一索引。支付回调经常重复通知如果你的流水插入逻辑没有做幂等同一笔充值会被插入两到三次余额跟着翻倍。有了这个唯一索引第二次插入直接报 Duplicate entry业务层捕获这个异常当作“已处理过”即可。balance_after 字段不是冗余它让对账时一眼就能看到每次变动后的余额不用再拿 SQL 从头加一遍。写入流水和更新 member.balance 必须在同一个数据库事务里顺序是先插流水、再更新余额。2.2 次卡与套餐过期用状态机而不是 delete 来管理卡项次卡是美容院现金流的核心管理不好特别容易和顾客起纠纷。很多源码包把次卡当成普通订单顾客退卡或补卡时直接 DELETE 一行后面财务审计、客诉溯源时完全找不到记录。正确的做法是把卡的生命周期建模成一张表加一个状态字段任何操作都只是状态变更不删数据。CREATE TABLE member_card ( id int(11) NOT NULL AUTO_INCREMENT, card_no varchar(32) NOT NULL COMMENT 卡号补卡后新卡重新生成原卡号保留, member_id int(11) NOT NULL, card_tpl_id int(11) NOT NULL COMMENT 卡项模板ID决定名称和总次数, total_times int(11) NOT NULL COMMENT 总次数, used_times int(11) NOT NULL DEFAULT 0 COMMENT 已核销次数只允许原子更新增加, expire_at datetime DEFAULT NULL COMMENT 到期时间, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 1正常 2用完 3过期 4挂失 5作废, created_at datetime NOT NULL, PRIMARY KEY (id), KEY idx_member_status (member_id,status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT会员持有的次卡;状态字段枚举看起来很基础但它解决了一个实际问题过期不等于作废。顾客次卡到期后店里搞活动可能会给老顾客续期这时候 status3 的卡可以直接改回 1把 expire_at 顺延历史记录还在。而作废status5是终态通常配合退款或客诉处理使用。日常还有一个定时任务要写进 cron每天凌晨把 expire_at 已过且 status1 的记录置为 3这样前台在微信端看到的卡状态才是准确的不会出现“卡过期了还能预约”的尴尬。2.3 从 openid 到手机号微信端与线下会员怎么对上号微信端登录最核心的问题不是怎么调 wx.login而是登录之后怎么找到这个微信用户对应的会员档案。线下手工开卡时我们手上只有手机号微信授权登录后我们拿到的是 openid。两条数据要能对上就必须有绑定关系。这里不推荐只在 member 表里加一个 openid 字段因为一个微信号可能绑家里两个人的卡一个会员也可能换了微信号一对多和多对一的情况在美容院太常见了。CREATE TABLE member_wechat_bind ( id int(11) NOT NULL AUTO_INCREMENT, member_id int(11) NOT NULL, openid varchar(64) NOT NULL COMMENT 微信端openid按平台区分, unionid varchar(64) DEFAULT NULL COMMENT 开放平台unionid网页端和小程序端同源识别的关键, platform varchar(20) NOT NULL DEFAULT miniprogram COMMENT miniprogram/public/website, bind_time datetime NOT NULL, unbind_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_openid_platform (platform,openid), KEY idx_member_id (member_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT会员与微信身份的绑定关系;有了这张表微信端登录的流程就变得清晰拿到 openid 去 member_wechat_bind 里查命中就直接登录没命中就让用户输手机号收验证码验证通过后往这张表插一行完成绑定。换绑手机号时也只需要把 bind 表里对应行的 member_id 迁移过去openid 不用动。这张表还是第 4 章“网页端同步小程序微信登录”的前提unionid 字段在那里会派上大用场。注意只有把小程序和网页应用绑定到同一个微信开放平台账号微信接口才会返回 unionid。没有开放平台的项目跨端统一身份只能靠手机号兜底。3. 本地跑通整套源码从数据库初始化到微信端联调的完整环境拿到交付包第一件事不是逐行读代码而是先把环境跑起来。这类源码的部署路径大差不差管理后台是 Web 端放在 Nginx 或 Apache 下微信小程序端用微信开发者工具打开两边共用一套 MySQL 数据库。先讲目录再讲配置能少走很多弯路。这套链路里没有玄学跑不通基本都是版本、配置、域名三者没对齐。3.1 交付包目录结构与运行环境先分清管理后台和微信小程序两端一套典型的美容院会员系统交付包解压后大致是这几个角色admin 目录是给收银和店长用的管理后台通常用 PHP 写成包含收银台、会员列表、卡项设置、经营报表api 目录是接口层微信小程序端所有请求都打到这个入口登录、支付回调、卡项核销都在这层实现miniprogram 目录是微信小程序源码只能用微信开发者工具打开db 目录放着 init.sql这是会员业务核心表的初始化脚本doc 目录放部署说明和接口文档。不同源码包命名可能略有差异有的把 admin 和 api 合并成一个项目但角色是固定的。运行环境方面最常见的组合是 PHP 7.4 MySQL 5.7 Nginx。PHP 版本不够 7.4 时一些面向对象语法和内置函数会直接报错建议直接装 7.4 或 8.0别在旧版本上浪费时间。MySQL 建议 5.7 以上如果交付包用的是 utf8mb4 字符集低于 5.5 的版本连建表语句都会跑不过。这套环境用宝塔面板或者 PHPStudy 都能搭没必要在生产服务器上折腾源码编译。3.2 导入数据库并初始化系统配置三条命令与两处必改参数数据库初始化是整个部署里最不容易出错也最容易忽略的一步。很多人喜欢用 phpMyAdmin 或宝塔的可视化导入但命令行方式更可控能看到每一条报错尤其是在 SQL 脚本里带触发器或存储过程时。我一般用三条命令完成mysql -uroot -p -e CREATE DATABASE IF NOT EXISTS beauty_member DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; mysql -uroot -p beauty_member db/init.sql mysql -uroot -p beauty_member -e SET GLOBAL time_zone 08:00;第一条命令建库字符集显式指定 utf8mb4排序规则用 utf8mb4_unicode_ci。很多源码包的 init.sql 里没有 CREATE DATABASE 语句它默认你已经在 MySQL 里建好了一个库所以库名必须和后面代码里的配置严格对齐。第二条命令把 db/init.sql 导入 beauty_member 库。第三条命令设置全局时区不然 PHP 和 MySQL 各按各的时间走做支付回调验签时会出现“当前时间与服务器时间差八小时”的诡异问题。两条必改参数分别是数据库连接配置和时区。数据库连接通常在 admin/config/ 下常见是一个 db.php 文件PHP 侧时区一般在入口文件加 date_default_timezone_set(Asia/Shanghai)。如果数据库账号没有 SUPER 权限第三条命令执行不了可以改成在 MySQL 配置文件 [mysqld] 段加 default-time-zone08:00 再重启服务。// admin/config/db.php典型交付包中的数据库连接配置 return [ host 127.0.0.1, port 3306, database beauty_member, username beauty_user, password 改成你自己的强密码, charset utf8mb4, ];charset 参数保持和建库时一致。username 和 password 在本地开发时可以用 root但既然要往生产环境走最好单独建一个库账号只授予 beauty_member 这一个库的读写权限避免源码里存在 SQL 注入漏洞时把整个 MySQL 实例拖下水。数据库导入完成后先执行 SHOW TABLES 看一遍表清单再 SELECT 一下 member 表确认数据真的进来了再往下走。3.3 管理后台与微信小程序端联调appid、secret、域名一次性填对数据库跑起来之后剩下的联调集中在三处配置对齐管理后台的接口地址、小程序端的 API 地址、微信公众平台上的合法域名列表。这三处只要错一处表现就是小程序端白屏、请求失败或一直转圈。先改小程序端配置再改微信公众平台最后在开发者工具里清一遍缓存大部分联调问题都能解决。// miniprogram/config.js微信小程序端的全局配置 module.exports { apiBaseUrl: https://api.yourdomain.com, // 后端接口地址上线必须HTTPS appid: wx1234567890abcdef, // 微信公众平台的小程序AppID requestTimeout: 10000, // 请求超时时间单位毫秒 enableDebug: false // true时在控制台打印请求日志 };apiBaseUrl 在本地联调阶段可以先填 http://127.0.0.1:8080配合微信开发者工具里的“不校验合法域名”选项能把整个链路跑通。但真机预览不认这个开关所以只要涉及真机测试就必须改成线上域名并配置 HTTPS 证书。appid 是你在微信公众平台注册小程序时拿到的secret 在“开发管理-开发设置”里查看这两个值必须和小程序后台一一对应。特别提醒secret 只能放在后端不能写进小程序代码里。很多交付包为了演示方便把 secret 写死在 config.js 里这种项目直接上线等于把钥匙挂在门上二开时第一件事就应该把它挪到后端接口里。联调时如果发现小程序请求一直 401不要先怀疑代码先看请求头里的 token 有没有被正确携带再看后端接口有没有正确读取。这两个问题占了微信端联调故障的一大半。4. 微信端登录与会员卡展示网页端同步小程序微信登录的核心链路管理后台能登录只是第一步微信端会员登录才是“含微信端”源码包里的重头戏。这里要解决一个根本问题微信用户不等于会员。用户授权微信登录后你需要知道他是谁、他名下的卡和储值余额是多少、他在网页端操作时能不能也命中同一个会员身份。很多人在这里翻车是因为把“拿到 openid”和“登录成功”划了等号结果会员卡、余额全是空数据。4.1 微信端登录的三种实现路径静默登录、手机号快捷校验、扫码登录先看源码包当前用的是哪一种缺哪种再补哪种。三种方式适用场景完全不同我一般这样选型实现方式用户流程典型场景注意点wx.login 静默登录用户无感后端拿 code 换 openid识别老会员、弹窗提醒、免密余额查询拿不到手机号新会员无法开户手机号快捷校验用户点授权按钮微信返回加密手机号数据新会员注册、老会员绑定微信需要企业主体注意计费规则网页扫码登录PC 后台展示二维码用户用微信扫码确认管理后台店长登录、网页端会员中心依赖开放平台 unionid无 unionid 时退化为手机号验证所谓“网页端同步小程序微信登录”本质上不是让网页跳一个小程序而是让同一个微信用户在小程序和网页两端落到同一个会员 ID 上。标准做法靠 unionid备选方案靠手机号两个都没有就是登录断裂。很多项目在网页端重新接一遍短信验证码然后查手机号有没有在微信端绑定过这也能实现对同一会员的识别只是体验比扫码登录差一些。4.2 后端用 code 换 openid 并签发 token一段可直接复用的 PHP 接口微信小程序端 wx.login 会拿到一个临时 code有效期五分钟且只能用一次。后端拿这个 code 去微信的 jscode2session 接口换 openid 和 session_key。openid 是用户在这个小程序下的唯一身份session_key 用于解密手机号数据绝不能下发到前端unionid 只在你的小程序和网页应用绑定了同一个微信开放平台账号时才返回。下面这段 PHP 代码是常见的登录接口骨架可以直接套进交付包的 api 目录// api/login.php微信小程序登录接口 $code trim($_POST[code] ?? ); if (!$code) { exit(json_encode([code 400, msg 缺少code])); } $appid wx1234567890abcdef; $secret 你的小程序secret只存在后端; $uri https://api.weixin.qq.com/sns/jscode2session?appid{$appid}secret{$secret}js_code{$code}grant_typeauthorization_code; $resp file_get_contents($uri); // 建议生产环境换成cURL并设置超时 $data json_decode($resp, true); if (isset($data[errcode])) { exit(json_encode([code 400, msg $data[errmsg]])); } $member Member::getByOpenid($data[openid]); if (!$member) { // 没有绑定会员身份时返回ticket让前端走手机号验证绑定流程 $ticket bin2hex(random_bytes(16)); Redis::setex(bind:ticket:{$ticket}, 300, $data[openid]); exit(json_encode([code 302, ticket $ticket])); } $token bin2hex(random_bytes(32)); Redis::setex(ws:token:{$token}, 7 * 86400, $member[id]); $member Member::getById($member[id]); echo json_encode([code 0, token $token, member $member]);关键参数有三个。第一code 用完即废不能缓存也不能重复提交前端做了防重复点击也要在后端兜底。第二session_key 属于敏感数据终端需要用它解密手机号时应该由后端解密后再返回明文而不是把 session_key 交给前端。第三token 有效期我习惯设七天美容院顾客到店频率一般一周一次七天不活跃强制重新登录是合理的。Redis 没装的环境可以退化成数据库 token 表或文件缓存但 Redis 最省事一个 setex 就能搞定过期时间。注意上面示例用 file_get_contents 请求微信接口部分生产环境会关闭 allow_url_fopen。遇到请求微信超时或返回空值时优先检查 php.ini 里这个开关换成 cURL 实现更稳。4.3 网页端同步小程序微信登录从 unionid 到扫码确认的落地姿势如果源码包里的 admin 后台是给店长在电脑上用的扫码登录是体验最好的方案。实现路径分五步先在微信开放平台注册一个“网站应用”再把小程序绑定到同一个开放平台账号拿到网站应用自己的 AppID 和 SecretPC 后台展示二维码用户扫码授权后回调带 code后端用网站应用的 appid 和 secret 调 oauth2/access_token 接口得到这个用户在网站应用下的 openid 和 unionid最后拿 unionid 去 member_wechat_bind 表里查 member_id命中就直接登录成功。没有开放平台账号的项目这套方案做不了因为微信只对同一开放平台账号下的应用返回相同 unionid。这时候退而求其次网页端用手机号加短信验证码登录登录后先去 member_wechat_bind 表查这个手机号有没有绑定过微信端的 openid有就自动建立会话没有就提示先去小程序端绑定。这套退化方案安全性和可用性都够只是用户在两端之间首次切换时需要多输一次验证码。还有一点容易忽略网页端扫码登录成功后的 token和小程序端拿到的 token必须走同一套会话体系。也就是说两个端签发的 token 都要能通过同一个 Redis key 前缀找到会员 ID这样美容院老板在电脑上看的报表和小程序端展示的卡数据才是同一份。5. 美容院会员系统避坑指南支付回调、短信验证码与卡项并发的重灾区这一章写的都是线上真实会遇到的故障每一条都来自我在类似项目里的血泪经验。现象、原因、解决按顺序写你可以直接对照排查。美容院会员系统的坑不在页面交互而在钱、卡、次数这些数据的一致性上。5.1 支付回调重复入账同一笔充值出现在流水里三次现象顾客充值 500 元余额到账 1500流水表里出现三条相同 biz_no 的充值记录。原因微信支付和支付宝的回调通知是异步且会重试的。正常情况下通知一次但如果你的接口响应超时或返回非成功状态支付平台会按一定间隔重试多次。源码包里如果直接在回调里写“更新余额、插入流水”没有先查重就会重复入账。解决在设计层解决用 2.1 节里那张流水表上的唯一索引做幂等。插入前先按 biz_type 和 biz_no 查一遍查到了就直接返回成功不再更新余额插入遇到 Duplicate entry 也一样处理。另外回调接口必须返回支付平台约定的成功字符串微信支付要求返回纯文本 success不能返回 JSON 或空内容否则支付平台会一直认为通知失败并持续重试。5.2 短信验证码被刷一场活动让短信账单翻了三倍现象9.9 元体验卡活动上线两小时短信服务商后台发送量暴增账单金额变成日常的三倍。原因验证码发送接口只做了前端 60 秒倒计时没有在后端限制发送频率。脚本可以直接绕过前端循环调用接口一条手机号一分钟能收十几条验证码。解决后端用 Redis 做限制同一手机号 1 分钟内最多 1 条、同一天最多 5 条同时把图形验证码或滑块校验加到发送前。限制逻辑很简单对 sms:limit:13800138000 这个 key 做 INCR第一次执行时同时设置过期时间值超过阈值直接拒绝。短信发送日志要落库记录手机号、IP、发送内容、发送结果出事时能按发起 IP 追到源头。限制参数不要写死在代码里放进配置可以随时调免得活动期间误伤正常顾客。5.3 次卡并发核销剩余一次却扣成负数现象顾客 A 和顾客 B 同时到店服务员同时操作同一张次卡核销两次库里 used_times 变成 -1两位顾客都说自己核销成功了。原因扣减次数走了“读出剩余次数、判断够不够、写回新次数”三步。两个请求同时读到剩余 1 次各自判断够各自写回 0最终结果就是 -1。解决改成原子更新把判断和写回合并成一条 SQLUPDATE member_card SET used_times used_times 1 WHERE id ? AND used_times total_times;affected rows 返回 1 说明扣减成功返回 0 说明次数已经用完业务层直接提示顾客。这条 SQL 背后的逻辑是让数据库行锁替你做并发控制而不是靠应用层加锁。配合事务一起用次卡核销和对应的服务记录写入要么同时成功要么同时失败不会出现卡扣了但服务单没生成。5.4 微信端请求全部失败合法域名与 HTTPS 证书缺一不可现象微信开发者工具里一切正常真机预览后页面白屏真机调试面板里全是请求失败或 401。原因开发者工具默认有一个“不校验合法域名”的选项本地联调时开着它请求什么域名都放行真机上没有这个豁免微信要求所有请求域名必须在公众平台配置过 request 合法域名且必须是 HTTPS。还有一些源码包自带的 SSL 配置用的是自签名证书桌面浏览器会拦截微信小程序直接不认。解决上线前在微信公众平台的“开发管理-开发设置-服务器域名”里把 API 域名填进 request 合法域名HTTPS 证书用正规 CA 签发并确保证书链完整。部署完仍有请求失败时先用真机调试看具体报错证书问题和域名未配置两者错误信息差别很大不要混在一起排查。千万别为了省事在线上环境关掉合法域名校验那只是把问题推迟到真机爆发还多搭上一个安全漏洞。6. 运营后台四个高频动作查重、补卡、换绑手机号与经营报表源码上线后真正的高频使用集中在运营后台。这四个动作做好店员不用天天找你写 SQL。查重。美容院最容易出现一人多卡、一个手机号对应多条会员记录。用一条 GROUP BY 把重复手机号捞出来人工核对后合并储值和次卡。SELECT mobile, COUNT(*) AS c, GROUP_CONCAT(id) AS ids FROM member GROUP BY mobile HAVING c 1;ids 列是同一手机号下的会员 ID 列表核对后保留一条主卡其余余额和次卡合并或挂失不要直接 DELETE留痕才能应对后续客诉。补卡。实体卡丢了别把卡记录删除原卡状态改成挂失status4再生成一张新卡继承剩余次数和余额新卡卡号重新生成。原卡记录保留顾客哪天翻旧账你才有据可查。换绑手机号。顾客换号时除了更新 member 表的 mobile还要检查 member_wechat_bind 表里有没有绑定的 openid有就一起迁移只改一个字段会导致微信端和网页端各认各的号。经营报表。真正能反映美容院健康度的不是流水总额而是储值余额、在途积分、未核销次卡这三项。储值余额代表负债未核销次卡代表未来要付出的服务成本这两项越高现金流压力越大。每天看一次这三列比盯着营业额有用得多。我做这类系统最深的教训是会员系统的价值不在功能多而在数据经得起对账。所有余额、次数、积分都能顺着流水追到源头这个系统才算真正落地希望帮到你。本文还有配套的精品资源点击获取
返回列表