ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

智慧美容小程序源码解析:连锁门店管理、会员卡与数据权限设计

智慧美容小程序源码解析:连锁门店管理、会员卡与数据权限设计 简介面向连锁美容机构线上化运营的智慧美容小程序完整源码整合客户管理、门店管理、商品管理、项目管理四大业务模块并支持在线预约、在线下单与特色项目展示既可作计算机相关专业学生的课程设计、毕业设计素材也可用于初期项目立项演练。压缩包共1148个文件约4.13MB主体由500个Java后端源码、241个XML配置、93个JS逻辑、67个WXML页面以及61个WXSS样式构成同时包含JSON、PNG、SQL等辅助文件前端小程序与后台服务结构清晰便于对照阅读和二次开发。目前已有292人学习下载。随包附带的项目说明文档和测试运行无误的代码能帮助入门者理解小程序页面交互、后端接口与数据表设计对有经验者而言多门店模式下客户、商品、项目之间的业务组织方式也具备参考价值适合快速搭建同类预约交易类小程序原型。1. 智慧美容小程序源码先弄清这份 zip 在解决什么连锁美容机构的业务痛点是很多系统解决不了的客户在总店办的卡到分店就“失灵”美容师离职带走一票老客商品和项目库存对不上账。这份「智慧美容小程序完整源码说明」打包了一个能直接改的答案——一套同时覆盖客户管理、门店管理、商品管理、项目管理的小程序前后端代码外加配套说明文档适合连锁机构的 IT 或运营负责人拿来自建系统也适合外包开发者当底座二次开发还能让想验证美容行业 SaaS 模式的团队省掉从零搭框架的时间。下面按一条能落地的路径拆开这套源码先看技术选型和数据表再本地跑通最后说清楚坑在哪、怎么改造成自己的版本。2. 拆开 zip 看门道智慧美容小程序的技术选型、目录结构与核心数据表拿到任何一套源码先别急着双击运行。第一件事是确认它用的是什么技术栈、目录怎么组织、数据库长什么样。这三样决定了你后续改造成本有多高。这套源码的常见结构是一个「小程序前端 管理后台前端 后端接口服务 SQL 脚本」四段式。前端通常是微信原生小程序后端常见做法是 Node.js 的 Express 或 Java 的 Spring Boot数据库基本都是 MySQL。如果你要后续同时出安卓、iOS、鸿蒙端前端可以考虑往 uni-app 平移但原生小程序起步最稳调试工具链也最成熟。2.1 从「五张表」看懂客户、门店、商品、项目管理的数据模型美容连锁业务和普通电商小程序的差异在表设计上体现得最明显。电商只需要用户、商品、订单三张主表而美容业务必须额外管「门店归属」和「会员卡资产」。最少需要五张核心表客户表、门店表、商品表、服务项目表、订单表另加一张会员卡表来承载储值和次数。-- 客户表store_id 控制数据归属openid 关联微信登录身份 CREATE TABLE customer ( id bigint NOT NULL AUTO_INCREMENT, store_id bigint NOT NULL COMMENT 所属门店0 表示总部账号, openid varchar(64) DEFAULT COMMENT 微信身份标识, name varchar(50) DEFAULT , phone varchar(20) DEFAULT , balance decimal(10,2) DEFAULT 0.00 COMMENT 账户余额, level tinyint DEFAULT 1 COMMENT 会员等级 1-5, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_store (store_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有个关键细节字符集必须用 utf8mb4。美容行业的客户昵称、商品名里经常出现 emoji 和生僻字utf8 存不下会直接报错。store_id 字段是这条表的核心客户在哪个门店注册数据就挂在哪个门店下面连锁场景的所有权限隔离都依赖这个字段。会员卡表是另一个容易设计错的点。很多人会把「余额」和「剩余次数」混在一张卡里但美容院的真实情况是一张卡可能同时有余额、有某个项目的剩余次数、还有有效期。拆开看更清晰。-- 会员卡表一张卡同时表达储值余额和项目剩余次数 CREATE TABLE membership_card ( id bigint NOT NULL AUTO_INCREMENT, customer_id bigint NOT NULL, store_id bigint NOT NULL, card_no varchar(32) DEFAULT COMMENT 卡号线下对账用, balance decimal(10,2) DEFAULT 0.00 COMMENT 储值金额, remain_times int DEFAULT 0 COMMENT 通用项目剩余次数, start_date date DEFAULT NULL, expire_date date DEFAULT NULL, status tinyint DEFAULT 1 COMMENT 1正常 2冻结 3过期 4注销, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表的设计决定了核销逻辑的复杂度。remain_times 记录的是「通用次数」如果要做「特定项目专用次数」还得加一张卡项子表或者把项目 ID 直接冗余进卡表。建议先按通用次数跑通主流程再扩展专项卡。门店表、商品表、服务项目表相对常规但有两个字段不能省门店表的营业时间美容院分早班晚班预约要用服务项目表的价格和时长核销和结算要算提成。商品表则要注意「统一商品、分店库存」还是「各店独立商品」这个会在第 4 章展开说。2.2 前端小程序与后端接口的分层方式request 封装与鉴权看完了表再看代码组织。这套源码的小程序端一般按业务模块分页面首页、门店列表、商品列表、项目列表、订单详情、个人中心。后端按 controller 层暴露接口一个模块一个路由文件。前后端联调最核心的就是 request 封装。很多新手直接在每个页面里写 wx.request导致改接口地址要全局搜索替换。成熟的源码会抽一个公共的 request 工具统一处理 baseUrl、token 注入和 401 跳转。// utils/request.js const BASE_URL http://localhost:3000/api function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL path, method, data, header: { Content-Type: application/json, // token 从缓存取出后端据此识别当前登录用户 Authorization: wx.getStorageSync(token) || }, success(res) { // 401 说明登录态失效不能无限跳登录要先清缓存 if (res.statusCode 401) { wx.removeStorageSync(token) wx.navigateTo({ url: /pages/login/index }) reject(new Error(登录过期)) return } resolve(res.data) }, fail(err) { reject(err) } }) }) } module.exports { request, BASE_URL }这个封装里的关键参数是 BASE_URL。开发者工具里可以用 localhost但真机预览时手机访问不到你电脑的 localhost必须改成电脑的局域网 IP比如 http://192.168.1.5:3000/api。所以一定要把 BASE_URL 单独放一个 config 文件而不是散落在各个页面里。token 的处理要特别留意。wx.getStorageSync(token) 每次请求都读缓存后端通过 Authorization 头识别用户。如果后端返回 401正确做法是先清缓存再引导登录如果不清缓存直接跳转会出现「登录页来回横跳」的怪圈。2.3 连锁门店的数据权限store_id 隔离与超级管理员连锁场景和单店系统最大的区别在权限模型。单店只需要区分「普通用户」和「管理员」连锁必须区分「总部管理员」「门店店长」「普通员工」三个角色。数据权限的核心规则是普通用户只能看到自己 store_id 的数据门店店长能看本店全部数据总部管理员能跨店看所有数据。在代码层面前端每次请求带上当前用户属于哪个门店后端不能轻信前端传的 store_id必须从 token 里解析出来的用户信息里取。// 后端伪代码从 token 解析用户强制过滤门店 function getUserFromToken(token) { // jwt 或 session 解析得到 { userId, storeId, role } } router.get(/customer/list, (req, res) { const user getUserFromToken(req.headers.authorization) const where user.role admin ? {} // 总部管理员不加门店条件 : { store_id: user.storeId } // 门店角色强制带门店条件 const list db.query(SELECT * FROM customer WHERE ?, where) res.json(list) })注意这里门店管理员和总部的分支逻辑admin 不加过滤条件其他角色必须从 token 里取 storeId 拼进 SQL。我看到过不少源码在查询列表时让前端传 storeId这会导致用户改一下请求参数就能看到别店数据属于高危漏洞。3. 用微信开发者工具跑通智慧美容小程序导入、建库、联调三步走原理看清了开始动手。本地跑通这套源码需要三样东西微信开发者工具、MySQL 数据库、Node.js 或 Java 运行环境。按下面三步走半小时内能见到登录页。3.1 从 zip 到编译成功导入工程与 AppID 配置先把 zip 解压。Windows 用户直接用系统自带解压大概率能行但如果你看到解压出来的目录名乱码说明原打包用了中文编码建议用 7-Zip 解压并手动指定编码。解压后找到小程序前端目录一般叫 miniprogram 或 client。打开微信开发者工具点「导入项目」目录选择刚才解压出来的前端目录AppID 选择「测试号」即可不需要注册企业小程序。导入后先做的事不是编译而是改本地设置在详情面板勾选「不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」。这一步不勾后面所有请求都会报域名校验失败。常见的一个小坑是「开发版小程序已过期请在开发者工具重新扫码」。这是微信对开发版的 30 天有效期限制重新编译并扫码刷新 session 就好不用动代码。首次编译如果报某个组件找不到大概率是项目依赖没有安装看后端目录有没有 package.json有的话先执行 npm install。3.2 初始化数据库美容院数据模型建表与测试数据数据库初始化是跑通的关键。源码包里一般有个 sql 或 database 目录里面有建表脚本和演示数据脚本。先建库再导表注意字符集。# 创建数据库并指定 utf8mb4避免 emoji 报错 mysql -u root -p -e CREATE DATABASE beauty DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入建表语句和初始数据 mysql -u root -p beauty server/sql/init.sql执行成功后登录 MySQL 检查一下表数量。正常情况下应该有 customer、store、goods、project、order、membership_card 这六张核心表外加管理员表。演示数据里一般会有两到三家门店方便你验证数据隔离。数据库账号密码配置在后端目录的配置文件中常见位置是 config.js 或 application.yml。要把用户名密码改成你本机的真实值否则后端启动会报连接失败。这一步最容易被忽略很多新手明明 MySQL 装好了后端却起不来日志一翻全是 access denied。3.3 本地接口联调config.js、代理与首个登录请求数据库就绪后启动后端服务。如果是 Node 后端在 server 目录下执行 npm install 然后 npm start如果是 Java 后端用 IDE 打开或 mvn spring-boot:run。启动成功会看到监听 3000 或 8080 端口的日志。后端起来了改前端 config 里的接口地址。本地调试用 localhost但注意开发者工具里模拟器和真机的网络环境不同。// config.js module.exports { // 本地调试用 localhost真机预览必须改成电脑的局域网 IP baseUrl: http://localhost:3000/api, // 图片上传走独立路径不要和业务接口混在一起 uploadUrl: http://localhost:3000/upload }改完后在开发者工具里重新编译进入小程序首页点击登录看 Network 面板有没有发出请求。如果请求返回 200 且拿到用户信息说明整套链路通了。此时可以在登录成功的回调里动态设置首页标题比如 wx.setNavigationBarTitle({ title: user.storeName })这个能力后面做多门店切换时会用到。联调阶段最常见的报错就是 request 请求失败原因不外乎三个后端没启动、baseUrl 指向不对、域名校验没关。按这个顺序排查90% 的问题能解决。4. 四个管理模块的核心业务逻辑客户、门店、商品、项目的实现要点跑通只是开始真正决定系统能不能用起来的是业务逻辑的严谨程度。连锁美容场景里客户管理、门店管理、商品管理、项目管理这四个模块各自有容易踩坑的设计点。4.1 客户管理会员卡余额、剩余次数与过期时间的判定顺序客户管理的核心是「会员卡资产」的正确计算。一张卡同时有余额、剩余次数、开始日期、结束日期、状态五个维度的信息用户在收银台消费时判定顺序不能乱。// 核销判定伪代码顺序是状态 - 过期 - 次数反了会出现负数次数 function checkCard(card) { const now new Date() if (card.status ! 1) return { ok: false, msg: 卡片已冻结或注销 } if (card.expire_date new Date(card.expire_date) now) { return { ok: false, msg: 会员卡已过期 } } if (card.remain_times 0 card.balance 0) { return { ok: false, msg: 余额和次数均不足 } } return { ok: true } }这个判定顺序有三个细节要注意。第一先查状态再查过期因为一张已注销的卡即使没过期也不能用第二过期时间比较要转成统一的时间戳格式直接比较字符串会出「2024-1-31」和「2024-02-01」这种字符串排序错误第三次数和余额要分开判断很多美容院的项目卡是「次数用完了但还有余额」此时应该引导用户用余额支付而不是拒绝服务。客户列表的查询接口还要注意分页。连锁机构客户量大动辄几万条不分页的话小程序端会卡死。常见做法是 limit/offset 分页加一个 keyword 参数支持手机号和姓名模糊搜索。4.2 门店商品连锁场景下「统一商品、分店库存」的选型「小程序商城」式的商品管理和美容院的商品管理有个本质区别电商是总仓发货美容院是门店现货。一个洗护产品在 A 店有 10 件在 B 店可能只有 2 件卖掉之后该扣哪个店的库存常见做法有两种。第一种是商品表里直接放一个总库存字段简单但没法知道各门店还剩多少第二种是加一张「门店-商品」关联表每个门店维护自己的库存。连锁场景必须选第二种否则盘点和对账会出大问题。-- 门店-商品库存关联表每个门店独立库存 CREATE TABLE store_goods ( id bigint NOT NULL AUTO_INCREMENT, store_id bigint NOT NULL, goods_id bigint NOT NULL, stock int DEFAULT 0 COMMENT 当前门店库存, warning_stock int DEFAULT 5 COMMENT 库存预警阈值, PRIMARY KEY (id), UNIQUE KEY uk_store_goods (store_id, goods_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表的设计里唯一索引 uk_store_goods 是防止同一商品在同一个门店出现两条记录。扣库存时用 update ... where stock 0 的原子操作避免并发下单把库存扣成负数。门店管理模块本身相对简单主要就是门店信息的增删改查和营业状态切换。但注意营业时间的存储建议用字符串「09:00-21:00」而不是两个时间字段因为后续做预约功能时解析更灵活。门店排序要支持自定义 sort 字段总部门店要能置顶。4.3 项目管理与核销订单状态机是业务的主心骨项目管理是美容小程序和普通商城差异最大的地方。一个美容项目不是「下单即完成」而是「购买卡项 - 预约到店 - 服务核销」的长链路。如果代码里没有状态机会出现用户买了两次项目、核销时重复扣次数的严重 bug。订单表的设计要拆成两个维度支付状态和服务状态。字段取值含义业务说明pay_status0/1/2未支付/已支付/已退款决定资金流service_status0/1/2/3待使用/已预约/已完成/已取消决定服务流支付状态和服务状态必须分开存不能合并成一个字段。理由很直接用户可能支付了但一直没来核销此时 pay_status1 但 service_status0合并字段表达不了这种状态。核销操作是整个系统的关键事务必须放在数据库事务里执行扣减会员卡剩余次数 更新订单服务状态 记录核销人。任何一个操作失败都要回滚。之前见过一个翻车案例核销时只扣了卡次数没更新订单状态用户投诉说「明明做了项目订单还显示待使用」。5. 智慧美容小程序避坑指南从 zip 解压到真机预览的 5 个血泪坑这套源码的坑不少下面 5 条是我按踩坑频率排出来的。每条都是真实发生过的按「现象 → 原因 → 解决」写清楚。5.1 现象zip 解压报错或解压后目录乱码从网盘下载下来的 zip 在 Windows 上双击解压弹窗提示「文件损坏」或「密码错误」但文件明明没有设密码。用 macOS 自带归档工具解压中文目录名解压出来是一堆乱码。原因分两种。中文 zip 常见的问题是打包工具用了 GBK 编码而现代解压工具默认按 UTF-8 解导致文件名乱码。另一种是 zip 的加密标志位被篡改过也就是常说的「伪加密」解压工具误以为文件有密码。解决先用 7-Zip 打开压缩包如果能看到正确的文件名说明只是编码问题7-Zip 会自动检测如果 7-Zip 提示有密码右键文件属性里检查加密标志位用 7-Zip 的「修复压缩文件」功能可以去掉误标志位。Linux 服务器上解压中文 zip 记得加参数 unzip -O gbk 指定编码。5.2 现象开发版过期登录态频繁 401小程序用着用着突然跳「开发版已过期请在开发者工具重新扫码」或者请求批量返回 401重新登录后过一会儿又掉线。原因有两个层面。开发者工具的「开发版」有 30 天有效期到期必须重新扫码这是微信的硬限制。401 频发则通常是后端 session 有效期太短把 token 过期时间设成了 2 小时用户中午登录下午就失效了。解决开发者工具里重新点击「预览」或「真机调试」扫码刷新即可。后端把 token 有效期改成 7 天或 30 天并在每次请求时做滑动续期——如果 token 剩余有效期小于一半就下发新 token客户端收到后更新缓存。这个方案能避免用户做到一半被强制踢出登录。5.3 现象真机上拿不到用户头像和昵称代码里写了 wx.getUserProfile开发者工具里能弹出授权窗并拿到头像昵称但真机预览时弹窗不见了拿到的昵称全是「微信用户」头像是个灰色默认图。原因微信在 2022 年 10 月后调整了用户信息授权策略wx.getUserProfile 返回的已经是匿名信息不再提供真实头像昵称。这是平台策略变化不是代码 bug改代码也没用。解决改用微信官方的「头像昵称填写能力」——头像用 button 组件 open-typechooseAvatar昵称用 input 组件的 typenickname。如果业务不强制要昵称直接引导用户绑定手机号手机号在美容行业比微信昵称更有业务价值。5.4 现象上传的图片本地能看到换台手机就看不见用户在小程序里上传了头像或评价图片自己在开发者工具和自己的手机上都看到图片正常显示但同事的另一台手机打开图片就是空白。原因wx.chooseMedia 选择图片后返回的是本地临时路径类似 wxfile://tmp_xxx这个路径只在当前设备当前会话有效。代码里直接把临时路径存进了数据库没有上传到服务器。解决图片必须走上传流程。wx.chooseMedia 拿到临时路径后调用 wx.uploadFile 把文件传到后端或对象存储后端返回一个可访问的 URL再把 URL 存入数据库并 setData 回页面。这里要注意上传接口要单独配 uploadUrl不要和业务接口共用 baseUrl因为上传的请求格式和普通 JSON 请求不一样。5.5 现象A 店的员工登录后看到了 B 店的客户数据系统上线后A 店员工登录账号客户列表里混着 B 店的客户资料和订单记录。一开始以为是数据库串了后来发现是接口查询条件漏了门店过滤。原因后端查询列表的 SQL 写成了 SELECT * FROM customer没有带 store_id 条件。前端登录用户是 A 店的但接口返回了所有门店的数据。这不是 bug是数据权限设计缺失。解决所有涉及门店数据的查询接口统一从 token 解析用户所属门店并拼入 SQL不允许前端传 storeId。参考第 2.3 节的做法按角色区分总部管理员不加过滤门店角色强制过滤。上线前用两个门店的测试账号交叉验证能多拦住一批这类问题。6. 改造这套智慧美容源码的三个进阶方向从「能跑」到「能用」基础版本跑通后离真正商用还差几步。这里给出三个值得动手的进阶方向每个都不需要大改架构但能把用户体验拉高一个档次。6.1 给项目管理加一张预约表解决「到店排队」问题美容项目的服务时间是可预期的一个深层清洁做一小时一个烫染做三小时。没有预约功能客户只能到店碰运气。给项目模块补一张预约表字段包括门店、客户、美容师、日期、开始时间、结束时间、状态。预约冲突的判断逻辑是同一美容师在同一时间段不能存在两条状态为「待服务」或「已确认」的预约。最简单的方式是查重叠区间SELECT id FROM appointment WHERE staff_id ? AND status IN (1, 2) AND appoint_date ? AND start_time ? AND end_time ?;查询参数里的 ? 依次是美容师 ID、日期、预约结束时间、预约开始时间意思是「只要有已存在的预约它的开始时间早于我要约的结束时间并且结束时间晚于我要约的开始时间就说明撞上了」。6.2 从「总仓库存」升级到「门店分仓」第 4.2 节讲过 store_goods 关联表的做法现在把它落地。给商品表增加一个「库存模式」字段0 表示总仓模式1 表示门店分仓模式。下单时如果是分仓模式扣减当前门店的库存不是总库存。这个升级的核心价值是支持连锁总部统一采购、分店独立售卖盘点的真实业务流。库存调整建议留操作日志记录调整人、调整前后数量、原因。美容院的库存损耗率不低过期产品和破损产品都要在系统里走「报损」流程不能直接改库存数字否则月底对账对不上。6.3 用三个测试账号验证权限隔离再决定是否上线改完代码打算上线前我每次都会做一轮权限验证准备一个总部管理员账号、一个 A 店店长账号、一个 B 店普通员工账号。分别登录逐个点开客户列表、订单列表、商品库存页面确认 A 店账号看不到 B 店任何数据总部账号能看到全部。然后故意用 B 店账号去请求 A 店订单详情接口看后端是否返回了数据——如果返回了说明后端缺归属校验必须修完再上。这套验证方法半小时能做完却是上线后最省心的一步。我拿到任何一套源码都会先删掉演示数据用三个测试账号从登录到核销走一遍主流程再考虑接真实数据。这个习惯帮我避开了至少三次「上线当天发现数据串店」的事故。希望帮到你。本文还有配套的精品资源点击获取
返回列表