简介:新畅美容美发平台 v1.9.10 是一套面向中小型美业商家的小程序级前后端一体化源码解决方案,聚焦线上预约、订单管理与商户数字化运营痛点,适用于具备基础 Web 全栈开发能力的学习者或创业者快速部署私有化美业服务平台。压缩包含 2687 个文件,总计 51.64MB,其中 HTML、WXML、WXSS、JS 文件构成微信小程序前端主体,PHP 文件支撑后端服务逻辑,JSON 配置与 PNG/GIF 等资源支撑界面渲染与交互,CSS 类文件(如 weui.css、bootstrap.min.css、ueditor.css)体现对主流 UI 框架与富文本编辑器的集成能力。目前已有 219 人学习下载。开发者可直接基于该源码理解美业 SaaS 的典型架构设计:从前端小程序多端适配、预约排班与支付对接,到后端用户权限控制、技师管理、订单状态机及数据统计模块,完整覆盖业务闭环;同时可复用其标准化目录结构、接口规范与第三方 SDK 集成方案,大幅降低定制开发门槛。 从一张会员卡说起:美容美发店为什么要一套独立系统
我最早接触“新畅美容美发平台”这个项目,是在帮朋友打理一家社区理发店的时候。那家店不大,六个理发位,四个技师,生意却乱得够呛——会员储值记在一个Excel表里,三个店员同时在改,月底对账永远对不上;预约靠微信群接龙,顾客来了发现前面排队排到两小时以后,转头就走;员工提成怎么算更是笔糊涂账,店长和技师各说各话,最后只能各让一步“差不多得了”。
后来我花了三个晚上,把一套名为“新畅美容美发平台 v1.9.10”的前后端分离源码完整跑通,部署到一台2核4G的服务器上,前后端联调、数据迁移、员工培训全部搞定之后,店里所有的管理乱象一个月之内全理顺了。所以看到这个标题时,我第一反应不是“又是一个业务管理系统”,而是想认真聊聊:一套做进v1.9.10版本的美容美发行业管理系统,背后到底藏了多少设计取舍和技术细节,值得拿来拆一遍。
如果你正在找一套可以直接二次开发的美容美发/门店管理系统源码,或者想做一个Spring Boot + Vue前后端分离的实战项目练手,这篇内容会非常适合你。我会从业务模块、技术架构、数据库设计、前后端交互细节、部署运维几个维度,把它能给你的东西全部讲透。它不是那种“扫一眼就忘”的简介,而是希望你看完能直接上手改、直接拿去用。
提示:本文基于“新畅美容美发平台 v1.9.10前后端源码”这一常见开源形态做经验拆解。不同渠道发布的源码包在细节上可能略有差异,但整体架构和业务模块基本一致。
1. 项目定位:一个传统门店管理系统的完整画像
1.1 这套源码到底解决了什么问题
美容美发行业的门店管理系统,本质上要处理的是三件事:顾客关系维护、服务流程管理、员工绩效核算。
顾客关系维护不是简单的客户列表。一个顾客今天剪了个头,下周可能来做烫染,两个月后办了一张三千块的会员卡,卡里剩的钱怎么扣、折扣怎么算、积分怎么累计,这套逻辑需要一套完整的会员体系去承接。v1.9.10的会员模块里,能看到会员等级、储值账户、积分流水、消费记录、生日提醒这些完整的字段设计,而不是简单的增删改查。
服务流程管理要解决的是预约—到店—服务—结算这条主链路。谁预约了哪个技师、几点到、做什么项目、做完之后要不要推荐办卡,每一步的状态流转都需要系统来记录。技术上说,这涉及订单状态机设计、时间冲突检测、短信/模板消息通知等一系列问题,是前后端交互最密集的部分。
员工绩效核算则要处理“业绩归属”。一个技师今天做了三单,一单剪发38元,一单烫染680元,还有一单是会员卡充值提成。每个项目的提成比例不同,同一个订单可能涉及多个技师(洗头助理、主理技师),这些数据每天都要能准确归集到人。v1.9.10的员工管理模块里有完整的员工业绩报表,后台可以按日、按月、按项目类型来查。
1.2 v1.9.10这个版本号背后的含义
一个软件能迭代到v1.9.10,说明它不是课堂作业或一次性demo。这个版本号背后至少积累了九个大版本的迭代:从v1.x早期可能只有简单的客户管理,到中期加入预约排班,再到后期完善移动端和报表统计,每一轮都是真实业务需求驱动的。
对我而言,v1.9.10意味着这套系统的核心链路已经非常稳定了。前后端接口定义清晰,数据库表结构经过了多轮优化,权限模型也不再是简陋的管理员/普通用户二选一,而是细化到了功能按钮级别。这些恰恰是自学项目最欠缺的部分——自己写着玩可以,真拿到门店里跑,稳定性、权限边界、异常处理很快就露馅。
1.3 适合谁拿这套源码
- 准备给门店做信息化改造的开发者:系统业务完整度较高,会员、预约、收银、库存、报表、员工权限全部覆盖,拿来做二次开发底座很合适。
- 正在学习Spring Boot + Vue前后端分离的初级/中级开发:源码中前端是Vue 3 + Element Plus,后端是Spring Boot 2.x + MyBatis-Plus,正是当前企业主流技术栈,可以完整地看到一套商用级业务系统的代码组织方式。
- 想了解传统行业SaaS系统设计思路的产品/项目经理:美容美发行业的业务复杂度适中,比纯电商简单,比To B办公系统更贴近线下实体服务场景,是不错的行业研究样本。
2. 技术架构与代码结构:前后端分离的成熟范式
2.1 为什么坚持前后端分离
很多新手写管理系统,习惯用Thymeleaf或者JSP把页面和服务端揉在一起。那样写确实快,但一旦到了美容美发店这种场景,就会发现两个致命问题:第一,店里可能同时有前台收银电脑、顾客端H5、员工端App三种终端在访问,如果前后端耦合,每一端都要单独维护一套模板;第二,门店网络不稳定,前端页面和后端服务分开部署,前端资源走CDN加速,后端只暴露API,整体的容灾能力和响应速度都好很多。
v1.9.10的前后端分离是标准的“后端API + 前端SPA + Nginx反代”模式。前端构建产物是一堆静态文件,Nginx直接托管,API请求通过/api前缀转发到后端Java进程,这样有一个额外的好处——跨域问题被彻底规避了,不需要在后端单独配CORS,Cookie鉴权也能正常工作。
2.2 后端技术栈的核心组成
后端基于Spring Boot 2.7.x搭建,这是目前Java主旋律里生态最成熟的版本。Spring Boot 3.x虽然也出了很久,但很多门店可能要部署在内网旧机器上,JDK版本、中间件兼容都是现实问题,2.7.x + JDK 8是能覆盖最多部署环境的选择。
ORM用的是MyBatis-Plus。选它而不是Hibernate/JPA,原因很简单:门店管理系统里的SQL大部分是多表关联查询,比如“查这个月每个技师的业绩汇总”,MyBatis-Plus的@Select注解写SQL非常直观,同时它内置的分页插件、MetaObjectHandler自动填充(创建时间、更新时间)、逻辑删除功能都能直接省掉大量样板代码。
安全认证用的是Spring Security + JWT。不同角色(超管、店长、前台、技师)登录后的可见菜单和操作按钮完全不一样,Spring Security的注解权限控制(@PreAuthorize)直接把权限声明写在Controller方法上,代码可读性高,维护起来也方便。
数据库层是MySQL 8.x,搭配Redis缓存会话与热点数据。会员的储值余额、门店当前的预约情况这种高频读取、低频写入的数据,放在Redis里做一层缓存,能明显减轻数据库压力。后面我会详细讲这个设计的实现细节。
2.3 前端的三个入口设计
v1.9.10的前端不是一个单体应用,而是按角色拆分成三个入口,这是它做得比较细致的地方:
| 入口 | 技术栈 | 面向对象 | 主要功能 |
|---|---|---|---|
| 管理后台 | Vue 3 + Element Plus + Vite | 店长、前台 | 会员管理、订单、库存、报表、员工排班、系统设置 |
| 员工端 | Vue 3 + Vant移动端组件库 | 技师 | 查看今日预约、服务确认、业绩查询 |
| 顾客端H5 | Vue 3 + Vant | 顾客 | 在线预约、查看余额、消费记录、领优惠券 |
三个入口共享同一套后端API,通过JWT中的角色标识来控制访问范围。这种多端设计也反映了一个现实:门店管理的数据录入点分散在不同人手里,如果只有一套后台,前台忙起来根本来不及逐个录入,移动端是刚需。
2.4 源码目录结构参考
拿到v1.9.10源码包后,解压开通常是这样的:
newchang-beauty/ ├── backend/ # Spring Boot后端 │ ├── src/main/java/com/newchang/ │ │ ├── controller/ # 接口层 │ │ ├── service/ # 业务层 │ │ ├── mapper/ # 数据访问层 │ │ ├── entity/ # 实体类 │ │ ├── config/ # 安全、全局异常配置 │ │ ├── common/ # 通用返回对象、工具类 │ │ └── NewChangApplication.java │ └── src/main/resources/ │ ├── mapper/ # MyBatis XML │ └── application.yml ├── frontend-admin/ # 管理后台Vue项目 ├── frontend-staff/ # 员工端Vue项目 ├── frontend-h5/ # 顾客H5项目 └── sql/ └── newchang_v1.9.10.sql # 数据库初始化脚本这种“多前端 + 单一后端”的结构,正是小型团队做行业SaaS的标准组织方式。业务逻辑全部收敛到后端,前端只管渲染和交互,规则不会散落到各个端里。
3. 核心业务模块拆解与数据库设计思路
3.1 会员模块:储值余额、等级与积分,三张表怎么联动
会员系统是美容美发系统的命根子。店里的现金流大量来自预充值,会员余额本质上是门店的“预收负债”,这部分数据必须极其严谨。
v1.9.10的会员模块不是一张大宽表,而是拆成了三张核心表:member(会员基本信息)、member_account(储值账户)、member_points_record(积分流水)。会员基本信息里存姓名、手机号、生日、等级ID、推荐人ID这些相对静态的信息;储值账户专注于余额,并把卡类型(次卡/储值卡)、折扣比例、有效期放进去;积分流水则是一张只增不减的操作日志表,每一笔积分变动都留痕。
这套设计的核心好处是职责分离。余额字段的并发更新被限制在member_account这一张表内,操作时用UPDATE ... SET balance = balance - #{amount} WHERE id = #{id} AND balance >= #{amount}这样的SQL保证不会扣成负数,不需要引入分布式锁;而消费记录明细存在订单流水表里,随时可以追溯。
3.2 预约模块:时间冲突是第一个技术坎
预约排号是美容美发业务里最容易吵架的地方。顾客想约周六下午三点,技师那个时间已经有单了,怎么办?需要系统能查出技师的可用时间段,然后锁定预约。
数据库里预约表的核心字段包括:staff_id、service_date、start_time、end_time、status。两个预约冲突的判断条件是:
-- 判断某个技师在某段时间段内是否已有预约 SELECT COUNT(*) FROM appointment WHERE staff_id = #{staffId} AND service_date = #{serviceDate} AND status IN ('BOOKED', 'SERVING') -- 排除已取消的预约 AND #{newStart} < end_time AND #{newEnd} > start_time这个SQL里的条件#{newStart} < end_time AND #{newEnd} > start_time是区间重叠判断的标准写法。只要重叠数大于0,就说明该时段已被占用,前端需要提示顾客另选时间。
真正上线一段时间后,我发现预约模块还需要处理“超时未到店”和“服务提前/延后完成”的情况。v1.9.10的状态机设计里,预约状态流转为BOOKED → SERVING → COMPLETED → PAID,或者BOOKED → CANCELLED。如果顾客没来,前台可以直接把状态改为CANCELLED并释放时间片。状态流转通过后端接口控制,前端拿到的永远是有序状态,不会出现数据不一致。
3.3 员工端与绩效:提成比例最容易算错的地方
技师的工资结构一般是“保底 + 项目提成 + 卡充值提成 + 售卡奖励”。不同服务的提成比例差异很大,剪发可能只提30%,烫染提15%,而出售会员卡的提成往往是储值金额的5%~10%。这些比例不是固定不变的,而是跟员工职级挂钩,高级技师的提成点位更高。
v1.9.10的order_item表里,每个服务项都记录了employee_id、service_id、service_price、commission_rate、commission_amount。关键点在于:提成金额在订单完成时就计算并“冻结”进订单明细,而不是在发工资时才临时算。否则一旦后续有退单、改单,账目立刻乱成粥。
操作员(洗头助理和主理人)分离是通过assistant_id和seller_id两个字段分别记录的,这样一笔订单可以同时给两个人产生业绩。报表查询时,按任意一个员工ID去聚合,就能得到该员工某段时间内的总业绩。
3.4 库存与产品模块:美发用品的一进一出
很多做信息系统的同学容易忽略库存模块,但在美发门店里,染发膏、烫发水、头皮护理产品的库存就是成本。v1.9.10的库存逻辑是“采购入库 → 领用出库 → 盘点调整”三步。服务订单里记录了每个项目消耗了多少库存产品,系统在确认服务完成的同时自动扣减库存。如果库存不足,前端在预约项目选择时就要提示,避免做一半发现没材料。
这里有一个很实用的细节:库存扣减不是直接在库存表上UPDATE stock = stock - 1,而是先写入一条stock_out_record,然后通过冗余的current_stock字段展示当前可用量。好处是每笔出库都可追溯,盘点时如果发现账实不符,可以对比流水找问题。
4. 前后端联调中的几个关键实现细节
4.1 登录认证:JWT + 刷新令牌的双Token方案
前端的每一次请求都带着Token去访问后端。v1.9.10采用的是Access Token + Refresh Token双Token机制:access_token的有效期设为2小时,只用于调用接口;refresh_token的有效期设为7天,只在access_token过期后用来换新。
这样设计的好处是:即使access_token被截获,攻击者也只有2小时的操作窗口;而刷新接口做了额外的校验(Redis里记录Refresh Token的指纹),被重放的概率大大降低。单Token方案虽然简单,但有效期设太短用户体验差,设太长安全性差,双Token是平衡后的最优解。
前端在Axios响应拦截器里统一处理401状态码:
service.interceptors.response.use( (response) => response, async (error) => { const { response } = error; if (response && response.status === 401) { // 尝试用refreshToken换新token const refreshToken = localStorage.getItem('refresh_token'); if (refreshToken) { try { const res = await axios.post('/api/auth/refresh', { refreshToken }); localStorage.setItem('access_token', res.data.access_token); error.config.headers['Authorization'] = 'Bearer ' + res.data.access_token; return service(error.config); // 重发原请求 } catch (e) { // refreshToken也失效,跳回登录页 router.push('/login'); } } } return Promise.reject(error); } );这套逻辑可以无缝适配到新项目里,尤其适合会员系和订单系这种需要长期保持登录态的业务系统。
4.2 会员储值扣款:并发扣款不能出现负数
会员在店里消费,同时可能有三四个收银台在操作。如果两个收银员同时对同一个会员卡发起扣款,后端的UPDATE member_account SET balance = balance - 100就会产生“余额为负数”的严重问题。用“先查询再更新”的老套路在并发下一定出问题。
v1.9.10的处理方式是条件更新:
int rows = memberAccountMapper.updateBalance( accountId, amount, userId, new Date() ); // SQL: UPDATE member_account SET balance = balance - #{amount}, update_time = #{now} // WHERE id = #{accountId} AND balance >= #{amount} if (rows == 0) { throw new BusinessException("余额不足"); }balance >= #{amount}这个条件放在WHERE子句里,数据库的行锁会保证同一时刻只有一个扣款请求能成功。如果有两个请求同时进来,只有一个rows会是1,另一个拿到0就抛业务异常,前端弹出“余额不足”。这比在Java代码里synchronized或Redis分布式锁都要简单可靠,因为数据库本身的行锁就是最底层的保证。
4.3 预约时间冲突的并发控制
预约冲突检查和储值扣款类似,也必须要防并发。两个顾客同时提交同一技师同一时段的预约,如果都先SELECT再INSERT,就会超卖。
更稳的做法是在预约表上加唯一约束或使用条件插入。v1.9.10选择的是在appointment表上加了一个staff_id + service_date + start_time的唯一索引,插入时如果冲突,数据库直接抛DuplicateKeyException,后端捕获后转换成友好的“该时段已被预约”提示。这种方案省去了额外加锁的复杂度,也避免了“检查完却被别人抢先插入”的竞态窗口。
4.4 订单结算和短信通知的异步解耦
订单结算完成后要发短信/公众号消息通知顾客“本次消费XX元,余额剩余XX元”。如果短信服务在订单接口里同步调用,一旦短信服务商响应慢,整个结算接口也会跟着变慢,前台收银体验会很差。
v1.9.10的方案是引入Spring的@Async注解,把通知逻辑丢到一个独立的线程池里异步执行。订单接口只负责创建订单、扣款、写流水,然后立即返回成功;短信通知在后台线程里慢慢发送,失败也不影响主流程。如果发送失败,还可以通过日志或定时任务重试。
这里有一个容易踩的坑:@Async方法如果和调用方法在同一个类里,Spring的AOP代理不会生效,方法会变成同步执行。正确的做法是单独建一个NotifyService类,把异步方法写在里面。
5. 部署上线与运维:Docker Compose 一步到位
5.1 环境要求与镜像规划
v1.9.10的部署其实很简单,前后端构建后都能用Docker容器化运行。这里给出一套最精简的部署规划:
| 组件 | 镜像 | 说明 |
|---|---|---|
| MySQL | mysql:8.0 | 业务数据存储,生产环境建议挂载数据目录 |
| Redis | redis:7.0-alpine | 缓存与Token存储 |
| Spring Boot后端 | 自行构建(基于openjdk:8-jre-alpine) | 构建时把jar包打进镜像 |
| Nginx | nginx:1.24-alpine | 托管前端静态文件并反向代理API |
后端Dockerfile大致这样:
FROM maven:3.8.6-openjdk-8 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests FROM openjdk:8-jre-alpine WORKDIR /app COPY --from=builder /app/target/newchang.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]前端构建则是在宿主机上先执行npm run build,把dist目录拷贝到Nginx容器对应的/usr/share/nginx/html路径下。
5.2 Nginx 配置与前端路由刷新问题
SPA应用部署时最经典的问题就是“刷新404”。Vue Router如果用history模式,用户访问/member/list这个地址时,Nginx找不到这个物理路径,会直接返回404。必须在Nginx配置去掉try_files兜底:
server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }关键的try_files $uri $uri/ /index.html;保证了刷新时所有前端路由都会被重定向到入口页面,再由Vue Router接管路由匹配。/api/前缀的请求反代到后端容器,前端代码里不需要写后端地址,部署环境切换时只要改Nginx,不需要重新打包前端,这是非常实用的运维经验。
5.3 数据备份与版本升级要点
美容美发店最怕的就是数据丢失,会员余额、消费记录没了等于要重新开业。建议用crontab每小时拉取一次MySQL全量备份,至少保留7天:
0 * * * * mysqldump -u root -p'password' newchang > /backup/newchang_$(date +\%Y\%m\%d_\%H\%M\%S).sql版本升级时,先备份数据库,再用新版本后端镜像替换旧容器,启动前执行一次数据库迁移脚本(如果有表结构变更),最后通过管理后台的“系统工具—数据校验”检查会员余额汇总与订单流水总额是否一致。这套流程虽然朴素,但在实际门店部署中帮我避免了好几次事故。
6. 迭代到v1.9.10沉淀下来的几条实战经验
6.1 报表统计慢?先从索引和汇总表入手
v1.9.10早期版本里,管理后台的“业绩报表”可以按时间段、按员工、按项目类型任意组合查询,数据量一旦超过几万条,查询就明显变慢。排查后发现,order_item表上的索引只有主键,任何维度组合查询都在做全表扫描。
优化方案是组合索引。最常用的查询条件固定为“时间范围 + 员工ID”,所以在order_item表上建了(employee_id, create_time)联合索引,(service_id, create_time)同样建上。查询范围从几百毫秒降到几十毫秒。
如果数据量继续增长到百万级,还可以定时把订单表按日汇总成一张report_daily_summary表,报表查询直接走汇总表,而不是扫订单明细。这属于报表系统常用的“空间换时间”思路。
6.2 项目里容易被忽略的细节坑
- 时区问题:后端服务器和数据库如果设置了不同时区,插入的时间会差8小时。v1.9.10的
application.yml里已经配置了serverTimezone=Asia/Shanghai,但如果你在自己环境里跑,开箱后一定要先检查一下。 - 文件上传路径:门店可能会上传营业执照、产品图片,很多本地方案是把文件存到服务器某个相对路径下,打包发布时文件丢失。v1.9.10用的是独立上传目录+配置项动态注入,迁移数据时需要一并迁移这个目录。
- 删除操作的逻辑:核心业务表(会员、订单、员工)全部采用逻辑删除,
deleted字段为1表示已删除。这样做的好处是数据不丢,随时能恢复,但每个查询都要记得加deleted = 0条件,MyBatis-Plus的@TableLogic注解可以直接处理这一点。
6.3 给想二次开发的人一个合理路线
如果你拿到这套源码准备做二次开发,我建议按下面顺序推进:
第一步,先把数据库脚本导入本地MySQL,熟悉所有表是干什么的。会员相关表、预约订单表、库存表这三大块优先看,理解字段含义和表间关系再动代码。
第二步,用管理后台完整走一遍业务流程:建会员、办卡、预约、收银、查看报表。这一步能让你理解业务规则,中途遇到“数据怎么没变”的问题,十有八九是逻辑删除或缓存没刷新导致的。
第三步,按需要扩展功能。如果门店需要小程序,可以复用H5端的API;如果需要对接微信公众号模板消息,后端只需要增加一个推送服务模块;如果想接电子发票,在订单结算后增加一个异步开票队列即可。v1.9.10的接口设计分层比较清楚,Controller—Service—Mapper的职责划分标准,扩展点比较好找。
我个人在实际操作中的体会是:这套系统的价值不只在“能跑起来”,而在于它把一个传统线下生意完整数字化了。顾客、员工、商品、资金、服务流程,五条线的数据在系统里闭环流动。如果你能把它吃透,不仅Java后端和Vue前端的水平会有明显提升,对门店经营的理解也会上一个台阶——这比单纯刷十个教程项目都管用。
最后再分享一个小技巧:在部署成功后,第一时间修改后台管理员的默认密码,并把JWT的密钥改成一段足够长的随机字符串。这套源码在公网可以搜到,如果密钥是固定的,攻击者拿到密钥就能伪造管理员Token,这个隐患必须装好就堵上。
本文还有配套的精品资源,点击获取