ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue美发门店管理系统源码实操:从环境搭建到二次开发

SpringBoot+Vue美发门店管理系统源码实操:从环境搭建到二次开发 最近帮朋友的美发连锁店梳理门店管理流程顺手拿了一套基于SpringBootVue的美发门店管理系统源码后端是SpringBoot MyBatis MySQL前端用Vue整体是一个标准的前后端分离项目。这套源码在毕设、课设和小型商业项目里都非常常见但很多人下载下来后卡在跑不起来、看不懂业务逻辑、不知道怎么改成自己能用的版本。这篇文章我把整个实操过程完整记录下来从环境搭建、数据库初始化、前后端联调到核心业务模块拆解和二次开发方向都给梳理一遍如果你手里正好也有类似的源码或者正准备自己写一套门店管理系统可以直接照着走。先说结论这类系统并不复杂本质上就是围绕“会员、预约、消费、统计”四条主线做增删改查再加上一点状态流转和报表聚合逻辑。技术栈选得也很务实SpringBoot负责接口和业务MyBatis控制SQLMySQL存数据Vue管页面交互没有炫技的成分每一步都落到业务上。下面我按实操流程来拆解。1. 这个管理系统到底解决了什么问题1.1 美发门店的日常管理痛点我去朋友店里坐了半小时就看明白为什么他们需要一套系统了。前台小姐姐手里一本厚厚的纸质登记本会员充值时靠手写记录余额客人打电话预约只能翻本子看技师排班月底算提成的时候要把所有小票翻出来手动汇总。这还只是一家单店如果开第二家店数据对不上员工绩效算不清会员卡在两家店通用更是一笔糊涂账。这些问题落到系统层面其实可以拆成几个很具体的需求点会员信息要能查、能改、能分级余额和积分必须实时准确充值、办卡、消费要留痕每次扣费都要能追溯到具体订单预约要有时间维度技师当天能接多少个客人系统里一眼就能看到月底要能自动汇总营收、消耗、提成不用人工翻本子不同门店之间会员能通用或者至少数据能汇总看。这套SpringBootVue的管理系统核心就是围绕这几个需求去设计数据模型和业务流程。理解了业务痛点再去看代码你才会明白为什么会有那么多张表以及为什么很多字段要那么冗余设计。1.2 系统核心价值与适合人群从源码结构来看这套系统覆盖了美发门店最常用的几个场景会员开卡、预约登记、套餐购买、消费扣次、业绩统计。它不追求大而全但把核心链路做完整了对于学习和二次开发来说反而是个很好的起点。适合谁去折腾这套东西首先是Java后端初学者尤其是准备毕设或课设的同学这种项目结构清晰、业务不复杂、技术栈通用拿来学习SpringBoot的接口编写、MyBatis的SQL映射、Vue的页面交互非常合适。其次是真正有门店管理需求的经营者或开发者哪怕业务不完全一致在这个基础上改一改会员规则、加一个次卡类型成本比从零写要低得多。我拿到这套源码后第一件事不是急着跑起来而是先把数据库脚本和项目目录结构过了一遍搞清楚每个模块大概对应哪些表和接口。这一步非常重要直接决定了后面改代码的时候是“无头苍蝇”还是“按图索骥”。2. 技术选型为什么是SpringBootVueMyBatisMySQL2.1 后端组合的成熟度分析SpringBoot在Java后端领域已经算是事实标准了它的自动配置把以前SpringMVC那一大堆XML配置压缩到极简一个application.yml文件搞定数据源、端口、日志这些基础项。MyBatis作为持久层框架和SpringBoot的整合非常成熟核心价值在于SQL由你自己写多表联查、条件统计、复杂报表都能精确控制不会像JPA那样在复杂查询时让SQL变得不可控。这个选择放在门店管理系统里特别合理。因为这种系统的数据查询大多是多表关联加条件过滤的比如查一个会员的消费记录要同时关联会员表、订单表、服务项目表还可能要根据时间范围、支付方式做筛选。用MyBatis写XML映射一条SQL就能搞定而且结果可以直接映射成Map或VO对象返回给前端。SpringBoot MyBatis还有一个好处就是社区资料极其丰富。你随便遇到一个报错搜索引擎一翻基本都能找到解决方案。对于初学者来说这真的很重要不然卡在某个配置问题上一整天心态直接崩掉。2.2 前后端分离的工程优势前端用Vue这是当下最主流的选择之一。Vue的组件化开发思路很适合门店管理系统这种后台界面一个会员列表组件、一个预约日历组件、一个统计图表组件可以拆开各自维护互不干扰。这套源码用的前后端分离架构前端开发时通过代理把请求转发到后端接口部署时前端打包成静态文件扔到Nginx或直接扔到SpringBoot的静态资源目录里非常灵活。我在实际跑通后发现前端npm run dev启动的开发服务器默认端口和后端SpringBoot的8080端口做了代理转发也就是说前端页面上发的请求统一走/api开头然后代理到后端的localhost:8080这个设计在联调阶段非常方便。有人可能会问为什么不直接用JSP或者Thymeleaf把前后端揉在一起答案很简单门店系统未来大概率要扩展多个端比如小程序端、移动H5端前后端分离后后端接口可以同时被不同端复用前端各写各的互不影响。这套源码选择Vue跑道算是给后续扩展留了空间。2.3 核心表结构设计思路看一套系统的源码先看数据库脚本是最快的路径。这套系统的主要表大概有这些表名核心字段作用说明memberid, name, phone, balance, points, level会员基础信息余额和积分冗余存放card_typeid, name, type, price, total_times, discount卡项类型定义区分次卡、期限卡、折扣卡member_cardid, member_id, card_type_id, remain_times, expire_time会员实际持有的卡记录剩余次数和有效期service_itemid, name, price, duration, category服务项目比如剪发、染发、烫发employeeid, name, phone, post, status技师信息排班和提成的依据appointmentid, member_id, employee_id, service_item_id, appointment_time, status预约记录状态区分已预约、已消费、已取消consume_recordid, member_id, appointment_id, amount, pay_type消费流水存储每笔实际扣款这个表结构有几个值得学习的设计点。第一member表里直接冗余了balance和points为什么因为门店高频操作就是查余额、扣余额如果每次都要去几十万条流水里SUM一下性能和复杂度都不划算用冗余字段加事务控制来保证一致性是实际项目里非常常见的取舍。第二member_card作为会员和卡项之间的关联表单独存remain_times和expire_time这样可以支持一个会员同时持有多种卡场景上完全说得通。第三consume_record不直接存服务名称而是存service_item_id通过关联查询拿到名称避免冗余更新困难。我在导数据的时候特别注意了表之间的外键关系虽然是逻辑外键代码层保证而不是物理外键但业务上的关联非常清晰。这种设计在中小型项目中很常见性能好、解耦灵活缺点是必须在业务代码里注意事务和一致性。3. 核心功能模块拆解从会员到报表3.1 会员与储值卡管理会员模块是整套系统的地基因为美发门店最大的价值就是会员储值带来的现金流。这个模块的代码量不算大但逻辑链路长开卡要生成会员档案、创建储值卡、写入开卡流水充值要更新余额、写充值记录消费时要校验余额、扣款、积分、更新会员等级。我在读代码时最关注的是余额扣减是怎么保证不超扣的。源码里用了事务加条件更新的方式核心SQL类似这样UPDATE member SET balance balance - #{amount}, points points #{points} WHERE id #{memberId} AND balance #{amount}这个写法的妙处在于数据库层面就判定余额是否充足如果不满足条件受影响行数为0业务层再抛出异常回滚事务从根上避免了并发扣款把余额扣成负数的问题。比起“先查余额再判断再更新”的做法这种条件更新写法在高并发场景下安全得多。我建议你拿到源码后重点看看这部分的Service层和Mapper层事务隔离级别和回滚策略都值得学。开卡业务稍微复杂一点因为要同时操作member表、member_card表、consume_record表如果其中一步失败前面已执行的插入就必须回滚。源码里用了Transactional注解来完成这个多表一致性保证这也是SpringBoot声明式事务最常见的用法。我在测试时特意手动造了一个卡项ID不存在的场景观察事务是否能正确回滚实测下来没有问题。3.2 预约与技师排班预约模块的核心难点是时间冲突判断。一个技师在某个时间段只能接待一个客人如果两个预约在系统里撞车前台在录入时就应该给提示而不是等当天才发现。源码里的预约表设计得比较直接存了appointment_time和employee_id判断冲突时用一条条件查询查同一个技师、同一个时间段、且状态不是“已取消”的记录是否存在存在就提示换人或者换时间。我粗略估算了一下这个查询在单店几万条预约量级下性能完全没问题配合employee_id和appointment_time的联合索引毫秒级返回。排班这块其实在系统里比较轻量没有做复杂的周排班表而是通过列表展示技师在某天的预约时段来体现。如果你要扩展得更细致可以考虑增加一张schedule表提前定义技师的每日可预约时段预约下单时只能在可预约时段里选。这是一个很典型的二次开发方向我后面在第五节会展开说。3.3 套餐与消费记录卡项类型设计上分成了次卡、期限卡、折扣卡这基本覆盖了美发店的主流玩法。次卡按次数扣减适合剪发卡、烫染卡这类高频低价项目期限卡按有效期限制比如三百元三个月内无限次洗吹折扣卡则是充一笔钱后续消费按折扣实时结算。消费记录的设计我比较喜欢因为它没有把所有扣费逻辑固化在代码里而是把“卡类型不同导致扣费方式不同”作为策略去处理。次卡走次数扣减余额卡走金额扣减要是未来再新增一种“次数金额混合卡”只需要在卡类型判断处加一个分支扩展成本很低。扣费记录统一写到consume_record里每条记录都带pay_type字段标明本次消费用的是余额、次卡次数还是现金。这样一来财务对账的时候就不会乱月底统计营收时按支付类型分组求和就行。我在测试时特意验证了“次卡次数为0且余额也不足”的场景系统能正确提示“请先充值”没有出现扣费成功但实际没扣到的脏数据。3.4 门店经营统计报表报表模块虽然代码量不多但却是店长最关注的模块。统计维度主要是三个每日营收、消费项目排名、技师业绩。每日营收的SQL思路其实很简单对consume_record表按日期分组求和再关联到支付方式维度SELECT DATE(create_time) AS date, pay_type, SUM(amount) AS total FROM consume_record WHERE create_time BETWEEN #{startTime} AND #{endTime} GROUP BY DATE(create_time), pay_type ORDER BY date DESC这类SQL在MyBatis的XML里写起来很清楚读源码时建议顺着这条线去看StatisticMapper.xml里各个统计SQL的写法。技师业绩排名则是按consume_record关联appointment表再用employee_id分组汇总逻辑上跟营收统计类似。报表模块在Vue前端通常配合图表库来做可视化展示。这套源码里用的是ECharts折线图展示每日营收趋势柱状图展示项目销量排名饼图展示支付方式占比。如果你在二次开发时要加新的报表比如“会员复购率”、“客单价趋势”后端加SQL、前端加图表组件的套路是完全一样的复制现有模式就能扩展。4. 源码快速跑通环境搭建与启动实录4.1 环境准备清单在开始之前先把环境准备好这一步别省。我之前给朋友调试时发现很多人项目跑不起来根本不是代码问题而是环境版本不对造成的。我这次用的是这套组合依赖项推荐版本备注JDK1.8SpringBoot 2.x 项目标配太高的Java版本反而容易出兼容问题Maven3.6用IDEA自带或单独安装都行MySQL5.7 或 8.08.0时需要改驱动和时区配置Node.js14~16Vue2项目建议装16申请npm包时兼容性最好IDEIntelliJ IDEA后端用IDEA前端可以继续用IDEA或VS Code这里有个容易踩坑的地方如果你的机器装的是Java 17甚至更高版本SpringBoot 2.x的老项目跑起来可能报一些奇怪的反射或字节码错误。解决方法有两个要么在本机多装一个JDK 1.8并切过去要么在工程pom.xml里把java.version改成1.8并结合IDE指定SDK。我实测用JDK 1.8 SpringBoot 2.x MySQL 5.7这套组合是最稳的。4.2 数据库初始化步骤拿到源码后先找SQL脚本。通常放在项目的sql目录或docs目录下文件名类似hairstyle.sql或init.sql。执行导入前我建议先创建一个独立的数据库避免跟现有库混淆。mysql -uroot -p CREATE DATABASE hairstyle DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hairstyle; SOURCE /path/to/hairstyle.sql;导入完成后用SHOW TABLES;确认表是否齐全再随机抽查一行数据比如SELECT * FROM member LIMIT 5;确保数据真的导进去了。很多人在导入时报错大部分原因是SQL脚本里有特殊字符或版本不兼容但也有小概率是脚本本身编码不对这时用文本编辑器另存为UTF-8编码再导入基本能解决。4.3 后端启动细节与常见报错后端工程用IDEA打开后Maven会自动开始下载依赖等右下角进度条走完再去看源码结构。默认包名通常和项目名一致重点关注主启动类上的SpringBootApplication注解以及application.yml文件里的配置。数据源配置是最关键的一步需要根据自己的MySQL账号密码做调整server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/hairstyle?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.hairstyle.entity configuration: map-underscore-to-camel-case: true如果你用的是MySQL 5.7驱动类用com.mysql.jdbc.Driver也还是可以的但如果你用MySQL 8.0必须改成com.mysql.cj.jdbc.Driver而且URL里一定要加serverTimezoneAsia/Shanghai和useSSLfalse否则启动或首次查询时会抛时区错误和SSL连接错误。这个配置我反复确认过是新手最容易出错的地方。配置改好后直接启动主类看到Started Application in x.xxx seconds就说明后端起来了。访问http://localhost:8080能看到一个“未找到页面提示”也别慌这只是因为后端没有根路径的Controller接口都放在/api/...下面接口正常返回JSON才是关键。4.4 前端安装依赖与联调前端工程通常是一个单独的子目录比如frontend或web打开后先安装依赖这一步耗时可能比较长cd frontend npm install如果你遇到node-sass安装失败或编译报错大概率是Node版本和sass-loader不匹配。项目如果是Vue2 webpack的老组合推荐直接用Node 16然后把node_modules整个删掉重新npm install。我实测用Node 16一次就过了用Node 18反而在编译时卡住。依赖装完后启动开发服务器npm run dev看到App running at Local: http://localhost:8081/就说明前端起来了。打开页面后如果接口请求报404或跨域错误先检查前端代理配置。Vue2项目的代理在vue.config.js里Vite项目则在vite.config.js里核心配置类似这样module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }确保/api开头的请求都被转发到了后端8080端口前端的登录请求、列表请求就能正常拿到了。如果代理配置没问题但依然加载不出数据就用浏览器的开发者工具看Network面板里具体哪个请求失败了一般问题出在后端没启动、CORS配置、请求路径不对这三个方向。完整跑通后你应该能用测试账号登录系统看到左侧菜单的会员管理、预约管理、卡项管理、统计报表这些页面并且能在页面上做新增、修改、查询这些基本操作。5. 深入源码关键代码片段与二次开发方向5.1 代码结构与请求链路梳理先把整体结构看明白。后端典型的分层结构是Controller接收请求、Service处理业务逻辑、Mapper操作数据库、Entity对应数据表。前端则是页面组件加API请求模块。一次完整请求的链路是这样的Vue页面里调用api模块封装的axios方法发出HTTP请求到/api/member/list后端MemberController收到请求调用MemberService的list方法MemberService里组织业务参数调用MemberMapper接口MemberMapper的SQL映射在MemberMapper.xml里找到对应SQL执行结果反射成实体对象一路返回给前端渲染。我在阅读时最推荐从MemberMapper.xml入手因为里面集中了最常用的SQL写法。比如分页查询会员列表的SQL通常用LIMIT配合PageHelper插件或手动计算offset模糊搜索一般用LIKE CONCAT(%, #{keyword}, %)这里用CONCAT而不是直接%${keyword}%是为了避免SQL注入这个点面试里也经常被问到。后端Controller层还有一个值得注意的设计就是统一返回体的封装。这类管理系统一般会定义一个Result类里面包含code、message、data三个字段所有接口都返回这个结构前端拿到后先判断code是否为200再取数据。这样一旦后端出现业务异常前端就能统一弹错误提示而不是每个接口各写一套判断逻辑。二次开发时如果新增接口记得也按这个格式返回能少踩很多坑。5.2 MyBatis映射与SQL实战示例我摘一段典型的Mapper代码来说明。拿会员储值卡变更来说除了要更新余额还要插入一条流水记录。MyBatis的useGeneratedKeys和事务配合在这种场景下很重要:public interface MemberMapper { Transactional int payByBalance(Param(memberId) Integer memberId, Param(amount) BigDecimal amount, Param(points) Integer points); }对应的XML映射update idpayByBalance UPDATE member SET balance balance - #{amount}, points points #{points} WHERE id #{memberId} AND balance #{amount} /update不得不说MyBatis这种把SQL和Java代码分离的做法在业务逻辑变动频繁的管理系统里确实灵活。不用像JPA那样为了一个多表查询去维护实体关系直接在XML里写好SQL就行。改查询条件时只需要在XML里加一个if标签做动态SQL调接口时的各种组合搜索就都覆盖了。比如会员列表的筛选条件就是通过if testphone ! null这种写法来拼SQL的select idselectMemberList resultTypecom.hairstyle.entity.Member SELECT * FROM member where if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testphone ! null and phone ! AND phone LIKE CONCAT(%, #{phone}, %) /if /where ORDER BY id DESC /select静态SQL拼起来容易出错但MyBatis的动态SQL标签把这块做了很好的封装这也是为什么实际项目里MyBatis用得更顺手的原因。你在读这套源码时把每个Mapper.xml里的where、if、foreach标签过一遍基本就掌握了动态SQL的八成用法。5.3 二次开发可以怎么玩源码最大的价值是可扩展。我根据门店实际运营需求整理了三个最实用的二次开发方向第一个方向是做技师提成规则扩展。现在源码里提成大概率是走固定比例或人工计算如果店里不同项目提成比例不同比如染发提成8%、剪发提成5%可以在service_item表加一个commission_rate字段然后在统计报表SQL里按比例实时计算或者在做消费记录时同步计算出提成金额存到订单字段里。加字段后所有关联处的查询都要带上这个字段改动量不大但很实用。第二个方向是做预约提醒。源码里目前应该只是简单的预约记录没有到期提醒能力。如果门店想降低爽约率可以加一个定时任务框架比如Spring自带的Scheduled每天扫描次日的预约记录把未取消的预约通过短信或微信模板消息发出去。短信服务可以接阿里云短信或腾讯云短信微信模板消息则要研究一下服务号接口扩展的思路很清晰。第三个方向是做一个移动端查看统计报表的页面。前端Vue项目加一个H5页面复用后端的统计接口只展示报表数据不需要登录写操作。这个功能对老板特别友好人在外面也能实时看到当日营收。因为你用的是前后端分离架构后端接口已经具备前端新建几个页面组件就够了我算了下工作量纯新手一到两周内也能做完。6. 实测踩坑与问题排查实录6.1 高频问题速查表我把实操过程中遇到的典型问题整理了一张速查表按启动顺序排列方便你对症状找药方。问题现象可能原因解决办法后端启动报java.sql.SQLException: The server time zone valueMySQL版本和JDBC驱动时区不一致URL加serverTimezoneAsia/Shanghai启动报Failed to bind properties under spring.datasource配置缩进或数据库账号密码错误检查YAML缩进确认密码正确且MySQL服务已启动请求接口返回404Controller路径与Mapper路径不一致或Mapper.xml没扫到确认RequestMapping路径、mapper-locations配置路径请求接口报Failed to parse HTTP message前端JSON格式错误或实体类缺少无参构造器用Postman直接调后端接口定位问题在端到端哪一层启动报BindingException: Invalid bound statementMapper接口和XML文件不匹配检查Mapper方法ID与XML的id一致、XML路径在配置范围内前端npm install报node-sass编译失败Node版本与sass-loader兼容性问题降Node到16删掉node_modules重新安装页面请求跨域报错后端CORS没配置或前端代理配置错误前后端联调优先使用devServer代理绕过跨域数据中文乱码数据库字符集不是utf8mb4建库时指定utf8mb4连接URL配置characterEncodingutf8排查的顺序有个笨但有效的方法先确认后端接口用Postman能通再排查前端代理和渲染逻辑。因为前端报错往往只是现象根因多在后端接口没返回预期数据。我在调预约列表时碰到过一次页面一直报接口500结果自己一看是MyBatis的XML里用了DATEDIFF函数数据库版本不支持换了个函数就好了。6.2 几个值得注意的细节和小建议再说几个我在实际使用中发现的细节这些如果没人提醒你可能要折腾很久才能意识到。第一application.yml里map-underscore-to-camel-case: true这个配置非常重要。因为MySQL字段习惯用下划线命名比如create_timeJava实体类习惯用驼峰命名比如createTime打开这个配置后MyBatis会自动完成映射不需要手写大量resultMap。我遇到过有人没开这个配置实体类属性全是null排查了大半天。第二事务不要扩大也不要遗漏。比如发卡这个操作如果只给member_card插入卡记录但忘了写consume_record流水那后续对账就对不上反过来如果连发送短信的远程调用也放在事务里短信服务响应慢时会长时间占用数据库连接。正确做法是把数据库更新放在事务里远程调用放事务外并且做好失败补偿。第三前端组件里注意表单校验的时机。管理系统的表单不少比如会员手机号校验应该在失焦时提醒还是提交时统一校验我建议前端的失焦校验用async-validator这类库做规则校验后端Controller参数上再用Validated做兜底校验。两边都做体验和安全性才有保障。第四关于密码安全。这类管理系统的登录模块往往是最薄弱的很多源码直接用MD5存密码甚至明文存放。建议二次开发时至少升级为BCrypt加盐哈希Spring Security自带BCryptPasswordEncoder集成成本很低。这虽然不是业务功能但上线前不做这一项风险很大。第五报表数据要用索引支撑。统计模块的SQL一般会GROUP BY日期或员工ID如果consume_record表数据量大一定要在create_time、employee_id这些字段上建索引。我朋友这家店一个月大概产生五六千条流水目前不加索引也能跑但数据积累到十万级以上报表查询就会明显变慢。提前用EXPLAIN看下执行计划加上索引是成本最低的优化手段。从我的实操体验来看这套SpringBootVue的美发门店管理系统源码技术层面并不高深都是Java Web开发里最常见的那套东西但它的业务链路很完整从会员开卡到消费统计能串成一个闭环。拿它来做毕设、课设或者作为门店系统的开发底座都是一个性价比很高的选择。如果你正在研究这套源码建议每看完一个模块就自己动手加一个小功能比如给会员列表加一个“本月消费”列或者给预约模块加一个取消原因备注通过改代码来验证你的理解比单纯看代码要扎实得多。
返回列表