stepsCompleted: ['step-01-init', 'step-02-discovery', 'step-03-success', 'step-04-journeys', 'step-07-project-type', 'step-08-scoping', 'step-09-functional', 'step-10-nonfunctional', 'step-11-complete', 'revision-2025-01-10', 'revision-2026-01-12', 'revision-2026-01-15-business-context', 'revision-2026-01-15-order-stats-fields'] inputDocuments:
作者: Root 创建日期: 2026-01-07 修订日期: 2026-01-15
这是一个残疾人招聘管理系统,包含三个端:
| 端 | 名称 | 用途 |
|---|---|---|
| 管理后台 | WEB | 系统管理员和企业管理员使用,负责所有配置和数据管理 |
| 企业小程序 | yongren | 企业用户查看订单、人才、统计数据 |
| 人才小程序 | rencai | 残疾人查看个人信息、打卡、查看考勤薪资 |
核心价值:为残疾人提供就业机会,为企业提供合规的残疾人用工管理。
本系统的核心业务目标是:
系统包含三类主要用户角色:
使用端: 管理后台(WEB)
主要职责:
权限特点: 拥有系统的最高权限,可以执行所有写操作。
使用端: 管理后台 + 企业小程序(yongren)
主要职责:
权限特点:
使用端: 人才小程序(rencai)
主要职责:
权限特点: 主要是只读操作,仅可进行打卡操作。
┌─────────────────────────────────────────────────────────────────────┐
│ 残疾人招聘管理流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 管理员配置基础数据 │
│ └─ 创建 Platform(平台) │
│ └─ 创建 Company(公司),关联 Platform │
│ └─ 创建 Channel(渠道,可选) │
│ │
│ 2. 企业管理员创建订单 │
│ └─ 创建 Order,选择 Platform、Company、Channel │
│ └─ 订单状态:草稿 → 已确认 → 进行中 → 已完成 │
│ │
│ 3. 管理员分配残疾人到订单 │
│ └─ 为订单添加 DisabledPerson(残疾人) │
│ └─ 设置残疾人在订单中的工作状态 │
│ │
│ 4. 人才通过小程序打卡上岗 │
│ └─ 使用人才小程序进行上下班打卡 │
│ └─ 打卡记录同步到 Attendance 表 │
│ │
│ 5. 企业查看统计数据并发放薪资 │
│ └─ 通过企业小程序查看订单统计 │
│ └─ 发放薪资到人才银行卡 │
│ │
└─────────────────────────────────────────────────────────────────────┘
Platform (平台)
│ 1:N
▼
Company (公司)
│ 1:N (必须 platformId)
▼
Order (订单)
│ 1:N (必须 companyId,可选 channelId)
▼
DisabledPerson (残疾人/人才)
│ 关联
├─► Attendance (考勤打卡记录)
└─► Salary (薪资记录)
实体说明:
| 实体 | 说明 | 关键字段 |
|---|---|---|
| Platform | 平台 | 平台名称、配置信息 |
| Company | 公司 | 公司名称、platformId(必选) |
| Channel | 渠道 | 渠道名称、类型(可选) |
| Order | 订单 | 订单标题、platformId、companyId、channelId、状态 |
| DisabledPerson | 残疾人/人才 | 姓名、身份证号、残疾类型、银行卡信息 |
| Attendance | 考勤打卡 | 关联人员、打卡时间、打卡类型(上班/下班) |
| Salary | 薪资 | 关联人员、金额、发放时间 |
重要约束:
企业小程序的订单卡片中显示以下统计字段,用于企业了解订单执行情况:
order_person 表order_person 表中 order_id 等于当前订单 ID 的记录数量XX人order_person_asset 表,asset_type='checkin_video'实际打卡数/订单总人数 百分比显示格式:
本月打卡
24/30
80%
order_person_asset 表,asset_type='salary_video'显示格式:
工资视频
22/24
92%
order_person_asset 表,asset_type='tax_video'显示格式:
个税视频
20/24
83%
OrderPersonAsset (人员资产/视频)
├─► CHECKIN_VIDEO (打卡视频) → 本月打卡统计
├─► SALARY_VIDEO (工资视频) → 工资视频统计
└─► TAX_VIDEO (个税视频) → 个税视频统计
| 操作类型 | 管理后台 | 企业小程序 | 人才小程序 |
|---|---|---|---|
| 创建 | 全部创建权限 | 无 | 无 |
| 编辑/更新 | 全部编辑权限 | 无 | 无 |
| 删除 | 全部删除权限 | 无 | 无 |
| 查看 | 全部查看权限 | 订单、人才、统计 | 个人信息、考勤、薪资 |
| 打卡 | - | - | 上班/下班打卡 |
数据同步机制:
/mini-ws/talent-mini-ws| 层级 | 技术 |
|---|---|
| 前端 | React 19 |
| 后端 | Hono 4.x |
| 数据库 | PostgreSQL + TypeORM |
| E2E 测试 | Playwright |
| 项目结构 | Monorepo |
本 PRD 专注于 Web 管理后台的 E2E 测试覆盖,确保以下功能的质量:
小程序端的测试(Epic D/E)作为扩展范围,验证完整的业务流程。
本项目旨在为 Web 管理后台的关键业务功能建立完整的 E2E 测试覆盖。在编写业务测试的过程中,将通用测试模式抽象到 @d8d/e2e-test-utils 包中,以加速后续功能的测试开发。
188-179 招聘系统是一个大型企业级 Monorepo 招聘管理平台,采用 React 19 + Hono 4.x + TypeORM 技术栈。现有 E2E 测试使用 Playwright,遵循 Page Object 模式。
当前残疾人管理功能已经实现了完整的业务逻辑,包括:
订单管理是招聘系统的核心业务功能,包括:
但 E2E 测试覆盖不完整,需要补充这些功能的测试用例。
主目标:为 Web 管理后台的业务功能建立完整的 E2E 测试覆盖
副目标:在测试过程中抽象可复用工具
工作方式:测试驱动 + 工具演进
编写业务 E2E 测试(主任务)
抽象通用测试工具(自然演进)
@d8d/e2e-test-utils 包输出两种形式
packages/e2e-test-utils本项目按业务功能组织为多个 Epic:
| Epic | 内容 | 状态 |
|---|---|---|
| Epic A: 残疾人管理 E2E 测试 | 完整的残疾人管理功能测试覆盖 | ✅ 已完成 |
| Epic B: 区域管理 E2E 测试 | 区域管理(省市区街道)测试覆盖 | 🔄 待开发 |
| Epic C: 订单管理 E2E 测试 | 订单管理(CRUD、状态流转、人员关联)测试覆盖 | 📋 待开发 |
| Epic D: 用户管理与小程序登录测试 | 用户创建、小程序登录 E2E 测试 | 🆕 待开发 |
| Epic E: 跨端数据同步测试 | 后台操作 → 小程序数据验证 | 🆕 待开发 |
| Epic F: 基础配置管理测试 | 平台、公司、渠道配置管理测试 | 🆕 待开发 |
| Epic G: e2e-test-utils 包维护 | 支持性任务:维护测试工具包 | 🌟 持续演进 |
说明:
这个项目的特殊之处在于:
业务价值优先:直接交付业务测试覆盖,确保产品质量
工具自然演进:不是"为了做工具而做工具",而是从实践中抽象
双重收益:
降低认知负担:新测试开发者不需要深入理解 Radix UI 内部机制,只需调用统一 API
提高测试稳定性:统一的等待策略、错误处理和重试逻辑
技术类型: testing(E2E 测试覆盖)
领域: web_admin(Web 管理后台业务功能测试)
复杂度: medium(业务测试 + 工具抽象)
项目上下文: 棕地项目 - 为现有 Web 管理后台补充 E2E 测试覆盖,遵循现有 Page Object 模式
测试开发者视角的成功指标:
快速上手
一次成功
易于维护
稳定的开发体验
业务测试覆盖率(主指标):
| 业务功能 | 目标覆盖率 | 当前状态 | 测量方式 |
|---|---|---|---|
| 残疾人管理 | 关键流程 100% | ✅ 已完成 | 代码覆盖率报告 |
| 区域管理 | 关键流程 100% | 🔄 待开发 | 代码覆盖率报告 |
| 订单管理 | 关键流程 100% | 📋 待开发 | 代码覆盖率报告 |
| 其他管理功能 | 按需补充 | ⏳ 规划中 | 业务优先级 |
短期(1-3个月)
中期(3-6个月)
长期(6-12个月)
代码质量
可扩展性
文档完整性
| 指标 | 当前状态 | 目标状态 | 测量方式 |
|---|---|---|---|
| 残疾人管理测试覆盖率 | ✅ 关键流程 100% | ✅ 已达成 | 代码覆盖率报告 |
| 区域管理测试覆盖率 | 0% | 关键流程 100% | 代码覆盖率报告 |
| 订单管理测试覆盖率 | 0% | 关键流程 100% | 代码覆盖率报告 |
| Radix Select 测试编写时间 | 需要研究/摸索 | 5 分钟内完成 | 计时实验 |
| 测试稳定性(通过率) | 良好 | 20 次连续运行 100% 通过 | 自动化运行 |
| 工具函数测试覆盖率 | 进行中 | ≥ 80% | 代码覆盖率报告 |
| 文档完整性 | 基础 | 覆盖所有工具 + 示例 | 文档检查清单 |
核心:为 Web 管理后台的业务功能建立完整的 E2E 测试覆盖
1. Epic A: 残疾人管理 E2E 测试 ✅ 已完成
完整的残疾人管理功能测试覆盖:
测试过程中抽象的工具:
2. Epic B: 区域管理 E2E 测试 🔄 当前目标
区域管理功能的完整测试覆盖:
可能需要抽象的工具:
3. Epic C: 订单管理 E2E 测试 📋 待开发
订单管理功能的完整测试覆盖(核心业务功能):
可能需要抽象的工具(按需):
4. e2e-test-utils 包 🌟 支持性任务
作为业务测试的支持工具,自然演进:
packages/e2e-test-utils/
├── src/
│ ├── index.ts
│ ├── select.ts # Radix UI Select 工具 ✅
│ ├── file-upload.ts # 文件上传工具 🔄
│ └── ... # 其他工具(按需添加)
├── tests/
└── README.md
价值主张: 业务测试覆盖直接交付价值,工具包是自然演进的支持产物。
5. Epic D: 用户管理与小程序登录测试 🆕 待开发
用户管理和小程序登录的完整测试覆盖:
测试前置条件:
测试场景:
技术要点:
/mini 和 /talent-mini)6. Epic E: 跨端数据同步测试 🆕 待开发
验证后台操作后小程序端的数据同步:
测试场景:
技术要点:
/mini-ws、/talent-mini-ws)7. Epic F: 基础配置管理测试 🆕 待开发
平台、公司、渠道配置管理的测试覆盖:
实体关系链:
Platform (平台)
↓ 1:N
Company (公司) - 必须 platformId
↓ 1:N
Order (订单) - 必须 companyId
测试场景:
重要性说明:
Epic 优先级建议:
Phase 1(当前进行中):
Phase 2(小程序测试):
更多业务功能的 E2E 测试覆盖:
工具包持续演进(按需):
开发体验增强:
完整的 E2E 测试覆盖:
智能化测试开发:
团队测试文化:
人物设定:
他的故事:
周一早上,张伟接到任务:"残疾人管理功能需要补充照片上传、银行卡管理等功能的 E2E 测试,周三前完成。"
张伟叹了口气。上次写测试时,他花了整整一下午研究 Radix UI Select 的测试方法,还要处理各种时序问题。测试写完后还经常 flaky,团队 CI 管道里经常因为这个功能失败。
他打开项目,注意到有个新包 packages/e2e-test-utils。他好奇地点开 README,发现里面正好有他需要的 Radix UI Select 测试工具。
他决定试一试。只需要导入工具函数,然后调用 selectRadixOption(page, '残疾类型', '视力残疾') 就可以了。不需要研究 DOM 结构,不需要处理复杂的等待逻辑。
第一个小时: 张伟轻松完成了照片上传和银行卡管理的测试。这些功能之前他一直觉得很难测试,现在居然这么简单。
第二个小时: 他继续添加备注和回访功能的测试。遇到异步加载的省份选择器时,他发现工具函数已经处理了等待逻辑,直接用 selectRadixOptionAsync 就可以了。
第三个小时: 张伟写完最后一个测试,点击运行。所有测试一次性通过!他有些不敢相信,又运行了 5 次,全部稳定通过。
结果: 周二下午,张伟不仅完成了所有测试,还把工具函数推荐给了团队其他成员。他甚至有时间优化测试代码,让测试更清晰易懂。
这些用户旅程揭示了以下核心需求:
测试开发者(张伟)的需求:
新手测试开发者的需求:
QA 工程师的需求:
Tech Lead 的需求:
188-179 E2E 测试工具包是一个开发者工具库,专注于简化 Playwright E2E 测试中对 Radix UI 组件的测试。作为内部工具包,它需要提供清晰的 API、完整的类型安全和易用的开发体验。
语言与类型系统:
any 类型包结构:
packages/e2e-test-utils/
├── package.json
├── src/
│ ├── index.ts # 主导出
│ ├── radix-select.ts # Radix UI Select 工具
│ ├── file-upload.ts # 文件上传工具
│ ├── form-helper.ts # 表单辅助函数
│ ├── dialog.ts # 对话框操作
│ └── dynamic-list.ts # 动态列表管理
├── tests/ # 工具函数的单元测试
└── README.md
依赖管理:
@playwright/test (由测试项目提供)| 组件类型 | 工具函数 | 参数 | 返回值 |
|---|---|---|---|
| Radix UI Select (静态) | selectRadixOption(page, label, value) |
Page, 标签文本, 选项值 | Promise<void> |
| Radix UI Select (异步) | selectRadixOptionAsync(page, label, value, options) |
Page, 标签文本, 选项值, 等待配置 | Promise<void> |
| 文件上传 | uploadFileToField(page, selector, fileName) |
Page, 选择器, 文件名 | Promise<void> |
| 多步骤表单 | fillMultiStepForm(page, steps) |
Page, 步骤数组 | Promise<void> |
| 动态列表 | addDynamicListItem(page, itemType, data) |
Page, 项类型, 数据 | Promise<void> |
| 对话框操作 | handleDialog(page, action) |
Page, 操作类型 | Promise<void> |
在测试项目中安装:
# 在 web/ 目录下
pnpm add -D @d8d/e2e-test-utils@workspace:*
在测试文件中导入:
import { selectRadixOption, uploadFileToField } from '@d8d/e2e-test-utils';
核心工具函数签名:
/**
* 选择 Radix UI 下拉框的静态选项
* @param page Playwright Page 对象
* @param label 下拉框标签文本
* @param value 要选择的选项值
*/
export async function selectRadixOption(
page: Page,
label: string,
value: string
): Promise<void>
/**
* 选择 Radix UI 下拉框的异步加载选项
* @param page Playwright Page 对象
* @param label 下拉框标签文本
* @param value 要选择的选项值
* @param options 等待配置 (超时、重试等)
*/
export async function selectRadixOptionAsync(
page: Page,
label: string,
value: string,
options?: AsyncSelectOptions
): Promise<void>
/**
* 上传文件到指定字段
* @param page Playwright Page 对象
* @param selector 文件输入选择器
* @param fileName 要上传的文件名(相对于 fixtures 目录)
*/
export async function uploadFileToField(
page: Page,
selector: string,
fileName: string
): Promise<void>
示例 1:选择静态枚举型下拉框
import { selectRadixOption } from '@d8d/e2e-test-utils';
// 选择残疾类型
await selectRadixOption(page, '残疾类型', '视力残疾');
示例 2:选择异步加载的下拉框
import { selectRadixOptionAsync } from '@d8d/e2e-test-utils';
// 选择省份(异步加载)
await selectRadixOptionAsync(page, '省份', '广东省', {
timeout: 5000,
waitForOption: true
});
示例 3:上传照片
import { uploadFileToField } from '@d8d/e2e-test-utils';
// 上传身份证照片
await uploadFileToField(page, '[data-testid="id-card-photo-input"]', 'sample-id-card.jpg');
从现有的内联测试代码迁移到工具函数:
之前(内联代码):
// 需要手动处理 Radix UI 的复杂 DOM 结构和时序
await page.click(`text=残疾类型`);
await page.waitForSelector('[role="option"]');
await page.click(`[role="option"]:has-text("视力残疾")`);
之后(使用工具函数):
import { selectRadixOption } from '@d8d/e2e-test-utils';
await selectRadixOption(page, '残疾类型', '视力残疾');
迁移步骤:
pnpm add -D @d8d/e2e-test-utils@workspace:*等待策略:
waitForLoadState('networkidle') 处理异步加载错误处理:
测试稳定性:
可扩展性:
MVP 方法: 测试驱动 + 工具演进
核心理念:优先完成业务功能的 E2E 测试覆盖,在测试过程中自然抽象可复用工具。工具包是支持手段,不是目标本身。
资源需求:
核心用户旅程支持:
Epic A: 残疾人管理 E2E 测试 ✅ 已完成
业务测试覆盖:
测试过程中抽象的工具:
selectRadixOption() - 静态枚举型下拉框selectRadixOptionAsync() - 异步加载型下拉框uploadFileToField() - 文件上传Epic B: 区域管理 E2E 测试 🔄 当前目标
业务测试覆盖:
可能需要抽象的工具(按需):
selectCascade 不满足需求)Epic C: 订单管理 E2E 测试 📋 待开发
业务测试覆盖:
可能需要抽象的工具(按需):
e2e-test-utils 包维护 🌟 持续演进
MVP 排除的功能(留给后续版本):
Phase 2 (Post-MVP) - 业务测试扩展:
更多业务功能测试:
工具包按需演进:
开发体验增强:
质量保障:
技术风险:
业务测试风险:
市场/采用风险:
资源风险:
质量风险:
为什么 MVP 范围这样设计:
业务价值优先:直接交付业务测试覆盖,确保产品质量
工具自然演进:不预设工具需求,从实践中抽象
渐进式扩展:每个业务功能独立 Epic,可并行或串行
可测量成果:每个 Epic 都有明确的成功标准(覆盖率、稳定性)
风险可控:范围明确,时间盒限制,有降级方案
MVP 成功标志:
测试稳定性(最关键):
错误处理和诊断:
测试执行效率:
等待策略优化:
Playwright 兼容性:
测试框架集成:
Monorepo 集成:
类型安全:
any 类型代码可维护性:
文档质量:
开发者体验:
浏览器和环境:
版本兼容性: