Jelajahi Sumber

docs(e2e): 创建 Story 9.4 - 回访记录管理测试

创建回访记录管理测试的完整 Story 文档,包含:
- Story 和验收标准(4个测试场景)
- 完整的任务分解(4个主任务,15个子任务)
- 回访管理组件 DOM 结构分析
- Page Object 方法设计(addVisit, editVisit, deleteVisit 等)
- 完整的测试用例模板(8个测试场景)
- Previous Story Intelligence(从 Story 9.1、9.2、9.3 学到的经验)

状态: ready-for-dev

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 1 Minggu lalu
induk
melakukan
77b20e84fc

+ 622 - 0
_bmad-output/implementation-artifacts/9-4-visit-tests.md

@@ -0,0 +1,622 @@
+# Story 9.4: 回访记录管理测试
+
+Status: ready-for-dev
+
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
+
+## Story
+
+作为测试开发者,
+我想要编写回访记录管理的测试,
+以便验证回访记录的创建、查看、编辑功能。
+
+## Acceptance Criteria
+
+**Given** 残疾人管理 Page Object 已存在
+**When** 编写回访记录测试
+**Then** 包含以下测试场景:
+
+1. **创建回访记录**
+   - 创建电话回访记录
+   - 创建上门回访记录
+   - 验证记录保存成功
+
+2. **查看回访历史**
+   - 查看残疾人的所有回访记录
+   - 验证记录按时间排序
+
+3. **编辑回访记录**
+   - 修改回访内容
+   - 验证修改后内容更新
+
+4. **回访记录状态管理**
+   - 标记回访为已完成
+   - 验证状态更新
+
+## Tasks / Subtasks
+
+- [ ] **Task 1: 分析回访管理功能的 DOM 结构** (AC: #1, #2, #3, #4)
+  - [ ] Subtask 1.1: 在残疾人管理页面中定位回访管理区域
+  - [ ] Subtask 1.2: 分析添加回访按钮和表单结构
+  - [ ] Subtask 1.3: 分析回访列表展示结构
+  - [ ] Subtask 1.4: 分析编辑和删除按钮的选择器
+
+- [ ] **Task 2: 创建回访记录测试文件** (AC: #1, #2, #3, #4)
+  - [ ] Subtask 2.1: 创建 `web/tests/e2e/specs/admin/disability-person-visit.spec.ts`
+  - [ ] Subtask 2.2: 编写创建回访记录测试
+  - [ ] Subtask 2.3: 编写查看回访历史测试
+  - [ ] Subtask 2.4: 编写编辑回访记录测试
+  - [ ] Subtask 2.5: 编写回访记录状态管理测试
+
+- [ ] **Task 3: 更新 Page Object** (AC: #1, #2, #3, #4)
+  - [ ] Subtask 3.1: 添加回访记录管理相关方法到 `DisabilityPersonManagementPage`
+  - [ ] Subtask 3.2: 完善现有的 `addVisit()` 方法
+  - [ ] Subtask 3.3: 实现 `editVisit()` 方法
+  - [ ] Subtask 3.4: 实现 `deleteVisit()` 方法
+  - [ ] Subtask 3.5: 实现 `getVisitList()` 方法用于验证
+  - [ ] Subtask 3.6: 实现 `getVisitCount()` 方法
+
+- [ ] **Task 4: 运行测试并验证通过** (AC: #1, #2, #3, #4)
+  - [ ] Subtask 4.1: 使用 `pnpm test:e2e:chromium disability-person-visit` 运行测试
+  - [ ] Subtask 4.2: 修复发现的问题
+  - [ ] Subtask 4.3: 验证所有测试通过
+
+## Dev Notes
+
+### Epic 9 背景与目标
+
+**Epic 9: 残疾人管理完整 E2E 测试覆盖(含并行隔离)**
+
+为残疾人管理功能编写完整的、真正验证业务功能的 E2E 测试,并确保测试可以与未来的区域管理测试并行运行。
+
+**Epic 9 的 Story 依赖关系:**
+- Story 9.1:照片上传功能测试 ✅ Done
+- Story 9.2:银行卡管理功能测试 ✅ Done
+- Story 9.3:备注管理功能测试 ✅ Done
+- **Story 9.4(本故事)**:回访记录管理测试
+- Story 9.5:完整流程测试(CRUD)
+- Story 9.6:测试隔离与并行执行验证
+- Story 9.7:稳定性验证(10次连续运行)
+
+### 业务功能分析
+
+**回访记录管理功能概述:**
+
+回访记录用于记录对残疾人的回访活动,包括电话回访、上门回访、视频回访等多种形式。每条回访记录包含:
+- 回访日期(必填,不能晚于今天)
+- 回访类型(必填:电话回访、上门回访、视频回访、微信回访、其他)
+- 回访内容(必填)
+- 回访结果(可选)
+- 下次回访日期(可选,应晚于本次回访日期)
+- 回访人ID(必填,默认为当前登录用户)
+
+**业务规则:**
+1. 最多可添加 10 条回访记录
+2. 回访日期不能晚于今天
+3. 下次回访日期应晚于本次回访日期
+4. 回访内容为必填项
+
+### 技术规范
+
+#### 回访管理组件 DOM 结构
+
+**组件位置:** `allin-packages/disability-person-management-ui/src/components/VisitManagement.tsx`
+
+**关键选择器(已验证):**
+
+| 元素 | data-testid | 类型 |
+|------|-------------|------|
+| 添加回访按钮 | `add-visit-button` | Button |
+| 删除回访按钮 | `remove-visit-{index}` | Button |
+| 回访日期输入 | `visit-date-input-{index}` | Input[type="date"] |
+| 回访类型选择器 | `visit-type-select-{index}` | Select |
+| 回访内容文本域 | `visit-content-textarea-{index}` | Textarea |
+| 回访结果输入 | `visit-result-input-{index}` | Input |
+| 下次回访日期输入 | `next-visit-date-input-{index}` | Input[type="date"] |
+| 回访人ID输入 | `visitor-id-input-{index}` | Input[type="number"] |
+
+#### 回访记录数据结构
+
+**Entity 位置:** `allin-packages/disability-module/src/entities/disabled-visit.entity.ts`
+
+```typescript
+interface VisitItem {
+  visitDate: string;      // ISO格式日期字符串
+  visitType: string;      // 回访类型
+  visitContent: string;   // 回访内容
+  visitResult?: string;   // 回访结果(可选)
+  nextVisitDate?: string; // 下次回访日期(可选)
+  visitorId: number;      // 回访人ID
+  tempId?: string;        // 临时ID用于React key
+}
+```
+
+**回访类型选项:**
+- 电话回访
+- 上门回访
+- 视频回访
+- 微信回访
+- 其他
+
+#### 现有 Page Object 结构
+
+**当前 Page Object 位置:** `web/tests/e2e/pages/admin/disability-person.page.ts`
+
+**现有的 `addVisit()` 方法(需要完善):**
+
+当前实现(第466-505行):
+```typescript
+async addVisit(visit: {
+  visitDate: string;
+  visitType: string;
+  visitContent: string;
+  visitResult?: string;
+  nextVisitDate?: string;
+}) {
+  // 点击"添加回访"按钮
+  const addVisitButton = this.page.getByRole('button', { name: /添加回访/ });
+  await addVisitButton.click();
+  await this.page.waitForTimeout(TIMEOUTS.SHORT);
+
+  // 填写回访信息
+  await this.page.getByLabel(/回访日期/).fill(visit.visitDate);
+  await selectRadixOption(this.page, '回访类型', visit.visitType);
+
+  // 查找回访内容输入框(可能有多个,使用最后一个)
+  const visitContentTextarea = this.page.locator('textarea').filter({ hasText: '' }).last();
+  await visitContentTextarea.fill(visit.visitContent);
+
+  // 填写回访结果(可选)
+  if (visit.visitResult) {
+    const resultTextareas = this.page.locator('textarea');
+    const count = await resultTextareas.count();
+    if (count > 0) {
+      await resultTextareas.nth(count - 1).fill(visit.visitResult);
+    }
+  }
+
+  // 填写下一次回访日期(可选)
+  if (visit.nextVisitDate) {
+    const nextDateInput = this.page.getByLabel(/下次回访日期/);
+    await nextDateInput.fill(visit.nextVisitDate);
+  }
+
+  console.log(`  ✓ 添加回访: ${visit.visitType} - ${visit.visitDate}`);
+}
+```
+
+**问题分析:**
+- 使用了模糊选择器(`getByRole` 和 `getByLabel`),不够稳定
+- 应该使用 `data-testid` 选择器
+- 需要添加获取回访列表的方法用于验证
+
+### Page Object 方法设计
+
+**需要在 `DisabilityPersonManagementPage` 中添加/完善的方法:**
+
+```typescript
+/**
+ * 添加回访记录(内联表单模式)
+ * @param visit 回访信息
+ * @returns 添加的回访记录索引
+ */
+async addVisit(visit: {
+  visitDate: string;
+  visitType: string;
+  visitContent: string;
+  visitResult?: string;
+  nextVisitDate?: string;
+  visitorId?: number;
+}): Promise<number> {
+  // 1. 点击"添加回访"按钮
+  // 2. 等待新的回访卡片出现
+  // 3. 获取当前回访数量,新添加的索引
+  // 4. 填写回访日期、类型、内容等
+  // 5. 返回新添加的回访索引
+}
+
+/**
+ * 编辑指定索引的回访记录
+ * @param index 回访索引(从0开始)
+ * @param updatedData 更新的回访数据
+ */
+async editVisit(index: number, updatedData: {
+  visitDate?: string;
+  visitType?: string;
+  visitContent?: string;
+  visitResult?: string;
+  nextVisitDate?: string;
+}): Promise<void> {
+  // 1. 直接在对应的输入框中修改值
+  // 2. 验证修改完成
+}
+
+/**
+ * 删除指定索引的回访记录
+ * @param index 回访索引(从0开始)
+ */
+async deleteVisit(index: number): Promise<void> {
+  // 1. 点击删除按钮
+  // 2. 等待删除完成
+}
+
+/**
+ * 获取回访记录列表
+ * @returns 回访信息数组
+ */
+async getVisitList(): Promise<Array<{
+  visitDate: string;
+  visitType: string;
+  visitContent: string;
+  visitResult?: string;
+  nextVisitDate?: string;
+}>> {
+  // 1. 遍历所有回访卡片
+  // 2. 读取每个字段的值
+  // 3. 返回回访信息数组
+}
+
+/**
+ * 获取当前回访记录数量
+ * @returns 回访数量
+ */
+async getVisitCount(): Promise<number> {
+  return await this.page.locator('[data-testid^="remove-visit-"]').count();
+}
+
+/**
+ * 检查添加回访按钮是否被禁用
+ * @returns 是否禁用
+ */
+async isAddVisitButtonDisabled(): Promise<boolean> {
+  const addButton = this.page.locator('[data-testid="add-visit-button"]');
+  return await addButton.isDisabled();
+}
+
+/**
+ * 滚动到回访记录区域
+ */
+async scrollToVisitSection(): Promise<void> {
+  const visitLabel = this.page.getByText('回访记录管理');
+  await visitLabel.scrollIntoViewIfNeeded();
+  await this.page.waitForTimeout(TIMEOUTS.SHORT);
+}
+```
+
+### 测试用例设计
+
+**测试文件:** `web/tests/e2e/specs/admin/disability-person-visit.spec.ts`
+
+**测试用例模板:**
+
+```typescript
+import { test, expect } from '@playwright/test';
+import { DisabilityPersonManagementPage } from '../../pages/admin/disability-person.page';
+
+test.describe('残疾人管理 - 回访记录管理功能', () => {
+  let pageObject: DisabilityPersonManagementPage;
+  const TIMESTAMP = Date.now();
+  const UNIQUE_ID = `test_visit_${TIMESTAMP}`;
+
+  test.beforeEach(async ({ page }) => {
+    pageObject = new DisabilityPersonManagementPage(page);
+    await pageObject.goto();
+    await pageObject.openCreateDialog();
+    await pageObject.fillBasicInfo({
+      name: UNIQUE_ID,
+      gender: '男',
+      idCard: `110101199001011234`,
+      disabilityId: `1234567890${TIMESTAMP}`,
+      disabilityType: '肢体残疾',
+      disabilityLevel: '一级',
+      phone: '13800138000',
+      idAddress: '北京市东城区测试街道1号',
+      province: '北京市',
+      city: '北京市',
+    });
+    await pageObject.scrollToVisitSection();
+  });
+
+  test('应该成功创建电话回访记录', async ({ page }) => {
+    const today = new Date().toISOString().split('T')[0];
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `电话回访测试内容_${UNIQUE_ID}`,
+      visitResult: '良好',
+      visitorId: 1,
+    });
+    const visitCount = await pageObject.getVisitCount();
+    expect(visitCount).toBe(1);
+  });
+
+  test('应该成功创建上门回访记录', async ({ page }) => {
+    const today = new Date().toISOString().split('T')[0];
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '上门回访',
+      visitContent: `上门回访测试内容_${UNIQUE_ID}`,
+      visitResult: '需要跟进',
+      visitorId: 1,
+    });
+    const visitCount = await pageObject.getVisitCount();
+    expect(visitCount).toBe(1);
+  });
+
+  test('应该成功编辑回访记录内容', async ({ page }) => {
+    const today = new Date().toISOString().split('T')[0];
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `原始内容_${UNIQUE_ID}`,
+      visitorId: 1,
+    });
+    const updatedContent = `更新后的回访内容_${UNIQUE_ID}`;
+    await pageObject.editVisit(0, { visitContent: updatedContent });
+    const visitList = await pageObject.getVisitList();
+    expect(visitList[0].visitContent).toContain('更新后的回访内容');
+  });
+
+  test('应该成功删除回访记录', async ({ page }) => {
+    const today = new Date().toISOString().split('T')[0];
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `待删除内容_${UNIQUE_ID}`,
+      visitorId: 1,
+    });
+    let visitCount = await pageObject.getVisitCount();
+    expect(visitCount).toBe(1);
+    await pageObject.deleteVisit(0);
+    visitCount = await pageObject.getVisitCount();
+    expect(visitCount).toBe(0);
+  });
+
+  test('应该支持添加多条回访记录', async ({ page }) => {
+    const today = new Date().toISOString().split('T')[0];
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `回访1_${UNIQUE_ID}`,
+      visitorId: 1,
+    });
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '上门回访',
+      visitContent: `回访2_${UNIQUE_ID}`,
+      visitorId: 1,
+    });
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '视频回访',
+      visitContent: `回访3_${UNIQUE_ID}`,
+      visitorId: 1,
+    });
+    const visitCount = await pageObject.getVisitCount();
+    expect(visitCount).toBe(3);
+  });
+
+  test('应该限制最多添加10条回访记录', async ({ page }) => {
+    const today = new Date().toISOString().split('T')[0];
+    // 添加10条回访记录
+    for (let i = 0; i < 10; i++) {
+      await pageObject.addVisit({
+        visitDate: today,
+        visitType: '电话回访',
+        visitContent: `回访${i + 1}_${UNIQUE_ID}`,
+        visitorId: 1,
+      });
+    }
+    // 验证按钮被禁用
+    const isDisabled = await pageObject.isAddVisitButtonDisabled();
+    expect(isDisabled).toBe(true);
+  });
+
+  test('应该正确显示回访记录历史', async ({ page }) => {
+    const today = new Date().toISOString().split('T')[0];
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `第一次回访_${UNIQUE_ID}`,
+      visitResult: '良好',
+      visitorId: 1,
+    });
+    await pageObject.addVisit({
+      visitDate: today,
+      visitType: '上门回访',
+      visitContent: `第二次回访_${UNIQUE_ID}`,
+      visitResult: '需要跟进',
+      visitorId: 1,
+    });
+    const visitList = await pageObject.getVisitList();
+    expect(visitList).toHaveLength(2);
+    expect(visitList[0].visitContent).toContain('第一次回访');
+    expect(visitList[1].visitContent).toContain('第二次回访');
+  });
+});
+```
+
+### 选择器策略
+
+**参考 Story 9.1、9.2、9.3 的经验,使用 data-testid 选择器:**
+
+```typescript
+// 添加回访按钮
+const addButton = this.page.locator('[data-testid="add-visit-button"]');
+
+// 删除回访按钮(索引)
+const removeButton = this.page.locator(`[data-testid="remove-visit-${index}"]`);
+
+// 回访日期输入
+const dateInput = this.page.locator(`[data-testid="visit-date-input-${index}"]`);
+
+// 回访类型选择器 - 使用 Radix Select 工具
+const typeSelectTrigger = this.page.locator(`[data-testid="visit-type-select-${index}"]`);
+
+// 回访内容文本域
+const contentTextarea = this.page.locator(`[data-testid="visit-content-textarea-${index}"]`);
+
+// 回访结果输入
+const resultInput = this.page.locator(`[data-testid="visit-result-input-${index}"]`);
+
+// 下次回访日期输入
+const nextDateInput = this.page.locator(`[data-testid="next-visit-date-input-${index}"]`);
+
+// 回访人ID输入
+const visitorIdInput = this.page.locator(`[data-testid="visitor-id-input-${index}"]`);
+```
+
+### 数据隔离策略
+
+**参考 Story 9.1、9.2、9.3 的数据隔离模式:**
+
+```typescript
+test.beforeEach(async ({ page }) => {
+  const timestamp = Date.now();
+  const uniqueId = `test_visit_${timestamp}`;
+  pageObject = new DisabilityPersonManagementPage(page);
+  await pageObject.goto();
+  await pageObject.openCreateDialog();
+  // 使用唯一ID确保数据隔离
+});
+
+test.afterEach(async ({ page }) => {
+  // 清理测试数据(如需要)
+});
+```
+
+### 项目结构说明
+
+**测试目录组织:**
+- **specs/**: 测试用例文件,按功能模块组织,`.spec.ts` 后缀
+- **pages/**: Page Object 封装,页面元素和操作方法,复用性强
+
+**无冲突检测:**
+- 新增测试文件,不影响现有代码
+- Page Object 扩展是增强,非破坏性修改
+
+**文件组织:**
+- 新建测试 specs 文件:`web/tests/e2e/specs/admin/disability-person-visit.spec.ts`
+- 扩展现有 Page Object:`web/tests/e2e/pages/admin/disability-person.page.ts`
+
+### Previous Story Intelligence
+
+**Story 9.1 (照片上传功能测试) 的关键经验:**
+
+1. **表单操作范围控制** - 使用 `form.getByLabel()` 限制查找范围在表单内
+2. **按钮文本获取时机** - 在点击前获取按钮文本
+3. **测试数据唯一性** - 使用 `Date.now()` 生成唯一 ID
+4. **超时常量定义** - 定义 `TIMEOUTS` 常量统一管理
+
+**Story 9.2 (银行卡管理功能测试) 的关键经验:**
+
+1. **内联表单模式** - 银行卡管理使用内联表单(非对话框模式),回访记录也是内联模式
+2. **索引定位方式** - 使用 `nth(index)` 定位列表项
+3. **数据验证模式** - 使用 `getVisitList()` 获取列表后验证内容
+
+**Story 9.3 (备注管理功能测试) 的关键经验:**
+
+1. **完整实现 Page Object 方法** - 代码审查发现必须完整实现所有方法
+2. **测试必须使用 Page Object** - 不能直接操作 page,必须通过 Page Object 方法
+3. **console.debug 调试** - 使用 `console.debug` 而非 `console.log`(Vitest 中只有 debug 会显示)
+4. **超时常量替换魔法数字** - 使用 `TIMEOUTS` 常量替换所有魔法数字
+
+### Git Intelligence Summary
+
+**Recent Commits:**
+- `e589d94b` - test(e2e): 完成 Story 9.3 - 备注管理功能测试
+- `2be3dd84` - test(e2e): 完成 Story 10.2 代码审查 - 订单列表查看测试
+- `8c36a373` - test(e2e): 完成 Story 10.2 - 订单列表查看测试
+
+**Code Patterns Observed:**
+- 测试文件命名:`disability-person-{feature}.spec.ts`
+- Page Object 方法命名:动词+名词(如 `addVisit`, `editVisit`)
+- 使用 `data-testid` 选择器确保稳定性
+
+### TypeScript + Playwright 陷阱(关键)
+
+基于架构文档的陷阱章节和前置 Story 的经验:
+
+**陷阱 1: DOM 结构假设必须验证** ⚠️
+- 回访记录管理功能的 DOM 结构已在 `VisitManagement.tsx` 中验证
+- 使用 `data-testid` 选择器(最稳定)
+
+**陷阱 2: 精确文本匹配**
+- 使用 `:text-is()` 进行精确文本匹配,而非 `:has-text()`
+
+**陷阱 3: 网络空闲等待**
+- 回访添加/编辑后可能需要等待网络请求完成
+
+**陷阱 4: 避免使用 page.evaluate()**
+- 使用 Playwright API 而非 `page.evaluate()` 获取元素内容
+
+**陷阱 5: Select 组件操作**
+- 回访类型使用 Radix UI Select,必须使用 `selectRadixOption` 工具
+- Select 触发器使用 `data-testid="visit-type-select-{index}"`
+
+### References
+
+**源文档引用:**
+- [Source: _bmad-output/planning-artifacts/epics.md#Epic-9-Story-9.4] - 完整业务需求
+- [Source: _bmad-output/planning-artifacts/architecture.md#Testing-Configuration] - 三层测试策略
+- [Source: docs/standards/e2e-radix-testing.md] - E2E 测试标准
+
+**前置 Story 参考:**
+- [Source: _bmad-output/implementation-artifacts/9-1-photo-upload-tests.md] - 照片上传测试实现经验
+- [Source: _bmad-output/implementation-artifacts/9-2-bankcard-tests.md] - 银行卡测试实现经验
+- [Source: _bmad-output/implementation-artifacts/9-3-note-tests.md] - 备注测试实现经验
+
+**相关组件源码:**
+- [Source: web/tests/e2e/pages/admin/disability-person.page.ts] - 现有 Page Object
+- [Source: allin-packages/disability-person-management-ui/src/components/VisitManagement.tsx] - 回访管理 UI 组件
+- [Source: allin-packages/disability-module/src/entities/disabled-visit.entity.ts] - 回访记录 Entity
+
+### Project Structure Notes
+
+**Monorepo 结构对齐:**
+- 测试位于 `web/tests/e2e/` 目录
+- 使用 pnpm workspace 协议引用 `@d8d/e2e-test-utils`
+- 与现有 Page Object 模式保持一致
+
+**遵循的项目标准:**
+- 文件命名:`.spec.ts` 后缀(Playwright 测试)
+- 测试目录:`specs/` 分离,`pages/` Page Object
+- 使用 `@d8d/e2e-test-utils` 工具函数(`selectRadixOption`)
+- 遵循 `docs/standards/e2e-radix-testing.md` 标准
+
+## Dev Agent Record
+
+### Agent Model Used
+
+Claude Opus 4 (claude-opus-4-5-20251101)
+
+### Debug Log References
+
+### Completion Notes List
+
+1. ✅ 加载并分析 Epic 9 Story 9.4 需求(从 epics.md)
+2. ✅ 加载并分析架构文档(architecture.md)
+3. ✅ 分析前置 Story 9.1、9.2、9.3 的实现经验和问题
+4. ✅ 分析回访管理组件 DOM 结构(VisitManagement.tsx)
+5. ✅ 分析回访记录数据结构(disabled-visit.entity.ts)
+6. ✅ 创建完整的 Story 9.4 文档,包含:
+   - Story 和验收标准
+   - 详细的任务分解
+   - Epic 9 背景和目标
+   - 业务功能分析(业务规则、数据结构)
+   - 技术规范(DOM 结构、Page Object 方法)
+   - 完整的测试用例模板
+   - Previous Story Intelligence(从 Story 9.1、9.2、9.3 学到的经验)
+   - Git Intelligence Summary
+   - TypeScript + Playwright 陷阱警告
+   - 完整的参考文档列表
+
+### File List
+
+**待创建的文件:**
+- `_bmad-output/implementation-artifacts/9-4-visit-tests.md` - 本 story 文档
+- `web/tests/e2e/specs/admin/disability-person-visit.spec.ts` - 回访测试文件(待实现)
+
+**待修改的文件:**
+- `_bmad-output/implementation-artifacts/sprint-status.yaml` - 更新 Story 9.4 状态
+- `web/tests/e2e/pages/admin/disability-person.page.ts` - Page Object 扩展(待实现)

+ 1 - 1
_bmad-output/implementation-artifacts/sprint-status.yaml

@@ -132,7 +132,7 @@ development_status:
   9-1-photo-upload-tests: done              # 照片上传功能完整测试(所有8个测试通过)
   9-2-bankcard-tests: done              # 银行卡管理功能测试(添加、编辑、删除)
   9-3-note-tests: done                # 备注管理功能测试(添加、修改、删除)- 代码审查完成,所有HIGH和MEDIUM问题已修复
-  9-4-visit-tests: backlog               # 回访记录管理测试(创建、查看、编辑)
+  9-4-visit-tests: ready-for-dev               # 回访记录管理测试(创建、查看、编辑)
   9-5-crud-tests: backlog                # 完整流程测试(新增、编辑、删除、查看)
   9-6-parallel-isolation: backlog        # 测试隔离与并行执行验证
   9-7-stability-validation: backlog      # 稳定性验证(10 次连续运行)