做门店管理系统的这些年我总结出一个规律凡是老板能坚持用超过三个月的系统往往不是功能最多的那套而是把“钱”和“账”算得最清楚的那套。美发门店尤其明显——会员储值、划卡、员工提成每一笔都牵动着老板的神经。最近整理了一套美发门店管理系统源码后端SpringBoot、前端Vue、数据库MySQL环境配置好就能直接运行。名字里虽然有两个“管理”听起来有点啰嗦但内容很务实会员储值、预约排班、员工提成、商品库存、营业报表全都覆盖。它适合两类人一类是学完前后端分离、想拿完整项目练手的开发者另一类是准备给中小型美发店做信息化改造、需要一套能快速交付的私有化方案的朋友。这篇文章我打算从这套系统的业务设计、数据模型、前后端实现、直接运行步骤到二次开发建议一条龙讲清楚。1. 项目整体设计与技术选型思路1.1 美发门店的业务痛点往往比技术难点更棘手美发店的日常运营和互联网公司的“业务”完全是两个世界。前台小妹用本子记预约老板用Excel记会员余额烫染技师凭感觉算提成——这种场景在中小型美发店里太常见了。我见过一家开了五年的店会员充值记录全靠微信聊天记录翻月底对账能对到凌晨。做美发门店管理系统本质上是把“人、卡、单、货、账”这五条线全部数字化谁是谁的会员、卡里还剩多少钱、今天谁做了什么项目、这个月每个发型师做了多少业绩、店里还有多少染膏库存这些信息不再依赖人脑记忆而是落在数据库里随时可查。这套系统建议把自己代入三个角色来看需求老板想看今天的营收、实收和欠账收银员要快速完成开卡、充值、划卡、卖产品发型师要在自己的账号下看到预约列表和提成明细。如果这三个角色用起来都顺手系统就算成功了一半。所以不管源码结构怎么变化业务模块一定要围绕这三个角色来铺开。1.2 为什么这套技术栈最“稳”市面上门店管理系统其实有很多现成的SaaS产品但如果你要的是“可定制、可二次开发、数据自己掌握”的私有化部署版本那用开源技术栈自己搭几乎是唯一选择。SpringBoot的好处有不少自动配置、内置Tomcat、生态成熟配合Maven管理依赖把原来Spring那些繁琐的XML配置全干掉了。Vue在前端领域占有率极高组件化开发让页面复用变得很舒服Element UI一套组件库就能把后台管理界面的质感拉起来。MySQL就更不用说了开源免费、普及度高中小型美发门店一天几千条流水MySQL稳稳扛住完全没必要一上来就上Oracle或者分布式数据库。我见过有人用Python Flask写这种系统也有人用PHP写写都能写但落到“客户现场改需求、后续找人维护、代码交接”这些现实问题时SpringBootSpring家族在国内的招聘市场和使用习惯上是最省事的。Vue同理会Vue的前端工程师比会React的多得多在三四线城市也容易找到人接手。技术选型有时候不是选最先进的而是选最不担心没人接盘的。1.3 功能模块拆解一套好用的美发系统该覆盖什么从功能模块上看这套系统大致可以分为六个大块系统管理登录认证、用户管理、角色权限分配给老板、店长、收银员、发型师等不同角色分配不同菜单和按钮权限。会员管理会员信息登记、会员卡开卡/续卡、储值充值、通过卡号/手机号快速查找、余额变动明细。预约与排队管理顾客提前预约某个发型师选择服务项目和时间段发型师端可以看到当天预约列表。服务与订单管理开单、选服务项目、选负责发型师、计算消费金额、收款或划卡生成消费明细。商品与库存管理洗发水、护发素、染膏、烫发剂等商品的进销存以及美发套餐的售卖与次数核销。营业报表日营收、月营收、会员储值余额汇总、员工业绩排名、商品销售统计。这六个模块基本覆盖了一家美发店从开门到打烊的所有业务流程。很多刚入行的同学容易把系统设计成“只有会员管理”或者“只有收银”做出来老板用两天就觉得不够。源码里把预约、提成、库存这些周边能力配齐才算是能真正落地的门店管理工具。2. 核心功能与数据模型设计解析2.1 会员管理与储值卡钱的流向要清清楚楚美发店的会员玩法其实蛮有行业特色的。常见的储值方式有三种一是充值金额赠送金额比如充1000送200二是充值享折扣充2000以后剪发打7折三是纯次数卡比如办一张“剪发年卡”一年内剪10次按次数消卡。这三种玩法对应的字段设计完全不同如果只用一个balance字段很容易在送金额和次数卡上打架。一般这套源码里的会员表会包含会员卡号、姓名、手机号、性别、生日、会员等级、卡内余额、累计消费、开卡时间和状态。充值记录表则需要记录充值金额、赠送金额、支付方式、操作员工、操作时间。我特别想提醒的是储值余额的变动一定要留流水不能用触发器硬改余额了事。因为门店纠纷最多的就是“我卡里到底还有多少钱”只要余额变更都有对应的流水记录客户投诉时查明细一查一个准账目也经得起对账。另外会员到期时间、锁定状态这两个字段也建议保留别等出现“过期卡还能继续划”这种问题再去补逻辑。会员卡号的生成策略也值得看一眼。有些版本用数据库自增ID有些用时间戳加随机数实际使用中我更喜欢用规则化卡号前缀日期序号比如VIP20250618001。这样前台在系统里直接输卡号或者用键盘扫码枪扫一下就能快速定位会员比输手机号快得多尤其在高峰期排队的时候体验差异很明显。2.2 预约、排班与员工提成门店运转的齿轮预约功能是美发店的刚需尤其是周末好的发型师档期能排满一整天。这里的核心数据结构是预约单顾客名称/会员卡号、预约项目、预约技师、预约日期、时间段、备注再到状态。状态字段一定要够细比如待确认、已确认、已完成、已取消、已到店。前台确认之后发型师在个人工作台就能看到今天的日程到了时间点提醒自己。预约模块还有一个容易被忽视的并发问题同一个发型师在同一天同一个时间段不能被两个顾客同时预约。如果只是前端做了选择时间控件后端接口却不去校验就可能出现“双重预约”。所以后端在写入预约时最好对employee_id、appoint_date、time_slot做一个唯一约束或者在Service层加锁判断。这个点源码里可能处理得比较基础但二次开发时务必要注意。员工提成则是另一个容易出bug的地方。美发店的提成规则很碎洗剪吹可能按固定金额提成烫染可能按业绩百分比提成套餐不同提成比例也不同甚至办卡充值也有销售提成。如果只在业务代码里写死提成比例后期换一个提成方案就要改代码。更合理的做法是把提成规则做成配置表服务项目、员工等级、提成类型固定金额/百分比、提成比例/金额、生效时间。订单完成后系统自动根据配置表计算当笔提成月底按员工汇总。源码里是不是完全按配置表设计的不好说但如果你拿到的版本还是硬编码二次开发时优先把这个模块重构掉后续会省心很多。2.3 商品库存与套餐别让“卖着卖着就没了”美发店不只是卖服务还卖产品染膏烫发剂更是消耗大头。商品库存模块基础是商品档案编码、名称、类别、进价、售价、库存单位、最低库存量、当前库存。库存变动要记录入库单和出库单例如采购入库、服务耗用出库、零售出库、盘点调整每一条都要有来源不然月底库存和实际对不上。我见过不少做门店系统的团队把库存做得像财务软件一样复杂——出入库单、审批流、调拨单全搞出来。实际上中小型美发店根本没有专职库管你要给他那么复杂的库存管理流程他根本不用。这里比较务实的做法是前台卖出商品或服务配方里包含耗材时系统自动扣减库存低于最低库存量时在首页给个提醒就好。把入口做得越少越好老板才愿意用。套餐的处理在美发行业也有点特殊。套餐本身可以看作一个“服务包”比如“新客体验套餐98元含剪发一次护理一次”。套餐订单生成后关联多条服务明细每次消费时按次数核销其中一项。为了支持“套餐剩余次数”这个显示订单明细里要有类型区分单次服务、套餐、套餐子项以及每次核销的记录表。不少开发者在设计时容易忽略套餐子项的核销表导致套餐做完一次后状态就乱了。这个模块建议大家在阅读源码时重点看。2.4 数据库表结构一张图看懂核心表我不贴完整建表语句但把核心表列出来大家对照源码看会容易得多。表名主要字段作用sys_userid, username, password, real_name, role_id, store_id系统用户与登录sys_role / sys_menu角色菜单关联表权限控制memberid, card_no, name, phone, balance, total_consume, status会员基础档案member_cardid, member_id, card_type, card_balance, expired_time会员卡可多卡recharge_recordid, member_id, amount, gift_amount, pay_method, operator_id储值/充值流水appointmentid, member_id, employee_id, service_id, appoint_time, status预约记录service_itemid, name, category_id, price, commission_type, commission_value服务项目与提成规则sale_order / order_item订单主表 订单明细服务/商品销售记录productid, name, category_id, purchase_price, sale_price, stock商品档案stock_in_outid, product_id, type, quantity, order_id库存出入库流水consume_recordid, member_id, card_id, amount, order_id会员卡扣费流水这些表设计出来之后整个系统的数据结构就有了骨架。我习惯先把ER关系理清楚再写代码特别是member、member_card、recharge_record、consume_record这四张表的关系是会员模块的核心闭环。建议打开数据库客户端把这几张表的关系画一遍看得比代码快。3. 前后端分离的实操落地3.1 后端SpringBoot工程三层结构怎么搭拿到源码后先别急着点启动花半小时扫一遍包结构。常规的SpringBoot后端是Controller-Service-Mapper三层Controller层接收HTTP请求做参数校验和基础封装不写业务逻辑。Service层业务核心处理事务、业务规则判断、跨表操作。Mapper层与数据库交互通常是MyBatis或MyBatis-Plus的Mapper接口。以会员充值为例Controller接收/member/recharge请求Service层里要做几件事校验会员状态、生成充值记录、更新会员卡余额、如果涉及赠送还要更新赠送金额、记录操作日志。这四个动作必须在同一个事务里要么全成功要么全回滚否则就会出“钱扣了余额没涨”的事故。源码里一般会在Service方法上加Transactional注解大家读代码时可以留意一下。另外统一返回体是前后端对暗号的基础。后端每个接口都应该返回固定的JSON结构一般是这样{ code: 200, message: success, data: {} }如果返回的格式五花八门前端axios就很难统一处理错误。这套源码里通常会封装一个Result类包括success()、error()等静态方法用起来比较方便。登录模块一般用JWT令牌拦截器里校验token后端接口的/login路径要放行其他路径统一拦截。密码存储记得用BCrypt加密千万别明文存在数据库里——我确实见过有项目这么做老板自己都能进数据库把所有人的密码看得清清楚楚完全是安全隐患。3.2 前端Vue页面从登录到报表的完整链路Vue这边一般是标准的管理后台结构。路由配置里登录页、工作台、会员管理、预约管理、商品管理、报表中心是独立页面。使用Vue Router时建议配置路由守卫判断本地token是否存在不存在就跳转到登录页。这一步很关键很多新手写前端时只想着展示页面忘了登录态控制结果页面能直接打开、接口全报401。axios封装也是一个值得学习的点。一般会创建一个request.js在里面设置baseURL、请求拦截器把token加到header里、响应拦截器判断code是否为200不是就统一弹提示并跳转登录。这么做的目的是让每个业务页面不用重复写错误处理代码。比如说会员管理页里点“充值”弹窗提交后只需要拿到成功结果至于401、500、余额不足这类失败场景拦截器已经统一弹了提示页面里不需要到处写try catch。页面交互上会员管理页就是典型的CRUD表格展示会员列表搜索框按手机号或卡号查询新增/编辑用弹窗表单充值操作单独一个弹窗。预约页则多了时间线视图前端拉取当天预约列表按时间段排列。报表页用Element UI的表格配合ECharts画折线和柱状图。这套组合做管理后台非常顺手也是Vue在国内企业应用里最常见的形态。如果源码里还做了动态菜单——根据登录用户的角色从后端拿菜单树然后动态生成路由那这部分代码值得重点学以后做任何后台系统都用得上。3.3 接口返回与状态码前后端怎么对暗号一个容易被忽略的细节是HTTP状态码和业务状态码的区分。有的团队习惯直接用HTTP 200表示成功500表示失败这样前端拦截起来很省事但业务层面的错误比如余额不足、会员已锁定也会被HTTP 500吞掉变得不好区分。更推荐的做法是HTTP层只表达网络和服务是否可用业务结果统一放JSON里的code字段。例如code含义200业务成功400参数错误401未登录或登录过期403无权限500服务器内部错误1001会员余额不足1002会员状态异常前端axios响应拦截器里先判断HTTP状态再判断业务code两个维度分层处理。这套源码如果你拿到可能不是严格这样做的但建议在二次开发时统一规范。特别是“会员余额不足”这类业务错误如果用HTTP 500返回前端就没办法针对性地提示“余额不足请先充值”只能显示一句“系统错误”用户体验就很差。4. 直接把项目跑起来的完整步骤4.1 环境准备装对版本能省一半时间网上关于“为什么我启动失败”的问题十个里有七个是环境版本不对。以这套技术栈的常见版本组合为例我建议这样配JDK 1.8不要用JDK 17直接跑老项目除非源码明确升级过Maven 3.6.x 或 3.8.xNode.js 14 LTS 或 16 LTSVue 2项目不要用最新的Node 20npm install容易报错MySQL 5.7 或 8.08.0需要留意驱动版本和时区设置IDE后端用IntelliJ IDEA前端可以用VSCode或IDEA都行安装顺序上先把MySQL装好并跑起来然后创建数据库执行SQL脚本。MySQL 8.0默认加密方式如果用的caching_sha2_password老版本JDBC驱动连不上建议在创建用户时指定mysql_native_password或者在pom.xml里把mysql-connector-java升到8.0.x以上。这一步属于经典坑先预防住。4.2 后端启动配置文件里的几个坑后端启动前先看application.yml或application.properties。最关键的三个配置都在数据源上server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/hair_salon?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver注意几个细节serverTimezoneAsia/Shanghai不加的话MySQL 8.0会报时区错误characterEncodingutf8最好改成utf8mb4因为会员昵称、备注里真有人会存emojiutf8存不下。改完配置后在IDEA里直接运行主类HairSalonApplication控制台看到Started就说明后端起来了。如果启动时报数据库连接失败先别怀疑代码用Navicat或命令行试一下能不能连上同一个库八成是密码、端口或驱动版本的问题。还有一个很容易踩的坑项目里可能配了spring.profiles.activedev而dev环境的配置跟本地不一致这时候要检查是不是多环境配置文件放错了位置。4.3 前端启动npm那点事前端项目目录下执行npm install npm run servenpm install慢是正常的可以换镜像源npm config set registry https://registry.npmmirror.com如果项目里有package-lock.json安装时可能会锁定旧版本依赖遇到node和依赖不兼容的问题可以删掉node_modules和package-lock.json重新安装。启动后访问前端页面但前端默认端口也是8080的话会跟后端冲突。一般会在vue.config.js里把devServer端口改成8081或8090并配置代理devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端请求/api/xxx就会自动转发到后端避开跨域问题。这也是前后端分离联调时最常用的方式比在每台机器上单独设置CORS头更省心。我特别建议前端启动后先打开浏览器控制台的Network面板随便点一个接口看请求有没有转发成功不要只看页面有没有出来。4.4 常见启动报错速查表我把自己跑这种项目踩过的坑整理成表格遇到报错可以对照排查。报错现象常见原因解决办法后端启动时报数据库连接失败密码错了、MySQL没启动、端口不对用客户端工具先测连接确认库名密码Access denied for user root用户权限问题检查MySQL用户授权或改配置文件账号前端npm install报ERESOLVE依赖冲突Node版本过高或依赖锁冲突换Node 16删除lock和node_modules重装前端访问接口跨域前端和后端端口不一致配置vue.config.js里的proxy代理登录后页面空白路由守卫或token处理异常打开浏览器控制台看Network定位是401还是JS报错中文乱码数据库字符集或连接字符集不对库表统一utf8mb4连接串加上characterEncoding端口被占用上一次启动没关干净netstat -ano找端口进程并结束这些坑单独看都不难但卡住的时候特别耗时间。建议按顺序来先确认数据库再启动后端最后启动前端逐步排查缩小范围。别一上来就全项目跑那样出问题都分不清是哪一层的锅。5. 源码学习与二次开发建议5.1 从这套源码里能学到什么对想练手的人来说这套美发门店管理系统其实是很好的“完整工程”示例因为它不是demo而是把很多真实业务场景串起来了。你能学到的东西包括一个完整的SpringBoot项目结构长什么样包名怎么划分、配置怎么分离。后端如何把业务规则落到Service层而不是全堆在Controller里。前端Vue Router和axios拦截器在真实项目里的配合方式。数据库表之间如何通过逻辑关联组织而不只是理论上的ER图。一套管理后台从登录到报表的完整链路。我特别建议初学者拿到源码后先跑通再试着改一个简单功能比如“给会员列表加一个按会员等级筛选的下拉框”。不要一上来就想看懂每一行代码先把数据流走通再去啃细节效率会高很多。技术面试时聊项目能讲清楚“为什么要设计充值流水表”“提成怎么算”“预约怎么防冲突”比背一百道八股文都管用。5.2 二次开发往哪个方向扩这套系统如果要做私有化交付一般有几个高频的扩展方向小程序端很多美发店希望顾客能自己在微信小程序上预约、查余额。后端接口可以复用前端单独写一个uni-app或原生小程序项目通过openid关联会员卡号。短信通知预约成功、到店提醒、余额不足提醒接入云短信服务需要加一个短信模板配置表和发送日志表。连锁门店给表结构加store_id字段把库存、员工、订单全部按门店隔离还要加跨店查询报表的权限。营销活动拼团、秒杀、到期提醒、沉睡会员召回这些运营功能本质上是基于会员数据和消费记录再加工。数据大屏把营业额、客单量、活跃会员数用大屏方式投到门店电视上前端用ECharts或DataV就能做。如果目标客户是连锁店还有一个必须考虑的点多租户权限和数据隔离怎么做。是每个门店单独一套库还是共库分店ID两种方案各有取舍共库分店ID维护容易、但跨店报表要小心写错查询条件分库则隔离彻底、但代码维护成本高。大多数中小型项目选择共库分店ID这是我在实际交付中比较推荐起步的方式。5.3 上线部署从开发机到服务器最后聊一下如何把项目从本地搬到服务器。后端打包执行mvn clean package -DskipTests拿到target目录下的jar包后放到服务器上运行nohup java -jar hair-salon.jar --spring.profiles.activeprod app.log 21 前端打包执行npm run build生成dist目录里面是纯静态文件交给Nginx托管。Nginx同时还要承担反向代理的角色把/api请求转发到SpringBoot端口server { listen 80; server_name your-domain.com; root /usr/share/nginx/html/hair-salon; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }数据库上线前不用全量导入演示数据但要保证建表脚本执行完整并修改默认密码、创建专门的业务账号。别忘了定期备份MySQL门店一天的数据损失老板都受不了。我见过有团队上线前不设备份结果一次误操作把会员表清掉的真实案例最后只能靠现金流水和回忆补录惊心动魄。上线后还有一件事很容易被忽略日志。后端日志至少要有info级别并做按天滚动。出了问题没有日志等于没有眼睛。我之前接手过一套没有日志输出的老系统线上出问题只能靠猜后来花了三天时间把所有关键业务节点补上日志再排查问题时效率完全不一样。我个人在实际操作中的体会是美发店老板不一定懂技术但他们一定懂自己的店该怎么管。系统做得再花哨如果会员余额对不上账、月底给员工算错提成老板下个月就不会再用。反过来只要数据准、操作快、报表一眼能看到今天赚多少哪怕界面朴素一点他们也愿意天天用。如果你手上刚好也有一份类似的源码别急着追求高大上的技术先把会员、订单、流水这三条数据链路理清楚这比任何炫技都管用。最后再分享一个小习惯拿到任何一套可运行的源码我建议先往数据库里塞一套模拟数据把一个月的营业流水造出来再在界面里一个模块一个模块地过。这样既能快速暴露设计缺陷也是给客户演示时最有力的说服方式。等到系统逻辑没什么问题了再考虑加新功能、换新界面那才是水到渠成的事。