Files
mp-pilates/docs/superpowers/specs/2026-04-02-pilates-booking-miniprogram-design.md
richarjiang 05337944d8 docs: add design spec for pilates booking mini-program
Initial design document covering architecture (monorepo with pnpm),
page design, data models, business flows, and API endpoints for
the private Pilates lesson booking WeChat mini-program.
2026-04-02 10:15:25 +08:00

13 KiB
Raw Permalink Blame History

普拉提私教约课小程序 — 设计文档

概述

为单人普拉提工作室开发的微信小程序约课系统。核心功能:学员在线预约私教课、购买会员卡、查看训练记录;教练管理可约时段和会员。

约束

  • 单一工作室、单一教练,不涉及多场馆/多教练切换
  • 仅私教课,不涉及小班课
  • 微信小程序为唯一客户端

技术架构

Monorepo 结构

mp-pilates/
├── packages/
│   ├── app/              # uni-app 微信小程序前端
│   ├── server/           # NestJS 后端
│   └── shared/           # 前后端共享类型和常量
├── package.json          # 根 workspace 配置
├── pnpm-workspace.yaml
└── tsconfig.base.json

技术选型

选型
包管理 pnpm workspace
前端 uni-app + Vue 3 + TypeScript + Pinia + uni-ui
后端 NestJS + TypeScript + Prisma ORM
数据库 PostgreSQL
共享层 @mp-pilates/shared(类型、常量、枚举)

角色体系

角色 说明
MEMBER 学员(默认),约课、查看记录、购卡
ADMIN 管理员/教练,排课、管理会员、查看订单

管理员通过数据库标记,小程序内根据角色展示不同菜单入口。


页面设计

底部 Tab 导航3 个)

Tab 路径 说明
首页 /pages/home/index 品牌展示 + 快捷入口
课程预约 /pages/booking/index 私教时段 + 预约
我的 /pages/profile/index 个人中心

1. 首页

顶部:品牌 bannerLogo + 名称 + 背景图)

场馆信息

  • 场馆照片横滑轮播
  • 地址(点击跳转地图导航)+ 电话(点击拨打)

快捷入口卡片(根据用户状态智能展示):

  • 新用户 → 「预约体验课」醒目入口
  • 已有私教卡/时间卡 → 「一键约课」跳转课程预约页
  • 卡次即将用完 → 「续卡」提醒入口
  • 无卡用户 → 「购买会员卡」入口

今日/近期课程:已预约课程卡片,快速查看。

会员卡商城:卡种列表(私教次卡、月卡/季卡/年卡、体验课包),点击进入购买详情。

2. 课程预约页

顶部日期选择器:横向滚动,展示未来 7 天,标注星期几。

筛选栏(简化版):

  • 时段筛选(全部 / 上午 / 下午 / 晚上)
  • 单教练,无需老师筛选

时段列表

  • 时段卡片:时间段、剩余名额、预约按钮
  • 已约满 → 灰色置灰
  • 已预约 → 显示「已预约」+「取消」

预约流程

  1. 点击「预约」→ 检查是否有有效卡 → 无卡则引导购卡
  2. 有卡 → 确认预约弹窗(时段信息 + 扣卡信息)
  3. 确认 → 扣减卡次 / 验证时间卡有效期 → 预约成功

取消规则

  • 开课前 N 小时可免费取消N 在 StudioConfig 中配置,默认 2 小时)
  • 超时取消仍扣卡次

3. 个人中心

顶部用户卡片:头像 + 昵称 + 手机号 + 累计训练天数 / 训练次数

本月训练统计:训练次数 / 训练天数 / 训练时长

功能入口

  • 我的会员卡 → 持有卡列表、有效期、剩余次数
  • 我的预约 → 预约记录(即将上课 / 历史)
  • 个人信息 → 编辑昵称、手机号

4. 管理功能ADMIN 角色可见)

个人中心底部显示「管理中心」入口:

  • 周模板设置 → 7 天网格,每天添加多个时间段,开关启用/停用
  • 临时调整 → 日历视图,点某天关闭/新增时段
  • 会员管理 → 所有会员列表、卡状态、预约记录
  • 订单管理 → 购卡订单列表、支付状态
  • 卡种管理 → 设置卡种、价格、有效期
  • 工作室设置 → 名称、Logo、地址、电话、照片

子页面清单

页面 路径 说明
卡种详情/购买 /pages/card/detail 卡种信息 + 购买按钮
预约确认 弹窗组件 确认预约信息
我的会员卡 /pages/profile/membership 持有卡列表
我的预约 /pages/profile/bookings 预约记录
个人信息 /pages/profile/info 编辑个人信息
管理中心 /pages/admin/index 管理功能入口
周模板管理 /pages/admin/week-template 设置周课表
临时时段调整 /pages/admin/slot-adjust 按日期调整时段
会员管理 /pages/admin/members 会员列表与详情
订单管理 /pages/admin/orders 订单列表
卡种管理 /pages/admin/card-types 管理卡种
工作室设置 /pages/admin/studio 编辑工作室信息

数据模型

User用户

字段 类型 说明
id UUID 主键
openid String 微信 openid唯一
unionid String? 微信 unionid
phone String? 手机号(微信授权获取)
nickname String 昵称
avatarUrl String? 头像
role Enum(MEMBER, ADMIN) 角色,默认 MEMBER
createdAt DateTime 注册时间
updatedAt DateTime 更新时间

CardType卡种定义

字段 类型 说明
id UUID 主键
name String 卡名,如「私教 10 次卡」
type Enum(TIMES, DURATION, TRIAL) 次卡 / 时间卡 / 体验课
totalTimes Int? 总次数TIMES / TRIAL 用)
durationDays Int 有效天数
price Decimal 售价(分)
originalPrice Decimal? 原价(划线价,分)
description String? 描述
isActive Boolean 是否上架,默认 true
sortOrder Int 排序权重,默认 0
createdAt DateTime 创建时间
updatedAt DateTime 更新时间

Membership用户持有的卡

字段 类型 说明
id UUID 主键
userId UUID 关联用户
cardTypeId UUID 关联卡种
remainingTimes Int? 剩余次数(次卡用)
startDate DateTime 生效日期
expireDate DateTime 到期日期
status Enum(ACTIVE, EXPIRED, USED_UP) 状态
createdAt DateTime 创建时间
updatedAt DateTime 更新时间

WeekTemplate周模板时段

字段 类型 说明
id UUID 主键
dayOfWeek Int 星期几1=周一 ~ 7=周日)
startTime String 开始时间,如 "09:00"
endTime String 结束时间,如 "10:00"
capacity Int 容量,默认 1
isActive Boolean 是否启用,默认 true
createdAt DateTime 创建时间
updatedAt DateTime 更新时间

TimeSlot实际可约时段

字段 类型 说明
id UUID 主键
date Date 具体日期
startTime String 开始时间
endTime String 结束时间
capacity Int 容量
bookedCount Int 已预约数,默认 0
status Enum(OPEN, FULL, CLOSED) 状态
source Enum(TEMPLATE, MANUAL) 来源:模板生成 / 手动添加
templateId UUID? 关联周模板(模板生成时)
createdAt DateTime 创建时间
updatedAt DateTime 更新时间

生成逻辑:定时任务每天按周模板自动生成未来 7-14 天的 TimeSlot。教练可手动添加/关闭某天的特定时段。

Booking预约记录

字段 类型 说明
id UUID 主键
userId UUID 关联用户
timeSlotId UUID 关联时段
membershipId UUID 扣的哪张卡
status Enum(CONFIRMED, CANCELLED, COMPLETED, NO_SHOW) 状态
cancelledAt DateTime? 取消时间
createdAt DateTime 预约时间
updatedAt DateTime 更新时间

Order购卡订单

字段 类型 说明
id UUID 主键
userId UUID 关联用户
cardTypeId UUID 购买的卡种
orderNo String 订单号,唯一
amount Decimal 支付金额(分)
status Enum(PENDING, PAID, REFUNDED) 支付状态
wxTransactionId String? 微信支付单号
paidAt DateTime? 支付时间
createdAt DateTime 下单时间
updatedAt DateTime 更新时间

StudioConfig工作室配置

字段 类型 说明
id UUID 主键(仅一条记录)
name String 工作室名称
logo String? Logo URL
bannerUrl String? 背景图 URL
address String 地址
phone String 联系电话
latitude Decimal? 纬度
longitude Decimal? 经度
cancelHoursLimit Int 免费取消截止小时数,默认 2
photos Json 场馆照片 URL 列表
updatedAt DateTime 更新时间

业务流程

用户登录

小程序 wx.login() 获取 code
  → 服务端用 code 换取 openid
  → 查找/创建用户 → 签发 JWT
  → 需要手机号时,前端 getPhoneNumber 按钮授权
  → 服务端解密手机号 → 更新用户信息

预约

学员点击「预约」
  → 检查登录(未登录引导登录)
  → 检查有效会员卡
    → 无卡 → 引导购卡
    → 有卡 → 确认弹窗(时段 + 扣卡信息)
      → 服务端校验(时段可约 + 卡有效 + 无重复预约)
        → 成功 → 扣减卡次 → 预约成功
        → 失败 → 提示原因

取消预约

学员点击「取消」
  → 检查距开课时间
    → >= cancelHoursLimit → 免费取消,退还卡次
    → < cancelHoursLimit → 提示超时取消不退卡次,二次确认
  → 更新 Booking 状态 → 释放 TimeSlot 名额

购卡支付

选择卡种 → 创建 OrderPENDING
  → 调用微信支付统一下单 → 返回支付参数
  → 前端 wx.requestPayment
    → 支付成功 → 微信回调服务端 → 验签
      → 更新 Order 为 PAID → 创建 Membership
    → 支付失败 → 保持 PENDING可重新支付

时段自动生成

定时任务(每天凌晨执行)
  → 读取所有 isActive 的 WeekTemplate
  → 生成未来 7-14 天的 TimeSlot跳过已存在的日期
  → 检查过期 Membership标记 EXPIRED
  → 检查已过期 TimeSlot标记 CLOSED

API 端点

认证 /auth

方法 路径 说明
POST /auth/login 微信 code 登录,返回 JWT
POST /auth/phone 绑定手机号

用户 /user

方法 路径 说明
GET /user/profile 获取个人信息
PUT /user/profile 更新个人信息
GET /user/stats 训练统计(本月/累计)

时段 /time-slot

方法 路径 说明
GET /time-slot/available 查询可约时段(按日期范围)
GET /time-slot/:id 时段详情

预约 /booking

方法 路径 说明
POST /booking 创建预约
PUT /booking/:id/cancel 取消预约
GET /booking/my 我的预约列表
GET /booking/my/upcoming 即将上课的预约

会员卡 /membership

方法 路径 说明
GET /membership/card-types 可购买的卡种列表
GET /membership/my 我持有的卡列表

支付 /payment

方法 路径 说明
POST /payment/create-order 创建购卡订单 + 发起微信支付
POST /payment/wx-notify 微信支付回调
GET /payment/orders 我的订单列表

管理 /adminADMIN 角色)

方法 路径 说明
GET /admin/week-template 获取周模板
PUT /admin/week-template 更新周模板(全量替换)
POST /admin/time-slot/manual 手动添加时段
PUT /admin/time-slot/:id/close 关闭某个时段
GET /admin/members 会员列表
GET /admin/members/:id 会员详情
GET /admin/bookings 所有预约记录
GET /admin/card-types 卡种列表
POST /admin/card-types 创建卡种
PUT /admin/card-types/:id 更新卡种
DELETE /admin/card-types/:id 删除卡种
POST /admin/generate-slots 手动触发生成时段

工作室 /studio

方法 路径 说明
GET /studio/info 获取工作室信息
PUT /admin/studio/info 更新工作室信息

关键业务规则

  1. 预约扣卡:次卡 → remainingTimes - 1时间卡 → 仅检查 expireDate 有效性;体验课 → 同次卡逻辑
  2. 取消退还:距开课 >= cancelHoursLimit 小时 → 次卡退还次数;否则不退
  3. 时段状态bookedCount >= capacity → 自动标记 FULL
  4. 卡过期检测:定时任务每天检查,过期卡标记 EXPIRED次数用完标记 USED_UP
  5. 重复预约:同一用户不可重复预约同一时段
  6. 金额单位:所有金额以「分」存储,避免浮点精度问题

不做的事项YAGNI

  • 多场馆切换
  • 小班课
  • 多教练筛选
  • 积分系统
  • 拼团功能
  • 美团/抖音验券
  • 体测记录
  • 签到功能
  • 代金券