健身房的私教业务看起来简单------教练有空、会员约课、上完课扣课时。但真正落到系统里,问题会迅速变复杂:排课时段被重复预约怎么办?会员临时取消,课时怎么退?教练离职后历史记录怎么留?管理员、店长、教练看到的数据范围怎么区分?
本文以一套健身私教课预约管理系统为例,聊聊这类"预约 + 资源 + 计费"型业务的技术选型与实现思路。
源码:点头像--个人简介
一、技术栈
| 层次 | 选型 |
|---|---|
| 后端 | Python 3.12 + FastAPI |
| ORM / 校验 | SQLAlchemy + Pydantic |
| 鉴权 | JWT + MD5 加盐 |
| 前端 | Vue3 + Element Plus + Pinia |
| 可视化 | ECharts |
| 数据库 | MySQL 8.0 |
前后端分离,后端只提供 RESTful 接口,前端通过 Axios 统一拦截请求与响应,职责边界清晰。
二、系统功能
系统围绕私教业务的核心链路设计,模块划分如下:
- 登录与仪表盘:登录鉴权,首页汇总会员数、课程数、预约量等关键指标
- 会员管理:会员档案、剩余课时、状态维护
- 教练管理:教练信息、擅长方向、在职状态
- 课程管理:私教课程类型、单次课时消耗、价格
- 排课管理:按教练与时间生成可预约时段
- 预约管理:会员约课、确认、取消、完成
- 课时记录:充值、扣减、返还的全量变动流水
- 系统管理 :菜单、角色、账号三级权限配置


三、关键技术实现
1. 预约与课时的强一致事务
这是整个系统最核心的部分。一次"完成上课"操作,实际上要同时完成三件事:预约状态置为已完成、会员剩余课时扣减、写入一条课时变动记录。三者必须同生共死,否则就会出现"课上完了课时没扣"或"课时扣了记录查不到"的对账灾难。
实现上把这三步收敛在同一个数据库事务中,任一环节失败整体回滚,保证业务数据始终自洽。取消预约同理------状态回退、课时返还、流水补记,一个事务内完成。
2. RBAC 权限控制
系统采用经典的"账号---角色---菜单"三层模型。角色绑定菜单权限,账号绑定角色,后端接口按权限校验,前端通过自定义指令控制按钮与菜单的显隐。
这样做的价值在于:不同岗位的管理员登录后看到的是各自的工作台,而不是一套全量菜单。权限判断在后端兜底,前端只做体验层的隐藏,避免"藏了按钮但接口能调"的常见漏洞。
3. 递归树形菜单
菜单数据以父子结构存储,后端通过递归构建树形结构返回,前端直接渲染为多级导航。新增一个模块只需要插入一条菜单记录并挂到对应父节点下,无需改动前端路由代码。
4. 逻辑删除
会员、教练、课程等主数据不做物理删除,而是打上删除标记。原因很实际:历史预约和课时流水都引用了这些数据,物理删除会直接破坏历史记录的完整性。查询时统一过滤已删除数据,既保留了审计能力,也避免了外键断裂。
5. 鉴权与安全
登录成功后签发 JWT,前端在请求头携带 Token,后端通过依赖注入(Depends)在路由层统一校验身份与权限,业务代码不必重复写鉴权逻辑。密码采用 MD5 加盐存储,避免明文落库。
6. 工程化细节
- Pydantic 负责请求体校验与响应序列化,接口入参在进入业务逻辑前就被拦截
- Lifespan 管理应用生命周期,统一处理启动与关闭时的资源初始化
- CORS 配置解决前后端分离下的跨域问题
- Axios 拦截器 统一处理 Token 注入、错误提示与登录态失效跳转
- Vue Router 导航守卫 拦截未登录访问与越权路由
四、解决了什么问题
排课与预约的冲突问题。 排课是"资源供给",预约是"资源消费",两者分离建模后,每个时段的可约容量、已约数量一目了然,避免了口头排课导致的撞课。
课时账目不清。 传统健身房靠纸质或 Excel 记课时,退课、补课、赠送课时混在一起极易出错。系统把每一次课时变动都落成一条不可篡改的流水,剩余课时由流水推导,任何一笔都能追溯到时间、操作人和关联预约。
权限边界模糊。 多岗位共用一套后台时,谁能改价格、谁能删会员、谁能看营收,必须有明确规则。RBAC 把这件事从"约定"变成了"配置"。
数据无法沉淀。 仪表盘把会员、课程、预约等数据聚合展示,运营者能直观看到业务热度,而不是靠感觉判断。
五、小结
这类"预约 + 资源 + 计费"的系统,技术难点从来不在框架本身,而在于状态流转的严谨性 和数据一致性。FastAPI 的依赖注入与 Pydantic 校验让接口层足够干净,SQLAlchemy 的事务能力保证了业务层的可靠,Vue3 + Element Plus 则让后台界面的开发成本大幅降低。
对于想深入 Python 全栈开发的同学,这套系统覆盖了从接口设计、权限建模、事务处理到前端工程化的完整链路,是一个贴近真实业务的实践样本。