Product Design Methodology — CUJ 四步走

从用户旅程到 E2E 测试 | Celoria Platform

Section 1: 方法论概览

核心原则: 每层为下一层提供输入,不是机械1:1转换。设计与工程协同,从"为什么"到"怎么做"。
graph TD A["Layer 1: CUJ - Critical User Journey"] --> B["Layer 2: User Flow - 流程图"] B --> C["Layer 3: BDD Scenarios - Given-When-Then"] C --> D["Layer 4: E2E Test Code - Automated Testing"] A1["识别什么最重要 - 产品/UX 视角"] -.-> A B1["定义怎么走 - 设计/技术视角"] -.-> B C1["定义什么算对 - 业务与工程桥梁"] -.-> C D1["自动验证 - integration_test/Playwright"] -.-> D style A fill:#0f3460,stroke:#00d4ff,color:#fff style B fill:#0f3460,stroke:#00d4ff,color:#fff style C fill:#0f3460,stroke:#00d4ff,color:#fff style D fill:#0f3460,stroke:#00d4ff,color:#fff style A1 fill:#1e2a47,stroke:#666,color:#aaa style B1 fill:#1e2a47,stroke:#666,color:#aaa style C1 fill:#1e2a47,stroke:#666,color:#aaa style D1 fill:#1e2a47,stroke:#666,color:#aaa

四层流水线 (4-Layer Pipeline)

  1. CUJ (Critical User Journey) — 识别"什么最重要" (产品/UX 视角)
  2. User Flow (流程图) — 定义"怎么走" (设计/技术视角)
  3. BDD Scenarios (Given-When-Then) — 定义"什么算对" (业务↔工程桥梁)
  4. E2E Test Code — 自动验证 (integration_test / Playwright)

Section 2: 适用范围

App Pages CUJs E2E Framework Doc Location
Guest App 25 7 Flutter integration_test product-design/guest-app-cuj.html
Employee App 24 9 Flutter integration_test product-design/employee-app-cuj.html
Kiosk App 8 3 Flutter integration_test product-design/kiosk-app-cuj.html
Web App 134 pages + 37 modals TBD (按模块拆分) Playwright notes/product-design/web-*.html

Section 3: CUJ 优先级体系

优先级 定义 示例 测试覆盖要求
P0 核心收入/安全路径,必须100%覆盖 登录、预约、支付、打卡 100% E2E 覆盖
P1 高频使用路径,应该覆盖 查看排班、预约历史、账户设置 应该覆盖 (80%+)
P2 辅助功能,按资源覆盖 语言切换、主题设置、通知设置 按资源覆盖 (50%+)
P3 边缘场景,可选 错误恢复、离线提示、版本更新 可选 (best effort)
优先级原则: 聚焦 P0/P1,确保核心体验可靠。P2/P3 根据团队资源和用户反馈动态调整。

Section 4: 各层详解

Layer 1: CUJ (Critical User Journey)

关键用户旅程,定义用户完成核心任务的完整路径。

元素 说明 示例
触发点 用户为什么开始这个旅程 客人想预约下周二的美甲服务
阶段 旅程中的主要步骤 发现 → 选择服务 → 选技师 → 选时间 → 确认 → 完成
情感曲线 用户在每个阶段的满意度 选服务(焦虑)→ 看到可用时段(安心)→ 确认(满足)
触点 用户与系统交互的界面 App 主页、服务列表页、技师选择页、日历页
痛点 当前体验中的摩擦 技师头像加载慢、时间段选择不直观
机会点 优化方向 预加载技师图片、增加时间段推荐

Layer 2: User Flow (流程图)

将 CUJ 转化为页面级流程图,定义页面跳转和决策点。

graph LR A[主页] --> B[选择服务] B --> C[选择技师] C --> D{任意技师?} D -->|是| E[选择时间] D -->|否| F[查看技师可用性] F --> E E --> G[填写联系信息] G --> H[确认预约] H --> I[预约成功页] style A fill:#0f3460,stroke:#00d4ff style I fill:#00d4ff,stroke:#00d4ff,color:#1a1a2e style D fill:#1e2a47,stroke:#ffa502

Layer 3: BDD Scenarios (Given-When-Then)

用业务语言描述测试场景,连接产品需求和技术实现。

# Scenario: 客人成功预约服务

Given 用户已登录 Guest App
  And 用户在服务选择页
  And "Manicure" 服务可用

When 用户点击 "Manicure"
  And 用户选择 "Any Technician"
  And 用户选择明天下午 2:00 PM 的时段
  And 用户填写联系信息(姓名、电话)
  And 用户点击 "Confirm Booking"

Then 系统应显示 "Booking Successful" 消息
  And 用户应看到预约确认页
  And 预约应出现在 "My Appointments" 列表中

Layer 4: E2E Test Code

将 BDD 场景转化为可执行的自动化测试代码。

// Flutter Integration Test 示例
testWidgets('CUJ1: Guest books a manicure service', (tester) async {
  // Given: 用户已登录
  await _login(tester, 'guest@example.com', 'password123');
  await tester.pumpAndSettle();

  // When: 选择服务
  await tester.tap(find.text('Manicure'));
  await tester.pumpAndSettle();

  // When: 选择技师和时间
  await tester.tap(find.text('Any Technician'));
  await tester.pumpAndSettle();

  await tester.tap(find.byType(TimeSlotCard).first);
  await tester.pumpAndSettle();

  // When: 填写信息并确认
  await tester.enterText(find.byType(TextField).first, 'John Doe');
  await tester.enterText(find.byType(TextField).last, '555-0123');
  await tester.tap(find.text('Confirm Booking'));
  await tester.pumpAndSettle();

  // Then: 验证成功
  expect(find.text('Booking Successful'), findsOneWidget);
  expect(find.byType(SuccessPage), findsOneWidget);
});

Section 5: 轻量化执行方案

Lean Execution Principle: 聚焦高价值输出,避免过度设计。文档即代码,代码即文档。
Step Output Format Location 工时估算
1 梳理 CUJ HTML 表格 notes/product-design/*.html 2-4 小时
2 画 User Flow Mermaid 流程图 同上(内嵌在 HTML 中) 1-2 小时
3 写 BDD Scenarios Given-When-Then (Markdown) 同上(每条 CUJ 2-3 个场景) 2-3 小时
4 实现 E2E 测试 Dart / TypeScript 代码 integration_test/tests/e2e/ 8-16 小时

✅ 建议做的事

❌ 不建议做的事

Section 6: 概念辨析

概念 形式 关注点 是否需要
Storyboard 视觉插画 + 场景描述 情感体验、场景氛围、视觉风格 ❌ 不需要 (时间成本高)
User Flow 流程图 (Mermaid) 页面跳转、决策分支、操作步骤 ✅ 需要 (Layer 2 核心)
Journey Map 横向时间轴 + 情感曲线 用户目标、痛点、触点、情感波动 ✅ 需要 (Layer 1 核心)
BDD Scenario Given-When-Then 文本 业务逻辑、验收标准、测试用例 ✅ 需要 (Layer 3 核心)
关键区别:
  • Storyboard 是给投资人/客户看的"电影预告片",适合早期概念验证
  • Journey Map 是给产品/设计团队用的"战略地图",识别优化机会
  • User Flow 是给开发团队用的"施工图",定义技术实现路径
  • BDD Scenario 是给测试/QA 团队用的"验收清单",确保交付质量

Section 7: Web 管理端 — 按模块拆分 CUJ

核心决策: Mobile App 每个 App 一个 CUJ 文档(流程单一、角色单一);Web 管理端按业务模块拆分,每个模块一个 CUJ 文档(流程复杂、多角色、非线性)。

为什么要拆分

建议的拆分方案

CUJ 文档 覆盖模块 Pages Modals 预估 CUJ 数
web-auth-cuj.html 登录 + 会话管理 + 租户选择 2 0 2-3
web-appointments-cuj.html 预约看板 + 新建/编辑/取消 + Walk-in + Checkout 8 8 4-5
web-day-end-closeout-cuj.html 日结关账 5 步 + 收银台 + 解锁 1 4 2-3
web-employees-cuj.html 员工管理 + 排班 + 考勤 + Payroll 10 7 4-5
web-guests-cuj.html 客户管理 + 标签 + 档案 + 黑名单 6 4 2-3
web-payments-cuj.html 支付 + 退款 + 拆单 + 终端 + 发票 4 5 3-4
web-booking-cuj.html 公共预约(Web Booking)+ Check-in + Terminal 14 2 3-4
web-reports-cuj.html 报表 + 分析 + 导出 12 1 2-3
web-marketing-cuj.html 营销 + 实验 + 策略 + 模板 + 礼品卡 22 1 3-4
web-settings-cuj.html 系统设置 + 权限 + 门店 + 服务 + 同步 + Platform 18 5 3-4
合计 ~97 核心页 37 ~28-38
注意: 剩余 ~37 个页面(developer tools, storybook, monitoring, legal, test 页等)属于内部工具或辅助页面,不需要 CUJ 覆盖,但需要在追溯矩阵中标注为 "Internal/Exempt"。

Section 8: Page↔CUJ 追溯矩阵 (Traceability Matrix)

问题: CUJ 是按"用户目标"组织的,不是按"页面"组织的。一个 CUJ 可能穿越 5 个页面 + 2 个弹窗;一个页面可能被 3 个 CUJ 触及。如何保证每个页面和弹窗都被至少一个 CUJ 覆盖

方法论: 四层追溯

graph TD R["Step 1: Page Registry — 自动生成"] --> C["Step 2: CUJ Documents — 人工定义"] C --> M["Step 3: Traceability Matrix — 自动校验"] M --> G["Step 4: Gap Report — 输出孤儿页面"] R1["脚本扫描 page.tsx + *Modal*.tsx 生成 JSON"] -.-> R C1["每个 CUJ 的 User Flow 标注触及的页面/弹窗"] -.-> C M1["交叉对比: Registry 中哪些条目没被任何 CUJ 引用"] -.-> M G1["未覆盖列表 + 缺失 E2E 测试 + 分类建议"] -.-> G style R fill:#0f3460,stroke:#00d4ff,color:#fff style C fill:#0f3460,stroke:#00d4ff,color:#fff style M fill:#0f3460,stroke:#00d4ff,color:#fff style G fill:#0f3460,stroke:#00d4ff,color:#fff style R1 fill:#1e2a47,stroke:#666,color:#aaa style C1 fill:#1e2a47,stroke:#666,color:#aaa style M1 fill:#1e2a47,stroke:#666,color:#aaa style G1 fill:#1e2a47,stroke:#666,color:#aaa

Step 1: Page Registry(自动)

脚本自动扫描所有 page.tsx*Modal*.tsx / *Dialog*.tsx,生成注册表。

// page-registry.json 格式
{
  "pages": [
    {
      "id": "admin-appointments",
      "route": "/admin/appointments",
      "file": "src/app/[locale]/[tenant]/admin/appointments/page.tsx",
      "module": "appointments",
      "cuj_refs": [],           // ← 由 Step 3 填充
      "e2e_refs": [],           // ← 由 Step 3 填充
      "status": "uncovered"     // uncovered | covered | exempt
    }
  ],
  "modals": [
    {
      "id": "new-appointment-modal",
      "file": "src/components/Appointment/NewAppointmentModal.tsx",
      "module": "appointments",
      "cuj_refs": [],
      "e2e_refs": [],
      "status": "uncovered"
    }
  ]
}

Step 2: CUJ 文档中标注触及的页面(人工)

每个 CUJ 文档的 User Flow 和 BDD Scenarios 中,用 <!-- TOUCHES: page-id, modal-id --> 注释标注触及的页面和弹窗。

<!-- ======= CUJ-1: 日结关账完整流程 ======= -->
<!-- TOUCHES: admin-day-end-closeout, day-end-closeout-modal,
              currency-count-modal, close-register-modal,
              unlock-closeout-modal -->

<h3>User Flow</h3>
<div class="mermaid">
  flowchart TD
    A[日结主页 admin-day-end-closeout] --> B[点击开始日结]
    B --> C[DayEndCloseoutModal 5步流程]
    ...
</div>

<h3>BDD Scenarios</h3>
<!-- ... Given/When/Then ... -->
<p class="test-file">E2E: day-end-closeout.spec.ts</p>

Step 3: Traceability Matrix(自动校验)

校验脚本读取 Page Registry + 所有 CUJ 文档中的 TOUCHES 注释,输出追溯矩阵。

// 校验脚本: scripts/check-cuj-coverage.js
//
// 输入:
//   1. page-registry.json(自动生成)
//   2. notes/product-design/web-*.html(人工标注 TOUCHES)
//   3. frontend/web_app/tests/e2e/*.spec.ts(E2E 测试文件)
//
// 输出:
//   ✅ admin-appointments     → W-APT-CUJ-1, W-APT-CUJ-2  → appointments-board.spec.ts
//   ✅ new-appointment-modal  → W-APT-CUJ-1               → appointments-board.spec.ts
//   ❌ admin-gift-cards       → (NOT COVERED)              → (NO E2E)
//   ⚠️ admin-storybook        → EXEMPT (internal tool)
//   ❌ refund-modal           → (NOT COVERED)              → (NO E2E)

Step 4: Gap Report(输出)

Gap Report 将未覆盖的页面/弹窗分为三类:

分类 含义 处理方式
❌ NOT COVERED 面向用户的页面/弹窗,但没有任何 CUJ 引用 必须创建或扩展 CUJ 来覆盖
⚠️ NO E2E CUJ 引用了,但没有对应的 E2E 测试文件 需要编写 E2E 测试
✅ EXEMPT 内部工具、开发页面、法律页面等 标注为 exempt,不计入覆盖率

Exempt(豁免)页面的判定标准

实施节奏

阶段 交付物 工时
Phase 1 Page Registry 生成脚本 + 首个 CUJ 文档(Day-End Closeout) 4-6 小时
Phase 2 剩余 9 个模块的 CUJ 文档(每个 3-4 小时) 27-36 小时
Phase 3 追溯矩阵校验脚本 + 首次 Gap Report 4-6 小时
持续 每次新增页面/弹窗时更新 CUJ + 运行校验脚本 ~30 分钟/次
自动化卡点建议: 将 check-cuj-coverage.js 集成到 CI/CD 管道中。当 PR 新增了 page.tsx*Modal*.tsx 但没有更新任何 CUJ 文档时,CI 发出 warning(不阻塞,但可见)。

总结

核心价值: 将用户体验设计与自动化测试连接起来,确保产品"做对的事"(CUJ)和"把事做对"(E2E)。

实施建议: 从 P0 CUJ 开始,每周迭代 1-2 条,3-6 个月内覆盖核心路径。保持文档简洁实用,避免过度设计。

覆盖保障: 通过 Page↔CUJ 追溯矩阵(Section 8)确保每个面向用户的页面和弹窗都被至少一个 CUJ 覆盖,防止"孤儿页面"遗漏测试。

相关文档: