Browse Source

📝 docs(story-007.005): 重新创建订单管理模块移植故事

- 吸取之前移植故事的经验教训
- 在任务中标注迁移文件的路径和参考对照文件的路径
- 包含完整的循环依赖处理方案
- 详细描述文件模块集成步骤
- 状态更新为Ready for Development

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

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 1 week ago
parent
commit
838e475418
1 changed files with 331 additions and 0 deletions
  1. 331 0
      docs/stories/007.005.transplant-order-management-module.story.md

+ 331 - 0
docs/stories/007.005.transplant-order-management-module.story.md

@@ -0,0 +1,331 @@
+# Story 007.005: 移植订单管理模块(order → @d8d/allin-order-module)
+
+## Status
+Ready for Development
+
+## Story
+**As a** 开发者,
+**I want** 将order模块从allin_system-master移植为独立包@d8d/allin-order-module,完成实体重构、文件模块集成和循环依赖处理,
+**so that** 我们可以将Allin系统的订单管理功能集成到当前项目中,遵循现有的模块化架构和编码标准,并实现与@d8d/file-module的文件集成和与@d8d/allin-disability-module的依赖解耦。
+
+## Acceptance Criteria
+1. 创建`allin-packages/order-module`目录结构
+2. 完成实体转换:3个实体(EmploymentOrder、OrderPerson、OrderPersonAsset)转换
+3. **枚举常量集成**:使用`@d8d/allin-enums`包中的`OrderStatus`和`WorkStatus`枚举
+4. **文件实体集成**:修改`OrderPersonAsset`实体,添加`fileId`字段引用`File`实体
+5. **解决循环依赖**:与disability-module的相互引用处理
+6. 完成服务层转换:订单业务逻辑,包含资产文件关联处理
+7. 完成路由层转换:Hono路由实现,API接收`fileId`参数
+8. 完成验证系统转换:订单相关验证,包含文件类型验证和枚举值验证
+9. 配置package.json:依赖管理,包含对`@d8d/file-module`和`@d8d/allin-enums`的依赖
+10. 编写API集成测试:覆盖订单全生命周期,包含资产文件关联测试和枚举值测试
+11. 通过类型检查和基本测试验证
+12. 验证循环依赖解决方案的有效性
+
+## Tasks / Subtasks
+
+### 1. 创建包结构和配置文件 (AC: 1)
+- [ ] 创建目录:`allin-packages/order-module/`
+- [ ] 初始化package.json:参考`allin-packages/salary-module/package.json`
+  - **参考文件**: `allin-packages/salary-module/package.json`
+  - **修改点**: 包名改为`@d8d/allin-order-module`,添加对`@d8d/file-module`和`@d8d/allin-enums`的依赖
+  - **关键依赖**: `@d8d/file-module`, `@d8d/allin-enums`, `@d8d/core-module`, `@d8d/shared-crud`, `@d8d/shared-utils`
+  - **注意吸取经验**: 根据故事007.006的经验,需要在`pnpm-workspace.yaml`中添加`allin-packages/*`配置
+- [ ] 配置tsconfig.json:参考`allin-packages/salary-module/tsconfig.json`
+  - **参考文件**: `allin-packages/salary-module/tsconfig.json`
+- [ ] 配置vitest.config.ts:参考`allin-packages/salary-module/vitest.config.ts`
+  - **参考文件**: `allin-packages/salary-module/vitest.config.ts`
+- [ ] 配置workspace:在根目录`pnpm-workspace.yaml`中添加`allin-packages/order-module`
+  - **修改文件**: `/mnt/code/188-179-template-6/pnpm-workspace.yaml`
+  - **添加内容**: `'allin-packages/order-module'`
+
+### 2. 迁移实体文件 (AC: 2, 3, 4)
+
+#### 2.1 迁移EmploymentOrder实体
+**源文件路径**: `allin_system-master/server/src/order/employment_order.entity.ts`
+**目标文件路径**: `allin-packages/order-module/src/entities/employment-order.entity.ts`
+**参考对照文件**:
+- `allin-packages/salary-module/src/entities/salary-level.entity.ts`(实体结构)
+- `allin-packages/enums/src/enums/order-status.enum.ts`(枚举引用)
+- `allin-packages/enums/src/enums/work-status.enum.ts`(枚举引用)
+
+**修改要点**:
+1. 将主键字段名从`orderId`改为`id`以遵循GenericCrudService约定
+2. 将`orderStatus`字段类型从`string`改为`OrderStatus`枚举
+3. 添加`workStatus`字段,使用`WorkStatus`枚举
+4. 将下划线字段名转换为驼峰命名:`order_name` → `orderName`, `platform_id` → `platformId`等
+5. 添加详细的TypeORM装饰器配置
+6. 保持数据库表名不变:`@Entity('employment_order')`
+
+#### 2.2 迁移OrderPerson实体
+**源文件路径**: `allin_system-master/server/src/order/order_person.entity.ts`
+**目标文件路径**: `allin-packages/order-module/src/entities/order-person.entity.ts`
+**参考对照文件**:
+- `allin-packages/disability-module/src/entities/disabled-person.entity.ts`(实体结构)
+- **注意循环依赖**: 原实体引用`DisabledPerson`实体,需要解耦处理
+
+**修改要点**:
+1. 将主键字段名从`opId`改为`id`
+2. 将`personId`字段类型保持为`number`(不直接引用DisabledPerson实体)
+3. 将下划线字段名转换为驼峰命名
+4. 添加详细的TypeORM装饰器配置
+5. 保持数据库表名不变:`@Entity('order_person')`
+6. **循环依赖处理**: 移除对`DisabledPerson`的直接引用,改为使用`personId: number`
+
+#### 2.3 迁移OrderPersonAsset实体
+**源文件路径**: `allin_system-master/server/src/order/order_person_asset.entity.ts`
+**目标文件路径**: `allin-packages/order-module/src/entities/order-person-asset.entity.ts`
+**参考对照文件**:
+- `allin-packages/disability-module/src/entities/disabled-photo.entity.ts`(文件实体集成)
+- `packages/file-module/src/entities/file.entity.ts`(文件实体引用)
+
+**修改要点**:
+1. 将主键字段名从`opId`改为`id`
+2. **文件模块集成**: 移除`assetUrl: string`字段,添加`fileId: number`字段
+3. 添加与File实体的关联关系:`@ManyToOne(() => File)`
+4. **循环依赖处理**: 移除对`DisabledPerson`的直接引用,改为使用`personId: number`
+5. 将`AssetType`和`AssetFileType`枚举移到实体文件中(模块内部枚举)
+6. 将下划线字段名转换为驼峰命名
+7. 添加详细的TypeORM装饰器配置
+8. 保持数据库表名不变:`@Entity('order_person_asset')`
+
+### 3. 创建DTO和Schema文件 (AC: 8)
+**目标文件路径**:
+- `allin-packages/order-module/src/dto/create-order.dto.ts`
+- `allin-packages/order-module/src/dto/update-order.dto.ts`
+- `allin-packages/order-module/src/dto/create-order-person.dto.ts`
+- `allin-packages/order-module/src/dto/create-order-person-asset.dto.ts`
+- `allin-packages/order-module/src/schemas/order.schema.ts`
+
+**参考对照文件**:
+- `allin-packages/salary-module/src/dto/create-salary.dto.ts`(DTO结构)
+- `allin-packages/salary-module/src/dto/update-salary.dto.ts`
+- `allin-packages/salary-module/src/schemas/salary.schema.ts`(Zod schema)
+- `allin-packages/disability-module/src/schemas/disabled-person.schema.ts`(文件ID验证)
+
+**修改要点**:
+1. 将class-validator DTO转换为Zod Schema
+2. 在OrderPersonAsset的Schema中添加`fileId`验证
+3. 添加枚举值验证:使用`OrderStatus`和`WorkStatus`枚举
+4. 保持与原始API相同的字段名和验证规则
+
+### 4. 迁移控制器和服务 (AC: 6, 7)
+
+#### 4.1 迁移OrderService
+**源文件路径**: `allin_system-master/server/src/order/order.service.ts`
+**目标文件路径**: `allin-packages/order-module/src/services/order.service.ts`
+**参考对照文件**:
+- `allin-packages/salary-module/src/services/salary.service.ts`(服务层结构)
+- `allin-packages/disability-module/src/services/disabled-person.service.ts`(文件验证逻辑)
+
+**修改要点**:
+1. 将NestJS服务转换为继承`GenericCrudService<EmploymentOrder>`
+2. **循环依赖处理**: 移除对`DisabledPersonRepository`的直接依赖
+3. **文件验证**: 在创建/更新OrderPersonAsset时验证`fileId`的有效性
+4. 将业务逻辑从NestJS风格转换为Hono风格
+5. 保持原始的业务逻辑和错误处理
+
+#### 4.2 迁移OrderController为Hono路由
+**源文件路径**: `allin_system-master/server/src/order/order.controller.ts`
+**目标文件路径**: `allin-packages/order-module/src/routes/order.routes.ts`
+**参考对照文件**:
+- `allin-packages/salary-module/src/routes/salary.routes.ts`(Hono路由结构)
+- `allin-packages/disability-module/src/routes/disabled-person.routes.ts`(文件ID参数处理)
+
+**修改要点**:
+1. 将NestJS控制器转换为Hono路由
+2. 将class-validator管道验证转换为Zod验证
+3. 更新API端点路径,保持与原始API兼容
+4. 在OrderPersonAsset路由中添加`fileId`参数处理
+5. 配置OpenAPI文档
+
+### 5. 创建模块主文件 (AC: 1)
+**目标文件路径**:
+- `allin-packages/order-module/src/index.ts`
+- `allin-packages/order-module/src/order.module.ts`
+
+**参考对照文件**:
+- `allin-packages/salary-module/src/index.ts`(导出结构)
+- `allin-packages/salary-module/src/salary.module.ts`(模块定义)
+
+### 6. 创建测试文件 (AC: 10)
+**目标文件路径**:
+- `allin-packages/order-module/tests/unit/order.controller.test.ts`
+- `allin-packages/order-module/tests/unit/order.service.test.ts`
+- `allin-packages/order-module/tests/integration/order.integration.test.ts`
+
+**参考对照文件**:
+- `allin-packages/salary-module/tests/unit/salary.controller.test.ts`(单元测试结构)
+- `allin-packages/salary-module/tests/integration/salary.integration.test.ts`(集成测试结构)
+- `allin-packages/disability-module/tests/integration/disability.integration.test.ts`(文件集成测试)
+
+**测试重点**:
+1. **枚举测试**: 验证`OrderStatus`和`WorkStatus`枚举的正确使用
+2. **文件集成测试**: 测试OrderPersonAsset与File实体的关联
+3. **循环依赖测试**: 验证与disability-module的解耦效果
+4. **API兼容性测试**: 验证与原始NestJS API的兼容性
+
+### 7. 处理循环依赖 (AC: 5)
+**问题**: 订单模块与disability-module存在相互引用
+**源文件分析**:
+- 订单模块引用: `DisabledPerson`实体
+- 残疾人模块引用: `EmploymentOrder`和`OrderPerson`实体
+
+**解决方案**:
+1. **接口抽象**: 在订单模块中创建`PersonReference`接口,避免直接引用`DisabledPerson`实体
+2. **ID引用**: 使用`personId: number`代替直接的实体引用
+3. **事件解耦**: 通过事件或消息队列处理跨模块业务逻辑
+4. **参考处理**: 参考`allin-packages/salary-module`与`@d8d/geo-areas`的集成方式
+
+**实施步骤**:
+1. 在订单模块中定义`PersonReference`类型
+2. 修改OrderPerson实体,使用`personId: number`代替`person: DisabledPerson`
+3. 修改OrderPersonAsset实体,使用`personId: number`代替`person: DisabledPerson`
+4. 在服务层通过ID验证人员存在性(可选:通过API调用验证)
+5. 更新残疾人管理模块,移除对订单实体的直接引用
+
+### 8. 集成测试和验证 (AC: 11, 12)
+- [ ] 运行单元测试:`pnpm test`
+- [ ] 运行集成测试:`pnpm test:integration`
+- [ ] 类型检查:`pnpm typecheck`
+- [ ] 构建验证:`pnpm build`
+- [ ] 循环依赖验证:检查模块间导入关系
+
+## Dev Notes
+
+### 从之前故事吸取的经验教训:
+1. **实体主键命名**:确保所有实体使用`id`作为主键字段名,而不是`orderId`、`opId`等特定名称
+2. **workspace配置**:在根目录`pnpm-workspace.yaml`中添加新包路径`'allin-packages/order-module'`
+3. **测试依赖**:集成测试需要配置测试数据库,参考salary-module的`tests/integration/setup.ts`
+4. **类型检查问题**:使用`pnpm typecheck`检查类型错误,特别注意枚举导入和循环依赖
+5. **API兼容性**:保持与原始NestJS API相同的请求/响应格式
+6. **文件实体集成**:正确配置与`@d8d/file-module`的关联关系,参考disabled-photo.entity.ts
+7. **枚举使用**:通过`@d8d/allin-enums`包导入OrderStatus和WorkStatus枚举
+8. **区域包集成经验**:参考salary-module与geo-areas的集成方式处理外部依赖
+
+### 技术细节:
+1. **OrderStatus和WorkStatus枚举集成**:
+   ```typescript
+   import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
+
+   @Column({
+     name: 'order_status',
+     type: 'enum',
+     enum: OrderStatus,
+     default: OrderStatus.DRAFT,
+     comment: '订单状态'
+   })
+   orderStatus!: OrderStatus;
+
+   @Column({
+     name: 'work_status',
+     type: 'enum',
+     enum: WorkStatus,
+     default: WorkStatus.NOT_WORKING,
+     comment: '工作状态'
+   })
+   workStatus!: WorkStatus;
+   ```
+
+2. **OrderPersonAsset实体文件集成**:
+   ```typescript
+   import { File } from '@d8d/file-module/entities';
+
+   @Column({
+     name: 'file_id',
+     type: 'int',
+     nullable: false,
+     comment: '文件ID,引用files表'
+   })
+   fileId!: number;
+
+   @ManyToOne(() => File)
+   @JoinColumn({ name: 'file_id' })
+   file!: File;
+   ```
+
+3. **循环依赖处理方案**:
+   ```typescript
+   // 原代码(有循环依赖):
+   // @ManyToOne(() => DisabledPerson)
+   // person!: DisabledPerson;
+
+   // 新代码(解耦):
+   @Column({
+     name: 'person_id',
+     type: 'int',
+     nullable: false,
+     comment: '人员ID'
+   })
+   personId!: number;
+
+   // 可选:定义引用接口
+   export interface PersonReference {
+     id: number;
+     name: string;
+     idCard: string;
+   }
+   ```
+
+4. **AssetType和AssetFileType枚举**:
+   - 这些是模块内部枚举,不需要移到enums包
+   - 在`order-person-asset.entity.ts`中定义即可
+   - 保持与原始值一致:`TAX = 'tax'`, `SALARY = 'salary'`等
+
+### 文件模块集成步骤:
+1. 安装依赖:`@d8d/file-module`
+2. 在OrderPersonAsset实体中建立与File实体的关联
+3. 在DTO中验证fileId的有效性
+4. 在服务层处理文件关联逻辑
+5. 在集成测试中模拟文件上传和关联
+
+### 测试策略:
+1. **单元测试**:覆盖所有控制器和服务方法
+2. **集成测试**:测试数据库操作和API端点
+3. **枚举验证测试**:测试OrderStatus和WorkStatus枚举的正确使用
+4. **文件关联测试**:测试OrderPersonAsset与File实体的关联
+5. **循环依赖测试**:验证解耦方案的有效性
+6. **API兼容性测试**:验证与原始NestJS API的兼容性
+
+## Dependencies
+- @d8d/allin-enums
+- @d8d/file-module
+- @d8d/shared-crud
+- @d8d/shared-utils
+- @d8d/shared-types
+- @d8d/auth-module
+- @d8d/user-module
+- typeorm
+- zod
+- hono
+- vitest (dev dependency)
+- @d8d/shared-test-util (dev dependency)
+
+## Test Plan
+1. **单元测试**:覆盖所有控制器和服务方法
+2. **集成测试**:测试数据库操作和API端点
+3. **枚举验证**:测试OrderStatus和WorkStatus枚举的正确使用
+4. **文件关联测试**:测试OrderPersonAsset与File实体的关联
+5. **循环依赖测试**:验证与disability-module的解耦效果
+6. **API兼容性测试**:验证与原始NestJS API的兼容性
+
+## Definition of Done
+- [ ] 所有实体成功迁移并转换为TypeORM + Zod
+- [ ] 完整的CRUD API端点可用
+- [ ] 集成@d8d/allin-enums包中的枚举
+- [ ] 集成@d8d/file-module包,正确处理文件关联
+- [ ] 解决与disability-module的循环依赖
+- [ ] 所有测试通过(单元测试、集成测试)
+- [ ] 类型检查通过
+- [ ] 构建成功
+- [ ] 代码符合项目代码规范
+- [ ] 文档完整(README、API文档)
+
+## Dev Agent Record
+*将在开发完成后填写*
+
+---
+
+*故事创建时间:2025-12-02*
+*创建者:Bob (Scrum Master)*
+*更新说明:重新创建故事,吸取之前移植故事的经验教训,在任务中标注迁移文件的路径和参考对照文件的路径*