type
Post
status
Published
date
Apr 28, 2026
slug
fastapi-booking-app-backend-with-ai
summary
用 Claude Code AI 结对编程,4周内用 FastAPI + SQLAlchemy 2.0 + Redis + Celery 搭建健身工作室约课签到系统后端,涵盖分布式锁、事务隔离、卡时管理等核心设计
tags
开发
AI
docker
工具
category
技术分享
icon
password
前言:如果你曾经手写过预约系统,一定踩过这些坑——超卖、重复预约、取消后课时没退回、测试数据污染……这篇文章记录了我用 Claude Code 作为 AI 结对编程伙伴,在 4 周内从零搭建一套健身工作室约课签到系统后端的完整过程。技术栈是 FastAPI + SQLAlchemy 2.0 + MySQL + Redis + Celery,所有踩过的坑和解法都在这里。
🗺️ 系统全貌
先看地图,再走路。
整个后端分 4 个主要域:
域 | 核心内容 |
认证 | 商家后台登录、微信小程序登录(mock 模式)、JWT |
门店/教练/课程 | 多门店、教练档案、课程产品、排课(含冲突检测) |
预约核心 | 卡商品 + 适用范围、选卡算法、Redis 分布式锁、原子扣课时 |
签到 | 二维码、4 种签到模式、card_log 审计 |
数据库采用多租户设计——所有业务表带
merchant_id,软删除用 deleted_at IS NULL,卡操作强制双写 card + card_log(同一事务)。🔐 预约核心:分布式锁 + 原子操作
超卖是约课系统的第一杀手,必须在架构层解决。
预约流程分两阶段:
阶段一(锁外,只读)
- 检查排课状态(BOOKING/FULL)
- 检查预约窗口(
book_open_hours/book_close_hours)
- 检查重复预约
- 运行选卡算法找到最合适的卡
阶段二(锁内,写操作)
Redis 锁用
SET NX EX + Lua 脚本原子释放,彻底避免锁误删。🃏 选卡算法:优先用最快过期的卡
贪心策略,减少课时浪费。
卡的适用范围(
scope_type)支持 4 种:ALL / INCLUDE_COURSE / INCLUDE_COACH / EXCLUDE_COURSE,通过 card_product_scope 表存储目标 ID 列表。🐛 三个让人印象深刻的坑
坑 1:SQLite 不认 BIGINT 自增主键
测试用 SQLite,生产用 MySQL。SQLite 只有
INTEGER PRIMARY KEY 才自增,BIGINT PRIMARY KEY 会报 NOT NULL。解法:
_BigInt = BigInteger().with_variant(Integer, "sqlite"),所有主键统一用这个类型。坑 2:passlib + Python 3.12 崩溃
passlib 1.7.x 在 Python 3.12 的 bcrypt wrap-bug 检测逻辑里直接抛异常。解法:移除 passlib,改用
import bcrypt 直接调用 bcrypt.hashpw() / bcrypt.checkpw()。坑 3:测试事务隔离失效
Service 层调用
db.commit(),导致 pytest fixture 的 session.rollback() 无法回滚——数据跨测试泄漏。解法:改用连接级事务包裹——
connection.begin() → Session(bind=connection) → 测试结束后 trans.rollback(),无论 service 内部 commit 多少次,外层事务始终未提交,SQLite 可以完整回滚。📊 4 周交付物一览
API 共 30+ 个端点,覆盖商家后台(
/api/m/)和客户端(/api/c/),全部通过单元测试。🤗 总结归纳
用 AI 结对编程最大的收益不是"写得快",而是边写边设计——每一个踩坑都被即时记录、修复、沉淀为测试用例。分布式锁 + 数据库乐观锁的双保险方案,让约课超卖从根本上消失。如果你也在做预约类系统,建议优先把"选卡 + 扣课时 + 写审计日志"三步放进同一个事务,再套一层 Redis 锁,这是最稳的组合。
📎 参考资料
你在做预约/排课系统时遇到过哪些并发或数据一致性问题?欢迎评论区分享你的解法,说不定比文中的方案更优雅 🙌

