Files
venue-miniapp/miniprogram/docs/superpowers/specs/2026-07-12-comprehensive-venue-frontend-integration-design.md
T

580 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 综合场馆新增业务前端对接设计
> 日期:2026-07-12
> 范围:共用小程序前端 `场馆小程序/miniprogram` 中的综合场馆模块
> 对接后端:`综合场馆小程序/backend`
## 1. 背景
综合场馆后端已经完成以下新增需求:
- 将会员端可预约对象从旧的课程记录拆分为“课程模板 + 排课 `CourseSession`”,会员端以排课为操作对象。
- 新增课包/次卡套餐购买、用户持卡、适用排课查询、次卡预约及使用流水。
- 新增储值卡商品、储值卡购买订单及支付后余额充值。
- 延续已有余额、余额流水、订单、普通课程预约、包场和门票能力。
共用小程序前端仍使用旧课程接口和旧字段,且尚无综合场馆的次卡、储值卡商城和次卡预约页面。因此需要调整现有课程链路,并新增商城、次卡和个人中心相关能力。
## 2. 目标
- 将综合场馆会员端课程链路统一迁移到排课接口和 `session_id`
- 修复首页近期排课、普通课程预约列表和详情的字段兼容问题。
- 提供课包/次卡套餐浏览、购买、持卡查看、使用流水和次卡预约能力。
- 提供储值卡浏览、购买和微信支付能力。
- 在个人中心展示余额,并提供充值、我的次卡、余额流水和消费记录入口。
- 保持包场、门票、订单和冰场馆模块行为不变。
## 3. 非目标
- 不包含“场地页面按参考小程序重写/UI 对标”。
- 不实现或调整后台锁场功能。
- 不修改冰场馆页面、API、登录态或业务流程。
- 不重构门票、包场和订单为统一商品或统一预约领域模型。
- 不修改综合场馆后端的课程模板、排课、次卡或储值卡业务规则。
- 不在前端实现核销或人工取消;次卡预约的核销和取消由后台处理。
- 不在前端缓存计算余额、次卡剩余次数或排课剩余名额,以上数据以后端为准。
## 4. 方案选择
采用“独立业务模块 + 复用现有通用页面”方案:
- 保留综合场馆既有页面与 API 分层。
- 会员端课程对象统一命名为 `session`,但保留现有 `pages/comprehensive/courses` 路由目录,避免无业务价值的路由迁移。
- 新增独立的次卡和储值卡 API 模块。
- 新增商城、我的次卡、次卡可约排课和次卡预约详情页面。
- 复用现有订单支付页和订单列表,不额外建立第二套支付或消费记录系统。
- 普通课程预约与次卡预约分别建模和展示,不强行合并为一个含订单的前端对象。
该方案与现有 `modules/comprehensive` 目录结构一致,能明确区分课程模板 ID、排课 ID、普通订单预约和无订单的次卡预约,同时将改动限制在新增需求范围内。
## 5. 工程边界
综合场馆模块继续使用以下分层:
```text
src/pages/comprehensive/ 页面与交互
src/modules/comprehensive/api/ 接口请求与响应映射
src/modules/comprehensive/stores/ 登录会员状态
src/modules/comprehensive/utils/ 日期、金额和状态工具
```
新增 API 模块:
```text
src/modules/comprehensive/api/membership.js
src/modules/comprehensive/api/storedValue.js
```
新增页面:
```text
src/pages/comprehensive/mall/index.vue
src/pages/comprehensive/membership/cards.vue
src/pages/comprehensive/membership/card-detail.vue
src/pages/comprehensive/membership/available-sessions.vue
src/pages/comprehensive/membership/booking-detail.vue
```
以上页面需注册到 `src/pages.json`
### 5.1 页面职责
| 页面 | 职责 |
|---|---|
| `mall/index` | 分区展示储值卡和课包/次卡;创建购买订单并跳转支付 |
| `membership/cards` | 展示用户持有的次卡、总次数、剩余次数和状态 |
| `membership/card-detail` | 展示次卡信息、适用课程、使用流水及“去预约”入口 |
| `membership/available-sessions` | 展示用户次卡覆盖的未来排课,支持选择用于预约的次卡 |
| `membership/booking-detail` | 展示次卡预约状态、排期和所用次卡,不展示订单信息 |
### 5.2 现有页面调整
- 首页读取 `upcoming_sessions`
- 课程列表、详情和确认页使用排课接口及 `session_id`
- 我的预约增加普通课程预约与次卡预约的切换入口。
- 普通课程预约列表和详情读取 `booking.session`
- 个人中心增加余额、充值、我的次卡、余额流水和消费记录入口。
- 支付页识别储值卡订单,并强制使用微信支付。
- 我的订单继续作为消费记录页面,展示新增的会员卡和储值卡订单类型。
## 6. 排课模型与标识规则
会员端所有可预约课程均为排课 `session`
- `session.id`:排课 ID,用于排课详情、普通课程预约和次卡预约。
- `session.course_id`:课程模板 ID,仅用于展示、关联和识别课程定义,不能作为预约请求参数。
- 页面路由中的课程详情 `id` 实际表示 `session.id`
- 前端不得把 `course_id` 回退用作排课 ID。
统一排课视图模型:
```js
{
id,
course_id,
title,
cover_image,
coach_id,
coach_name,
start_at,
end_at,
capacity,
booked_count,
remaining_count,
price,
status,
status_label,
courts,
detail_html,
created_at,
updated_at
}
```
列表接口没有的详情字段使用空值填充,使列表和详情可以复用 `toSession()`,但页面不得依赖列表响应中的 `detail_html`
## 7. 接口设计
所有接口继续使用综合场馆现有请求封装,基础路径为综合场馆后端的 `/api`,响应仍由请求层解包为 `body.data`
### 7.1 排课
| 功能 | 方法与路径 | 参数 |
|---|---|---|
| 排课列表 | `GET /api/sessions` | `page``page_size``date``coach_id``court_type` |
| 排课详情 | `GET /api/sessions/{session_id}` | 排课 ID |
| 创建普通课程预约 | `POST /api/bookings/course` | `{ session_id }` |
`/api/courses` 不再使用。原普通预约请求中的 `{ course_id }` 改为 `{ session_id }`
首页 `GET /api/contents/home` 返回 `upcoming_sessions`,前端使用同一个 `toSession()` 映射。
### 7.2 普通课程预约
| 功能 | 方法与路径 |
|---|---|
| 预约列表 | `GET /api/bookings/courses` |
| 预约详情 | `GET /api/bookings/courses/{booking_id}` |
| 用户取消 | `POST /api/bookings/courses/{booking_id}/cancel` |
普通预约视图模型:
```js
{
id,
session_id,
order_id,
status,
status_label,
expire_at,
checked_in_at,
cancelled_at,
cancel_by,
cancel_reason,
can_cancel,
cancel_deadline_at,
created_at,
session,
order
}
```
前端不再读取 `booking.course_id``booking.course`
### 7.3 次卡套餐购买
| 功能 | 方法与路径 | 参数 |
|---|---|---|
| 套餐列表 | `GET /api/membership/plans` | 无 |
| 创建购买订单 | `POST /api/membership/orders` | `{ plan_id }` |
套餐视图模型包含:
```js
{
id,
name,
price,
total_count,
is_active,
courses: [{ id, title }],
created_at
}
```
创建成功后使用响应中的 `order.id` 跳转现有支付页。次卡购买订单允许余额支付和微信支付。只有后端确认订单支付成功后,用户持卡才可用于预约。
### 7.4 我的次卡与使用流水
| 功能 | 方法与路径 | 参数 |
|---|---|---|
| 我的次卡 | `GET /api/membership/cards` | `page``page_size` |
| 次卡使用流水 | `GET /api/membership/cards/{card_id}/usage-records` | `page``page_size` |
用户次卡视图模型:
```js
{
id,
plan_id,
plan_name,
total_count,
remaining_count,
status,
status_label,
order_id,
created_at
}
```
后端当前没有单张用户次卡详情接口。`card-detail` 使用从卡列表传入或重新在卡列表中查得的卡片数据,并单独请求使用流水。页面不得请求未定义的 `/membership/cards/{card_id}`
使用流水重点展示 `kind_label``count``remark``booking_id``order_id``created_at`
### 7.5 次卡可约排课与预约
| 功能 | 方法与路径 | 参数 |
|---|---|---|
| 次卡可约排课 | `GET /api/membership/available-courses` | `page``page_size` |
| 创建次卡预约 | `POST /api/membership/bookings` | `{ card_id, session_id }` |
| 次卡预约列表 | `GET /api/membership/bookings` | `page``page_size` |
| 次卡预约详情 | `GET /api/membership/bookings/{booking_id}` | 预约 ID |
`available-courses` 虽保留旧路径命名,但返回对象是排课。前端将其映射为 `availableSession`
```js
{
id,
course_id,
title,
cover_image,
coach_name,
start_at,
end_at,
capacity,
booked_count,
remaining_count,
price,
available_card_ids
}
```
其中 `id``session_id`。前端不自行根据套餐课程计算是否可约,只使用后端返回的列表及 `available_card_ids`
次卡预约视图模型:
```js
{
id,
card_id,
session_id,
status,
status_label,
checked_in_at,
cancelled_at,
cancel_reason,
created_at,
card,
session
}
```
次卡预约没有支付订单,不应补造空 `order` 对象,也不得跳转支付页。
### 7.6 储值卡
| 功能 | 方法与路径 | 参数 |
|---|---|---|
| 储值卡列表 | `GET /api/stored-value/cards` | 无 |
| 创建购买订单 | `POST /api/stored-value/orders` | `{ card_id }` |
储值卡视图模型:
```js
{
id,
name,
price,
face_value,
is_active,
created_at
}
```
页面必须区分售价 `price` 和充值到账面值 `face_value`。创建订单后使用响应中的 `order.id` 跳转现有支付页。
后端实际实现禁止储值卡订单使用余额支付。因此支付页检测到 `order.business_type === "stored_value"` 时:
- 不显示或禁用余额支付选项。
- 默认选择微信支付。
- 不调用 `/orders/{id}/pay/balance`
- 支付完成后重新查询后端余额,不在前端直接加上面值。
### 7.7 个人中心
| 展示内容 | 数据来源 |
|---|---|
| 当前余额 | `GET /api/members/balance` |
| 余额流水 | `GET /api/members/balance-records` |
| 我的次卡 | `GET /api/membership/cards` |
| 消费记录 | `GET /api/orders` |
余额流水和消费记录保持为两个概念:
- 余额流水展示余额账户的充值、消费和退款等变动。
- 消费记录复用订单列表,展示课程、包场、门票、会员卡和储值卡订单。
储值卡支付成功后,余额流水由后端产生 `kind="recharge"``kind_label="充值"` 的正数记录,前端沿用通用余额流水映射和正负金额样式。
## 8. 业务流程
### 8.1 普通课程预约
```text
排课列表或首页
→ 排课详情(session.id
→ 确认预约
→ POST /bookings/course { session_id }
→ 取得 order.id
→ 订单支付页
→ 余额支付或微信支付
→ 普通预约详情
```
### 8.2 次卡购买
```text
商城选择次卡套餐
→ POST /membership/orders { plan_id }
→ 取得 order.id
→ 订单支付页
→ 余额支付或微信支付
→ 刷新 /membership/cards
→ 我的次卡
```
### 8.3 次卡预约
```text
我的次卡详情或课程入口
→ GET /membership/available-courses
→ 选择排课
→ 确定 available_card_ids 中的 card_id
→ POST /membership/bookings { card_id, session_id }
→ 次卡预约详情
```
选择规则:
- `available_card_ids` 只有一个值时直接使用该卡。
- 有多个值时展示卡片选择器,由用户确认具体次卡。
- 从某张次卡详情进入时预选该卡,并过滤 `available_card_ids` 不包含该卡 ID 的排课。
- 预约成功只占名额,不立即减少次数。
- 后台核销后扣减次数;后台取消后释放该预约状态。
- 后端当前未提供会员自行取消次卡预约接口,前端不显示取消按钮。
### 8.4 储值卡购买
```text
商城选择储值卡
→ POST /stored-value/orders { card_id }
→ 取得 order.id
→ 订单支付页(仅微信支付)
→ 后端确认支付并按 face_value 充值
→ 重新查询余额和余额流水
```
关闭未支付储值卡订单不改变余额。
## 9. 我的预约展示
“我的预约”页面保留课程、包场和门票业务入口,并在课程预约内区分:
- 普通课程预约:来自 `/bookings/courses`,展示订单金额和订单状态,可按后端 `can_cancel` 取消。
- 次卡预约:来自 `/membership/bookings`,展示次卡名称和剩余次数,金额位置显示“次卡预约”,不展示订单操作。
两类预约分别维护请求状态和分页,不将次卡预约伪装成普通预约。切换分类时加载对应接口。
次卡预约状态包括:
- `booked`:已预约,等待到场处理。
- `checked_in`:已核销,后台已扣减次数。
- `cancelled`:已由后台取消。
## 10. 预约场地字段兼容
排课列表和排课详情返回 `courts`,可以正常展示场地。
普通预约与次卡预约响应中的嵌套 `session` 当前不包含 `courts`。本次前端实施采用以下规则:
- 预约列表和预约详情不依赖 `session.courts`,暂不展示场地名称。
- 显示标题、教练、上课时间、状态及订单或次卡信息。
- 映射函数可兼容接收未来新增的 `courts`,但不得假设该字段存在。
- 后端未来补充 `courts` 后,前端可再恢复预约详情中的场地展示。
## 11. 登录与访问控制
无需登录即可访问:
- 首页及排课列表、排课详情。
- 商城商品列表。
- 次卡套餐列表和储值卡列表。
必须登录才能执行:
- 创建普通课程预约。
- 购买次卡或储值卡。
- 使用次卡预约。
- 查看我的次卡、次卡流水、预约、余额、余额流水和订单。
受限操作发现未登录时调用现有综合场馆登录流程。登录成功后继续原操作,并保留当前选中的商品、排课和次卡;登录失败或用户取消时停留在当前页面。
401 或业务码 `40001` 继续由综合场馆请求层清理综合场馆 token,不影响冰场馆登录态。
## 12. 可用性与提交控制
### 12.1 普通预约
普通预约按钮至少满足以下条件才可用:
- `status === "published"`
- `remaining_count > 0`
- `start_at` 晚于当前时间。
最终可预约性以后端创建预约接口校验结果为准。
### 12.2 次卡预约
- 只从 `/membership/available-courses` 选择排课。
- 不在前端根据套餐课程列表推导可预约范围。
- 选中的 `card_id` 必须存在于该排课的 `available_card_ids`
### 12.3 防重复提交
以下操作设置独立的 `isSubmitting` 或等价状态,并在请求期间禁用操作按钮:
- 创建普通课程预约。
- 创建次卡购买订单。
- 创建储值卡购买订单。
- 创建次卡预约。
- 发起订单支付。
提交状态在 `finally` 中恢复。接口成功后的页面跳转只执行一次。
## 13. 支付处理
- 余额支付接口成功后可视为后端已经完成支付及业务确认。
- 微信 `uni.requestPayment` 成功后重新查询订单详情,以后端订单状态为准。
- 微信支付取消或失败时保留待支付订单,用户可从我的订单再次进入支付。
- 储值卡订单仅允许微信支付。
- 次卡购买订单允许余额支付和微信支付。
- 支付成功后不在前端直接修改余额、持卡列表或剩余次数,而是重新请求对应接口。
## 14. 错误处理
请求层继续统一展示后端 `message`。页面在以下业务错误后刷新相关数据,避免保留过期按钮状态:
- 排课已开始、已取消或未上架。
- 排课名额已满。
- 用户已存在普通预约或次卡预约。
- 次卡不可用、次数已用完或不覆盖该课程。
- 次卡套餐或储值卡已下架。
- 余额不足。
- 订单已支付、已关闭或已过期。
页面级错误状态至少区分:
- 首次加载中。
- 正常内容。
- 空数据。
- 请求失败,可重试。
- 提交中,按钮禁用。
## 15. 分页与刷新
以下列表使用现有分页约定 `page``page_size``items``total`
- 排课列表。
- 普通课程预约。
- 次卡预约。
- 我的次卡。
- 次卡使用流水。
- 余额流水。
- 消费记录。
下拉刷新时重置为第 1 页并替换列表;触底加载时追加下一页;以当前项目统一规则 `items.length < total` 判断是否还有更多数据。加载更多请求期间不得重复发起同一页请求。
次卡套餐列表和储值卡列表目前不分页,直接加载全部上架商品。
## 16. 验收标准
### 16.1 排课链路
- 首页读取并展示 `upcoming_sessions`
- 排课列表筛选继续支持日期、教练和场地类型。
- 排课列表、详情和普通预约全程使用同一个 `session.id`
- 普通预约请求发送 `{ session_id }`,成功后进入支付页。
- 普通预约列表和详情正确读取 `booking.session`,不再访问旧 `booking.course`
### 16.2 次卡购买与持卡
- 商城可以展示上架的次卡套餐、价格、总次数和适用课程。
- 创建次卡订单后可以选择余额或微信支付。
- 支付成功后可以在“我的次卡”看到卡片及正确剩余次数。
- 次卡详情可以查看使用流水,并进入可约排课页面。
### 16.3 次卡预约
- 可约排课只显示后端返回的、用户次卡覆盖的未来排课。
- 一个排课有多张可用次卡时可以选择具体卡。
- 次卡预约请求发送 `{ card_id, session_id }`
- 次卡预约成功后不创建前端支付流程,也不立即减少次数。
- 后台核销后刷新页面显示 `checked_in`,卡片剩余次数同步减少。
- 后台取消后刷新页面显示 `cancelled`
- 前端不提供次卡预约取消按钮。
### 16.4 储值卡
- 商城正确区分售价和到账面值。
- 创建储值卡订单后支付页只允许微信支付。
- 支付成功后余额增加 `face_value`,而不是 `price`
- 余额流水出现“充值”记录。
- 关闭未支付订单不会改变余额。
### 16.5 个人中心和消费记录
- 个人中心展示后端最新余额。
- 可以进入充值、我的次卡、余额流水和消费记录。
- 订单列表正确显示“会员卡”和“储值卡”业务类型。
- 余额流水与订单消费记录分开呈现。
### 16.6 回归
- 包场预约流程不受影响。
- 门票购买、持有和核销展示不受影响。
- 已有订单详情、关闭订单、余额支付和微信支付流程不受影响。
- 冰场馆模块的页面、接口、token 和状态不受影响。
## 17. 实施顺序
1. 修改排课 API、首页字段和普通预约映射,恢复现有课程链路。
2. 新增次卡 API 模块、商城套餐购买和我的次卡。
3. 新增次卡可约排课、创建预约、预约列表和详情。
4. 新增储值卡 API、商城展示和购买流程。
5. 修改支付页,落实储值卡仅微信支付。
6. 完善个人中心余额、充值、次卡、流水和消费记录入口。
7. 完成分页、刷新、错误状态和全链路回归验证。
## 18. 后端契约依赖
本设计以当前综合场馆后端实际实现为准,依赖以下既有行为:
- `/api/sessions` 返回会员端排课。
- 普通预约接收 `session_id`,预约响应嵌套字段名为 `session`
- 次卡套餐绑定课程模板,但可约接口返回模板覆盖的具体排课。
- 次卡预约时占用名额,后台核销时扣减次数。
- 储值卡订单禁止余额支付,微信支付成功后按面值充值。
- 余额、余额流水和订单状态均可在支付或后台操作后重新查询。
普通预约和次卡预约的嵌套 `session` 暂无 `courts` 是已知契约限制,本次通过不依赖该字段完成前端对接,不阻塞实施。