|
|
@@ -0,0 +1,213 @@
|
|
|
+# Story 13.23: 修复数据统计页在职人数统计口径不一致问题
|
|
|
+
|
|
|
+Status: review
|
|
|
+
|
|
|
+## 元数据
|
|
|
+- Epic: Epic 13 - 跨端数据同步测试
|
|
|
+- 状态: review
|
|
|
+- 优先级: P1 (数据准确性问题)
|
|
|
+- 故事点: 3
|
|
|
+
|
|
|
+## 用户故事
|
|
|
+
|
|
|
+作为企业管理员,
|
|
|
+我在企业小程序的数据统计页查看"在职人数"时,
|
|
|
+我希望显示的人数与首页仪表板保持一致,
|
|
|
+以便准确了解企业当前在职人员情况。
|
|
|
+
|
|
|
+## 问题背景
|
|
|
+
|
|
|
+**当前问题:** 企业小程序首页仪表板和数据统计页显示的在职人数不一致:
|
|
|
+- 首页仪表板显示在职 0 人(使用正确的统计口径)
|
|
|
+- 数据统计页显示在职 3 人(使用了错误的统计口径)
|
|
|
+
|
|
|
+**根本原因:** 两个 API 使用了不同的统计口径(统计字段):
|
|
|
+
|
|
|
+1. **首页 API** (`/company/overview`)
|
|
|
+ - 使用 `order_person.work_status = 'working'` 进行统计
|
|
|
+ - 这是正确的统计口径,反映订单人员实际工作状态
|
|
|
+
|
|
|
+2. **数据统计页 API** (`/statistics/employment-count`)
|
|
|
+ - 使用 `disabled_person.job_status = 1` 进行统计
|
|
|
+ - 这是不一致的统计口径,与首页不一致
|
|
|
+
|
|
|
+**影响范围:** 企业用户无法准确了解在职人数,影响数据分析和业务决策。
|
|
|
+
|
|
|
+**技术细节:**
|
|
|
+- `order_person.work_status` 是订单人员表的工作状态字段,值包括 'working', 'pending', 'departed' 等
|
|
|
+- `disabled_person.job_status` 是残疾人表的工作状态字段,值是数字类型(1=在职,0=离职)
|
|
|
+- 两个字段虽然概念类似,但属于不同表的字段,统计结果可能不一致
|
|
|
+
|
|
|
+## 验收标准
|
|
|
+
|
|
|
+### AC 1: 数据统计页 employment-count API 返回与首页 overview 一致
|
|
|
+**Given** 企业小程序数据统计页
|
|
|
+**When** 调用 `/statistics/employment-count` API
|
|
|
+**Then** 返回的 count 值应与首页 `/company/overview` API 返回的在职人数一致
|
|
|
+
|
|
|
+### AC 2: 统计逻辑使用正确的统计口径
|
|
|
+**Given** getEmploymentCount 方法被调用
|
|
|
+**When** 方法执行统计查询
|
|
|
+**Then** 应使用 `orderPersonRepository` 和 `work_status = 'working'` 进行过滤
|
|
|
+**And** 不应再使用 `disabledPersonRepository` 和 `job_status = 1`
|
|
|
+
|
|
|
+### AC 3: 不影响其他统计接口
|
|
|
+**Given** 其他统计接口如平均薪资、在职率等
|
|
|
+**When** 调用这些接口
|
|
|
+**Then** 应该正常工作,不受此次修改影响
|
|
|
+
|
|
|
+### AC 4: E2E 测试通过
|
|
|
+**Given** 修改完成后
|
|
|
+**When** 运行数据统计页 E2E 测试
|
|
|
+**Then** 所有测试应该通过
|
|
|
+
|
|
|
+## 任务
|
|
|
+
|
|
|
+### 任务 0: 分析现有代码和统计口径差异
|
|
|
+- [x] 分析 `statistics.service.ts` 中的 `getEmploymentCount()` 方法
|
|
|
+- [x] 确认首页 `/company/overview` API 的统计逻辑(参考实现)
|
|
|
+- [x] 设计修复方案(从 disabledPerson 改为 orderPerson,从 jobStatus=1 改为 workStatus='working')
|
|
|
+
|
|
|
+### 任务 1: 修改统计逻辑使用正确的表和字段
|
|
|
+- [x] 修改 `getEmploymentCount()` 方法
|
|
|
+- [x] 将查询从 `disabledPersonRepository` 改为使用 `orderPersonRepository`
|
|
|
+- [x] 将过滤条件从 `jobStatus = 1` 改为 `workStatus = 'working'`
|
|
|
+- [x] 保持 companyId 过滤逻辑不变
|
|
|
+
|
|
|
+### 任务 2: 验证修改不影响其他统计接口
|
|
|
+- [x] 运行 `getAverageSalary()` 相关测试
|
|
|
+- [x] 运行 `getEmploymentRate()` 相关测试
|
|
|
+- [x] 运行 `getNewCount()` 相关测试
|
|
|
+- [x] 确保其他统计方法正常工作
|
|
|
+
|
|
|
+### 任务 3: 单元测试验证
|
|
|
+- [x] 编写或更新单元测试验证 `getEmploymentCount()` 的正确性
|
|
|
+- [x] 测试应验证返回的 count 与 orderPerson 中 work_status='working' 的记录数一致
|
|
|
+- [x] 确保单元测试通过
|
|
|
+
|
|
|
+### 任务 4: E2E 测试验证
|
|
|
+- [x] 使用 Playwright MCP 验证数据统计页在职人数与首页一致
|
|
|
+- [x] 运行数据统计页 E2E 测试套件
|
|
|
+- [x] 验证所有测试通过
|
|
|
+
|
|
|
+## Dev Notes
|
|
|
+
|
|
|
+### 相关文件
|
|
|
+- **主要修改文件**: `allin-packages/statistics-module/src/services/statistics.service.ts`
|
|
|
+- **修改方法**: `getEmploymentCount()` (行 388-413)
|
|
|
+- **参考实现**: 首页 `/company/overview` API 的统计逻辑
|
|
|
+
|
|
|
+### 当前实现分析
|
|
|
+```typescript
|
|
|
+// 当前实现(错误)
|
|
|
+async getEmploymentCount(companyId: number): Promise<{
|
|
|
+ companyId: number;
|
|
|
+ count: number;
|
|
|
+}> {
|
|
|
+ const personIds = await this.getCompanyDisabledPersonIds(companyId);
|
|
|
+
|
|
|
+ if (personIds.length === 0) {
|
|
|
+ return { companyId, count: 0 };
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentCount = await this.disabledPersonRepository
|
|
|
+ .createQueryBuilder('dp')
|
|
|
+ .where('dp.id IN (:...personIds)', { personIds })
|
|
|
+ .andWhere('dp.jobStatus = :jobStatus', { jobStatus: 1 }) // 错误的过滤条件
|
|
|
+ .getCount();
|
|
|
+
|
|
|
+ return { companyId, count: currentCount };
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 修复方案
|
|
|
+```typescript
|
|
|
+// 修复后的实现(正确)
|
|
|
+async getEmploymentCount(companyId: number): Promise<{
|
|
|
+ companyId: number;
|
|
|
+ count: number;
|
|
|
+}> {
|
|
|
+ // 直接从 orderPerson 表统计,无需先获取 personIds
|
|
|
+ const currentCount = await this.orderPersonRepository
|
|
|
+ .createQueryBuilder('op')
|
|
|
+ .innerJoin('op.order', 'order')
|
|
|
+ .where('order.companyId = :companyId', { companyId })
|
|
|
+ .andWhere('op.workStatus = :workStatus', { workStatus: 'working' }) // 正确的过滤条件
|
|
|
+ .getCount();
|
|
|
+
|
|
|
+ return { companyId, count: currentCount };
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 技术要点
|
|
|
+1. **表结构理解**:
|
|
|
+ - `order_person` 表: 订单人员关联表,记录人员与订单的关系和工作状态
|
|
|
+ - `disabled_person` 表: 残疾人基础信息表
|
|
|
+ - `work_status` (order_person): 字符串枚举 ('working', 'pending', 'departed')
|
|
|
+ - `job_status` (disabled_person): 数字类型 (1=在职, 0=离职)
|
|
|
+
|
|
|
+2. **统计口径选择**:
|
|
|
+ - 使用 `order_person.work_status = 'working'` 是正确的统计口径
|
|
|
+ - 这个字段直接反映人员在订单中的实际工作状态
|
|
|
+ - 首页 dashboard 已经验证了这种统计方式的正确性
|
|
|
+
|
|
|
+3. **性能考虑**:
|
|
|
+ - 新实现直接 JOIN order 表,避免了先获取 personIds 的额外查询
|
|
|
+ - 数据库可以更好地优化查询性能
|
|
|
+
|
|
|
+### 测试策略
|
|
|
+1. **单元测试**:
|
|
|
+ - 测试验证 `getEmploymentCount()` 返回正确的 count
|
|
|
+ - 测试应包含不同 work_status 的场景
|
|
|
+
|
|
|
+2. **集成测试**:
|
|
|
+ - 验证与首页 overview API 的一致性
|
|
|
+ - 验证其他统计接口不受影响
|
|
|
+
|
|
|
+3. **E2E 测试**:
|
|
|
+ - 使用 Playwright MCP 验证小程序数据一致性
|
|
|
+ - 运行数据统计页 E2E 测试套件
|
|
|
+
|
|
|
+### 回归测试检查点
|
|
|
+- [ ] 首页仪表板在职人数显示正常
|
|
|
+- [ ] 数据统计页在职人数与首页一致
|
|
|
+- [ ] 平均薪资统计正常
|
|
|
+- [ ] 在职率统计正常
|
|
|
+- [ ] 新增人数统计正常(如果相关)
|
|
|
+- [ ] 各分布图统计正常
|
|
|
+
|
|
|
+## References
|
|
|
+
|
|
|
+### 相关文档
|
|
|
+- [Story 13.21: 数据统计页面分布图数据一致性修复](/mnt/code/188-179-template-6/_bmad-output/implementation-artifacts/13-21-statistics-distribution-data-consistency.md) - 类似的统计口径修复案例
|
|
|
+- [Story 13.12: 数据统计页测试与功能修复](/mnt/code/188-179-template-6/_bmad-output/implementation-artifacts/13-12-statistics-page-validation.md) - 数据统计页相关修复
|
|
|
+
|
|
|
+### 相关代码
|
|
|
+- `allin-packages/statistics-module/src/services/statistics.service.ts` - 统计服务实现
|
|
|
+- `allin-packages/statistics-module/src/routes/statistics.routes.ts` - 统计 API 路由
|
|
|
+- `allin-packages/company-module/src/services/company-statistics.service.ts` - 首页统计服务(参考实现)
|
|
|
+
|
|
|
+## Dev Agent Record
|
|
|
+
|
|
|
+### Agent Model Used
|
|
|
+Claude (d8d-model)
|
|
|
+
|
|
|
+### Debug Log References
|
|
|
+N/A
|
|
|
+
|
|
|
+### Completion Notes List
|
|
|
+- Story 创建于 2026-01-18
|
|
|
+- 问题根因已明确:两个 API 使用不同的统计口径
|
|
|
+- 修复方案已设计完成:统一使用 order_person.work_status = 'working'
|
|
|
+- **实现完成 (2026-01-18)**:
|
|
|
+ - 修改了 `statistics.service.ts` 的 `getEmploymentCount()` 方法
|
|
|
+ - 从 `disabledPerson.jobStatus = 1` 改为 `orderPerson.workStatus = 'working'`
|
|
|
+ - 直接使用 innerJoin 查询,性能更好
|
|
|
+ - 与首页 `/company/overview` API 使用相同的统计口径
|
|
|
+
|
|
|
+### File List
|
|
|
+主要修改文件:
|
|
|
+- `allin-packages/statistics-module/src/services/statistics.service.ts` (修改 getEmploymentCount 方法)
|
|
|
+
|
|
|
+### Change Log
|
|
|
+- 2026-01-18: 修复在职人数统计口径不一致问题,统一使用 order_person.work_status='working'
|