Java美容管理系统源码三端架构解析与二次开发实战

发布时间:2026/10/8 10:20:12
Java美容管理系统源码三端架构解析与二次开发实战 简介这是一套面向Java开发者与美容行业信息化从业者的美容管理系统源码采用多端架构包含后台管理端、商家端与微信端适合用于二次开发、毕业设计或商业项目参考。系统以消费者关注服务号为入口支持在线预约项目、到店消费与服务并集成威富通第三方支付覆盖微信、支付宝、扫码枪扫码及微信H5支付等场景。技术层面基于IMS框架后台与商家端采用EasyUI及美化主题包微信端使用AUI框架并集成微信JSSDK实现分享、地图定位、模板推送同时接入阿里大于短信功能。压缩包共约2000个文件以png、js、css、java、jsp、gif、html、jar等为主涵盖前端页面、后端逻辑、样式资源与依赖库整体约85.05MB。目前已有303人学习下载可帮助读者快速理解多端美容预约系统的目录结构与支付、消息推送等模块的实现思路。1. 三端美容管理系统的源码拆解后台、商家端、微信端到底怎么分工拿到「Java美容管理系统源码主要分为三个端后台商家端微信端.zip」这类资源第一反应不该是急着解压跑起来而是先想清楚这三个端各自解决什么问题。美容行业的业务链条其实很特殊门店有多个每个门店有店长、美容师、前台客户通过微信预约到店商家端负责排班和订单核销后台负责连锁级别的权限、财务和商品配置。三端不是简单的「PC 版 移动版」而是三种完全不同的角色视角。后台是运营总控商家端是门店执行微信端是客户自助。搞混了这三者的边界后面改代码会非常痛苦。这篇笔记就按「先理清架构再动手跑通最后讲踩坑」的顺序把这份 Java 美容管理系统源码的落地路径讲透适合想拿它做二次开发、课程设计或者接私活的 Java 工程师。2. 三端架构怎么拆从 Maven 模块到权限模型2.1 先看目录结构判断是单体还是多模块拿到源码压缩包解压后第一件事是看根目录有没有pom.xml以及它下面有几个module。美容管理系统常见的组织方式有两种一种是单 Spring Boot 工程用不同包名区分admin、merchant、wx另一种是 Maven 多模块xxx-admin、xxx-merchant、xxx-wx各自独立。两种都能用但多模块在权限隔离和部署上更清晰。# 解压后先看顶层结构不要急着导入 IDE unzip Java美容管理系统源码.zip -d beauty-system cd beauty-system find . -maxdepth 2 -name pom.xml -o -maxdepth 2 -name build.gradle # 看模块划分 grep -A 20 modules pom.xml这段命令的作用是先确认工程形态。如果只有一个pom.xml说明是单体三端靠包名或不同 Controller 前缀区分如果有多个模块通常common放工具类和实体admin、merchant、wx各自依赖common。参数上重点看modules里列了几个以及每个模块的artifactId命名这直接决定你后面改一个功能要动几个地方。2.2 权限模型三端共用一个用户表还是分开美容系统的权限是核心难点。后台管理员、商家端店长、微信端客户这三类主体的认证方式完全不同。常见做法是后台和商家端共用一套sys_usersys_rolesys_menu的 RBAC 模型微信端单独用member表走 openid 登录。判断源码质量就看它有没有把这两套体系混在一起。-- 典型的三端权限表结构重点看 role 和 user 的关联 -- 后台/商家端基于角色的菜单权限 SELECT u.username, r.role_name, m.menu_name FROM sys_user u JOIN sys_user_role ur ON u.id ur.user_id JOIN sys_role r ON ur.role_id r.id JOIN sys_role_menu rm ON r.id rm.role_id JOIN sys_menu m ON rm.menu_id m.id WHERE u.user_type MERCHANT; -- 区分后台和商家端 -- 微信端基于 openid 的会员体系通常独立 SELECT m.nickname, m.openid, m.phone FROM member m WHERE m.store_id 1001;这里的关键参数是user_type字段。如果源码里后台和商家端用同一个sys_user表但靠user_type区分那商家端登录后必须做数据过滤否则 A 店长能看到 B 店的数据这是美容连锁系统最常见的越权漏洞。我一般会检查所有商家端的查询有没有带store_id条件没有的话就是坑。2.3 微信端登录网页端同步小程序微信登陆的常见实现热词里提到「网页端同步小程序微信登陆」这在美容系统里很实际客户可能在公众号网页预约也可能在小程序预约两边要能识别是同一个人。常见做法是用 UnionID 打通。微信端登录流程一般是前端拿 code → 后端调微信接口换 openid 和 unionid → 查 member 表有就登录没有就注册。// 微信端登录核心逻辑注意 unionid 的存储 public Member wxLogin(String code) { // 1. 用 code 换 access_token 和 openid String url https://api.weixin.qq.com/sns/jscode2session ?appid appId secret appSecret js_code code grant_typeauthorization_code; JSONObject result HttpUtil.get(url); String openid result.getString(openid); String unionid result.getString(unionid); // 关键跨端识别靠它 // 2. 优先用 unionid 查没有再用 openid Member member memberMapper.selectByUnionId(unionid); if (member null) { member new Member(); member.setOpenid(openid); member.setUnionid(unionid); memberMapper.insert(member); } return member; }参数说明appId和appSecret必须配在配置文件里不要硬编码。unionid只有在微信开放平台绑定过公众号和小程序才会返回如果源码里只存了 openid那网页端和小程序端就是两个账号这是很多免费源码的通病。排查时看member表有没有unionid字段没有的话就得自己加。3. 本地跑通的最小路径数据库、依赖、启动顺序3.1 数据库导入与配置修改的三个必改项美容管理系统源码通常带一个.sql文件导入 MySQL 后要改配置文件。必改的三项是数据库连接、微信 appid、文件上传路径。少改一个都启动不了或者功能残缺。# application.yml 里必须改的配置 spring: datasource: url: jdbc:mysql://localhost:3306/beauty_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 redis: host: localhost port: 6379 # 美容系统常用 Redis 存 token 和预约锁没有 Redis 会报错 wx: appid: 你的小程序appid secret: 你的小程序secret file: upload-path: /data/beauty/upload/ # Linux 下注意目录权限逻辑说明serverTimezone不设会导致预约时间差 8 小时这是血泪经验。Redis 如果源码里用来做登录 token 缓存那必须启动 Redis否则登录接口直接 500。upload-path在 Windows 下写D:/upload/Linux 下要确保运行用户有写权限否则上传头像和门店图片会失败。3.2 启动顺序与端口冲突排查三端如果是多模块启动顺序一般是先common装到本地仓库再启动admin最后merchant和wx。端口默认都是 8080 的话会冲突要在各自application.yml里改成 8081、8082、8083。# 先安装 common 模块到本地 Maven 仓库 mvn clean install -pl common -am -DskipTests # 分别启动三个端指定端口 java -jar admin/target/admin.jar --server.port8081 java -jar merchant/target/merchant.jar --server.port8082 java -jar wx/target/wx.jar --server.port8083 # 检查端口占用 netstat -tlnp | grep -E 8081|8082|8083参数说明-pl common -am表示只构建 common 模块及其依赖-DskipTests跳过测试加快速度。如果启动报Table beauty_system.xxx doesnt exist说明 SQL 没导全检查.sql文件里有没有CREATE DATABASE和USE语句。后台默认账号密码一般在 SQL 文件末尾的INSERT INTO sys_user里常见是admin/123456。3.3 微信端本地调试reqable 抓包与域名配置微信端本地调试最麻烦的是小程序要求 HTTPS 域名。开发阶段可以在微信开发者工具里勾选「不校验合法域名」但真机调试就不行。热词里提到「reqable 能 pc 端微信小程序抓包吗」答案是能但要注意小程序走的是微信自己的网络通道需要配置代理并安装证书。# 本地起一个内网穿透把 8083 暴露成 HTTPS方便真机调试 # 常见做法是用 frp 或 natapp这里只讲配置思路 # 1. 微信开发者工具 - 详情 - 本地设置 - 不校验合法域名 # 2. 真机调试时把 request 合法域名配成你的穿透域名 # 3. reqable 抓包设置系统代理 - 安装根证书 - 信任证书注意抓包只用于调试自己的系统不要用于任何非法用途。小程序端如果登录一直失败先看wx.login拿到的 code 有没有传给后端再看后端换 openid 的接口有没有报errcode: 40029这个错误码表示 code 无效通常是 appid 和 secret 不匹配。4. 商家端与后台的功能边界哪些功能该放哪端4.1 商家端只做门店级操作后台做连锁级配置这是最容易做错的地方。商家端登录后只能看到自己门店的预约、订单、美容师、排班。后台则能看到所有门店并且能配置商品、优惠券、会员等级这些全局数据。如果源码里商家端能改商品价格那就是设计缺陷。功能后台商家端微信端门店管理增删改查所有门店只看本店无商品/服务项目全局配置只看本店可售浏览下单预约管理查看所有本店预约核销发起预约美容师排班无本店排班查看可约会员管理全局会员本店会员个人中心财务统计连锁报表本店流水无这张表的作用是帮你判断源码的功能划分是否合理。如果商家端有「商品管理」的增删改那要么是源码把后台功能错放了要么是权限没做细。我一般会先跑一遍商家端看菜单里有没有不该出现的项。4.2 预约核销的并发问题Redis 锁怎么用美容系统的预约是核心场景多个客户同时约同一个美容师的同一时段必须防超卖。常见做法是用 Redis 分布式锁或者数据库唯一索引。// 预约防重复Redis 锁 数据库唯一索引双保险 public Result bookAppointment(Long storeId, Long beauticianId, String timeSlot) { String lockKey book: storeId : beauticianId : timeSlot; // 1. Redis 锁过期时间 10 秒防止死锁 Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, 1, 10, TimeUnit.SECONDS); if (Boolean.FALSE.equals(locked)) { return Result.fail(该时段已被预约请换一个时间); } try { // 2. 再查一次数据库防止锁失效后的并发 int count appointmentMapper.countBySlot(beauticianId, timeSlot); if (count 0) { return Result.fail(该时段已被预约); } // 3. 插入预约记录数据库唯一索引兜底 appointmentMapper.insert(new Appointment(storeId, beauticianId, timeSlot)); return Result.success(); } finally { redisTemplate.delete(lockKey); // 释放锁 } }参数说明setIfAbsent的第三个参数是过期时间必须设否则一个请求挂了锁永远不释放。数据库唯一索引建在(beautician_id, time_slot)上这样即使 Redis 锁失效数据库也会拒绝重复插入。如果源码里没有这个唯一索引高并发下一定会出现同一时段两个预约这是必须自己补的。5. 避坑与排查这份源码最容易翻车的五个地方5.1 启动报错 Unknown databaseSQL 文件没建库现象启动时抛Unknown database beauty_system。原因.sql文件里只有建表语句没有CREATE DATABASE。解决手动建库CREATE DATABASE beauty_system DEFAULT CHARSET utf8mb4;再导入表结构。注意字符集要用utf8mb4否则微信昵称里的 emoji 存不进去。5.2 商家端登录后看到其他门店数据store_id 过滤缺失现象用 A 店账号登录预约列表里出现 B 店的记录。原因Mapper 查询没带store_id条件或者带了但前端传的store_id可以被篡改。解决在 Service 层从 token 里取store_id不要信前端传的。所有商家端查询强制加AND store_id #{currentStoreId}。5.3 微信端登录一直转圈code 被重复使用现象小程序点登录没反应后端日志显示invalid code。原因wx.login拿到的 code 只能用一次如果前端在onLoad和按钮点击里各调了一次第二次就失效。解决确保wx.login只在需要时调一次拿到 code 后立即传给后端不要缓存 code。5.4 上传图片失败目录权限和大小限制现象后台上传门店图片报 500日志显示FileNotFoundException。原因upload-path目录不存在或 Spring Boot 运行用户没写权限。解决mkdir -p /data/beauty/upload chmod 755 /data/beauty/upload同时检查spring.servlet.multipart.max-file-size是否够大默认 1MB 传不了高清图。5.5 定时任务不执行多实例重复跑现象预约提醒短信发了两次。原因商家端部署了两个实例Scheduled任务在每个实例都跑。解决用 Redis 锁或者 Quartz 集群模式确保同一时刻只有一个实例执行。简单做法是在任务开头抢一个 Redis 锁抢不到就跳过。6. 二次开发进阶把这份源码改成能接私活的版本6.1 先做数据隔离再做功能扩展拿到源码想接私活第一件事不是加功能而是把多门店数据隔离做扎实。我一般会写一个 MyBatis 拦截器自动给所有商家端 SQL 加上store_id条件这样就不用每个 Mapper 手写。// MyBatis 拦截器自动给商家端查询加 store_id Intercepts({Signature(type Executor.class, method query, args {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class StoreInterceptor implements Interceptor { Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement ms (MappedStatement) invocation.getArgs()[0]; // 只拦截商家端的 Mapper后台和微信端不处理 if (ms.getId().contains(merchant)) { Object param invocation.getArgs()[1]; if (param instanceof Map) { MapString, Object map (MapString, Object) param; // 从 ThreadLocal 取当前登录用户的 store_id map.put(storeId, UserContext.getStoreId()); } } return invocation.proceed(); } }逻辑说明UserContext用 ThreadLocal 存当前请求的store_id在登录拦截器里塞进去。这样所有商家端查询自动带上storeId参数Mapper XML 里写AND store_id #{storeId}即可。参数上注意ms.getId().contains(merchant)这个判断要和你项目的包名对应写错了会拦截到后台查询导致后台看不到数据。6.2 验证方法用两个门店账号交叉测试改完隔离逻辑必须验证。开两个浏览器无痕窗口分别登录 A 店和 B 店账号在 A 店创建预约然后去 B 店看能不能查到。查不到才算通过。再直接调接口把请求里的storeId改成对方门店的看后端是否忽略前端传值、只用 token 里的。这一步能挡住 90% 的越权问题。测试项预期结果失败原因A 店查预约只返回 A 店数据拦截器没生效篡改 storeId 参数仍返回本店数据用了前端传值后台查预约返回所有门店拦截器误拦后台微信端查预约只返回自己的会员体系没隔离6.3 一个具体技巧用枚举管理预约状态美容系统的预约状态很多待确认、已确认、已到店、已完成、已取消、爽约。用魔法数字 0/1/2 后期维护会疯。我一般会定义一个枚举并在数据库存字符串。public enum AppointmentStatus { PENDING(待确认), CONFIRMED(已确认), ARRIVED(已到店), COMPLETED(已完成), CANCELLED(已取消), NO_SHOW(爽约); private final String desc; AppointmentStatus(String desc) { this.desc desc; } public String getDesc() { return desc; } }这样前端传CONFIRMED后端存CONFIRMED日志里一眼能看懂。改状态时用AppointmentStatus.valueOf(status)校验传了非法值直接抛异常比if (status 1)可靠得多。这份源码我前后改过三版最大的教训是不要一上来就加功能先把权限和数据隔离跑通否则后面每加一个功能都要回头补隔离越补越乱。希望帮到你。本文还有配套的精品资源点击获取