← 所有项目

PROJECT CASE STUDY / WEB APPLICATION

已上线

FitStudio 健身工作室管理系统

围绕朋友的真实工作室需求,连接会员、预约与课时管理,让业务规则在软件中可靠落地。

BUSINESS RULES · TRACEABLE LEDGER · CLOUD DELIVERY

01 / PROJECT OVERVIEW

让问题、实现与交付连成一条线

把工作室的预约、课时余额和操作原因连起来。每一个状态变化都应有业务含义,每一次扣课都应能解释,而不只是让一个余额数字变小。

业务场景
小型私人健身工作室
使用入口
会员端 / 教练与管理端
交付状态
已上线,依据后续部署记录
工程重点
规则、隔离、事务与可追踪性
源码核验

以较新的 RC1 交付源码快照核验会员端与服务层,并对照历史部署记录。早期宿主源码不是最新版本;本轮未连接生产或访问会员数据。

02 / BACKGROUND

项目背景

朋友经营小型健身工作室,需要管理会员、课程套餐、购课、可预约时段、余额与上课记录。手机上完成这些日常操作,比维护零散人工记录更方便。

项目围绕真实规则持续迭代:什么时候占用课时、取消何时扣课、教练取消如何补课、误扣后怎样保留证据。它是服务具体工作室的系统,不是已实现多租户的商业 SaaS。

THE PROBLEM

解决的问题

预约不能提前耗掉实际余额,又不能重复承诺同一份可用课时。取消、爽约、改期和冲正还会改变预约状态,需要与余额、流水及会员权限同时保持一致。

03 / MY ROLE

个人职责

在 AI 辅助下参与需求梳理、业务规则制定、功能迭代与验收,并实践 Linux 生产部署。

  • 分析工作室业务需求,把课时、取消与改期规则整理为明确场景。
  • 在 AI 辅助下参与管理端与会员端开发迭代,确认用户操作和权限边界。
  • 围绕状态、余额与历史记录做功能验收,并实践服务器部署与后续维护准备。

项目在 AI 辅助下完成迭代。这里展示实际参与的需求、业务判断、实现与验收,不把辅助生成等同于独立精通。

MY DECISIONS / 个人贡献与取舍

我的取舍:先解释课时为什么变化,再实现操作

我的工作从工作室的日常流程开始:会员购课后能预约多少次,哪些取消会扣课,误操作怎样恢复。这些问题必须先有统一规则,页面和数据库才能围绕同一种含义实现。开发中使用 AI 辅助,我承担需求与规则判断、功能迭代、测试验收和部署维护,而不是声称每行代码都由我独立手写。

预约只占用可用课时,不立即扣余额。规则演示(非真实会员数据):余额为 10、待上课占用 1 时,可用为 9;正常完成后增加一笔 -1 流水,余额汇总为 9,待上课占用释放。迟取消和爽约也扣一次;教练取消只释放占用、不产生扣课流水,另安排补课。余额来自流水汇总,不是额外维护一份随时可能失配的数字。

完整链路是会员选择时段 → 身份与可用课时校验 → 创建预约并占用 → 按完成/取消/爽约结果分支 → 必要时写扣课流水并汇总余额 → 记录状态与操作事件。重复完成先返回已有状态;其他非法状态明确拒绝。改期生成替代预约,取消判断用新时间,会员成功自行改期后锁定再次自主取消/改期。每次操作仍要确认当次规则。

权限隔离需要在服务端重新确认会员绑定,不能信任前端传来的会员编号;身份、规则、状态变更在事务中处理。SQLite 使用 IMMEDIATE 事务与锁忙重试提示,不把 select_for_update 描述成 SQLite 行锁,也不据此宣称已经证明高并发能力。

部署上,浏览器经 HTTPS/Caddy 到 Gunicorn、Django 和 SQLite,应用使用 Docker。持久化、迁移、静态资源检查和发布前备份有历史记录;回滚需兼顾新业务账目,不能拿旧库直接覆盖。教练端与会员端已上线;历史多阶段测试记录存在,但最新完整会员手机流程、流水联调及恢复演练仍需补齐签字。

依据:RC1 studio/services.py:161–163,306–372;portal_services.py:19–35,100–111,134–192;portal_access.py:6–15;已有脱敏部署记录。

04 / TECH STACK

技术选择,要能解释用途

Python + Django
服务端模板、表单、会员登录、业务服务、权限与事务。
SQLite + Django ORM
记录会员、套餐、预约、流水及操作事件,以流水汇总余额。
HTML / CSS / JavaScript
手机优先页面、逐次确认和预约交互,服务端仍负责最终校验。
Docker + Gunicorn
容器运行 Django,持久化数据并输出服务日志。
Caddy + HTTPS
为用户访问提供反向代理与加密入口,不公开后端应用监听端口。

05 / SYSTEM ARCHITECTURE

系统架构

FitStudio 应用与容器部署架构ARCHITECTURE / SCHEMATIC
  1. 01用户访问
    • 手机 / 电脑浏览器
    • HTTPS
    • Caddy 反向代理
  2. 02Docker Web 容器
    • Gunicorn
    • Django 页面与登录权限
    • 业务服务 / ORM
  3. 03持久化与追踪
    • SQLite 持久化数据卷
    • 课时流水 + 预约状态
    • BookingEvent / 规则确认记录
  4. 04发布与维护
    • 配置与数据备份
    • 版本切换 / 健康检查
    • 保留回滚准备

访问链路为浏览器 → HTTPS → Caddy → Gunicorn → Django → SQLite。上线依据后续 RC1 部署记录;本轮仅核验本地源码与记录,不重新连接生产或验证恢复演练。

业务处理流程

从输入到输出的业务流程SCHEMATIC
  1. 01选择可用时段
  2. 02预约占用可用课时
  3. 03服务端确认规则与状态
  4. 04完成 / 迟取消 / 爽约扣课
  5. 05余额、流水与事件一起提交

06 / CORE FEATURES

核心功能

已实现

会员资料与账户

会员档案和登录账户分开绑定。会员请求从当前登录用户重新取得绑定,只能在服务层访问自己的预约;职员不能混入会员入口。

studio/portal_access.py:6–15 · portal_services.py:_owned

已实现

套餐、购课与余额

教练、课程套餐、购课与课时关联。余额以流水汇总为依据,可用课时扣除待上课占用,避免把显示余额当作可自由预约额度。

studio/services.py:credit_snapshot / register_purchase

已实现

预约、改期与补课

提供可预约时段与会员预约。改期新建有前后继关系的预约并重新计算截止;教练取消不扣课,补课保留来源。

studio/portal_services.py:member_reschedule · services.py:create_makeup_booking

已实现

逐次规则确认与锁定

会员每次取消或改期必须明确确认当前规则版本。成功自主改期一次后,预约链保持自主操作锁定,不能再次自行取消或改期;授权职员仍可处理。

studio/portal_services.py:85–111,135–192

已实现

受控的会员开户

具备相应会员权限的职员可以开户。表单把新账户固定为普通用户,会员、账户、绑定与审计共同置于事务内,阻止重复绑定。

studio/forms.py:MemberAccountOpeningForm · views.py:MemberAccountCreateView

已实现

可追溯扣课与冲正

完成、迟取消与爽约按规则记一次负流水;授权冲正新增反向流水,不抹去原扣课,并记录原因和操作者。

studio/services.py:complete_booking / restore_booking · models.py:LessonEntry

课时业务规则与一致性

业务操作课时与状态处理
预约成功只占用可用课时,不立即扣实际余额。
正常完成课程结束后确认完成,按预约成本扣一次;重复提交不再扣。
迟取消 / 爽约按规则扣一次,保留独立状态与原因。
教练取消 / 补课原课不扣,释放占用并建立关联补课;补课按自身结果记账。
误扣冲正新增正向流水并关联原扣课,原记录保留;不重复恢复。
改期截止依据新预约的上课日期重新判断取消截止,不沿用旧日期。
会员自主改期一次成功后沿预约前序链保持锁定,禁止再次自行改期或取消。
逐次确认每次取消或改期要求当前规则版本与明确确认,并保存规则快照。
余额一致性实际余额来自流水,可用课时扣除待上课占用;状态、流水和事件在事务内一起提交。

07 / TECHNICAL CHALLENGES

技术难点:把业务例外放进设计

规则之间存在依赖:只在页面上隐藏按钮不够,只给扣课函数加一个判断也不够。约束必须进入服务层、数据模型和可核对的记录。

08 / SOLUTIONS

解决方案

01

把复杂业务规则变成状态与记录

遇到的问题

同样叫“取消”,会员、教练、迟取消和改期后的取消可能产生不同结果。

代码中的解决方式

服务层按当前预约状态和上课日期计算结果;改期创建关联后继。会员端额外记录规则版本、文本快照与确认时间,自主改期锁定沿前序链传递。

代码依据 studio/services.py:cancel_outcome / reschedule_booking · portal_services.py:member_locked

02

重复请求与竞争不能重复扣课

遇到的问题

重复点击、请求重试或并发状态变更可能争用同一预约和可用课时。

代码中的解决方式

状态终态检查、请求 UUID 与内容摘要、唯一/检查约束共同兜底。SQLite 采用 IMMEDIATE 事务和有限等待,写入前取得数据库级写保留锁;失败回滚或返回可重试错误。

代码依据 studio/services.py:operation / _consume · config/settings.py:transaction_mode

实现边界:SQLite 不是行级锁。该机制和已有测试不等于生产高并发、跨进程压力或断电恢复已全面验证。

03

余额、流水与状态必须同时成立

遇到的问题

只改余额会丢失原因;写了扣课却没更新预约,会让下一次处理含义不清。

代码中的解决方式

以 LessonEntry 汇总余额,credit_snapshot 同时读取占用。统一业务事务处理预约状态、流水与 BookingEvent;冲正记录关联原流水,保留审计轨迹。

代码依据 studio/services.py:credit_snapshot / complete_booking / restore_booking · models.py:LessonEntry

04

会员隔离要在服务端再次检查

遇到的问题

登录成功或前端按钮限制,不能阻止用户伪造他人的预约编号或权限参数。

代码中的解决方式

portal_access 每次根据当前用户重新绑定会员;_owned 用预约主键与会员同时筛选。开户表单固定普通用户标志,服务层再次校验归属与规则确认。

代码依据 studio/portal_access.py · portal_services.py:_owned / _rule · forms.py:MemberAccountOpeningForm

实现边界:这是会员资料归属与角色权限隔离,不把它称作已完成的多租户平台。

05

部署完成之后仍需要可维护

遇到的问题

本地能打开页面,不意味着升级不会丢数据,或 HTTPS 与后端暴露方式正确。

代码中的解决方式

使用 Docker、Gunicorn、Caddy 和持久化配置。后续部署记录保留迁移、健康状态及 HTTPS/静态资源验证结果;版本切换前考虑备份与回滚。

代码依据 Dockerfile · compose.yaml · RC1 部署记录(脱敏摘要)

实现边界:本轮未运行服务器命令;持续运行、完整实机业务验收与恢复责任仍需后续确认。

SOURCE EXCERPT

代码节选:从实现看取舍

完成操作对已完成预约直接返回,拒绝不合适状态和提前完成,再调用统一扣课并写事件。并发保护还依赖 operation 装饰器和数据库约束,不能只从这一个判断推断全部保证。

授权、状态判断、扣课与事件在同一业务操作中RC1 · studio/services.py:306–320 · complete_booking
@operation("complete_booking")
def complete_booking(booking_id, *, actor, reason="确认课程完成"):
    reason = _reason(reason)
    booking = _get(Booking, booking_id)
    if booking.status == Booking.Status.COMPLETED:
        return booking
    if booking.status != Booking.Status.PENDING:
        raise BusinessError("仅待上课预约可以确认完成。")
    if booking.ends_at > timezone.now():
        raise BusinessError("课程尚未结束,不能提前确认完成。")
    _consume(booking, LessonEntry.Kind.COMPLETE, actor, reason, booking.ends_at)
    booking.status = Booking.Status.COMPLETED
    booking.save(update_fields=["status", "updated_at"])
    _event(booking, "complete", actor, reason, before="pending", after="completed")
    return booking

来自已核验的本地源码节选;为便于阅读保留原有实现片段,不是完整可运行模块。

DEVELOPMENT PROCESS

开发过程

按现有实现与版本资料整理阶段,不推断未经确认的具体开发日期。

  1. 01

    需求与规则先行

    围绕工作室真实日常确认预约占用、取消时限、补课和冲正,逐步形成可说明的状态流转。

  2. 02

    从管理操作扩展到会员入口

    在管理端基础上增加会员账户、可用时段、预约与记录,并把归属检查、规则确认和自主改期锁定落到服务层。

  3. 03

    形成测试依据与部署交付

    保留关键规则、重复操作和竞争场景的回归设计;后续 RC1 记录包含迁移、容器健康与 HTTPS 验证,不把早期部署准备文档当作最终状态。

09 / TESTING & VALIDATION

测试与验收

早期源码保存了扣课、改期和 SQLite 竞争等回归测试;较新交付包提供会员端实现,但该包未附完整测试源码。下面区分已有测试依据与应继续执行的验收设计。

  • 预约不扣余额,完成只扣一次

    已有早期测试检查余额与可用课时区别,并验证重复完成不重复记账。

    早期回归:studio/tests/test_bookings.py:test_booking_reserves_available_but_does_not_deduct_ledger / test_complete_deducts_once_and_records_event
  • 改期、教练取消与补课

    已有早期测试检查新日期的取消规则、冲突回滚、补课来源以及一次扣课。

    早期回归:studio/tests/test_m5_confirmed_rules.py · test_m4_rules.py
  • 并发与事务中途失败

    已有文件 SQLite + 独立连接的竞争测试,检查同时完成、时段争用、锁等待和回滚。

    早期回归:studio/tests/test_concurrency.py · test_bookings.py:test_failure_between_ledger_and_state_rolls_back
  • 会员归属、逐次确认与自主锁定

    已核验 _owned、_rule、member_locked 等实现。验收设计:伪造他人预约应拒绝;旧规则需重确认;一次自主改期后再次取消/改期应拒绝。

    RC1 源码核验 / 后续验收设计:studio/portal_services.py:38–192
  • 生产切换与后续验收

    历史 RC1 记录包含迁移、健康状态和 HTTPS/静态资源检查;完整正式版身份隔离与扣课联调、长期运行和恢复演练仍需明确。

    后续部署记录:DEPLOYMENT_RESULT.md(已脱敏核对)

以上为业务项目源码中的验证设计与测试依据。本轮验证的是作品集网站,没有重新运行这些业务项目的完整测试,也没有对线上系统做操作。

10 / RESULTS

项目成果与最终交付

教练端与会员端已完成上线,后续记录包含迁移成功、容器健康以及 HTTPS 和静态资源验证。这证明项目经历了部署交付,不代表已拥有大量用户、长期稳定运营或完整恢复验收。

  • 会员、课程、购课、预约与课时管理的 Web 应用及会员端。
  • 规则确认、归属检查、预约链、课时流水与操作事件实现。
  • Docker / Gunicorn / Caddy 部署配置及后续 RC1 发布记录。
  • 可追踪的规则与测试依据,及仍待完成的正式业务/持续运行验收清单。

11 / FUTURE IMPROVEMENTS

未来规划

以下是后续改进方向,不是已完成能力。

  • 规划在授权测试环境补齐正式版完整角色隔离、预约—取消—改期—锁定—扣课链路的可复现验收。
  • 规划建立明确的备份保留、恢复责任和恢复演练记录。
  • 规划基于真实使用反馈改善手机操作和业务反馈,不先引入复杂服务。
  • 规划有真实并发需求后评估 PostgreSQL 与压力测试,而不是宣称现有 SQLite 已解决所有并发场景。

PROJECT GALLERY

项目截图与输出示例

FitStudio 工作室首页,未显示会员数据
使用者提供的真实界面截图;未展示业务明细,不作为业务成效证明。打开原图 ↗

这张是真实教练端首页:展示新增会员、查看会员、创建预约及今日课程入口。空状态不证明完整会员流程验收;会员端、预约过程与脱敏验收截图尚待补充。

已展示使用者提供的真实界面。脱敏报表和更多业务场景待补充;不以空数据截图证明识别准确率、运营规模或完整验收。

查看 V1.1 项目补充说明

项目背景 · 从真实工作室的日常出发

朋友经营小型健身工作室,需要方便地管理会员、课程、预约和课时。项目围绕实际经营者展开,不以商业化 SaaS 为目标。

我把业务中的例外情况整理为规则,在 AI 辅助下迭代功能,并通过验收检查规则是否被正确执行。

核心功能 · 管理端与会员端

  • 管理端:会员、教练、课程套餐、会员购课、预约管理。
  • 课程处理:课程完成、取消与改期、爽约、补课、课时冲正和课时变动流水。
  • 会员端:独立登录、可用时段、预约、记录、取消、改期、个人中心及历史课程。
  • 服务端权限控制与数据隔离,防止跨会员访问个人数据。

课时规则:不只是一张预约表

  • 预约占用可用课时,不立即扣减实际课时;课程完成、迟取消、爽约按业务规则扣课。
  • 状态变更避免重复扣课,余额与流水保持一致。
  • 取消截止依据上课日期前一天自然日结束时间判断。
  • 会员每次取消、改期前逐次确认规则;自行改期成功后限制再次自行改期或取消。
  • 冲正操作同时考虑余额与记录,避免只改一个数字。

测试与验收

通过持续迭代自动化测试,覆盖预约、课时扣减、权限、并发及状态变更等关键业务流程。

此处不展示未经对应测试报告核实的精确数量、覆盖率或性能指标。测试与业务验收共同帮助发现规则遗漏。

从本地开发到 Linux 上线

采用 Python、Django、SQLite、Docker Compose、Gunicorn 和 Caddy,在 2 核 CPU、2GB RAM 的 Linux 云服务器上部署。2026 年 11 月初,教练端与会员端已上线。

实践涵盖数据库持久化、域名、HTTPS、健康检查、版本升级、备份与回滚准备及临时演示环境清理。上线状态依据项目经历陈述,不代表已经拥有大量用户。

我学到的事情

真实业务的难点往往在状态与边界条件。把规则说清楚、用测试验证、留下可追溯记录,和写出页面一样重要。

AI 能帮助实现与排错,但业务判断、验收和持续理解代码仍需要自己承担。