# 团课预约场景测试数据文档

## 概述

本文档描述了团课预约功能的测试数据，覆盖了各种预约场景，用于验证系统在不同业务条件下的正确性和健壮性。

---

## 一、测试数据概览

| 数据类别 | 数量 | 说明 |
|---------|------|------|
| 测试会员 | 5个 | 覆盖不同会员卡状态 |
| 会员卡类型 | 8种 | 次卡/时长卡/储值卡 |
| 会员卡记录 | 5条 | 会员持有的实际卡 |
| 团课预约记录 | 4条 | 不同状态的预约 |
| 交易流水 | 27条 | 完整的消费记录 |

---

## 二、会员测试数据

### 2.1 会员列表

| 会员ID | 会员编号 | 昵称 | 手机号 | 性别 | 会员卡状态 |
|--------|---------|------|--------|------|-----------|
| 1001 | MEM202606001 | 新用户小张 | 13800138001 | 男 | 无会员卡 |
| 1002 | MEM202606002 | 额度耗尽用户 | 13800138002 | 女 | 次卡已用完 |
| 1003 | MEM202606003 | 额度充足用户 | 13800138003 | 男 | 次卡剩余8次 |
| 1004 | MEM202606004 | 过期卡用户 | 13800138004 | 女 | 月卡已过期 |
| 1005 | MEM202606005 | 多卡用户小李 | 13800138005 | 男 | 次卡+储值卡 |

### 2.2 会员详情

**会员1001 - 新用户小张**
- 状态：新注册用户，尚未购买任何会员卡
- 用途：测试无会员卡情况下的预约行为

**会员1002 - 额度耗尽用户**
- 状态：持有10次团课卡，已全部用完
- 用途：测试会员卡额度耗尽时的预约行为

**会员1003 - 额度充足用户**
- 状态：持有20次团课卡，剩余8次
- 用途：测试正常预约流程

**会员1004 - 过期卡用户**
- 状态：持有月卡，已于2026-05-31过期
- 用途：测试过期会员卡的预约行为

**会员1005 - 多卡用户小李**
- 状态：持有50次团课卡(剩余45次) + 500元储值卡(剩余350元)
- 用途：测试多会员卡用户选择支付方式的场景

---

## 三、会员卡类型数据

### 3.1 次卡类型

| 卡类型ID | 卡名称 | 类型 | 价格 | 有效天数 | 总次数 | 状态 |
|---------|-------|------|------|---------|-------|------|
| 101 | 10次团课卡 | COUNT_CARD | 299.00 | 90 | 10 | 上架 |
| 102 | 20次团课卡 | COUNT_CARD | 499.00 | 180 | 20 | 上架 |
| 103 | 50次团课卡 | COUNT_CARD | 999.00 | 365 | 50 | 上架 |

### 3.2 时长卡类型

| 卡类型ID | 卡名称 | 类型 | 价格 | 有效天数 | 状态 |
|---------|-------|------|------|---------|------|
| 201 | 月卡 | TIME_CARD | 399.00 | 30 | 上架 |
| 202 | 季卡 | TIME_CARD | 899.00 | 90 | 上架 |
| 203 | 年卡 | TIME_CARD | 2999.00 | 365 | 上架 |

### 3.3 储值卡类型

| 卡类型ID | 卡名称 | 类型 | 价格 | 面额 | 有效天数 | 状态 |
|---------|-------|------|------|------|---------|------|
| 301 | 500元储值卡 | STORED_VALUE_CARD | 500.00 | 500.00 | 365 | 上架 |
| 302 | 1000元储值卡 | STORED_VALUE_CARD | 1000.00 | 1000.00 | 365 | 上架 |

---

## 四、会员卡记录数据（会员持有的卡）

| 记录ID | 会员ID | 卡类型ID | 状态 | 剩余次数 | 剩余金额 | 到期时间 | 购买时间 |
|-------|--------|---------|------|---------|---------|---------|---------|
| 2001 | 1002 | 101 | USED_UP | 0 | 0.00 | 2026-09-01 | 2026-06-01 |
| 2002 | 1003 | 102 | ACTIVE | 8 | 0.00 | 2026-12-01 | 2026-06-01 |
| 2003 | 1004 | 201 | EXPIRED | 0 | 0.00 | 2026-05-31 | 2026-05-01 |
| 2004 | 1005 | 103 | ACTIVE | 45 | 0.00 | 2027-06-01 | 2026-06-01 |
| 2005 | 1005 | 301 | ACTIVE | 0 | 350.00 | 2027-06-01 | 2026-05-15 |

---

## 五、团课预约记录数据

| 预约ID | 课程ID | 会员ID | 会员卡记录ID | 预约时间 | 状态 | 课程名称 |
|-------|-------|--------|-------------|---------|------|---------|
| 3001 | 1 | 1003 | 2002 | 2026-06-01 15:30 | 已预约(0) | 极速燃脂单车 |
| 3002 | 2 | 1003 | 2002 | 2026-06-01 10:00 | 已取消(1) | 清晨流瑜伽 |
| 3003 | 6 | 1003 | 2002 | 2026-05-28 19:00 | 已出席(2) | 蜜桃臀塑造 |
| 3004 | 7 | 1002 | 2001 | 2026-05-30 10:00 | 缺席(3) | 午间冥想放松 |

### 状态说明

| 状态码 | 状态名称 | 说明 |
|-------|---------|------|
| 0 | 已预约 | 用户已成功预约，等待上课 |
| 1 | 已取消 | 用户主动取消预约 |
| 2 | 已出席 | 用户已按时参加课程 |
| 3 | 缺席 | 用户预约后未出席 |

---

## 六、测试场景详解

### 场景1：新注册用户无会员卡预约

**测试目标**：验证系统对无会员卡用户的预约拦截

**测试数据**：
- 用户ID：1001（新用户小张）
- 目标课程：任意课程

**预期结果**：
- 返回错误信息："未绑定会员卡，请先购买会员卡"
- HTTP状态码：400 Bad Request

---

### 场景2：会员卡额度耗尽预约

**测试目标**：验证系统对额度耗尽会员卡的预约拦截

**测试数据**：
- 用户ID：1002（额度耗尽用户）
- 会员卡记录ID：2001
- 剩余次数：0

**预期结果**：
- 返回错误信息："会员卡余额不足"
- HTTP状态码：400 Bad Request

---

### 场景3：会员卡额度充足预约

**测试目标**：验证正常预约流程

**测试数据**：
- 用户ID：1003（额度充足用户）
- 会员卡记录ID：2002
- 剩余次数：8
- 目标课程：课程ID=1（极速燃脂单车）

**预期结果**：
- 预约成功
- 会员卡剩余次数减1（变为7次）
- 生成预约记录
- HTTP状态码：200 OK

---

### 场景4：会员卡已过期预约

**测试目标**：验证系统对过期会员卡的预约拦截

**测试数据**：
- 用户ID：1004（过期卡用户）
- 会员卡记录ID：2003
- 状态：EXPIRED
- 到期时间：2026-05-31

**预期结果**：
- 返回错误信息："会员卡已过期"
- HTTP状态码：400 Bad Request

---

### 场景5：课程已满员预约---------------

**测试目标**：验证系统对满员课程的预约拦截

**测试数据**：
- 课程ID：3（燃脂搏击）
- 当前人数：20/20

**预期结果**：
- 返回错误信息："课程已满员"
- HTTP状态码：400 Bad Request

---

### 场景6：课程已取消预约

**测试目标**：验证系统对已取消课程的预约拦截

**测试数据**：
- 课程ID：5（周末冥想修复）
- 状态：1（已取消）

**预期结果**：
- 返回错误信息："课程已取消"
- HTTP状态码：400 Bad Request

---

### 场景7：课程已结束预约

**测试目标**：验证系统对已结束课程的预约拦截

**测试数据**：
- 课程ID：6（蜜桃臀塑造）或 7（午间冥想放松）
- 状态：2（已结束）

**预期结果**：
- 返回错误信息："课程已结束"
- HTTP状态码：400 Bad Request

---

### 场景8：超出预约时间预约

**测试目标**：验证系统对预约时间窗口的控制

**测试数据**：
- 课程ID：4（哈他瑜伽）
- 开始时间：2026-06-01 15:20:00
- 测试时间：课程开始前30分钟内

**预期结果**：
- 返回错误信息："已过预约时间"
- HTTP状态码：400 Bad Request

---

### 场景9：重复预约同一课程

**测试目标**：验证系统对重复预约的拦截

**测试数据**：
- 用户ID：1003（额度充足用户）
- 课程ID：1（极速燃脂单车）
- 已有预约记录：booking_id=3001

**预期结果**：
- 返回错误信息："已预约该课程"
- HTTP状态码：400 Bad Request

---

### 场景10：多会员卡用户选择支付方式

**测试目标**：验证多卡用户的支付方式选择功能

**测试数据**：
- 用户ID：1005（多卡用户小李）
- 可用卡1：次卡（记录ID=2004，剩余45次）
- 可用卡2：储值卡（记录ID=2005，剩余350元）

**预期结果**：
- 系统列出用户所有可用会员卡
- 用户可选择使用次卡或储值卡支付
- 根据选择的卡类型扣减相应额度

---

## 七、交易流水说明

### 7.1 会员1002（额度耗尽用户）消费记录

| 操作顺序 | 操作类型 | 变动次数 | 剩余次数 | 备注 |
|---------|---------|---------|---------|------|
| 1 | PURCHASE | +10 | 10 | 购买10次团课卡 |
| 2-11 | DEDUCT | -1×10 | 0 | 10次课程消费 |

### 7.2 会员1003（额度充足用户）消费记录

| 操作顺序 | 操作类型 | 变动次数 | 剩余次数 | 备注 |
|---------|---------|---------|---------|------|
| 1 | PURCHASE | +20 | 20 | 购买20次团课卡 |
| 2-12 | DEDUCT | -12 | 8 | 12次课程消费 |

### 7.3 会员1005（多卡用户）消费记录

| 会员卡记录ID | 操作类型 | 变动次数 | 变动金额 | 备注 |
|-------------|---------|---------|---------|------|
| 2004 | PURCHASE | +50 | 0 | 购买50次团课卡 |
| 2004 | DEDUCT | -5 | 0 | 预约5节课程 |
| 2005 | PURCHASE | 0 | +500 | 购买500元储值卡 |
| 2005 | DEDUCT | 0 | -150 | 私教课消费150元 |

---

## 八、API测试示例

### 8.1 预约接口

```http
POST /api/group-course/bookings
Content-Type: application/json

{
  "courseId": 1,
  "memberCardRecordId": 2002
}
```

### 8.2 取消预约接口

```http
PUT /api/group-course/bookings/{bookingId}/cancel
Content-Type: application/json

{
  "reason": "临时有事无法参加"
}
```

### 8.3 查询用户预约列表

```http
GET /api/group-course/bookings?memberId=1003
```

---

## 九、测试数据对应关系图

```
会员表 (member_user)
    │
    ├── 1001 ──→ (无会员卡)
    ├── 1002 ──→ 会员卡记录 2001 (USED_UP)
    ├── 1003 ──→ 会员卡记录 2002 (ACTIVE, 8次)
    ├── 1004 ──→ 会员卡记录 2003 (EXPIRED)
    └── 1005 ──→ 会员卡记录 2004 (ACTIVE, 45次)
              └── 会员卡记录 2005 (ACTIVE, 350元)
                          │
                          ↓
                  预约记录表 (group_course_booking)
                          │
                          ├── 3001: 已预约
                          ├── 3002: 已取消
                          ├── 3003: 已出席
                          └── 3004: 缺席
```

---

## 十、数据初始化

测试数据通过 Flyway 迁移脚本 `V10__Insert_Booking_Test_Data.sql` 初始化，需在数据库初始化时执行。

**执行顺序**：
1. V1__Create_all_tables.sql - 创建基础表结构
2. V2__Insert_initial_data.sql - 插入初始数据
3. V6__Create_GroupCourse_table.sql - 创建团课表
4. V7__Insert_GroupCourse_test_data.sql - 插入团课测试数据
5. V8__Create_Member_And_MemberCard.sql - 创建会员和会员卡表
6. V9__Add_GroupCourse_Booking_Snapshot_Fields.sql - 添加冗余字段
7. V10__Insert_Booking_Test_Data.sql - 插入预约测试数据（本文件）

---

**文档版本**: v1.1  
**创建时间**: 2026-06-02  
**更新时间**: 2026-06-02  
**适用场景**: 团课预约功能测试

---

## 十一、重要说明

### 11.1 字段说明

`member_card_record` 表包含两个ID字段：
- `id` - 数据库主键（BIGSERIAL PRIMARY KEY）
- `member_card_record_id` - 业务ID（BIGSERIAL），代码中使用此字段作为查询和更新条件

### 11.2 测试数据修复记录

**v1.1 修复内容**：
- 修复了 `member_card_record` 表测试数据缺失 `member_card_record_id` 字段的问题
- 原问题：测试数据只插入了 `id` 字段，导致 `member_card_record_id` 为 NULL
- 影响：代码使用 `member_card_record_id` 作为WHERE条件时无法匹配到记录，导致扣减权益失败
- 解决：在INSERT语句中添加 `member_card_record_id` 字段，值与 `id` 相同

**修复前**：
```sql
INSERT INTO member_card_record (id, member_id, member_card_id, ...) VALUES
(2001, 1002, 101, ...);
```

**修复后**：
```sql
INSERT INTO member_card_record (id, member_card_record_id, member_id, member_card_id, ...) VALUES
(2001, 2001, 1002, 101, ...);
```
