Sfoglia il codice sorgente

docs(story): 更新故事012.003状态为Approved

- 将故事状态从Draft更新为Approved
- 故事012.003:企业统计与人才扩展API

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

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 1 settimana fa
parent
commit
ed3d657a02
1 ha cambiato i file con 358 aggiunte e 0 eliminazioni
  1. 358 0
      docs/stories/012.003.story.md

+ 358 - 0
docs/stories/012.003.story.md

@@ -0,0 +1,358 @@
+# 故事 012.003:企业统计与人才扩展API
+
+## 状态
+Approved
+
+## 故事
+**作为**企业用户,
+**我希望**查看企业概览统计和人才详细信息,
+**以便**更好地管理企业人力资源和订单业务。
+
+## 验收标准
+从史诗文件复制的验收标准编号列表
+
+1. [ ] 企业概览统计接口返回正确的在职人员数、订单数等数据
+2. [ ] 企业维度人才统计接口返回分配人才列表和状态分布
+3. [ ] 人才工作历史查询接口返回个人的历史工作记录
+4. [ ] 人才薪资历史查询接口返回历史薪资记录
+5. [ ] 个人征信信息查询接口返回征信截图信息
+6. [ ] 视频关联查询接口返回按类型分类的关联视频列表
+7. [ ] 查询性能优化,添加必要的数据索引
+8. [ ] 所有接口通过单元测试和集成测试
+
+## 任务 / 子任务
+将故事分解为实施所需的具体任务和子任务。
+在相关处引用适用的验收标准编号。
+
+- [ ] 任务1:企业统计API开发(company-module扩展)(AC: 1, 2, 7)
+  - [ ] 创建企业概览统计路由:`company-statistics.route.ts`,路径为`/api/v1/yongren/company/overview`
+  - [ ] 在`company.service.ts`中添加`getCompanyOverview`方法,基于`employer_company`、`employment_order`、`order_person`表实时计算统计指标
+  - [ ] 计算指标:在职人员数(`order_person.work_status = 'working'`)、进行中订单数(`employment_order.order_status = 'in_progress'`)、已完成订单数(`employment_order.order_status = 'completed'`)、累计订单数
+  - [ ] 创建企业维度人才统计路由:`company-talents.route.ts`,路径为`/api/v1/yongren/company/{id}/talents`
+  - [ ] 在`company.service.ts`中添加`getCompanyTalents`方法,返回分配人才列表和状态分布
+  - [ ] 添加数据库索引优化查询性能(`employment_order.company_id`、`order_person.order_id`等字段索引)
+  - [ ] 创建相应的Zod Schema验证:`CompanyOverviewSchema`、`CompanyTalentResponseSchema`
+
+- [ ] 任务2:人才扩展API开发(disability-module扩展)(AC: 3, 4, 5, 6, 7)
+  - [ ] 创建工作历史查询路由:`work-history.route.ts`,路径为`/api/v1/yongren/disability-person/{id}/work-history`
+  - [ ] 在`disabled-person.service.ts`中添加`getWorkHistory`方法,基于`order_person`表关联`employment_order`表查询历史工作记录
+  - [ ] 创建薪资历史查询路由:`salary-history.route.ts`,路径为`/api/v1/yongren/disability-person/{id}/salary-history`
+  - [ ] 在`disabled-person.service.ts`中添加`getSalaryHistory`方法,基于`order_person.salary_detail`字段和`order`表查询历史薪资记录
+  - [ ] 创建个人征信信息查询路由:`credit-info.route.ts`,路径为`/api/v1/yongren/disability-person/{id}/credit-info`
+  - [ ] 在`disabled-person.service.ts`中添加`getCreditInfo`方法,基于`disabled_bank_card.file_id`关联`files`表获取征信截图信息
+  - [ ] 创建视频关联查询路由:`person-videos.route.ts`,路径为`/api/v1/yongren/disability-person/{id}/videos`
+  - [ ] 在`disabled-person.service.ts`中添加`getPersonVideos`方法,基于`order_person_asset`表(`asset_type`为视频类型)查询关联视频列表
+  - [ ] 添加数据库索引优化查询性能(`order_person.person_id`、`disabled_bank_card.person_id`、`order_person_asset.person_id`等字段索引)
+  - [ ] 创建相应的Zod Schema验证:`WorkHistorySchema`、`SalaryHistorySchema`、`CreditInfoSchema`、`PersonVideosSchema`
+
+- [ ] 任务3:API路由集成和认证中间件配置(AC: 1-6)
+  - [ ] 在`company.routes.ts`中集成新的企业统计路由
+  - [ ] 在`disabled-person.routes.ts`中集成新的人才扩展路由
+  - [ ] 配置企业用户认证中间件(使用故事012.002实现的`enterpriseAuthMiddleware`)
+  - [ ] 验证所有新增接口都需要企业用户权限(通过`company_id`验证)
+  - [ ] 确保API路径前缀符合约定:`/api/v1/yongren/`
+  - [ ] 统一错误处理,使用标准错误响应格式:`{ "success": false, "message": "...", "code": "..." }`
+
+- [ ] 任务4:数据库性能优化(AC: 7)
+  - [ ] 分析查询性能,识别需要添加索引的字段
+  - [ ] 在相关表上添加索引:`employment_order.company_id`、`order_person.order_id`、`order_person.person_id`、`disabled_bank_card.person_id`、`order_person_asset.person_id`
+  - [ ] 考虑添加复合索引优化多表关联查询
+  - [ ] 验证索引效果,确保查询响应时间符合要求(< 200ms)
+
+- [ ] 任务5:集成测试开发(AC: 8)
+  - [ ] 在企业模块集成测试中新增测试用例:`company-statistics.integration.test.ts`
+  - [ ] 测试企业概览统计接口的各种场景:有数据、无数据、不同订单状态等
+  - [ ] 测试企业维度人才统计接口的正确性
+  - [ ] 在残疾人模块集成测试中新增测试用例:`person-extension.integration.test.ts`
+  - [ ] 测试人才扩展API的所有接口:工作历史、薪资历史、征信信息、视频关联
+  - [ ] 测试企业用户权限验证:非企业用户无法访问这些接口
+  - [ ] 测试错误场景:不存在的个人ID、无效的参数等
+  - [ ] 确保测试覆盖率≥60%(集成测试要求)
+
+- [ ] 任务6:API文档完善(AC: 8)
+  - [ ] 为所有新增接口添加OpenAPI文档注释
+  - [ ] 生成TypeScript类型定义文件,供前端使用
+  - [ ] 更新模块的README文档,说明新增的企业统计和人才扩展功能
+  - [ ] 验证所有接口的OpenAPI文档生成正确
+
+## 开发笔记
+仅填充从docs文件夹中的实际工件提取的相关信息,与此故事相关:
+
+### 先前故事洞察
+故事012.001(数据库schema扩展)和012.002(企业用户认证API扩展)已完成的变更:
+
+1. **数据库schema扩展(故事012.001)**:
+   - `disabled_person`表已添加`birth_date`字段(DATE类型,可为空)[来源:docs/stories/012.001.story.md#数据模型]
+   - `order_person_asset`表的`asset_type`枚举已扩展,新增视频类型:`salary_video`、`tax_video`、`checkin_video`、`work_video` [来源:docs/stories/012.001.story.md#数据模型]
+   - `users2`表已添加`company_id`字段(可为空的外键,引用`employer_company.company_id`)[来源:docs/stories/012.001.story.md#数据模型]
+   - TypeORM实体定义已更新 [来源:docs/stories/012.001.story.md#文件列表]
+
+2. **企业用户认证(故事012.002)**:
+   - 企业用户手机号密码登录接口已实现:`POST /api/v1/yongren/auth/login` [来源:docs/stories/012.002.story.md#文件列表]
+   - 企业用户退出登录接口已实现:`POST /api/v1/yongren/auth/logout` [来源:docs/stories/012.002.story.md#文件列表]
+   - 获取企业用户信息接口已实现:`GET /api/v1/yongren/auth/me`,返回包含企业详情的用户信息 [来源:docs/stories/012.002.story.md#文件列表]
+   - 企业用户认证中间件已实现:`enterpriseAuthMiddleware` [来源:docs/stories/012.002.story.md#文件列表]
+   - 企业用户权限验证逻辑:基于`users2`表的`company_id`字段验证 [来源:docs/stories/012.002.story.md#文件列表]
+
+### 数据模型
+基于现有实体定义和架构文档:
+
+**公司实体**(`allin-packages/company-module/src/entities/company.entity.ts`):
+- 表名:`employer_company`(@Entity('employer_company'))[来源:allin-packages/company-module/src/entities/company.entity.ts:4]
+- 主键:`id`(映射到`company_id`列)[来源:allin-packages/company-module/src/entities/company.entity.ts:7-13]
+- 字段:`companyName`、`contactPerson`、`contactPhone`、`contactEmail`、`address`、`status`等 [来源:allin-packages/company-module/src/entities/company.entity.ts:24-92]
+- 平台关联:`platformId`字段关联Platform实体 [来源:allin-packages/company-module/src/entities/company.entity.ts:15-22]
+
+**就业订单实体**(`allin-packages/order-module/src/entities/employment-order.entity.ts`):
+- 表名:`employment_order`(@Entity('employment_order'))[来源:allin-packages/order-module/src/entities/employment-order.entity.ts:4]
+- 主键:`id`(映射到`order_id`列)[来源:allin-packages/order-module/src/entities/employment-order.entity.ts:7-13]
+- 字段:`orderName`、`platformId`、`companyId`、`channelId`、`orderStatus`、`workStatus`等 [来源:allin-packages/order-module/src/entities/employment-order.entity.ts:15-104]
+- 公司关联:`companyId`字段关联Company实体 [来源:allin-packages/order-module/src/entities/employment-order.entity.ts:32-38]
+
+**订单人员实体**(`allin-packages/order-module/src/entities/order-person.entity.ts`):
+- 表名:`order_person`(@Entity('order_person'))[来源:allin-packages/order-module/src/entities/order-person.entity.ts:7]
+- 主键:`id`(映射到`op_id`列)[来源:allin-packages/order-module/src/entities/order-person.entity.ts:8-14]
+- 字段:`orderId`、`personId`、`joinDate`、`salaryDetail`、`workStatus`等 [来源:allin-packages/order-module/src/entities/order-person.entity.ts:16-73]
+- 关联:`orderId`关联EmploymentOrder,`personId`关联DisabledPerson [来源:allin-packages/order-module/src/entities/order-person.entity.ts:75-83]
+
+**残疾人实体**(`allin-packages/disability-module/src/entities/disabled-person.entity.ts`):
+- 表名:`disabled_person`(@Entity('disabled_person'))[来源:allin-packages/disability-module/src/entities/disabled-person.entity.ts:8]
+- 主键:`id`(映射到`person_id`列)[来源:allin-packages/disability-module/src/entities/disabled-person.entity.ts:9-14]
+- 字段:`name`、`gender`、`idCard`、`disabilityId`、`disabilityType`、`disabilityLevel`、`birthDate`(新增)、`phone`、`jobStatus`等 [来源:allin-packages/disability-module/src/entities/disabled-person.entity.ts:16-217]
+- 新增字段:`birthDate`(DATE类型,可为空)[来源:allin-packages/disability-module/src/entities/disabled-person.entity.ts:97-103]
+
+**订单人员资产实体**(`allin-packages/order-module/src/entities/order-person-asset.entity.ts`):
+- 表名:`order_person_asset`(@Entity('order_person_asset'))[来源:allin-packages/order-module/src/entities/order-person-asset.entity.ts:7]
+- 主键:`id`(映射到`op_id`列)[来源:allin-packages/order-module/src/entities/order-person-asset.entity.ts:8-14]
+- 字段:`orderId`、`personId`、`assetType`、`assetFileType`、`fileId`等 [来源:allin-packages/order-module/src/entities/order-person-asset.entity.ts:16-81]
+- 枚举扩展:`asset_type`枚举已扩展,新增视频类型:`salary_video`、`tax_video`、`checkin_video`、`work_video` [来源:allin-packages/order-module/src/entities/order-person-asset.entity.ts:33-39]
+
+**残疾人银行卡实体**(`allin-packages/disability-module/src/entities/disabled-bank-card.entity.ts`):
+- 表名:`disabled_bank_card`(@Entity('disabled_bank_card'))[来源:allin-packages/disability-module/src/entities/disabled-bank-card.entity.ts:7]
+- 主键:`id`(映射到`card_id`列)[来源:allin-packages/disability-module/src/entities/disabled-bank-card.entity.ts:8-13]
+- 字段:`personId`、`subBankName`、`bankNameId`、`cardNumber`、`cardholderName`、`fileId`等 [来源:allin-packages/disability-module/src/entities/disabled-bank-card.entity.ts:15-82]
+- 文件关联:通过`fileId`字段关联`files`表存储征信截图 [来源:allin-packages/disability-module/src/entities/disabled-bank-card.entity.ts:68-73]
+
+### API规范
+**API路径约定**(来自史诗012):
+- 所有用人方小程序的API路径统一使用 `api/v1/yongren` 前缀 [来源:docs/prd/epic-012-api-supplement-for-employer-mini-program.md#api路径约定]
+- 现有管理后台API保持不变,新增的用人方小程序API使用独立的路由前缀
+- 示例:企业概览统计:`GET /api/v1/yongren/company/overview` [来源:docs/prd/epic-012-api-supplement-for-employer-mini-program.md#api路径约定]
+
+**新增接口规范**:
+1. **企业概览统计**:
+   - 路径:`GET /api/v1/yongren/company/overview`
+   - 请求头:`Authorization: Bearer <企业用户token>`
+   - 响应:`{ "在职人员数": 10, "进行中订单数": 5, "已完成订单数": 20, "累计订单数": 25 }`
+   - 状态码:200成功,401未授权,403权限不足
+
+2. **企业维度人才统计**:
+   - 路径:`GET /api/v1/yongren/company/{id}/talents`
+   - 请求头:`Authorization: Bearer <企业用户token>`
+   - 响应:`{ "人才列表": [...], "状态分布": { "working": 10, "on_leave": 2, "left": 3 } }`
+   - 状态码:200成功,401未授权,403权限不足,404公司不存在
+
+3. **人才工作历史查询**:
+   - 路径:`GET /api/v1/yongren/disability-person/{id}/work-history`
+   - 请求头:`Authorization: Bearer <企业用户token>`
+   - 响应:`{ "工作历史": [{ "订单名称": "...", "入职日期": "...", "离职日期": "...", "工作状态": "..." }] }`
+   - 状态码:200成功,401未授权,404人员不存在
+
+4. **人才薪资历史查询**:
+   - 路径:`GET /api/v1/yongren/disability-person/{id}/salary-history`
+   - 请求头:`Authorization: Bearer <企业用户token>`
+   - 响应:`{ "薪资历史": [{ "月份": "...", "基本工资": "...", "补贴": "...", "扣款": "...", "实发工资": "..." }] }`
+   - 状态码:200成功,401未授权,404人员不存在
+
+5. **个人征信信息查询**:
+   - 路径:`GET /api/v1/yongren/disability-person/{id}/credit-info`
+   - 请求头:`Authorization: Bearer <企业用户token>`
+   - 响应:`{ "征信信息": [{ "文件ID": "...", "文件URL": "...", "上传时间": "...", "文件类型": "..." }] }`
+   - 状态码:200成功,401未授权,404人员不存在
+
+6. **视频关联查询**:
+   - 路径:`GET /api/v1/yongren/disability-person/{id}/videos`
+   - 请求头:`Authorization: Bearer <企业用户token>`
+   - 响应:`{ "视频列表": [{ "视频类型": "salary_video", "文件ID": "...", "文件URL": "...", "上传时间": "..." }] }`
+   - 状态码:200成功,401未授权,404人员不存在
+
+**认证要求**:
+- 所有新增接口需要企业用户权限验证
+- 使用故事012.002实现的`enterpriseAuthMiddleware`中间件
+- 验证用户`company_id`字段不为空
+- 企业用户只能访问自己关联的企业数据
+
+### 组件规范
+不适用(后端API故事)。
+
+### 文件位置
+基于项目结构和后端模块包规范 [来源:architecture/source-tree.md, architecture/backend-module-package-standards.md]:
+
+**公司模块位置**:
+- `allin-packages/company-module/src/routes/` - 现有路由文件位置
+- `allin-packages/company-module/src/services/` - 服务层文件位置
+- `allin-packages/company-module/src/schemas/` - Schema验证文件位置
+- `allin-packages/company-module/tests/integration/` - 集成测试位置
+
+**残疾人模块位置**:
+- `allin-packages/disability-module/src/routes/` - 现有路由文件位置
+- `allin-packages/disability-module/src/services/` - 服务层文件位置
+- `allin-packages/disability-module/src/schemas/` - Schema验证文件位置
+- `allin-packages/disability-module/tests/integration/` - 集成测试位置
+
+**订单模块位置**:
+- `allin-packages/order-module/src/entities/` - 相关实体定义位置
+- `allin-packages/order-module/src/services/` - 相关服务层位置
+
+**新文件建议位置**:
+1. **企业统计API文件**:
+   - `allin-packages/company-module/src/routes/company-statistics.route.ts` - 企业统计路由
+   - `allin-packages/company-module/src/schemas/company-statistics.schema.ts` - 企业统计Schema
+   - `allin-packages/company-module/tests/integration/company-statistics.integration.test.ts` - 企业统计集成测试
+
+2. **人才扩展API文件**:
+   - `allin-packages/disability-module/src/routes/person-extension.route.ts` - 人才扩展路由
+   - `allin-packages/disability-module/src/schemas/person-extension.schema.ts` - 人才扩展Schema
+   - `allin-packages/disability-module/tests/integration/person-extension.integration.test.ts` - 人才扩展集成测试
+
+### 技术约束
+基于技术栈和架构文档 [来源:architecture/tech-stack.md, architecture/backend-module-package-standards.md]:
+
+1. **技术栈约束**:
+   - 后端框架:Hono 4.8.5 [来源:architecture/tech-stack.md#现有技术栈维护]
+   - 数据库ORM:TypeORM 0.3.25 [来源:architecture/tech-stack.md#现有技术栈维护]
+   - 数据库:PostgreSQL 17 [来源:architecture/tech-stack.md#现有技术栈维护]
+   - 认证:JWT 9.0.2 [来源:architecture/tech-stack.md#现有技术栈维护]
+   - 测试框架:Vitest [来源:architecture/tech-stack.md#新技术添加]
+
+2. **API设计约束**:
+   - 使用Zod OpenAPI进行Schema验证和文档生成 [来源:architecture/backend-module-package-standards.md#验证系统规范]
+   - 遵循现有的路由聚合模式 [来源:architecture/backend-module-package-standards.md#路由层规范]
+   - 错误响应格式统一:`{ "success": false, "message": "...", "code": "..." }` [来源:architecture/backend-module-package-standards.md#错误处理规范]
+
+3. **性能约束**:
+   - 查询响应时间 < 200ms(现有测试基准)
+   - 需要添加适当的数据库索引优化查询性能
+   - 实时统计查询需要优化关联查询性能
+
+4. **安全约束**:
+   - 企业用户只能访问自己关联的企业数据
+   - 需要验证`company_id`匹配性
+   - 使用企业用户专属的认证中间件
+
+5. **向后兼容性**:
+   - 现有管理后台API保持不变
+   - 新增接口使用独立的路由前缀`/api/v1/yongren`
+   - 现有业务逻辑不受影响
+
+### 项目结构说明
+基于monorepo结构和模块化包架构 [来源:architecture/source-tree.md]:
+
+1. **模块化架构**:
+   - `allin-packages/` - Allin系统移植的业务模块
+   - `company-module`、`disability-module`、`order-module`等业务模块已存在
+   - 企业统计API在`company-module`中扩展
+   - 人才扩展API在`disability-module`中扩展
+
+2. **依赖关系**:
+   - `company-module`依赖`order-module`查询订单数据
+   - `disability-module`依赖`order-module`查询工作历史和视频数据
+   - 所有模块依赖核心的`auth-module`进行认证
+
+3. **路由结构**:
+   - 现有公司路由:`/company/*`(管理后台API)
+   - 新增企业统计路由:`/api/v1/yongren/company/overview`、`/api/v1/yongren/company/{id}/talents`
+   - 现有残疾人路由:`/disabled-person/*`(管理后台API)
+   - 新增人才扩展路由:`/api/v1/yongren/disability-person/{id}/*`
+
+4. **代码组织**:
+   - 遵循后端模块包规范 [来源:architecture/backend-module-package-standards.md]
+   - 实体、服务、路由、Schema分层清晰
+   - 测试文件与源码对应,位于`tests/integration/`目录
+
+### 测试
+列出开发者需要遵循的相关测试标准:
+
+#### 测试文件位置
+基于测试策略文档 [来源:architecture/testing-strategy.md]:
+- 集成测试:`allin-packages/company-module/tests/integration/company-statistics.integration.test.ts` [来源:architecture/testing-strategy.md#集成测试]
+- 集成测试:`allin-packages/disability-module/tests/integration/person-extension.integration.test.ts` [来源:architecture/testing-strategy.md#集成测试]
+- 测试基础设施:使用`@d8d/shared-test-util`提供的工具 [来源:architecture/testing-strategy.md#集成测试]
+
+#### 测试标准
+基于测试策略文档 [来源:architecture/testing-strategy.md]:
+- 测试框架:Vitest [来源:architecture/testing-strategy.md#单元测试]
+- API测试:使用`hono/testing`进行API端点测试 [来源:architecture/testing-strategy.md#集成测试]
+- 数据库测试:使用测试数据库和事务回滚 [来源:architecture/testing-strategy.md#数据库测试策略]
+- 覆盖率要求:集成测试 ≥ 60% [来源:architecture/testing-strategy.md#测试覆盖率标准]
+- 测试数据管理:使用`TestDataFactory`创建测试数据
+
+#### 测试框架和模式
+基于现有集成测试模式:
+1. **集成测试模式**:
+   - 使用`setupIntegrationDatabaseHooksWithEntities`设置测试数据库
+   - 使用`testClient`创建Hono测试客户端
+   - 每个测试前清理测试数据,避免唯一性约束冲突
+   - 使用`TestDataFactory`创建一致的测试数据
+
+2. **测试场景覆盖**:
+   - 成功场景:企业用户访问统计接口、人才扩展接口
+   - 失败场景:非企业用户访问、不存在的ID、无效参数
+   - 权限场景:企业用户只能访问自己关联的数据
+   - 性能场景:查询响应时间测试
+
+3. **断言模式**:
+   - 验证HTTP状态码:`expect(response.status).toBe(200)`
+   - 验证响应数据结构:`expect(responseData).toHaveProperty('在职人员数')`
+   - 验证业务逻辑:`expect(responseData.在职人员数).toBeGreaterThan(0)`
+   - 验证错误消息:`expect(responseData.message).toContain('权限不足')`
+
+#### 此故事的特定测试要求
+1. **企业统计API测试**:
+   - 测试企业概览统计接口返回正确的统计指标
+   - 测试企业维度人才统计接口返回正确的数据
+   - 测试非企业用户无法访问企业统计接口
+   - 测试不同企业用户只能访问自己企业的数据
+
+2. **人才扩展API测试**:
+   - 测试工作历史查询接口返回正确的历史记录
+   - 测试薪资历史查询接口返回正确的薪资数据
+   - 测试个人征信信息查询接口返回正确的文件信息
+   - 测试视频关联查询接口按类型分类返回视频列表
+   - 测试不存在的个人ID返回404错误
+   - 测试非企业用户无法访问人才扩展接口
+
+3. **性能测试**:
+   - 测试统计查询响应时间 < 200ms
+   - 测试数据库索引效果
+   - 测试多表关联查询性能
+
+4. **权限测试**:
+   - 测试企业用户权限验证正确
+   - 测试跨企业访问数据被拒绝
+   - 测试认证中间件正确拦截非企业用户
+
+## 变更日志
+跟踪对此故事文档所做的更改
+
+| 日期 | 版本 | 描述 | 作者 |
+|------|------|------|------|
+| 2025-12-16 | 1.0 | 初始故事创建 | Bob(Scrum Master) |
+
+## 开发代理记录
+此部分由开发代理在实施过程中填充
+
+### 使用的代理模型
+
+
+### 调试日志引用
+
+
+### 完成笔记列表
+
+
+### 文件列表
+
+
+## QA结果
+来自QA代理对已完成故事实施的QA审查结果