Przeglądaj źródła

docs(story): 创建故事012.015修复数据统计API安全漏洞和路由集成

- 创建故事012.015文档,修复statistics-module和order-module企业统计API的安全漏洞
- 移除companyId查询参数,强制从认证token获取企业ID,确保企业数据隔离
- 更新史诗012文档,添加故事012-15描述和完成状态
- 修复server包路由注册缺失和类型导出问题

🤖 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 3 tygodni temu
rodzic
commit
37ca6c0e16

Plik diff jest za duży
+ 41 - 4
docs/prd/epic-012-api-supplement-for-employer-mini-program.md


+ 8 - 8
docs/prd/epic-015-talent-mini-program-api-support.md

@@ -85,14 +85,14 @@
 注:数据库迁移脚本将在上线前统一生成,开发阶段通过TypeORM自动同步。
 1. 扩展`users2`表的`user_type`枚举,新增`talent`(人才用户)类型
 2. 在`users2`表添加`person_id`字段(外键引用`disabled_person.person_id`),建立人才用户关联
-3. 在`order_person`表添加打卡相关字段:`checkin_time`(上班打卡时间)、`checkout_time`(下班打卡时间)、`checkin_status`(打卡状态:正常、迟到、早退、缺勤)
-4. 更新对应的TypeORM实体定义
-5. 验证现有数据不受影响,新增字段可为空
+3. 更新对应的TypeORM实体定义
+4. 验证现有数据不受影响,新增字段可为空
+
+**注:** 打卡相关字段(`checkin_time`、`checkout_time`、`checkin_status`)暂不添加。打卡功能目前为前端模拟实现,待后续确定接口逻辑后再考虑数据库扩展。
 
 **验收标准:**
 - [ ] `users2`表的`user_type`枚举成功扩展,新增`talent`类型
 - [ ] `users2`表成功添加`person_id`字段,现有admin用户和企业用户的该字段值为NULL
-- [ ] `order_person`表成功添加打卡相关字段,现有记录该字段值为NULL
 - [ ] TypeORM实体定义更新完成
 - [ ] 现有业务功能不受影响,测试通过
 
@@ -142,8 +142,8 @@
 - [ ] 所有接口验证用户权限,确保用户只能查询自己的数据
 - [ ] 所有接口通过单元测试和集成测试
 
-### 故事015-04:考勤记录API
-**背景:** 人才用户需要查看考勤记录、考勤统计和打卡明细,基于`order_person`表的打卡记录进行查询和统计
+### 故事015-04:考勤记录API(P2 - 延期,打卡功能前端模拟)
+**背景:** 人才用户需要查看考勤记录、考勤统计和打卡明细。当前打卡功能为前端模拟实现,待后续确定真实打卡接口逻辑后再实现后端API
 
 **任务列表:**
 1. **考勤记录查询API**(order-module扩展):
@@ -269,8 +269,8 @@
 - [ ] 消息推送集成,重要通知实时推送到小程序
 - [ ] 所有接口通过单元测试和集成测试
 
-### 故事015-08:打卡状态查询API
-**背景:** 人才用户需要查询当天的打卡状态和历史打卡记录。打卡操作由管理员在管理后台统一处理,小程序只提供打卡状态查询功能
+### 故事015-08:打卡状态查询API(P2 - 延期,打卡功能前端模拟)
+**背景:** 人才用户需要查询当天的打卡状态和历史打卡记录。当前打卡功能为前端模拟实现,待后续确定真实打卡接口逻辑后再实现后端API
 
 **任务列表:**
 1. **当天打卡状态查询API**(order-module扩展):

+ 219 - 0
docs/stories/012.015.story.md

@@ -0,0 +1,219 @@
+# 故事 012.015:数据统计API安全修复与路由集成
+
+## 状态
+Ready
+
+## 故事
+**作为**企业用户,
+**我希望**数据统计API具备完整的企业数据隔离安全性并正确注册到server路由,
+**以便**确保企业数据安全隔离,防止越权访问,并保证API实际可用。
+
+**史诗上下文**:此故事是史诗012(用人方小程序API补充与数据库扩展)的第15个故事,解决故事012.004已实现的数据统计API中存在的安全漏洞和路由集成缺失问题,确保企业用户数据安全隔离和API实际可用性。
+
+## 验收标准
+
+1. [ ] statistics-module所有统计API不再接受`companyId`查询参数,企业ID强制从认证用户的JWT token中获取
+2. [ ] order-module企业统计API(打卡统计、视频统计)同样移除`companyId`查询参数,强制从认证token获取企业ID
+3. [ ] statistics-module路由正确注册到server包,路径前缀为`/api/v1/yongren/statistics`
+4. [ ] server包添加`@d8d/allin-statistics-module`依赖,并导出`EnterpriseStatisticsRoutes`类型
+5. [ ] 更新故事011.005文档,修正API规范中不准确的安全要求和集成状态描述
+6. [ ] 所有修复通过集成测试验证,确保企业数据隔离安全要求满足
+
+## 任务 / 子任务
+
+### 任务1:修复statistics-module安全漏洞(AC:1)
+- [ ] **移除查询参数**:修改`allin-packages/statistics-module/src/schemas/statistics.schema.ts`
+  - 移除`StatisticsQuerySchema`中的`companyId`字段定义
+  - 创建`EnterpriseStatisticsQuerySchema`(空的object,仅用于中间件验证)
+- [ ] **更新路由定义**:修改`allin-packages/statistics-module/src/routes/statistics.routes.ts`
+  - 所有6个统计路由使用`EnterpriseStatisticsQuerySchema`作为查询参数schema
+  - 移除路由handler中`query.companyId || user?.companyId`的逻辑
+  - 改为强制使用`user?.companyId`(从`enterpriseAuthMiddleware`验证获取)
+  - 当`user?.companyId`为空时返回403错误(无企业权限)
+- [ ] **更新响应Schema**:确保所有响应中的`companyId`字段使用从认证用户获取的值
+- [ ] **更新服务层**:修改`allin-packages/statistics-module/src/services/statistics.service.ts`
+  - 确保所有统计方法严格使用传入的`companyId`参数(从认证用户获取)
+  - 添加企业数据隔离验证日志(可选)
+- [ ] **更新集成测试**:修改`allin-packages/statistics-module/tests/integration/statistics.integration.test.ts`
+  - 移除测试中的`companyId`查询参数传递
+  - 验证企业用户只能访问自己企业的数据
+  - 测试尝试传递`companyId`参数应被忽略或返回错误
+
+### 任务2:修复order-module企业统计API安全漏洞(AC:2)
+- [ ] **移除查询参数**:修改`allin-packages/order-module/src/routes/order-custom.routes.ts`
+  - 移除企业统计路由中的`companyId`查询参数字段
+  - 打卡统计路由:`checkinStatisticsRoute`(第582-587行)
+  - 视频分类统计路由:`videoStatisticsRoute`(第620-625行)
+  - 企业维度订单查询路由:`companyOrdersRoute`(第665-670行)
+- [ ] **更新路由handler**:修改企业专用订单自定义路由(`enterpriseOrderCustomRoutes`)
+  - 移除`query.companyId || user?.companyId`逻辑
+  - 改为强制使用`user?.companyId`
+  - 当`user?.companyId`为空时返回403错误
+- [ ] **更新服务层**:修改`allin-packages/order-module/src/services/order.service.ts`
+  - `getCheckinStatistics`、`getVideoStatistics`、`getCompanyOrders`方法应严格使用传入的`companyId`
+- [ ] **更新集成测试**:修改`allin-packages/order-module/tests/integration/order.integration.test.ts`
+  - 移除企业统计API测试中的`companyId`查询参数
+  - 验证企业用户只能访问自己企业的数据
+
+### 任务3:server包集成与路由注册(AC:3,4)
+- [ ] **添加依赖**:修改`packages/server/package.json`
+  - 在dependencies中添加`"@d8d/allin-statistics-module": "workspace:*"`
+- [ ] **导入路由**:修改`packages/server/src/index.ts`
+  - 添加导入:`import { statisticsRoutes } from '@d8d/allin-statistics-module'`
+- [ ] **注册路由**:在同一文件中添加企业专用统计路由注册
+  - 在现有企业路由注册区域(第150-154行附近)添加:
+    ```typescript
+    export const enterpriseStatisticsApiRoutes = api.route('/api/v1/yongren/statistics', statisticsRoutes)
+    ```
+- [ ] **导出类型**:在同一文件的类型导出区域(第169-172行附近)添加:
+  ```typescript
+  export type EnterpriseStatisticsRoutes = typeof enterpriseStatisticsApiRoutes
+  ```
+- [ ] **验证注册**:运行server包集成测试,验证路由正确注册
+  - 检查`/api/v1/yongren/statistics/disability-type-distribution`等路径可访问
+
+### 任务4:文档更新(AC:5)
+- [ ] **更新故事011.005文档**:修改`docs/stories/011.005.story.md`
+  - API规范部分:移除`companyId?`查询参数描述
+  - 安全要求部分:强调企业ID只能从认证token获取,不接受查询参数
+  - 集成状态部分:注明statistics路由已注册,API实际可用
+  - 客户端示例:更新为正确的安全模式
+- [ ] **更新史诗012文档**:修改`docs/prd/epic-012-api-supplement-for-employer-mini-program.md`
+  - 在故事列表中添加故事012.015
+  - 更新史诗进度和完成状态
+- [ ] **更新变更日志**:在两个文档中添加相应的变更记录
+
+### 任务5:测试验证(AC:6)
+- [ ] **运行statistics-module测试**:`cd allin-packages/statistics-module && pnpm test`
+  - 确保7个集成测试全部通过
+  - 验证安全修复后的测试场景
+- [ ] **运行order-module测试**:`cd allin-packages/order-module && pnpm test`
+  - 确保企业统计API测试通过
+  - 验证不再接受`companyId`查询参数
+- [ ] **运行server包测试**:`cd packages/server && pnpm test`
+  - 验证新路由正确注册
+  - 运行现有的企业API连通性测试
+- [ ] **端到端验证**:使用企业用户token调用统计API
+  - 验证无法通过查询参数访问其他企业数据
+  - 验证返回数据仅限当前企业关联数据
+
+## 开发笔记
+
+### 发现问题背景
+在验证故事011.005(数据统计功能实现)的API描述准确性时,发现以下问题:
+
+1. **安全漏洞**:statistics-module和order-module的企业统计API接受`companyId`查询参数,允许覆盖认证用户的企业ID,违反企业数据隔离安全要求。
+2. **路由未注册**:statistics-module虽然已实现,但未在server包中注册,导致API端点实际不可用。
+3. **类型缺失**:server包未导出`EnterpriseStatisticsRoutes`类型,前端无法获得类型安全的API客户端。
+4. **文档不准确**:故事011.005中的API描述在安全要求和集成状态方面不准确。
+
+### 安全要求参考
+根据故事012.004第158-162行的企业数据隔离实现细节:
+> **"重要安全要求:所有统计API不接受`companyId`查询参数,企业ID强制从认证用户的JWT token中获取,确保企业用户只能访问自己关联的企业数据,防止越权访问。"**
+
+当前实现违反此安全要求,必须修复。
+
+### 相关文件位置
+
+**statistics-module文件**:
+- `allin-packages/statistics-module/src/schemas/statistics.schema.ts` - Schema定义
+- `allin-packages/statistics-module/src/routes/statistics.routes.ts` - 路由定义
+- `allin-packages/statistics-module/src/services/statistics.service.ts` - 服务层
+- `allin-packages/statistics-module/tests/integration/statistics.integration.test.ts` - 集成测试
+
+**order-module文件**:
+- `allin-packages/order-module/src/routes/order-custom.routes.ts` - 企业统计路由定义
+- `allin-packages/order-module/src/services/order.service.ts` - 订单服务
+- `allin-packages/order-module/tests/integration/order.integration.test.ts` - 集成测试
+
+**server包文件**:
+- `packages/server/package.json` - 依赖配置
+- `packages/server/src/index.ts` - 路由注册和类型导出
+
+**文档文件**:
+- `docs/stories/011.005.story.md` - 前端数据统计故事
+- `docs/prd/epic-012-api-supplement-for-employer-mini-program.md` - 史诗012文档
+
+### 修复原则
+
+1. **最小权限原则**:企业用户只能访问自己关联的企业数据
+2. **安全默认原则**:默认拒绝,必须显式授权
+3. **防御性编程**:验证所有输入,包括认证上下文
+4. **向后兼容**:修复不应破坏现有API契约(响应格式不变)
+5. **测试覆盖**:所有修复必须有相应的测试验证
+
+### 企业数据隔离实现
+
+**认证流程**:
+1. 企业用户通过`enterpriseAuthMiddleware`中间件验证
+2. 中间件从JWT token中提取`companyId`并添加到context
+3. 所有企业专用API通过`c.get('user')`获取包含`companyId`的用户对象
+
+**数据过滤**:
+- 统计查询必须包含`company_id`过滤条件
+- 通过`employment_order`→`order_person`→`disabled_person`关联链过滤
+- 服务层验证查询结果的企业ID与用户企业ID匹配
+
+**错误处理**:
+- 用户无`companyId`:返回403(无企业权限)
+- 用户有`companyId`但无关联数据:返回空结果或404
+- 尝试传递`companyId`查询参数:忽略参数或返回400错误
+
+## 文件位置
+
+**statistics-module**:
+- `allin-packages/statistics-module/src/schemas/statistics.schema.ts`
+- `allin-packages/statistics-module/src/routes/statistics.routes.ts`
+- `allin-packages/statistics-module/src/services/statistics.service.ts`
+- `allin-packages/statistics-module/tests/integration/statistics.integration.test.ts`
+
+**order-module**:
+- `allin-packages/order-module/src/routes/order-custom.routes.ts`
+- `allin-packages/order-module/src/services/order.service.ts`
+- `allin-packages/order-module/tests/integration/order.integration.test.ts`
+
+**server包**:
+- `packages/server/package.json`
+- `packages/server/src/index.ts`
+
+**文档**:
+- `docs/stories/011.005.story.md`
+- `docs/stories/012.015.story.md`(本文件)
+- `docs/prd/epic-012-api-supplement-for-employer-mini-program.md`
+
+## 技术约束
+
+- **API路径规范**:企业专用统计API使用`/api/v1/yongren/statistics`前缀
+- **企业数据隔离**:严格遵循故事012.004定义的安全要求
+- **中间件依赖**:所有统计API使用`enterpriseAuthMiddleware`中间件
+- **类型安全**:server包必须导出`EnterpriseStatisticsRoutes`类型供前端使用
+- **测试要求**:所有修复必须通过集成测试验证
+- **文档同步**:相关文档必须同步更新,确保准确性
+
+## 测试要求
+
+### 安全测试场景
+1. **企业用户访问自己企业数据**:应返回正确统计结果
+2. **企业用户尝试传递companyId参数**:参数应被忽略,仍返回自己企业数据
+3. **非企业用户访问统计API**:应返回403错误
+4. **企业用户访问不存在的企业ID**:应返回空结果或404
+5. **不同企业用户访问同一API**:应返回各自企业的数据
+
+### 集成测试场景
+1. **路由注册验证**:验证`/api/v1/yongren/statistics/*`路径可访问
+2. **类型导出验证**:验证`EnterpriseStatisticsRoutes`类型可从server包导入
+3. **前端客户端集成**:验证前端可使用正确类型创建API客户端
+4. **端到端流程**:企业登录→获取token→调用统计API→返回正确数据
+
+### 性能测试场景
+1. **大数据量统计查询**:验证查询性能不受安全修复影响
+2. **并发访问**:多个企业用户同时访问统计API
+
+## 变更日志
+
+| 日期 | 版本 | 描述 | 作者 |
+|------|------|------|------|
+| 2025-12-23 | 1.0 | 初始创建(数据统计API安全修复与路由集成故事) | Claude Code |
+
+## 开发代理记录
+*此部分由开发代理在实施过程中填充*

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików