Web 预约模块 — CUJ 关键用户旅程

Appointment Board & Management | 更新时间: 2026-02-09

TOUCHES: appointments/board/page.tsx, appointments/realtime/page.tsx, appointments/[id]/page.tsx, appointments/create/new/page.tsx, admin/appointments/page.tsx, QuickBookingForm/index.tsx (主创建入口), QuickBookingForm/GuestForm.tsx, QuickBookingForm/ServiceForm.tsx, QuickBookingForm/ServiceTable.tsx, QuickBookingForm/AddNoteModal.tsx, QuickBookingForm/ValidationModal.tsx, AppointmentDetailModal.tsx, AppointmentDrawer.tsx, AppointmentBoardFixed.tsx
Deprecated: NewAppointmentModal.tsx(5 步向导流程)已不再使用。当前唯一的预约创建入口是 QuickBookingForm(三列布局表单)。

目录

CUJ 总览与优先级矩阵

CUJ优先级描述触发点业务价值E2E 状态
CUJ-B1 P0 查看预约看板 登录后默认页面 核心运营视图,技师排班一目了然 已覆盖
CUJ-B2 P0 快速创建预约 看板上点击时段或拖拽 核心营收路径 — 前台快速下单 部分覆盖
CUJ-B3 P0 查看/编辑预约详情 看板上点击预约卡片 前台核对服务内容、修改细节 部分覆盖
CUJ-B4 P0 预约状态流转 客人到店 → 服务中 → 待支付 → 已支付 → 日结锁定 运营效率,流程标准化,与支付/日结联动 部分覆盖
CUJ-B5 P1 实时排队看板 访问 /appointments/realtime 门店大屏展示,客户等候信息 已覆盖
CUJ-B6 P1 取消/改期预约 详情弹窗中操作 灵活调整,减少爽约 缺失

E2E 覆盖状态

测试文件覆盖的 CUJ测试场景
appointments.spec.ts B1, B3 预约列表加载、看板渲染、预约卡片
appointment-board-schedule.spec.ts B1 看板日程视图、技师列排列
booking-wizard.spec.ts B2 创建预约流程(注: 测试的是旧向导,需更新为 QuickBookingForm)
full-flow/03-appointments.spec.ts B1, B2, B3, B4 完整预约管理流程
queue-display.spec.ts B5 实时排队看板显示

CUJ-B1: 查看预约看板

P0 核心运营视图 — 按技师列展示预约,支持日期和门店筛选

用户流程

flowchart TD
    A["登录成功"] --> B["/appointments/board"]
    B --> C["加载预约数据"]
    C --> D["看板按技师列展示"]
    D --> E{"切换日期?"}
    E -->|"是"| F["日期选择器"]
    F --> C
    E -->|"否"| G{"切换门店?"}
    G -->|"是"| H["StoreSelector 下拉"]
    H --> C
    G -->|"否"| I{"操作?"}
    I -->|"点击时段"| J["打开 QuickBookingForm"]
    I -->|"点击卡片"| K["打开 AppointmentDetailModal"]

    style B fill:#2196F3,stroke:#1565C0,color:#fff
    style D fill:#4CAF50,stroke:#2E7D32,color:#fff

BDD 场景

场景 B1.1: 正常加载看板

Given 用户已登录
  And 当前门店今日有 5 个预约
When 页面加载完成
Then 看板显示所有在班技师的列
  And 每个预约卡片显示: 客户名、服务、时间
  And 卡片颜色反映预约状态
  And 卡片来源标签区分颜色 (staff_web=靛蓝, walk_in=翡翠, online=蓝, phone=琥珀, zenoti=紫)

测试: appointments.spec.ts (已有)

场景 B1.2: 切换日期

Given 用户在看板页面
When 点击日期选择器,选择明天
Then 看板刷新为明天的预约数据
  And URL 参数更新为选中日期

测试: appointment-board-schedule.spec.ts (已有)

场景 B1.3: 切换门店

Given 用户管理多家门店
When 通过 StoreSelector 切换到门店 B
Then 看板刷新为门店 B 的技师和预约
  And URL 参数 center_id 更新

测试: 缺失 — 需新增

场景 B1.4: 空状态 — 无预约

Given 选择的日期没有任何预约
When 页面加载完成
Then 显示空状态提示
  And 显示"创建预约"的快捷入口

测试: 缺失 — 需新增

CUJ-B2: 快速创建预约

P0 前台快速下单 — 通过 QuickBookingForm 三列布局创建预约

QuickBookingForm 布局

三列布局:
左栏 — Guest: 手机号搜索/新建客户、姓名、邮箱、性别、推荐来源、"across centers" 跨店标记、"Guest is a minor" 未成年标记。支持多 Guest(团体预约),通过 "+ Add Guest" 添加。
中栏 — Appointment Info: Service/Package Tab 切换、Service 下拉、Price、Stylist 下拉(含 ANY 选项)、Start 时间、Duration、End 时间、Room。点击 "Add Service" 将服务加入右栏表格。
右栏 — Service Table: 已添加的服务列表,按 Guest 分组显示,支持编辑/删除,显示小计和税费。
顶部工具栏: Appointment Category 下拉 (Checked In / Group / No Show)、Repeat、Add Note、Take Payment、Save。

用户流程

flowchart TD
    A["看板上点击时段/拖拽"] --> B["打开 QuickBookingForm"]
    B --> C["左栏: 搜索或新建 Guest"]
    C --> D["中栏: 选择 Service"]
    D --> E["自动填充 Price/Duration"]
    E --> F["选择 Stylist"]
    F --> G["选择 Start 时间"]
    G --> H["点击 Add Service"]
    H --> I["右栏: 服务出现在表格中"]
    I --> J{"添加更多服务?"}
    J -->|"是"| D
    J -->|"否"| K{"添加备注?"}
    K -->|"是"| L["点击 Add Note"]
    K -->|"否"| M["点击 Save"]
    M --> N{"时间冲突?"}
    N -->|"是"| O["ValidationModal 显示冲突"]
    O --> P{"强制创建?"}
    P -->|"是"| Q["提交预约"]
    P -->|"否"| G
    N -->|"否"| Q
    Q --> R["看板自动刷新"]

    style B fill:#2196F3,stroke:#1565C0,color:#fff
    style R fill:#4CAF50,stroke:#2E7D32,color:#fff
    style O fill:#FF9800,stroke:#E65100,color:#fff

BDD 场景

场景 B2.1: 基本创建预约

Given 用户在看板页面
When 点击某技师列的空白时段
Then QuickBookingForm 打开,Stylist 和 Start 时间已预填充
When 左栏输入手机号搜索到客户 "Jane Doe"
  And 中栏选择服务 "Gel Manicure"(自动填充 Price $45, Duration 45 min)
  And 点击 "Add Service"
Then 右栏表格显示该服务条目
When 点击 "Save"
Then 预约创建成功,看板刷新显示新卡片

测试: full-flow/03-appointments.spec.ts (部分覆盖)

场景 B2.2: 多服务预约

Given 已添加一个 "Gel Manicure" 服务
When 继续选择 "Pedicure" 服务
  And 选择不同的 Stylist 和 Start 时间
  And 点击 "Add Service"
Then 右栏表格显示 2 个服务条目
  And 总价和总时长自动计算

测试: 缺失 — 需新增

场景 B2.3: 附加服务 (Add-on Service)

待实现: QuickBookingForm 目前不支持附加服务选择。后端 API 和 ExtraServiceSelection 组件已存在(在已弃用的 NewAppointmentModal 中),需要将附加服务功能集成到 ServiceForm 中。
Given 已选择主服务 "Gel Manicure"
When 展开附加服务区域
Then 显示该服务可用的附加服务列表(如 "Nail Art", "Hand Massage")
  And 每项显示价格和时长
When 勾选 "Nail Art" (数量 1)
Then Duration 自动增加附加服务时长
  And Price 自动增加附加服务价格
When 点击 "Add Service"
Then 右栏表格显示主服务 + 附加服务明细

测试: 待实现 — 前端功能待开发

场景 B2.4: 团体预约(多 Guest)

Given 已添加 Guest "Jane Doe"
When 点击 "+ Add Guest" 添加 "John Doe"
  And 为每个 Guest 分别添加服务
Then 右栏表格按 Guest 分组显示服务
  And 可以设置 Host Guest(主付款人)
  And Appointment Category 自动设为 "Group"

测试: 缺失 — 需新增

场景 B2.5: 新客户创建

Given 搜索手机号无匹配
When 填写 First Name, Last Name(必填)
  And 点击 "Add New Guest"
Then 新客户创建并自动关联到当前预约

测试: 缺失 — 需新增

场景 B2.6: 时间冲突警告

Given Alice 在 10:00-11:00 已有预约
When 用户创建 Alice 的 10:30 预约
Then ValidationModal 显示时间冲突警告
  And 用户可选择"强制创建"或"更换时间"

测试: 缺失 — 需新增

场景 B2.7: Walk-in 快速登记

Given 客户 Walk-in 到店
When 前台创建预约,Appointment Category 选择 "Checked In"
Then 预约直接创建为 checked_in 状态
  And 在看板上立即显示

测试: booking/walk-in.spec.ts (已有)

CUJ-B3: 查看/编辑预约详情

P0 前台核对 — 查看预约详细信息并修改

BDD 场景

场景 B3.1: 查看预约详情

Given 看板上有预约卡片
When 点击卡片
Then AppointmentDetailModal 打开
  And 显示: 客户姓名、手机号、服务列表、技师、时间、状态、备注
  And 显示预约来源标签 (staff_web/walk_in/online/phone)

测试: appointments.spec.ts (已有)

场景 B3.2: 编辑预约 — 更换技师

Given 预约详情弹窗已打开
When 点击"编辑",在 QuickBookingForm 编辑模式中更换技师为 "Bob"
  And 点击"Save"
Then 预约更新成功
  And 看板上卡片移动到 Bob 的列中

测试: 缺失 — 需新增

场景 B3.3: 修改预约状态

Given 预约详情弹窗已打开
When 从状态下拉中选择新状态
Then 状态更新,卡片颜色变化
  And 详情参见 CUJ-B4 状态流转

测试: full-flow/03-appointments.spec.ts (部分覆盖)

CUJ-B4: 预约状态流转

P0 运营流程 — 完整的 10 状态生命周期,跨 B/F/C 三个模块联动

完整状态流

flowchart LR
    A["pending"] -->|"确认"| B["confirmed"]
    B -->|"客人到店"| C["checked_in"]
    C -->|"开始服务"| D["in_progress"]
    D -->|"服务完成"| E["completed"]
    E -->|"部分收款"| F["pending_payment"]
    E -->|"全额收款"| G["finished"]
    F -->|"补齐收款"| G
    G -->|"日结锁定"| H["closed"]
    A -->|"取消"| I["cancelled"]
    B -->|"取消"| I
    E -->|"取消"| I
    A -->|"未到店"| J["no_show"]
    B -->|"未到店"| J
    G -->|"部分退款"| F
    G -->|"全额退款"| E

    style A fill:#87CEEB,stroke:#1565C0,color:#333
    style B fill:#2196F3,stroke:#1565C0,color:#fff
    style C fill:#FF9800,stroke:#E65100,color:#fff
    style D fill:#9C27B0,stroke:#6A1B9A,color:#fff
    style E fill:#FFD54F,stroke:#F9A825,color:#333
    style F fill:#FFC107,stroke:#F9A825,color:#333
    style G fill:#4CAF50,stroke:#2E7D32,color:#fff
    style H fill:#37474F,stroke:#263238,color:#fff
    style I fill:#f44336,stroke:#c62828,color:#fff
    style J fill:#607D8B,stroke:#455A64,color:#fff

状态说明

状态含义触发方式关联模块
pending待确认(含原 "new" 状态)预约刚创建B2
confirmed已确认管理员确认 / 在线预约自动确认B2
checked_in已签到(客人到店)签到按钮 / 自助终端签到B4, G2
in_progress服务进行中技师开始服务B4
completed服务完成(未支付)技师完成服务→ CUJ-F1
pending_payment部分支付,仍有欠款部分收款或部分退款后→ CUJ-F1
finished已结账(全额支付)收款完成← CUJ-F1
closed已锁定(日结清点后)日结流程完成,数据不可修改← CUJ-C1
cancelled已取消管理员或客户取消B6
no_show爽约/未到店管理员标记B4
跨模块联动: completed/pending_payment → 进入 CUJ-F (支付) 的结账流程; closed ← 由 CUJ-C (日结) 触发锁定; finished 退款后回退为 pending_paymentcompleted(见 CUJ-F3 (退款))。

BDD 场景

场景 B4.1: 正向流转 — 签到到完成服务

Given 预约状态为 confirmed
When 前台点击"签到"
Then 状态变为 checked_in,卡片颜色变为橙色
When 技师点击"开始服务"
Then 状态变为 in_progress,卡片颜色变为紫色
When 技师点击"完成"
Then 状态变为 pending_payment,卡片颜色变为黄色
  And 可以进入收款流程 (→ CUJ-F1)

测试: full-flow/03-appointments.spec.ts (部分覆盖 — 缺 pending_payment 验证)

场景 B4.2: 标记爽约

Given 预约状态为 confirmed,已过预约时间
When 用户在详情弹窗中标记为 "No Show"
Then 状态变为 no_show
  And 看板上卡片显示为灰色

测试: 缺失 — 需新增

CUJ-B5: 实时排队看板

P1 门店展示 — 大屏显示当前等候和服务中的客户

BDD 场景

场景 B5.1: 实时看板展示

Given 门店有 3 位已签到等候中的客户
When 访问 /appointments/realtime
Then 页面显示等候队列: 客户姓名、等待时间、服务项目
  And 自动轮询更新(无需手动刷新)

测试: queue-display.spec.ts (已有)

CUJ-B6: 取消/改期预约

P1 灵活调整 — 取消或改期已有预约

BDD 场景

场景 B6.1: 取消预约

Given AppointmentDetailModal 已打开,预约状态为 confirmed
When 点击"取消预约"
Then 显示取消原因选择(customer_request / schedule_conflict / technician_unavailable / weather / emergency / other)
When 选择原因并确认
Then 预约状态变为 cancelled
  And 看板上该卡片消失或显示为已取消

测试: 缺失 — 需新增

场景 B6.2: 改期预约

Given AppointmentDetailModal 已打开
When 点击"编辑" → 在 QuickBookingForm 编辑模式中修改日期/时间
  And 点击 "Save"
Then 预约时间更新
  And 如果改到其他日期,卡片从当前看板日期消失

测试: 缺失 — 需新增