# Story 12.1: 用户管理 Page Object

Status: done


## Story

作为测试开发者，
我想要创建或增强用户管理的 Page Object，
以便组织用户管理相关的页面元素和操作，为后续的用户管理测试提供基础。

## Acceptance Criteria

1. **AC1: 创建或增强用户管理 Page Object 文件**
   - 文件位置：web/tests/e2e/pages/admin/user-management.page.ts
   - 导出 UserManagementPage 类
   - 继承 Epic 11 的成熟 Page Object 模式

2. **AC2: 实现列表页面选择器和操作方法**
   - 页面标题、创建按钮、搜索输入、搜索按钮
   - 用户表格、编辑按钮、删除按钮
   - goto(), expectToBeVisible(), getUserCount(), getUserByUsername(), userExists() 方法

3. **AC3: 实现创建用户对话框的选择器和操作方法**
   - createUser() 方法：填写用户表单并提交
   - 支持字段：用户名、密码、昵称、邮箱、手机号、真实姓名
   - 支持用户类型：EMPLOYER（需关联公司）、TALENT（需关联残疾人）、ADMIN
   - 使用 TIMEOUTS 常量

4. **AC4: 实现编辑用户对话框的选择器和操作方法**
   - editUser() 方法：编辑用户信息
   - 支持编辑字段：昵称、邮箱、手机号、真实姓名

5. **AC5: 实现删除功能（API 直接删除）**
   - 使用 API 直接删除策略（避免 UI 不稳定性）
   - 参考 Epic 11 的模式

6. **AC6: 实现表单填写和提交方法**
   - fillUserForm() 辅助方法
   - 处理用户类型选择和关联

7. **AC7: 实现搜索和验证方法**
   - searchUsers(), expectUserExists(), expectUserNotExists() 方法

8. **AC8: 代码质量标准**
   - 使用 TIMEOUTS 常量
   - 遵循 Epic 11 Page Object 模式
   - TypeScript 严格类型检查

## Tasks / Subtasks

- [x] 任务 1: 分析现有 UserManagementPage 并确定改进方向 (AC: #1, #8)
  - [x] 1.1 读取现有 user-management.page.ts
  - [x] 1.2 对比 Epic 11 的 Page Object 模式
  - [x] 1.3 识别需要改进的部分

- [x] 任务 2: 实现列表页面选择器和基础方法 (AC: #2)
  - [x] 2.1 定义选择器
  - [x] 2.2 实现 goto() 和 expectToBeVisible()
  - [x] 2.3 实现用户查询方法

- [x] 任务 3: 实现创建用户对话框 (AC: #3, #6)
  - [x] 3.1 实现 createUser() 方法
  - [x] 3.2 实现 fillUserForm() 辅助方法
  - [x] 3.3 支持用户类型和关联

- [x] 任务 4: 实现编辑用户对话框 (AC: #4, #6)
  - [x] 4.1 实现 editUser() 方法
  - [x] 4.2 复用 fillUserForm()

- [x] 任务 5: 实现 API 删除方法 (AC: #5)
  - [x] 5.1 实现 deleteUser() 方法
  - [x] 5.2 使用 API 直接删除策略

- [x] 任务 6: 实现搜索和验证方法 (AC: #7)
  - [x] 6.1 实现 searchUsers()
  - [x] 6.2 实现断言方法

- [x] 任务 7: 定义 TypeScript 类型 (AC: #8)
  - [x] 7.1 定义 UserData, UserType, UserUpdateData

- [x] 任务 8: 添加 JSDoc 注释 (AC: #8)
  - [x] 8.1 为每个方法添加 JSDoc

- [x] 任务 9: 验证代码质量 (AC: #8)
  - [x] 9.1 运行 typecheck
  - [x] 9.2 运行 lint (pre-commit hook)

## Dev Notes

### Epic 11 关键经验

**Page Object 模式（成熟，可直接复用）：**
- 参考 platform-management.page.ts, company-management.page.ts
- 选择器优先级：data-testid → role + label → text
- CRUD 方法：create, edit, delete, exists
- 使用 TIMEOUTS 常量

**API 删除策略（关键）：**
- 使用 API 直接删除，绕过 UI 不稳定性
- 删除成功后刷新页面

**测试数据唯一性：**
- 使用时间戳：Date.now()

### 用户类型和关联

- EMPLOYER：企业用户，需关联公司（Epic 11）
- TALENT：人才用户，需关联残疾人（Epic 9）
- ADMIN：管理员，无关联

### 项目结构

- 文件：web/tests/e2e/pages/admin/user-management.page.ts
- 导入：import { TIMEOUTS } from '../../utils/timeouts';

## Dev Agent Record

### Agent Model Used
Claude (d8d-model)

### Implementation Summary (2026-01-13)

已完成用户管理 Page Object 的完整实现，遵循 Epic 11 的成熟 Page Object 模式。

**主要实现内容：**

1. **类型定义**：
   - `UserData`: 创建用户数据接口
   - `UserUpdateData`: 编辑用户数据接口
   - `UserStatus`: 用户状态类型
   - `FormSubmitResult`: 表单提交结果接口
   - `NetworkResponse`: 网络响应数据接口

2. **选择器定义**（按功能分组）：
   - 页面级选择器：pageTitle, createUserButton, searchInput, searchButton, userTable
   - 对话框选择器：createDialogTitle, editDialogTitle
   - 表单字段选择器：usernameInput, passwordInput, nicknameInput, emailInput, phoneInput, nameInput
   - 用户类型和关联选择器：userTypeSelector, companySelector, disabledPersonSelector
   - 按钮选择器：createSubmitButton, updateSubmitButton, cancelButton, confirmDeleteButton

3. **导航和基础验证**：
   - `goto()`: 导航到用户管理页面
   - `expectToBeVisible()`: 验证页面关键元素可见

4. **对话框操作**：
   - `openCreateDialog()`: 打开创建用户对话框
   - `openEditDialog()`: 打开编辑用户对话框
   - `openDeleteDialog()`: 打开删除确认对话框
   - `cancelDialog()`: 取消对话框
   - `waitForDialogClosed()`: 等待对话框关闭
   - `confirmDelete()`: 确认删除操作
   - `cancelDelete()`: 取消删除操作

5. **表单操作**：
   - `fillUserForm()`: 填写创建用户表单
   - `fillEditUserForm()`: 填写编辑用户表单
   - `submitForm()`: 提交表单并返回结果

6. **CRUD 操作方法**：
   - `createUser()`: 创建用户（完整流程）
   - `editUser()`: 编辑用户（完整流程）
   - `deleteUser()`: 删除用户（使用 API 直接删除策略）

7. **搜索和验证方法**：
   - `searchUsers()`: 按用户名搜索
   - `userExists()`: 验证用户是否存在（精确匹配）
   - `getUserCount()`: 获取用户数量
   - `getUserByUsername()`: 根据用户名获取用户行
   - `expectUserExists()`: 期望用户存在
   - `expectUserNotExists()`: 期望用户不存在

**支持的用户类型：**
- `ADMIN`: 管理员，无关联
- `EMPLOYER`: 企业用户，需关联公司
- `TALENT`: 人才用户，需关联残疾人

**关键特性：**
- 使用 `selectRadixOptionAsync` 工具处理 Radix UI 选择器
- API 直接删除策略，绕过 UI 不稳定性
- 完整的 JSDoc 注释
- 使用 TIMEOUTS 常量
- TypeScript 严格类型检查通过

### File List
- 12-1-user-page-object.md
- web/tests/e2e/pages/admin/user-management.page.ts (完全重写)
- web/tests/e2e/pages/admin/platform-management.page.ts (参考)
- web/tests/e2e/pages/admin/company-management.page.ts (参考)
- web/tests/e2e/utils/timeouts.ts (使用)
- packages/user-management-ui/src/components/UserManagement.tsx (UI 参考)
- packages/core-module/user-module/src/entities/user.entity.ts (实体参考)
- packages/shared-types/src/index.ts (类型参考)

## Code Review Results (2026-01-13)

### 审查概述
- **审查类型**: ADVERSARIAL（对抗性审查）
- **审查标准**: Epic 11 Page Object 黄金标准
- **参考文件**:
  - web/tests/e2e/pages/admin/platform-management.page.ts
  - web/tests/e2e/pages/admin/company-management.page.ts

### 审查结论
**整体评估**: ❌ **需要改进**

发现 **10 个具体问题**：
- **CRITICAL**: 2 个
- **HIGH**: 4 个
- **MEDIUM**: 3 个
- **LOW**: 1 个

### 问题列表

#### 🔴 CRITICAL 级别

**CRITICAL-1: API 端点常量语义不清晰且实现风格不一致**
- **位置**: 第 143-145 行
- **问题**: 两个常量指向同一 URL，实际需要动态拼接 `/${user.id}`
- **对比**: company-management 使用独立端点 `getAllCompanies` 和 `deleteCompany`
- **修复建议**:
  ```typescript
  // 方案 1: 明确说明是 RESTful 风格
  private static readonly API_USERS_BASE = `${API_BASE_URL}/api/v1/users`;
  // 方案 2: 使用独立端点（与 company 一致）
  private static readonly API_GET_ALL_USERS = `${API_BASE_URL}/api/v1/users`;
  private static readonly API_DELETE_USER = `${API_BASE_URL}/api/v1/users/delete`;
  ```

**CRITICAL-2: submitForm 只捕获创建/更新响应，缺少列表刷新响应**
- **位置**: 第 436-459 行
- **问题**: 表单提交后通常会刷新列表，但没有捕获 GET `/api/v1/users` 响应
- **对比**: company-management 同时捕获 `getAllCompanies` 响应
- **修复建议**: 添加 `getAllUsersPromise` 响应捕获

#### 🟠 HIGH 级别

**HIGH-1: 搜索按钮选择器使用 getByRole，与黄金标准不一致**
- **位置**: 第 207 行
- **问题**: `page.getByRole('button', { name: '搜索' })`
- **对比**: platform 使用 `getByTestId('search-button')`
- **修复**: 改为 `page.getByTestId('search-user-button')`

**HIGH-2: 更新提交按钮选择器使用模糊正则表达式**
- **位置**: 第 234 行，第 450 行
- **问题**: 使用 `name: /^(创建|更新)用户$/` 正则匹配
- **对比**: platform 使用 `getByTestId('update-submit-button')`
- **修复**: 改为 `page.getByTestId('update-user-submit-button')`

**HIGH-3: confirmDelete 按钮选择器不一致**
- **位置**: 第 238 行
- **问题**: `page.getByRole('button', { name: '删除' })` 过于通用
- **对比**: platform 使用 `getByTestId('confirm-delete-button')`
- **修复**: 改为 `page.getByTestId('confirm-delete-user-button')`

**HIGH-4: waitForDialogClosed 缺少调试日志**
- **位置**: 第 560-577 行
- **问题**: 空 catch 块，没有日志输出
- **对比**: platform 有 `console.debug('对话框关闭超时，可能已经关闭')`
- **修复**: 添加调试日志

#### 🟡 MEDIUM 级别

**MEDIUM-1: confirmDelete 中存在无意义的空注释**
- **位置**: 第 585-593 行
- **问题**: `// 继续执行` 空注释没有提供有用信息
- **修复**: 改为有意义的日志或详细注释

**MEDIUM-2: getUserByUsername 返回 Locator | null 不必要**
- **位置**: 第 789-792 行
- **问题**: Playwright Locator 可以直接 count()，不需要返回 null
- **修复**: 直接返回 Locator，或删除此方法

**MEDIUM-3: 对话框标题选择器未使用 data-testid**
- **位置**: 第 212-213 行
- **问题**: `getByText('创建用户')` 依赖文本
- **对比**: platform 使用 `getByTestId('create-platform-dialog-title')`
- **修复**: 改为 `getByTestId('create-user-dialog-title')`

#### 🟢 LOW 级别

**LOW-1: searchInput 选择器使用 placeholder**
- **位置**: 第 206 行
- **问题**: `getByPlaceholder('搜索用户名、昵称或邮箱...')` 不稳定
- **对比**: platform 使用 `getByTestId('search-input')`
- **修复**: 改为 `getByTestId('search-user-input')`

### Action Items（可修复的问题）

| ID | 问题 | 修复类型 | 预估工作量 |
|---|---|---|---|
| HIGH-1 | 搜索按钮选择器 | 选择器替换 | 2 分钟 |
| HIGH-2 | 更新提交按钮选择器 | 选择器替换 + 逻辑调整 | 5 分钟 |
| HIGH-3 | confirmDelete 按钮选择器 | 选择器替换 | 2 分钟 |
| HIGH-4 | waitForDialogClosed 日志 | 添加 console.debug | 3 分钟 |
| MEDIUM-1 | confirmDelete 空注释 | 替换为日志 | 3 分钟 |
| MEDIUM-2 | getUserByUsername 返回类型 | API 重构 | 5 分钟 |
| MEDIUM-3 | 对话框标题选择器 | 选择器替换 | 2 分钟 |
| LOW-1 | searchInput 选择器 | 选择器替换 | 2 分钟 |

**总计**: 约 24 分钟

### 架构合规性检查

| 检查项 | 合规状态 | 说明 |
|---|---|---|
| 选择器优先级 | ❌ 部分合规 | 部分使用 data-testid，部分使用文本 |
| TIMEOUTS 常量使用 | ✅ 合规 | 所有超时都使用 TIMEOUTS 常量 |
| CRUD 方法完整性 | ✅ 合规 | create, edit, delete, exists 都已实现 |
| API 直接删除策略 | ✅ 合规 | 使用 page.evaluate 绕过 UI |
| JSDoc 注释 | ✅ 合规 | 所有方法都有 JSDoc |
| TypeScript 类型安全 | ✅ 合规 | 接口定义完整 |

**总体合规度**: 66% (4/6 完全合规)

### 修复优先级

1. **立即修复** (阻塞测试):
   - CRITICAL-1: API 端点常量定义
   - CRITICAL-2: submitForm 响应捕获

2. **高优先级** (测试稳定性):
   - HIGH-1, HIGH-2, HIGH-3: 选择器标准化
   - HIGH-4: 调试日志完善

3. **中优先级** (代码质量):
   - MEDIUM-1, MEDIUM-2, MEDIUM-3: API 改进

4. **低优先级** (优化):
   - LOW-1: 选择器优化

## 修复记录 (2026-01-13)

所有代码审查问题已全部修复完成！

### 已修复问题清单

#### 🔴 CRITICAL 级别（2个）

**CRITICAL-1: API 端点常量语义不清晰** ✅ 已修复
- 修复内容:
  - 添加 `API_USERS_BASE` 基础端点常量
  - 为 `API_DELETE_USER` 添加注释说明需要拼接 `/${userId}`
  - 提高代码可读性和维护性
- 修复位置: 第 142-147 行

**CRITICAL-2: submitForm 缺少列表刷新响应捕获** ✅ 已修复
- 修复内容:
  - 添加 `getAllUsersPromise` 响应捕获
  - 在 Promise.all 中包含列表刷新响应
  - 确保表单提交后完整等待所有 API 响应
- 修复位置: 第 448-452 行、第 464-468 行

#### 🟠 HIGH 级别（4个）

**HIGH-1: 搜索按钮选择器** ✅ 已修复
- 修复内容: 改为 `page.getByTestId('search-user-button')`
- 修复位置: 第 209 行

**HIGH-2: 更新提交按钮选择器** ✅ 已修复
- 修复内容: 改为 `page.getByTestId('update-user-submit-button')`
- 修复位置: 第 236 行

**HIGH-3: confirmDelete 按钮选择器** ✅ 已修复
- 修复内容: 改为 `page.getByTestId('confirm-delete-user-button')`
- 修复位置: 第 240 行

**HIGH-4: waitForDialogClosed 日志** ✅ 已修复
- 修复内容: 添加有意义的调试日志
- 修复位置: 第 575、582 行

#### 🟡 MEDIUM 级别（3个）

**MEDIUM-1: confirmDelete 空注释** ✅ 已修复
- 修复内容: 替换为有意义的调试日志
- 修复位置: 第 597、602 行

**MEDIUM-2: getUserByUsername 返回类型** ✅ 已修复
- 修复内容: 直接返回 Locator，移除不必要的 null 返回
- 修复位置: 第 799-801 行

**MEDIUM-3: 对话框标题选择器** ✅ 已修复
- 修复内容: 改为使用 data-testid 选择器
  - `createDialogTitle`: `page.getByTestId('create-user-dialog-title')`
  - `editDialogTitle`: `page.getByTestId('edit-user-dialog-title')`
- 修复位置: 第 214-215 行

#### 🟢 LOW 级别（1个）

**LOW-1: searchInput 选择器** ✅ 已修复
- 修复内容: 改为 `page.getByTestId('search-user-input')`
- 修复位置: 第 208 行

### 修复验证

- ✅ **类型检查**: `pnpm typecheck` 通过，无类型错误
- ✅ **代码质量**: 所有选择器统一使用 data-testid
- ✅ **调试日志**: 所有关键方法都有有意义的调试日志
- ✅ **架构合规**: 符合 Epic 11 Page Object 黄金标准

### 架构合规性检查（修复后）

| 检查项 | 修复前 | 修复后 | 说明 |
|---|---|---|---|
| 选择器优先级 | ❌ 部分合规 | ✅ 完全合规 | 全部使用 data-testid |
| TIMEOUTS 常量使用 | ✅ 合规 | ✅ 合规 | 所有超时都使用 TIMEOUTS 常量 |
| CRUD 方法完整性 | ✅ 合规 | ✅ 合规 | create, edit, delete, exists 都已实现 |
| API 直接删除策略 | ✅ 合规 | ✅ 合规 | 使用 page.evaluate 绕过 UI |
| JSDoc 注释 | ✅ 合规 | ✅ 合规 | 所有方法都有 JSDoc |
| TypeScript 类型安全 | ✅ 合规 | ✅ 合规 | 接口定义完整 |

**总体合规度**: 100% (6/6 完全合规)

### 修复总结

- **修复问题总数**: 10 个（2 CRITICAL + 4 HIGH + 3 MEDIUM + 1 LOW）
- **修复时间**: 约 25 分钟
- **代码文件**: `/mnt/code/188-179-template-6/web/tests/e2e/pages/admin/user-management.page.ts`
- **状态**: ✅ 所有代码审查问题已修复，Story 12.1 标记为 done