尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Spring Boot + 微信小程序实战:宠物美容预约系统开发详解
很多人问过我毕设或者实际项目里做一个“预约类”小程序到底怎么下手今天拿一个真实的例子说说基于 Spring Boot 微信小程序 的宠物美容预约系统。这套系统做出来后能覆盖常见的单店或多店预约场景包括宠物档案、美容项目、美容师排班、用户提交预约、后台管理订单等核心功能。无论你是准备做毕业设计还是想给线下宠物店做一套简化版预约工具这个框架都够用。我先说结论这类项目最难的不是代码而是把“预约”这个动作背后的状态、时间、资源冲突想清楚。后端用 Spring Boot 负责业务和接口小程序端负责展示和交互两边通过 JSON 通信。整套东西拆开看并不复杂但把细节抠到位体验和稳定性就会完全不一样。1. 内容整体设计与思路拆解1.1 这个项目到底在解决什么问题宠物美容店最常遇到的运营痛点不是美容师手艺不行而是预约排期全靠微信聊天、手写登记沟通成本极高。顾客问“明天下午还有没有位置”店员得翻登记本、问美容师、来回确认最后还容易撞时间。所以这个系统的核心价值就是把“线下排班”变成“线上可视化预约”。围绕这个需求系统要拆成三个角色用户端微信小程序注册登录、维护宠物档案、选择美容项目、选择美容师和时间段、提交预约、查看订单状态、取消预约。美容师端可以并入小程序或管理端查看自己某个时间段的预约列表确认或完成服务。管理端Web或小程序管理服务项目、维护美容师排班、查看全店预约日历、统计收入。这个角色的划分决定了后来数据库表结构和接口设计。不要把“预约系统”当成单一功能而是要当成一个“资源调度系统”核心资源是美容师的可服务时间段。1.2 技术选型为什么是 Spring Boot 微信小程序先说后端。Spring Boot 在中小型项目里几乎是最稳的选择尤其是目前市面上 Spring Boot 2.7.18 还是一个兼容性好、资料多的版本。很多人纠结要不要直接上 Spring Boot 3.x如果你和我一样用 JDK8 环境或者跑的是云服务器老镜像2.7.x 最省心。Spring Boot 的优势并不是它有什么花哨特性而是“约定大于配置”把这套逻辑贯彻得很彻底。一个空项目能秒启自带内嵌 TomcatMaven 引入依赖就能跑配合 MyBatis-Plus 做数据库操作连常规的增删改查代码都可以少写大半。然后说前端。微信小程序选原生开发是最直接的理论上 OpenID 登录体系是现成的小程序端的组件和接口也够用。如果你团队里有前端基础也可以考虑 uni-app用 Vue 语法写一次将来还能编译到 H5、App。不过这个项目的核心是预约链路不是跨端适配原生小程序更省心遇到问题也能更快在开发者工具里定位。选微信小程序还有一个原因用户路径短。用户看完朋友圈广告或者店里扫码直接进小程序不用下载 App微信支付也可以直接接与线下门店场景天然契合。1.3 系统模块与业务流程整个业务流可以简化成一条线用户进入小程序 - 微信登录 - 创建宠物档案或选用已有档案 - 选择美容服务项目 - 选择门店/美容师 - 选择日期和时间段 - 提交预约 - 后台校验冲突 - 预约生成待服务 - 美容师/管理员看到预约 - 服务完成后确认 - 用户可评价这里有一个非常关键的设计点预约并不仅仅是“插入一条记录”。提交订单前后端必须校验同一个美容师在同一时间段是否已有其他预约。如果忽略了这一步就会出现两个顾客约了同一个美容师到店后只能干等。所以数据库设计和接口逻辑里时间冲突检测是最核心的业务规则。除了主流程还要考虑衍生功能取消预约时间规则限制、订单状态流转待服务/已完成/已取消、美容师排班表哪些日子上班、哪些时段可约、服务项目上下架。围绕业务后端接口大致分成四类登录鉴权接口、用户和宠物档案接口、服务项目和排班查询接口、预约单和评论接口。2. 核心细节解析与实操要点2.1 小程序端的“冰山”首页、预约、会员档案与我的小程序端看起来简单实际细节比后端更多。首页要展示门店信息、热门服务项目顶部建议放一个醒目的“立即预约”入口。预约页是核心页面至少包含四步选服务项目、选宠物、选美容师和时间、确认信息并提交。实操上个人建议时间选择不要用近乎原生的 picker 直接选“几点几分”那样会把选择范围扩大而且容易选到非排班时间。更合理的方案是后端返回当天可预约的时间片比如“10:00-10:45”“11:00-11:45”小程序端用标签宫格的方式展示。这样用户看到的每个时间点都是经过排班和冲突校验后的结果体验好后端压力也小。“我的档案”模块容易被忽略但宠物美容预约必须有宠物信息。宠物姓名、品种、体重、是否有过敏史这些字段会直接影响美容服务。例如有些小型犬和大型犬的洗澡流程不同有些猫绝育后不建议洗澡这类备注信息最好是用户填后端存下来。顶部导航栏高度这个问题新手机和小屏手机的状态栏高度不一样。如果项目用自定义导航栏一定要用wx.getMenuButtonBoundingClientRect()去动态计算右上角胶囊的位置再结合wx.getWindowInfo().statusBarHeight算导航栏高度。不要写死一个值否则在部分机型上会顶出屏幕。2.2 后端API设计RESTful与统一返回结构预约系统的接口数量不多但千万要定好“统一返回结构”。我习惯用这样的 JSON 结构{ code: 0, message: success, data: {} }code0表示成功非 0 表示业务错误码例如1001表示参数错误1002表示未登录2001表示时间冲突。这样小程序端用统一的wx.request封装就能处理大部分异常情况而不是每个接口单独判断。接口路径建议按资源划分POST /api/user/login登录GET /api/user/pets获取用户宠物列表POST /api/user/pet新增宠物GET /api/service/items获取服务项目GET /api/beautician/list获取美容师列表GET /api/schedule/available?date2024-01-01beauticianId1获取可约时间段POST /api/appointment提交预约GET /api/appointment/order/{orderNo}获取订单详情POST /api/appointment/cancel取消预约开发时用 Knife4j 或 Springdoc 生成接口文档前端同学不用等后端写完就能 mock 数据联调。Spring Boot 里加一个 Swagger 依赖启动后访问doc.html就能看到接口列表非常实用。2.3 数据库设计宠物档案、服务项目、预约单与状态流转先说一张核心的表appointment它包含预约单号、用户ID、宠物ID、服务项目ID、美容师ID、预约日期、开始时间、预计时长、订单状态、创建时间。字段设计时个人建议把“预计时长”存到预约单里而不是每次通过服务项目表关联去查。为什么因为如果服务项目被管理员改了时长已经生成的预约单不应该受影响。类似这种“下单时快照”的思路很多电商系统也在用保存下单时的商品快照保证历史订单可追溯。status字段不能只存一个中文要用整数枚举比如状态值含义说明0待服务用户已提交或支付定金等待到店1已完成美容师确认服务结束2已取消用户或管理员取消3已过期预约时间已过且未服务数据库更新时间字段用update_time并在代码里由 MyBatis-Plus 自动填充。这个小细节能省很多事比如管理端想看“预约单最近什么时候被改过”不用专门写 SQL。user表的核心是openid这个字段必须唯一它在每次用户登录时从微信服务器换取相当于用户在这个小程序里的唯一身份证。pet表通过user_id关联用户一张用户表可以对应多条宠物档案。2.4 关键业务逻辑排班与时间段冲突检测这个位置是预约系统好不好用的试金石。最简单的排班模型是美容师每天早九点到晚六点每小时一个服务时段休息时间不计。但如果做“相对宽松”的预约还要考虑同一个美容师上午只接两个订单下午接三个订单中间留休息时间。为了可落地推荐用“排班表 时间片段校验”方式排班表schedule里记录美容师某天是否上班、上下班时间。用户选择某个时间段时后端先判断该时间是否在上班时间内。再查询该美容师在选定的时间区间里是否已有预约。对应的冲突检测 SQL 类似SELECT COUNT(*) FROM appointment WHERE beautician_id #{beauticianId} AND appointment_date #{date} AND status IN (0, 1) AND start_time #{endTime} AND ADDTIME(start_time, SEC_TO_TIME(#{duration} * 60)) #{startTime}这段 SQL 的逻辑是“已有预约的开始时间早于新预约的结束时间且已有预约的结束时间晚于新预约的开始时间”表示两个时间段存在交集。如果用更直白的写法AND start_time #{endTime} AND end_time #{startTime}前提是表里存了end_time字段建议把开始时间和结束时间都存进去这样查询更简单索引也更好命中。这个检查不能只在前端做前端只是提示后端必须在事务里加锁。建议用select ... for update或者分布式锁防止同一时刻两个请求同时提交造成超卖。如果没有引入 Redis最简单的方法是在预约提交方法上加synchronized或者数据库唯一约束但最稳妥的还是用悲观锁锁定美容师的那一行排班记录。3. 实操过程与核心环节实现3.1 环境搭建与项目初始化后端环境示例JDK 8Maven 3.6MySQL 8.0Spring Boot 2.7.18MyBatis-Plus 3.5.xHutool工具类Knife4j接口文档创建项目可以先通过start.spring.io也可以直接在 IDEA 里用 Spring Initializr选择 Web、MyBatis、MySQL 相关依赖。不要选 Spring Boot 3.x除非你用的是 JDK17很多教程资料还停留在 2.x。项目结构建议src/main/java/com/example/petgroom/ ├── controller/ // 接口层 ├── service/ // 业务逻辑层 ├── mapper/ // MyBatis-Plus Mapper ├── entity/ // 数据库表对应实体 ├── dto/ // 接收前端参数的对象 ├── vo/ // 返回给前端的数据对象 ├── config/ // 配置类如拦截器、WebMvc ├── common/ // 统一返回结果、异常处理、常量 └── utils/ // 工具类如 JWT实体类用 Lombok 减少 getter/setter数据库字段用下划线风格Java 字段用驼峰风格MyBatis-Plus 会自动映射。3.2 微信小程序登录态与JWT鉴权微信小程序登录流程是固定的但实现上有几个坑值得说。小程序端调用wx.login()拿到临时code传给后端接口。后端拿到code后请求微信接口GET https://api.weixin.qq.com/sns/jscode2session?appidAPPIDsecretSECRETjs_codeCODEgrant_typeauthorization_code返回里有openid和session_key。用openid查本地user表如果不存在就自动注册存在就更新最近登录时间。然后后端生成一个 JWT token返回给小程序。之后小程序每次请求后端接口时在请求头带上Authorization: Bearer token。后端定义一个拦截器校验 token 是否有效并从中解析出 userId。这个方案比小程序自带的session_key更灵活因为如果你的预约系统还要接 Web 管理端JWT 也能复用。JWT 的 key 不要写在代码里放到application.yml用Value注入。token 过期时间建议 7 天因为小程序用户不会每天登录。3.3 核心接口实现与演示这里以“提交预约”接口为例看完整逻辑PostMapping(/api/appointment) public ResultAppointmentVO create(RequestBody AppointmentDTO dto) { // 1. 参数校验 // 2. 检查时间段是否可约 // 3. 创建预约单 // 4. 生成订单号 // 5. 返回前端需要的订单数据 }订单号建议用时间戳加随机数形如20250401xxxxxx一是前端展示方便二是后续对接支付时能直接用于商户订单号。时间可约性检查要放在事务里Transactional public Appointment createAppointment(AppointmentDTO dto) { // 锁定美容师当天的排班记录 Schedule schedule scheduleMapper.selectForUpdate(dto.getBeauticianId(), dto.getDate()); if (schedule null) { throw new BizException(该美容师当天未排班); } // 检查美容师当天是否有冲突预约 int count appointmentMapper.checkConflict( dto.getBeauticianId(), dto.getDate(), dto.getStartTime(), dto.getEndTime() ); if (count 0) { throw new BizException(该时间段已被预约请选择其他时间); } // 保存预约单 Appointment appointment new Appointment(); appointment.setOrderNo(generateOrderNo()); // ... 其他字段 appointmentMapper.insert(appointment); // 异步发送通知可选 notifyService.send(appointment); return appointment; }注意Transactional必须放在public方法上并且是同类调用不会生效最好通过 Service 对外方法调用别在 Controller 里直接调 Mapper。3.4 管理员端Web管理后台或小程序管理端如果预算有限管理端可以直接做成一个独立的小程序页面只有管理员角色能看。但实际体验更好的是 Web 管理后台采用 Vue Element Plus 这些技术后端接口直接复用。管理端核心功能包括服务项目管理增删改查设置价格和时长。美容师管理录入美容师、设置排班。预约单管理查看所有预约按日期/美容师筛选可以手动取消。数据看板统计今日订单量、完成率、时段热度等。在 Spring Boot 端管理员登录建议单独用一张admin表用户体系的 JWT 和管理员的 JWT 可以使用同一个 token 工具类但登录接口不同权限拦截逻辑要注意区分。3.5 部署本机到服务器的Docker方案本地开发时小程序可以通过开发者工具直接请求http://localhost:8080但手机端真机预览时手机不能直接访问电脑的 localhost。这里有两个办法电脑和手机在同一局域网后端启动时启用本机局域网 IP小程序开发工具里把请求地址改成http://192.168.x.x:8080并在开发工具里勾选“不校验合法域名”。用内网穿透工具比如 cpolar、natapp把本地 8080 端口映射到公网域名手机直接请求这个公网地址。到了正式发布我强烈建议用 Docker 部署。先给 Spring Boot 项目打一个镜像FROM openjdk:8-jre-alpine ENV TZAsia/Shanghai RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezone COPY pet-app.jar /app.jar ENTRYPOINT [java, -jar, /app.jar]服务器上用 Docker Compose 管理 MySQL、应用、Nginxversion: 3 services: mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD123456 - MYSQL_DATABASEpetdb volumes: - ./mysql/data:/var/lib/mysql app: build: . ports: - 8080:8080 environment: - SPRING_DATASOURCE_URLjdbc:mysql://mysql:3306/petdb需要提醒一句如果后端要上传宠物图片不建议把图片直接存数据库也不建议本地磁盘存因为服务器一旦扩容或迁移图片就丢了。个人建议用 MinIO 搭建私有对象存储或者直接用云对象存储。在小程序端上传图片到后端后后端回调或保存返回的文件 URL前端用 HTTPS 图片链接就能显示。4. 常见问题与排查技巧实录4.1 小程序真机调试连不上本地后端这个问题几乎每个人都遇到过。最常见原因是手机和电脑不在同一网络或者是电脑防火墙挡住了 8080 端口。可以先在小程序开发者工具里点“真机调试”手机扫码后看 Network 面板如果请求一直 pending大概率是网络问题。另一个容易忽略的点是微信小程序真机环境下http://localhost不等于手机本身也不等于电脑所以 target 地址必须写成电脑局域网 IP 或内网穿透域名。如果用的是内网穿透免费版域名里通常带一串随机前缀后端 CORS 也要允许该域名或直接全部允许。4.2 Spring Boot版本太高导致JDK8环境起不来很多人下载了最新的 Spring Boot 3.x结果项目根本跑不起来报错基本都是UnsupportedClassVersionError或者依赖不兼容。其实选择版本前先想清楚你是在本地自己玩还是要在旧服务器上跑如果服务器是 CentOS 7 默认 JDK8那 Spring Boot 2.7.18 是稳妥选择。实在遇到版本冲突第一件事用mvn dependency:tree查看依赖树找出冲突的包然后在pom.xml里排除旧版本。不要随意全局升级版本项目能稳定运行比一味追新更重要。4.3 预约时间冲突检查失效怎么办这个坑我踩过。一开始我只通过前端传入的开始时间判断没有算结束时间导致两个预约首尾相接时会被误判为不冲突。后来我把结束时间字段加入数据库并在查询条件里用“开始时间小于对方结束时间结束时间大于对方开始时间”的标准判断问题就解决了。还有一种情况同一天有多个美容师只查beautician_id是不够的。预约查询必须带上美容师ID否则会出现 A 美容师没空B 美容师有空系统却直接把 A 的时段锁死。排查时先看 SQL 条件是否完整。4.4 小程序端请求封装与缓存失效的问题小程序原生的wx.request每次都要写完整 url、method、data非常啰嗦。建议封装一个request.js默认带上Authorization统一处理 code 非 0 的情况const request (url, method, data) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method, data, header: { Authorization: Bearer ${wx.getStorageSync(token)} }, success(res) { if (res.data.code 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail(err) { reject(err); } }); }); }缓存 token 时有些场景希望 token 过期后自动清理可以用wx.setStorageSync加一个自定义时间戳读取时判断是否超过 7 天而不是简单判断 token 是否存在。这个“带过期时间的缓存”设计在缓存首页数据、服务项目列表时同样适用。4.5 小程序审核、类目与支付问题宠物美容属于线下服务不是虚拟商品可以申请“生活服务 宠物”类目。但如果个人主体小程序类目可能会受限建议用企业主体注册。支付方面如果不想接微信支付商户号可以做成“到店支付”预约单状态为“待服务”到店扫码核销。如果确实要在线支付就需要申请微信支付商户号并完成小程序绑定注意服务类目要一致否则审核可能不通过。上线前的体验优化里还有一个小技巧在小程序管理后台配置用户隐私保护指引声明收集的字段头像、手机号、位置等否则今年很多接口会受限。另外小程序每年要年审如果只是内部演示不发布线上版就不用担心这些但对外商用就必须提前把这些合规事项安排好。最后再分享一点个人心得。做预约系统最值得投入时间的不是页面炫不炫而是把“时间冲突”和“状态流转”这两个底层逻辑想清楚。状态机画清楚数据库索引建好接口做好幂等整个系统的稳定性自然就上来了。开发过程中多花点时间看看生产环境下容易出问题的点比如同一用户重复提交、同一个时间段被抢约、取消预约后的额度释放这些才是让用户和门店都觉得“好用”的关键。项目上线后最实用的功能是管理端的预约日历一眼能看出哪天排满了、哪个美容师空闲比一堆图表有用得多。
RELATED

相关推荐

Linux块层架构演变:从单队列到blk-mq与io_uring性能优化实战

Linux块层架构演变:从单队列到blk-mq与io_uring性能优化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/13 5:22:20
图论连通分量详解:从DFS到并查集与Tarjan实战

图论连通分量详解:从DFS到并查集与Tarjan实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/13 5:22:20
App版本升级数据表设计与接口实现:从字段拆解到灰度发布

App版本升级数据表设计与接口实现:从字段拆解到灰度发布

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/13 5:22:20
MORE NEWS

更多资讯

📰

go-jose Safe JSON 深度解析:cortex 依赖链中 fork 版 encoding/json 的大小写敏感匹配与重复键拒绝机制

可观测性时序数据库后端指标监控 【免费下载链接】cortex A horizontally scalable, highly available, multi-tenant, long term Prometheus. 项目地址: https://gitcode.com/gh_mirrors/cortex6/cortex 点击查看 免费下载 导读 JOSE(JSON Object Sig…

📰

PSR开关电源EMC设计实战:PCB布局、变压器绕法与滤波整改指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

2026年工业控制系统上位机:Qt高效工程搭建与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

点云连续卷积:原生建模的几何不变性实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

AI编程代理Codex生产环境实战:多场景自动化落地与避坑指南

1. 项目全貌与核心价值拆解先说结论:这个项目不是又一个"抄一段代码跑通就完事"的教程,而是把Codex这类AI编程代理真正丢进生产环境里,去碰那些文档里不会写的脏活累活——多仓库并行、流水线触发、测试自动修复、发布前卡点。项目…

📰

Faster RCNN小目标交通标志检测实战优化指南

1. 项目概述:为什么小目标交通标志检测是个“硬骨头”交通标志识别不是新鲜事,但真正落地到实际道路场景里,尤其是高速、长距离、复杂光照下的小目标检测,至今仍是不少团队卡壳的环节。我接触过不下十个类似项目,从某高…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬