# 史诗007 - Allin System有实体模块移植到独立包

## 史诗目标

将allin_system-master/server/src目录下有对应数据库实体的模块移植到packages目录下，作为非多租户独立包，遵循项目现有的独立包结构和命名规范，实现模块的现代化重构和标准化管理。

## 史诗描述

### 现有系统上下文

- **当前相关功能**：allin_system-master是一个基于NestJS的后端系统，根据allin_2025-11-25.sql分析，有以下有实体的模块：
  1. channel_info模块 - 对应channel_info表
  2. company模块 - 对应employer_company表
  3. dict_management模块 - 对应sys_dict表
  4. disability_person模块 - 对应disabled_person、disabled_bank_card、disabled_photo、disabled_remark、disabled_visit表
  5. order模块 - 对应employment_order、order_person、order_person_asset表
  6. platform模块 - 对应employer_platform表
  7. salary模块 - 对应salary_level表

- **无实体模块（本次不移植）**：admin、auth、users模块
- **技术栈**：TypeScript、NestJS、PostgreSQL、Redis、MinIO
- **集成点**：模块间通过NestJS模块系统集成，共享数据库连接和配置

### 增强详情

- **新增/变更内容**：将7个有实体的现有模块从allin_system-master/server/src移植到packages目录下的独立包
- **集成方式**：每个模块将重构为独立的npm包，遵循@d8d命名规范，通过workspace依赖管理
- **成功标准**：
  1. 7个有实体模块成功移植为独立包
  2. 保持原有功能完整性
  3. 遵循现有独立包结构模式
  4. 通过类型检查和基本测试验证

## 模块分析结果

### 1. 模块依赖关系分析

根据对每个模块`module.ts`文件的分析，依赖关系如下：

1. **channel_info模块** (`channel.module.ts`)
   - 依赖：`@nestjs/common`, `@nestjs/typeorm`
   - 实体：`Channel` (对应`channel_info`表)
   - 无其他模块依赖

2. **company模块** (`company.module.ts`)
   - 依赖：`@nestjs/common`, `@nestjs/typeorm`, `PlatformModule`
   - 实体：`Company` (对应`employer_company`表)
   - 依赖模块：`platform`

3. **dict_management模块** (`dict.module.ts`)
   - 依赖：`@nestjs/common`, `@nestjs/typeorm`
   - 实体：`Dict` (对应`sys_dict`表)
   - 无其他模块依赖

4. **disability_person模块** (`disability_person.module.ts`)
   - 依赖：`@nestjs/common`, `@nestjs/typeorm`
   - 实体：`DisabledPerson`, `DisabledBankCard`, `DisabledPhoto`, `DisabledRemark`, `DisabledVisit`
   - 引用其他模块实体：`EmploymentOrder`, `OrderPerson` (order模块), `Platform` (platform模块), `Company` (company模块), `Channel` (channel_info模块)
   - 复杂度：高（5个实体，多个控制器和服务）

5. **order模块** (`order.module.ts`)
   - 依赖：`@nestjs/common`, `@nestjs/typeorm`
   - 实体：`EmploymentOrder`, `OrderPerson`, `OrderPersonAsset`
   - 引用其他模块实体：`DisabledPerson` (disability_person模块), `Platform` (platform模块), `Company` (company模块), `Channel` (channel_info模块)
   - 复杂度：中高（3个实体，跨模块依赖）

6. **platform模块** (`platform.module.ts`)
   - 依赖：`@nestjs/common`, `@nestjs/typeorm`
   - 实体：`Platform` (对应`employer_platform`表)
   - 无其他模块依赖
   - 被依赖：`company`模块依赖此模块

7. **salary模块** (`salary.module.ts`)
   - 依赖：`@nestjs/common`, `@nestjs/typeorm`
   - 实体：`SalaryLevel` (对应`salary_level`表)
   - 无其他模块依赖

### 2. 依赖关系图

```
platform ───┐
            ↓
company ────┐
            ↓
channel_info ─┐
            ↓ ↓
order ───────┘
            ↑
disability_person
            ↑
dict_management (独立)
salary (独立)
```

**关键发现**：
- `platform`模块是基础依赖，被`company`模块直接依赖
- `company`、`channel_info`模块被`order`和`disability_person`模块引用
- `order`和`disability_person`模块相互引用实体，形成循环依赖
- `dict_management`和`salary`模块完全独立

### 3. 模块到独立包的映射方案

基于现有项目命名规范，并考虑这是Allin系统专属模块，制定以下映射方案：

| 原模块名 | 独立包名 | 目录名 | 说明 |
|---------|---------|--------|------|
| channel_info | `@d8d/allin-channel-module` | `channel-module` | 渠道管理模块 |
| company | `@d8d/allin-company-module` | `company-module` | 公司管理模块，依赖platform-module |
| dict_management | `@d8d/allin-dict-module` | `dict-module` | 字典管理模块 |
| disability_person | `@d8d/allin-disability-module` | `disability-module` | 残疾人管理模块（简化名称） |
| order | `@d8d/allin-order-module` | `order-module` | 订单管理模块 |
| platform | `@d8d/allin-platform-module` | `platform-module` | 平台管理模块 |
| salary | `@d8d/allin-salary-module` | `salary-module` | 薪资管理模块 |

**命名原则**：
1. **包名前缀**：使用`@d8d/allin-`前缀，明确表明是Allin系统专属包
2. **目录名**：使用简洁的`{模块名}-module`格式，便于在`allin-packages`目录中管理
3. **模块名**：使用单数形式 + `-module`后缀
4. **名称简洁**：反映模块核心功能
5. **非多租户**：不使用`-mt`后缀（本次移植为非多租户版本）

### 4. 目录结构和包规范

#### 目录结构决策
由于这些模块是`allin_system-master`项目独有的业务模块（非通用模块），将在项目根目录创建独立目录：

```
项目根目录/
├── allin-packages/          # Allin系统专属包目录
│   ├── channel-module/      # 渠道管理模块
│   ├── company-module/      # 公司管理模块
│   ├── dict-module/         # 字典管理模块
│   ├── disability-module/   # 残疾人管理模块
│   ├── order-module/        # 订单管理模块
│   ├── platform-module/     # 平台管理模块
│   └── salary-module/       # 薪资管理模块
├── packages/                # 现有通用包目录（保持不变）
└── allin_system-master/     # 原始代码目录（移植源）
```

**目录命名说明**：`allin-packages`清晰表明这是Allin系统专属的包目录，与`allin_system-master`源目录对应，区别于通用的`packages`目录。

#### 包结构规范
参考现有`auth-module`结构，每个独立包应包含：

```
allin-packages/{module-name}/
├── package.json          # 包配置，workspace依赖管理
├── tsconfig.json        # TypeScript配置
├── vitest.config.ts     # 测试配置（如需要）
├── src/
│   ├── index.ts         # 主导出文件
│   ├── schemas/         # Zod模式定义（如需要）
│   │   └── index.ts
│   ├── entities/        # 数据库实体
│   ├── services/        # 业务服务
│   ├── controllers/     # API控制器
│   ├── dtos/           # 数据传输对象
│   └── types/          # TypeScript类型定义
└── dist/               # 构建输出
```

**关键配置要求**：
1. `package.json`中设置`"type": "module"`
2. 主入口为`src/index.ts`
3. 使用workspace依赖：`"@d8d/core-module": "workspace:*"`
4. 包名使用`@d8d/allin-`前缀，如`@d8d/allin-channel-module`
5. 导出必要的服务和实体

### 5. 技术栈差异分析与移植方案

#### 技术栈对比分析

| 方面 | 当前项目（目标） | allin_system-master（源） | 差异程度 |
|------|------------------|---------------------------|----------|
| **Web框架** | Hono（轻量级） | NestJS（重量级） | **重大** |
| **路由系统** | Hono路由 | NestJS装饰器路由 | **重大** |
| **验证库** | Zod + @hono/zod-openapi | class-validator + class-transformer | **中等** |
| **服务架构** | GenericCrudService继承 | 自定义Service类 | **中等** |
| **实体配置** | 详细TypeORM装饰器 | 简单TypeORM装饰器 | **轻微** |
| **命名规范** | 驼峰命名（channelId） | 下划线命名（channel_id） | **轻微** |
| **模块系统** | npm包 + workspace | NestJS模块系统 | **重大** |
| **API风格** | RESTful + OpenAPI | 自定义端点 | **中等** |

#### 关键依赖对比
- **当前项目**：`@d8d/core-module`、`@d8d/shared-crud`、`@d8d/shared-utils`、TypeORM、Hono、Zod
- **allin_system**：`@nestjs/*`、TypeORM、class-validator、Passport/JWT

#### 移植调整方案

##### A. 架构转换策略
1. **NestJS控制器 → Hono路由**：重写路由层
2. **自定义Service → GenericCrudService继承**：重构服务层
3. **class-validator DTO → Zod Schema**：转换验证逻辑
4. **NestJS模块 → npm包**：重新组织模块结构

##### B. 实体层转换示例
```typescript
// 原allin实体（下划线命名）
@Entity('channel_info')
export class Channel {
  @PrimaryGeneratedColumn('increment')
  channel_id: number;  // 下划线
  @Column()
  channel_name: string;
}

// 转换后实体（驼峰命名，详细配置）
@Entity('channel_info')
export class Channel {
  @PrimaryGeneratedColumn({ name: 'channel_id', type: 'int' })
  channelId!: number;  // 驼峰
  @Column({ name: 'channel_name', type: 'varchar', length: 100 })
  channelName!: string;
}
```

##### C. 服务层转换示例
```typescript
// 原allin服务（NestJS风格）
@Injectable()
export class ChannelService {
  constructor(@InjectRepository(Channel) private repo: Repository<Channel>) {}
  async createChannel(data: CreateChannelDto): Promise<boolean> {
    // 自定义逻辑
  }
}

// 转换后服务（Hono + GenericCrudService）
export class ChannelService extends GenericCrudService<Channel> {
  constructor(dataSource: DataSource) {
    super(dataSource, Channel, {
      searchFields: ['channelName', 'contactPerson'],
    });
  }
  // 保留特殊业务方法
  async createChannel(data: CreateChannelDto): Promise<boolean> {
    // 复用或重写逻辑
  }
}
```

##### D. 路由层转换示例
```typescript
// 原NestJS控制器
@Controller('channel')
@UseGuards(JwtAuthGuard)
export class ChannelController {
  @Post('createChannel')
  createChannel(@Body() req: CreateChannelDto) {
    return this.service.createChannel(req);
  }
}

// 转换后Hono路由
import { OpenAPIHono } from '@hono/zod-openapi';
const channelRoutes = new OpenAPIHono()
  .post('/createChannel', async (c) => {
    const data = await c.req.json();
    const result = await channelService.createChannel(data);
    return c.json({ success: result });
  });
```

##### E. 验证系统转换
```typescript
// 原class-validator DTO
export class CreateChannelDto {
  @IsString()
  channel_name: string;
}

// 转换后Zod Schema
export const CreateChannelSchema = z.object({
  channelName: z.string().min(1).max(100),
});
export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
```

#### 依赖关系解决方案

针对发现的循环依赖问题（order ↔ disability_person），解决方案：

1. **创建共享实体包**：将相互引用的实体提取到共享包
2. **接口抽象**：使用接口而非具体实体引用
3. **依赖注入调整**：重构服务层减少直接实体依赖
4. **移植顺序策略**：按依赖顺序移植，先移植基础模块

**建议移植顺序**：
1. ✅ `allin-platform-module`（基础） - **已完成**
2. ✅ `allin-channel-module`（独立） - **已完成**
3. ✅ `allin-enums`（枚举常量包） - **已完成**
4. ✅ `allin-company-module`（依赖platform） - **已完成**
5. `allin-salary-module`（独立）
6. `allin-order-module`和`allin-disability-module`（最后处理，解决循环依赖）

#### 兼容性保证
- **保持**：数据库表结构、核心业务逻辑、数据关系
- **调整**：API端点路径、请求/响应格式、错误处理
- **新增**：OpenAPI文档、标准化CRUD操作、类型安全验证

## 故事分解（按模块拆分）

**注**：每个故事对应一个模块的完整移植，包括技术栈转换和API集成测试。

### 故事1：移植渠道管理模块（channel_info → @d8d/allin-channel-module）✅ **已完成**
**目标**：将channel_info模块移植为独立包，完成技术栈转换并验证功能完整性

**验收标准**：
1. ✅ 创建`allin-packages/channel-module`目录结构
2. ✅ 完成实体转换：`Channel`实体从下划线命名转换为驼峰命名
3. ✅ 完成服务层转换：从NestJS自定义Service转换为GenericCrudService继承
4. ✅ 完成路由层转换：从NestJS控制器转换为Hono路由
5. ✅ 完成验证系统转换：从class-validator DTO转换为Zod Schema
6. ✅ 配置package.json：使用`@d8d/allin-channel-module`包名，workspace依赖
7. ✅ 编写API集成测试：覆盖所有路由端点，验证CRUD操作
8. ✅ 通过类型检查和基本测试验证

**API集成测试要求**：
- 测试文件：`tests/integration/channel.integration.test.ts`
- 测试覆盖：GET/POST/PUT/DELETE所有端点
- 验证：认证、授权、数据验证、错误处理
- 遵循现有集成测试模式（参考advertisements-module）

**完成情况**：
- ✅ 已创建完整的模块结构和配置文件
- ✅ 已完成实体转换（主键属性名从channelId改为id以遵循GenericCrudService约定）
- ✅ 已完成服务层转换，继承GenericCrudService并覆盖关键方法
- ✅ 已完成路由层转换，实现所有6个API端点
- ✅ 已完成验证系统转换，使用Zod Schema
- ✅ 已编写11个集成测试，全部通过
- ✅ 类型检查通过，无任何错误
- ✅ 修复了workspace配置问题（添加allin-packages/*到pnpm-workspace.yaml）
- ✅ 修复了测试依赖问题（添加@d8d/user-module和@d8d/file-module依赖）

### 故事2：移植公司管理模块（company → @d8d/allin-company-module）✅ **已完成**
**目标**：将company模块移植为独立包，处理对platform-module的依赖

**验收标准**：
1. ✅ 创建`allin-packages/company-module`目录结构
2. ✅ 完成实体转换：`Company`实体转换
3. ✅ 处理模块依赖：正确配置对`@d8d/allin-platform-module`的依赖
4. ✅ 完成服务层转换：处理跨模块业务逻辑
5. ✅ 完成路由层转换：Hono路由实现
6. ✅ 完成验证系统转换：Zod Schema定义
7. ✅ 配置package.json：workspace依赖管理
8. ✅ 编写API集成测试：验证模块间依赖正常工作
9. ✅ 通过类型检查和基本测试验证

**API集成测试要求**：
- 测试文件：`tests/integration/company.integration.test.ts`
- 测试覆盖：公司管理的所有业务场景
- 验证：与platform-module的集成、数据关联查询
- 包含跨模块数据一致性测试

**完成情况**：
- ✅ 已创建完整的模块结构和配置文件
- ✅ 已完成实体转换：`Company`实体，配置复合唯一索引`idx_company_name_platform`
- ✅ 已完成服务层转换：`CompanyService`继承`GenericCrudService`，覆盖`create`、`update`方法实现业务逻辑
- ✅ 已完成路由层转换：实现7个API端点，保持原始布尔返回值格式
- ✅ 已完成验证系统转换：从class-validator DTO转换为Zod Schema
- ✅ 已编写15个集成测试，全部通过，特别测试与platform-module的集成
- ✅ 类型检查通过，修复了导出冲突、GenericCrudService配置等问题
- ✅ 修复了复合索引配置错误（从字段级别移到实体级别）
- ✅ 修复了服务层错误处理逻辑（重新抛出错误让路由层处理）
- ✅ 修复了测试类型安全问题（避免使用`as any`）
- ✅ 测试性能：所有测试在18.58秒内完成

## 设计决策：字典管理模块重构为枚举常量包

### 背景分析
经过对`dict_management`模块的深入分析，发现其存储的数据主要分为两类：

1. **固定业务枚举数据**（适合用TypeScript枚举）：
   - `disability_type`（残疾类型）：7种固定类型
   - `disability_level`（残疾等级）：4个固定等级
   - `order_status`（订单状态）：5种固定状态
   - `work_status`（工作状态）：4种固定状态

2. **动态配置数据**（但已有替代方案）：
   - 行政区划数据：已有专门的`@d8d/geo-areas`包管理
   - 其他动态配置：可根据需要单独处理

### 设计决策
**放弃移植`dict_management`模块，改为创建枚举常量包**，原因如下：

1. **类型安全**：枚举常量提供编译时类型检查
2. **性能优化**：无需数据库查询，减少IO操作
3. **代码清晰**：在代码中直接引用，提高可读性
4. **维护简单**：修改枚举值即可，无需数据库操作
5. **避免重复**：行政区划功能已有`@d8d/geo-areas`包

### 故事3：创建Allin系统枚举常量包（@d8d/allin-enums）✅ **已完成**
**目标**：创建专门供Allin系统使用的枚举常量包，替换原有的数据库字典管理

**验收标准**：
1. ✅ 创建`allin-packages/enums`目录结构
2. ✅ 定义残疾类型枚举：`DisabilityType`（vision, hearing, speech, physical, intellectual, mental, multiple）
3. ✅ 定义残疾等级枚举：`DisabilityLevel`（1, 2, 3, 4）
4. ✅ 定义订单状态枚举：`OrderStatus`（draft, confirmed, in_progress, completed, cancelled）
5. ✅ 定义工作状态枚举：`WorkStatus`（not_working, pre_working, working, resigned）
6. ✅ 配置package.json：包名`@d8d/allin-enums`，workspace依赖
7. ✅ 编写类型定义测试：验证枚举值正确性
8. ✅ 通过类型检查和导出验证
9. ✅ 更新后续模块依赖：确保disability-module和order-module使用枚举常量

**完成情况**：
- ✅ 已创建完整的`allin-packages/enums`目录结构
- ✅ 已定义4个枚举类型，值与数据库`sys_dict`表完全一致：
  - `DisabilityType`：7种残疾类型（vision, hearing, speech, physical, intellectual, mental, multiple）
  - `DisabilityLevel`：4个残疾等级（1, 2, 3, 4）
  - `OrderStatus`：5种订单状态（draft, confirmed, in_progress, completed, cancelled）
  - `WorkStatus`：4种工作状态（not_working, pre_working, working, resigned）
- ✅ 已添加辅助函数和标签映射：
  - `DISABILITY_TYPES`：残疾类型数组
  - `DISABILITY_LEVELS`：残疾等级数组（过滤掉反向映射值）
  - `ORDER_STATUSES`：订单状态数组
  - `WORK_STATUSES`：工作状态数组
  - `DisabilityTypeLabelMap`：残疾类型中文标签映射
  - `DisabilityLevelLabelMap`：残疾等级中文标签映射
  - `OrderStatusLabelMap`：订单状态中文标签映射
  - `WorkStatusLabelMap`：工作状态中文标签映射
- ✅ 已配置`package.json`：包名`@d8d/allin-enums`，`"type": "module"`，workspace依赖
- ✅ 已编写17个单元测试，覆盖所有枚举和辅助函数，测试通过率100%
- ✅ 已修复测试问题：处理数字枚举的反向映射（`Object.values()`返回键和值）
- ✅ 已添加`@vitest/coverage-v8`依赖，测试覆盖率100%
- ✅ 类型检查通过，无任何错误
- ✅ 已更新故事文件状态为"Ready for Review"

**技术实现要求**：
- 包位置：`allin-packages/enums/`
- 包名：`@d8d/allin-enums`
- 导出方式：通过`src/index.ts`统一导出所有枚举
- 类型定义：使用TypeScript `enum`或`const enum`定义
- 值映射：保持与原始数据库值一致（如`disability_type='vision'` → `DisabilityType.VISION = 'vision'`）

**对后续模块的影响**：
1. **disability-module**：使用`DisabilityType`和`DisabilityLevel`枚举
2. **order-module**：使用`OrderStatus`和`WorkStatus`枚举
3. **其他模块**：根据需要使用相应枚举

**优势**：
- ✅ 类型安全：编译时检查，避免无效值
- ✅ 性能优化：无需数据库查询
- ✅ 代码提示：IDE自动补全枚举值
- ✅ 维护简单：集中管理，一处修改处处生效
- ✅ 版本控制：枚举定义与代码一起版本控制

## 设计决策：区域数据统一使用@d8d/geo-areas包

### 背景分析
经过对Allin系统模块的分析，发现以下模块使用行政区划数据：

1. **残疾人管理模块** (`disability_person`)：
   - 存储`province`、`city`、`district`字段（字符串类型）
   - 用于记录残疾人的户籍地址和居住地址

2. **薪资管理模块** (`salary`)：
   - 存储`province`、`city`、`district`字段（字符串类型）
   - 用于按地区管理薪资水平，有唯一性约束`(province, city)`

### 问题分析
当前实现存在以下问题：
1. **数据不一致**：字符串存储，容易输入错误或格式不一致
2. **缺乏验证**：没有验证区域数据的有效性
3. **维护困难**：区域变更需要手动更新所有相关记录
4. **重复功能**：与已有的`@d8d/geo-areas`包功能重叠

### 设计决策
**采用方案A：完全依赖区域包**，将所有区域数据改为引用`@d8d/geo-areas`包中的`AreaEntity`。

### 改造方案
1. **残疾人管理模块改造**：
   - 移除`province`、`city`、`district`字符串字段
   - 添加`provinceId`、`cityId`、`districtId`字段（外键引用`AreaEntity`）
   - 使用`AreaService`验证区域数据的有效性和层级关系

2. **薪资管理模块改造**：
   - 移除`province`、`city`、`district`字符串字段
   - 添加`provinceId`、`cityId`、`districtId`字段（外键引用`AreaEntity`）
   - 修改唯一性约束为`(provinceId, cityId)`
   - 使用区域包API获取区域完整信息

3. **API兼容性处理**：
   - 创建DTO转换器，支持前端传递区域名称自动转换为ID
   - 在响应中返回区域完整信息（名称+ID+层级）
   - 提供区域查询API，支持按名称搜索区域

### 优势
- ✅ **数据一致性**：统一使用区域包数据，避免数据不一致
- ✅ **类型安全**：外键引用提供编译时检查
- ✅ **维护简单**：区域变更只需更新区域包数据
- ✅ **功能完整**：利用区域包的树形结构、层级验证等功能
- ✅ **性能优化**：减少重复数据存储，提高查询效率

### 故事4：移植残疾人管理模块（disability_person → @d8d/allin-disability-module）✅ **已完成**
**目标**：移植最复杂的模块，处理5个实体和跨模块依赖，完成文件实体集成，使用枚举常量

**验收标准**：
1. ✅ 创建`allin-packages/disability-module`目录结构
2. ✅ 完成实体转换：5个实体（DisabledPerson、DisabledBankCard等）转换
3. ✅ **枚举常量集成**：使用`@d8d/allin-enums`包中的`DisabilityType`和`DisabilityLevel`枚举
4. ✅ **文件实体集成**：修改`DisabledPhoto`实体，添加`fileId`字段引用`File`实体
5. ✅ **银行卡文件集成**：修改`DisabledBankCard`实体，添加`fileId`字段引用`File`实体
6. ✅ 处理跨模块依赖：引用order、platform、company、channel模块实体
7. ✅ 完成服务层转换：复杂业务逻辑处理，包含文件ID验证逻辑
8. ✅ 完成路由层转换：多个控制器转换为Hono路由，API接收`fileId`参数
9. ✅ 完成验证系统转换：复杂数据验证，包含文件ID验证、枚举值验证
10. ✅ 配置package.json：处理多个依赖，包含对`@d8d/file-module`、`@d8d/allin-enums`的依赖
11. ✅ 编写API集成测试：覆盖所有5个实体的管理功能，包含文件ID关联测试、枚举值测试
12. ✅ 通过类型检查和基本测试验证
13. ✅ 修复TypeScript类型错误和PostgreSQL类型兼容问题
14. ✅ 修复schema验证错误（实体字段与schema定义匹配）
15. ✅ 修复测试数据完整性和错误处理逻辑

**完成情况**：
- ✅ 已创建完整的模块结构和配置文件
- ✅ 已完成5个实体转换：`DisabledPerson`、`DisabledBankCard`、`DisabledPhoto`、`DisabledRemark`、`DisabledVisit`
- ✅ 已完成文件模块集成：照片和银行卡实体使用`fileId`字段引用`File`实体
- ✅ 已完成服务层转换：`DisabledPersonService`继承`GenericCrudService`，`AggregatedService`处理聚合业务逻辑
- ✅ 已完成路由层转换：实现22个API端点，包含聚合创建和查询功能
- ✅ 已完成验证系统转换：从class-validator DTO转换为Zod Schema，包含文件ID验证
- ✅ 已编写22个集成测试，全部通过，覆盖所有API端点和业务逻辑
- ✅ 修复了TypeScript类型错误：PostgreSQL类型兼容问题（tinyint → smallint，datetime → timestamp）
- ✅ 修复了schema验证错误：实体字段与schema定义不匹配（createTime/updateTime → uploadTime/remarkTime/visitTime）
- ✅ 修复了测试数据问题：缺少subBankName、operatorId等必填字段
- ✅ 修复了错误处理：DELETE端点返回404而不是200，GET聚合端点返回404而不是500
- ✅ 修复了文件模块集成：银行卡实体使用fileId字段而不是cardPhotoUrl
- ✅ 移除不存在的education字段，保持与原始数据表结构一致
- ✅ 类型检查通过，所有22个测试通过
- ✅ 清理了调试信息，恢复了干净的测试代码

**API集成测试要求**：
- 测试文件：`tests/integration/disability.integration.test.ts`
- 测试覆盖：残疾人信息、银行卡、照片、备注、走访记录
- **枚举测试重点**：
  - 验证残疾类型枚举值正确性
  - 验证残疾等级枚举值正确性
  - 测试无效枚举值的错误处理
- **区域测试重点**：
  - 验证区域ID的有效性检查
  - 测试区域层级关系验证（省→市→区）
  - 验证区域不存在时的错误处理
  - 测试区域数据在响应中的完整性
- **文件测试重点**：
  - 验证`fileId`参数接收和处理
  - 测试文件不存在时的错误处理
  - 验证返回数据包含文件URL
  - 测试文件关联的业务规则
- 验证：多实体关联、复杂业务规则
- 包含性能测试：大数据量查询

### 故事5：移植订单管理模块（order → @d8d/allin-order-module）✅ **已完成**
**目标**：移植订单模块，处理与disability-module的循环依赖，完成文件实体集成

**验收标准**：
1. ✅ 创建`allin-packages/order-module`目录结构
2. ✅ 完成实体转换：3个实体（EmploymentOrder、OrderPerson等）转换
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. ✅ 验证循环依赖解决方案的有效性

**完成情况**：
- ✅ 已创建完整的模块结构和配置文件
- ✅ 已完成3个实体转换：`EmploymentOrder`、`OrderPerson`、`OrderPersonAsset`
- ✅ 已完成文件模块集成：`OrderPersonAsset`实体使用`fileId`字段引用`File`实体
- ✅ 已完成服务层转换：`OrderService`继承`GenericCrudService`，处理订单业务逻辑和文件关联
- ✅ 已完成路由层转换：实现31个API端点，包含订单全生命周期管理
- ✅ 已完成验证系统转换：从class-validator DTO转换为Zod Schema，包含文件ID验证和枚举验证
- ✅ 已编写31个集成测试，全部通过，覆盖所有API端点和业务逻辑
- ✅ 修复了循环依赖问题：使用`personId: number`代替`DisabledPerson`直接引用
- ✅ 修复了类型检查问题：处理枚举值类型、实体创建类型、Schema设计等问题
- ✅ 修复了测试问题：GET /order/detail/:id返回500错误、错误处理顺序问题、资产类型枚举值不匹配问题
- ✅ 创建了测试数据工厂`OrderTestDataFactory`，减少测试代码重复
- ✅ 类型检查通过，所有31个测试通过
- ✅ 代码已提交，故事文档已更新为"Completed"状态

**API集成测试要求**：
- 测试文件：`tests/integration/order.integration.test.ts`
- 测试覆盖：订单创建、人员分配、资产关联、状态流转
- **枚举测试重点**：
  - 验证订单状态枚举值正确性
  - 验证工作状态枚举值正确性
  - 测试无效枚举值的错误处理
  - 验证状态流转的业务规则
- **文件测试重点**：
  - 测试不同资产类型（tax、salary、contract等）的文件关联
  - 验证文件类型（image/video）与资产类型的匹配
  - 测试资产文件的上传、查看、删除流程
  - 验证文件关联的权限控制
- 验证：与disability-module的集成、业务规则执行
- 包含工作流测试：订单状态机

### 故事6：移植平台管理模块（platform → @d8d/allin-platform-module）✅ **已完成**
**目标**：移植基础模块，作为其他模块的依赖

**验收标准**：
1. ✅ 创建`allin-packages/platform-module`目录结构
2. ✅ 完成实体转换：`Platform`实体转换
3. ✅ 完成服务层转换：基础CRUD服务
4. ✅ 完成路由层转换：Hono路由实现
5. ✅ 完成验证系统转换：Zod Schema定义
6. ✅ 配置package.json：作为基础依赖包
7. ✅ 编写API集成测试：验证基础功能
8. ✅ 通过类型检查和基本测试验证
9. ✅ 验证作为依赖包被其他模块引用的能力

**API集成测试要求**：
- 测试文件：`tests/integration/platform.integration.test.ts`
- 测试覆盖：平台CRUD操作
- 验证：作为基础数据的完整性和一致性
- 包含被引用场景的模拟测试

**完成情况**：
- ✅ 已创建完整的模块结构和配置文件
- ✅ 已完成实体转换：`Platform`实体从下划线命名转换为驼峰命名，添加详细TypeORM配置
- ✅ 已完成服务层转换：继承`GenericCrudService<Platform>`，覆盖create、update、delete方法，添加平台名称唯一性检查
- ✅ 已完成路由层转换：实现6个自定义Hono路由，保持原始API端点路径和功能
- ✅ 已完成验证系统转换：从class-validator DTO转换为Zod Schema，添加详细验证规则
- ✅ 已编写16个集成测试，全部通过，覆盖所有API端点和业务逻辑
- ✅ 类型检查通过，修复了Platform类型重复定义、relationFields配置错误等问题
- ✅ 测试性能：所有测试在32.52秒内完成
- ✅ 验证了作为基础依赖包的能力

### 故事7：移植薪资管理模块（salary → @d8d/allin-salary-module）✅ **已完成**
**目标**：移植独立模块，完成所有模块移植

**验收标准**：
1. ✅ 创建`allin-packages/salary-module`目录结构
2. ✅ 完成实体转换：`SalaryLevel`实体转换，添加区域ID字段
3. ✅ **区域包集成**：使用`@d8d/geo-areas`包管理区域数据
4. ✅ 完成服务层转换：薪资业务逻辑，包含区域数据验证
5. ✅ 完成路由层转换：Hono路由实现，支持区域ID参数
6. ✅ 完成验证系统转换：Zod Schema定义，包含区域ID验证
7. ✅ 配置package.json：依赖管理，包含对`@d8d/geo-areas`的依赖
8. ✅ 编写API集成测试：验证薪资管理功能，包含区域数据测试
9. ✅ 通过类型检查和基本测试验证
10. ✅ 整体验证：所有7个模块的集成测试

**API集成测试要求**：
- 测试文件：`tests/integration/salary.integration.test.ts`
- 测试覆盖：薪资等级管理、关联查询
- **区域测试重点**：
  - 验证区域ID的有效性检查
  - 测试区域层级关系验证（省→市→区）
  - 验证区域不存在时的错误处理
  - 测试区域唯一性约束`(provinceId, cityId)`
  - 验证区域数据在响应中的完整性
- 验证：数值计算、等级规则
- 包含整体集成测试：验证所有模块协同工作

**完成情况**：
- ✅ 已创建完整的模块结构和配置文件
- ✅ 已完成实体转换：`SalaryLevel`实体，配置复合唯一索引`UQ_51cd5d2613663c210df246f183c`（provinceId, cityId）
- ✅ 已完成服务层转换：`SalaryService`继承`GenericCrudService`，覆盖`create`、`update`方法实现业务逻辑，包含区域验证和唯一性检查
- ✅ 已完成路由层转换：实现6个API端点，支持区域ID参数和查询
- ✅ 已完成验证系统转换：从class-validator DTO转换为Zod Schema，包含区域ID验证
- ✅ 已编写16个集成测试，全部通过，特别测试与geo-areas包的集成和区域唯一性约束
- ✅ 类型检查通过，修复了Schema验证问题、Decimal字段处理、路由冲突等问题
- ✅ 修复了测试数据问题：添加第二个省份和城市测试数据，避免唯一性约束冲突
- ✅ 修复了计算逻辑问题：在`calculateTotalSalary`方法中添加字符串转换逻辑，正确处理数据库返回的decimal字符串格式
- ✅ 修复了Schema验证问题：将`basicSalary`、`totalSalary`、`allowance`、`insurance`、`housingFund`字段改为`z.coerce.number()`，正确处理字符串到数字的转换
- ✅ 修复了区域字段验证：将`districtId`字段改为`.nullable().optional()`，`district`字段改为`.nullable().optional()`，处理null值
- ✅ 测试性能：所有测试在21.07秒内完成
- ✅ 清理了调试信息，恢复了CRUD路由聚合

### 故事8：将Allin系统模块集成到packages/server（已完成）
**目标**：将所有移植的Allin模块集成到主server中，配置路由和依赖

**验收标准**：
1. ✅ 在server的package.json中添加所有Allin模块的workspace依赖
2. ✅ 在server/src/index.ts中导入并注册所有Allin模块的路由
3. ✅ 在数据库初始化中注册所有Allin模块的实体
4. ✅ 验证所有API端点可以通过server访问
5. ✅ 在server/tests/integration/中添加Allin模块的集成测试，只测试API路由连通性（不重复测试业务逻辑）
6. ✅ 通过类型检查和基本测试验证
7. ✅ 确保与现有模块的兼容性

**API集成测试要求**：
- 测试文件：`packages/server/tests/integration/allin-modules.integration.test.ts`
- 测试覆盖：只测试API路由连通性，不重复测试业务逻辑
- 验证：路由注册正确性、数据库实体初始化、API端点连通性
- 测试范围：6个Allin模块的关键列表端点连通性

**完成情况**：
- ✅ **依赖配置**：在`packages/server/package.json`中添加了所有7个Allin模块的workspace依赖
- ✅ **路由注册**：在`packages/server/src/index.ts`中导入并注册了所有Allin模块的路由：
  ```typescript
  export const channelApiRoutes = api.route('/api/v1/channel', channelRoutes)
  export const companyApiRoutes = api.route('/api/v1/company', companyRoutes)
  export const disabilityApiRoutes = api.route('/api/v1/disability', disabledPersonRoutes)
  export const orderApiRoutes = api.route('/api/v1/order', orderRoutes)
  export const platformApiRoutes = api.route('/api/v1/platform', platformRoutes)
  export const salaryApiRoutes = api.route('/api/v1/salary', salaryRoutes)
  ```
- ✅ **实体注册**：在数据库初始化中添加了所有Allin模块的实体
- ✅ **集成测试**：创建了`allin-modules.integration.test.ts`文件，包含6个模块的路由连通性测试
- ✅ **测试通过**：所有6个集成测试全部通过，验证了API端点连通性
- ✅ **类型检查**：集成测试文件无类型错误（项目整体依赖问题不影响Allin模块集成）
- ✅ **兼容性**：与现有模块兼容，不影响现有功能

**修复的问题**：
1. **依赖导入错误**：`disabilityRoutes`不存在，修复为`disabledPersonRoutes`
2. **实体导入错误**：`Company`被当作类型使用，修复为从`/entities`路径导入
3. **测试语法错误**：`$get()`需要`query: {}`参数，修复所有模块的测试调用
4. **端点名称错误**：`getAllOrders`和`getAllSalaryLevels`端点不存在，修复为`list`端点

**测试结果**：
- 渠道管理模块路由连通性测试 ✅
- 公司管理模块路由连通性测试 ✅
- 残疾人管理模块路由连通性测试 ✅
- 订单管理模块路由连通性测试 ✅
- 平台管理模块路由连通性测试 ✅
- 薪资管理模块路由连通性测试 ✅

## 文件实体集成方案

### 现状分析

#### 1. 现有file-module结构
- **实体**：`File`（对应`files`表）
- **核心功能**：
  - MinIO对象存储集成
  - 预签名URL生成（`fullUrl`属性）
  - 完整的文件元数据管理（名称、类型、大小、描述等）
  - 用户关联（上传用户、时间）
- **表结构**：`id`, `name`, `type`, `size`, `path`, `description`, `upload_user_id`, `upload_time`等

#### 2. allin_system-master中的文件相关实体
1. **DisabledPhoto**（`disabled_photo`表）：残疾人照片管理
   - 字段：`photo_id`, `person_id`, `photo_type`, `photo_url`, `upload_time`, `can_download`
   - 直接存储文件URL在`photo_url`字段

2. **OrderPersonAsset**（`order_person_asset`表）：订单人员资产管理
   - 字段：`op_id`, `order_id`, `person_id`, `asset_type`, `asset_file_type`, `asset_url`, `related_time`
   - 存储图片/视频URL在`asset_url`字段

### 集成方案（采用：实体重构 + UI层统一上传）

#### 架构设计原则
**核心思想**：文件上传在UI层通过文件选择器组件统一处理，后端只负责文件ID的关联管理

**架构分层**：
1. **UI层**：管理后台使用文件选择器组件上传文件，获取文件ID
2. **API层**：接收文件ID，与业务实体关联
3. **服务层**：处理文件ID与业务实体的关联逻辑
4. **实体层**：通过`file_id`引用`File`实体

#### 实施步骤
1. **数据库迁移**：
   ```sql
   -- 1. 添加file_id字段
   ALTER TABLE disabled_photo ADD COLUMN file_id INT;
   ALTER TABLE order_person_asset ADD COLUMN file_id INT;

   -- 2. 建立外键关系
   ALTER TABLE disabled_photo ADD FOREIGN KEY (file_id) REFERENCES files(id);
   ALTER TABLE order_person_asset ADD FOREIGN KEY (file_id) REFERENCES files(id);

   -- 3. 可选：迁移现有URL数据到files表
   -- 根据现有photo_url/asset_url创建files记录，更新file_id
   ```

2. **实体重构**：
   ```typescript
   // 重构后的DisabledPhoto实体
   @Entity('disabled_photo')
   export class DisabledPhoto {
     @PrimaryGeneratedColumn({ name: 'photo_id' })
     photoId!: number;

     @Column({ name: 'person_id' })
     personId!: number;

     @Column({ name: 'file_id' })
     fileId!: number; // 引用File实体的ID

     @ManyToOne(() => File)
     @JoinColumn({ name: 'file_id' })
     file!: File; // 关联File实体

     // 业务字段保持不变
     @Column({ name: 'photo_type' })
     photoType!: string;

     @Column({ name: 'can_download' })
     canDownload!: number;

     @CreateDateColumn({ name: 'upload_time' })
     uploadTime!: Date;

     // 关联残疾人实体
     @ManyToOne(() => DisabledPerson, person => person.photos)
     @JoinColumn({ name: 'person_id' })
     person!: DisabledPerson;
   }
   ```

3. **API接口设计**：
   ```typescript
   // 创建残疾人照片的API接口
   // UI层已通过文件选择器上传文件，这里只接收文件ID
   export const CreateDisabledPhotoSchema = z.object({
     personId: z.number().int().positive(),
     fileId: z.number().int().positive(), // 文件ID，由UI层提供
     photoType: z.string().min(1).max(50),
     canDownload: z.number().int().min(0).max(1).default(1)
   });

   // 路由实现
   channelRoutes.post('/photos', async (c) => {
     const data = await c.req.valid('json'); // 包含fileId
     const photo = await disabilityService.createPhoto(data);
     return c.json(photo, 201);
   });

   // 获取照片详情（包含文件URL）
   channelRoutes.get('/photos/:id', async (c) => {
     const { id } = c.req.param();
     const photo = await disabilityService.getPhotoWithFile(Number(id));

     // 构建响应数据，包含文件URL
     const response = {
       ...photo,
       photoUrl: await photo.file.fullUrl, // 通过关联实体获取文件URL
       fileName: photo.file.name,
       fileSize: photo.file.size,
       fileType: photo.file.type
     };

     return c.json(response);
   });

   // 获取残疾人的所有照片（包含文件信息）
   channelRoutes.get('/persons/:personId/photos', async (c) => {
     const { personId } = c.req.param();
     const photos = await disabilityService.getPersonPhotosWithFiles(Number(personId));

     // 为每张照片添加文件URL
     const photosWithUrls = await Promise.all(
       photos.map(async (photo) => ({
         ...photo,
         photoUrl: await photo.file.fullUrl,
         fileName: photo.file.name,
         fileSize: photo.file.size
       }))
     );

     return c.json(photosWithUrls);
   });
   ```

4. **服务层实现**：
   ```typescript
   export class DisabilityService {
     async createPhoto(data: CreateDisabledPhotoDto): Promise<DisabledPhoto> {
       // 验证文件是否存在
       const file = await this.fileRepository.findOne({
         where: { id: data.fileId }
       });

       if (!file) {
         throw new Error('文件不存在');
       }

       // 创建照片记录（只关联文件ID）
       const photo = this.photoRepository.create({
         personId: data.personId,
         fileId: data.fileId, // 使用UI层提供的文件ID
         photoType: data.photoType,
         canDownload: data.canDownload
       });

       return this.photoRepository.save(photo);
     }

     async getPhotoWithFile(photoId: number): Promise<DisabledPhoto> {
       // 查询照片时加载关联的File实体
       const photo = await this.photoRepository.findOne({
         where: { photoId },
         relations: ['file'] // 加载关联的File实体
       });

       if (!photo) {
         throw new Error('照片不存在');
       }

       return photo;
     }

     async getPhotoUrl(photoId: number): Promise<string> {
       const photo = await this.getPhotoWithFile(photoId);

       // 通过关联的file实体访问fullUrl属性
       // file.fullUrl是File实体中的Promise属性
       return await photo.file.fullUrl;
     }

     async getPersonPhotosWithFiles(personId: number): Promise<DisabledPhoto[]> {
       // 查询某个残疾人的所有照片，并加载关联的File实体
       return this.photoRepository.find({
         where: { personId },
         relations: ['file'], // 加载关联的File实体
         order: { uploadTime: 'DESC' }
       });
     }
   }
   ```

#### 方案B：服务层集成（备选）
**核心思想**：保持表结构不变，在服务层集成FileService

**实施步骤**：
1. **保持表结构**：不修改`disabled_photo`和`order_person_asset`表
2. **服务层集成**：
   ```typescript
   export class DisabilityService {
     async getPhotoUrl(photoId: number): Promise<string> {
       const photo = await this.getPhoto(photoId);

       // 如果photo_url是MinIO路径，使用FileService生成预签名URL
       if (this.isMinioPath(photo.photoUrl)) {
         const path = this.extractMinioPath(photo.photoUrl);
         return this.fileService.getPresignedUrl(path);
       }

       // 否则返回原始URL
       return photo.photoUrl;
     }
   }
   ```

### 采用方案A（实体重构）的理由

1. **架构清晰**：UI层统一处理文件上传，后端专注业务逻辑
2. **职责分离**：
   - UI层：文件选择、上传、进度显示
   - API层：验证文件ID、关联业务实体
   - 服务层：业务逻辑处理
   - 实体层：数据关系定义
3. **复用现有组件**：利用项目已有的文件选择器组件
4. **简化后端逻辑**：不需要处理文件上传的复杂逻辑（分片、重试、进度等）
5. **一致性**：与项目其他模块保持相同的文件处理模式
6. **安全性**：文件上传由统一的组件处理，避免安全漏洞

### 文件上传流程
```
UI层（管理后台）：
1. 用户点击"上传照片"按钮
2. 打开文件选择器组件（复用现有组件）
3. 选择文件 → 组件调用file-module上传接口
4. 获取文件ID → 填充到表单的fileId字段
5. 提交表单（包含fileId和其他业务数据）

API层（disability-module）：
1. 接收包含fileId的请求
2. 验证fileId对应的文件是否存在
3. 创建DisabledPhoto记录，关联fileId
4. 返回创建结果

服务层：
1. 验证业务规则（如照片类型限制）
2. 处理文件与业务的关联逻辑
3. 返回包含文件URL的完整数据
```

### 影响的故事

需要调整以下故事的文件实体处理：

1. **故事4（disability-module）**：
   - 修改`DisabledPhoto`实体，添加`fileId`字段引用`File`实体
   - 更新API接口，接收`fileId`参数（而非文件内容）
   - 更新服务层，验证文件存在性并关联业务
   - 调整API集成测试，验证文件ID关联功能

2. **故事5（order-module）**：
   - 修改`OrderPersonAsset`实体，添加`fileId`字段引用`File`实体
   - 更新API接口，接收`fileId`参数
   - 更新服务层，处理资产文件关联逻辑
   - 调整测试用例，模拟文件ID关联场景

### 实施注意事项

1. **数据迁移**：需要制定现有URL数据的迁移方案
2. **兼容性**：保持API兼容，逐步迁移
3. **性能优化**：
   - **关联查询**：使用`relations: ['file']`加载关联实体，避免N+1查询
   - **选择性加载**：根据需要选择加载的关联字段
   - **分页查询**：大数据量时使用分页，避免一次性加载所有关联数据
4. **查询模式**：
   - 通过关联的`file`实体访问文件属性（`file.fullUrl`、`file.name`等）
   - 不需要在业务实体中定义重复的文件URL属性
   - 使用TypeORM的`relations`选项加载关联数据
5. **错误处理**：
   - 验证`fileId`对应的文件是否存在
   - 处理文件服务不可用的情况
   - 处理关联实体加载失败的情况

## 兼容性要求

- [ ] 现有API接口保持不变
- [ ] 数据库schema保持向后兼容（通过迁移方案）
- [ ] 遵循现有TypeScript配置和构建模式
- [ ] 性能影响最小化
- [ ] 文件功能与现有file-module集成

## 风险缓解

- **主要风险**：模块间依赖关系复杂，移植过程中可能破坏现有功能
- **缓解措施**：逐个模块移植，每个模块完成后进行功能验证
- **回滚计划**：保留原始allin_system-master目录作为备份，可随时恢复

## 完成定义

- [x] 所有7个模块移植故事完成，验收标准满足
  - ✅ 故事1：渠道管理模块（channel-module）已完成
  - ✅ 故事2：公司管理模块（company-module）已完成
  - ✅ 故事3：枚举常量包（allin-enums）已完成
  - ✅ 故事6：平台管理模块（platform-module）已完成
  - ✅ 故事4：残疾人管理模块（disability-module）已完成
  - ✅ 故事5：订单管理模块（order-module）已完成
  - ✅ 故事7：薪资管理模块（salary-module）已完成
- [x] 故事8：集成到packages/server（已完成）
- [x] 现有功能通过测试验证
- [x] 集成点正常工作
- [x] 文档更新适当
- [x] 现有功能无回归

## 验证清单

### 范围验证
- [ ] 史诗可在4个故事内完成
- [ ] 不需要架构文档变更
- [ ] 增强遵循现有模式
- [ ] 集成复杂度可管理

### 风险评估
- [ ] 对现有系统风险较低
- [ ] 回滚计划可行
- [ ] 测试方法覆盖现有功能
- [ ] 团队对集成点有足够了解

### 完整性检查
- [ ] 史诗目标清晰可实现
- [ ] 故事范围适当
- [ ] 成功标准可衡量
- [ ] 依赖关系已识别

---

## 故事经理交接说明

"模块分析和技术栈分析已完成，关键发现：

1. **依赖关系分析完成**：7个模块的依赖关系已明确，发现order和disability_person模块存在循环依赖
2. **技术栈差异分析完成**：源系统使用NestJS，目标系统使用Hono，存在重大架构差异
3. **命名方案确定**：使用`@d8d/allin-`前缀，`-module`后缀，非多租户版本
4. **目录结构**：在根目录创建`allin-packages/`目录存放专属包
5. **移植顺序建议**：platform → channel/dict/salary → company → order/disability
6. **技术栈转换方案**：已制定从NestJS到Hono的详细转换策略
7. **故事拆分完成**：按模块拆分为7个故事，每个故事包含API集成测试要求

**新的故事拆分方案**：
- **故事1**：移植渠道管理模块（channel_info → @d8d/allin-channel-module）
- **故事2**：移植公司管理模块（company → @d8d/allin-company-module）
- **故事3**：移植字典管理模块（dict_management → @d8d/allin-dict-module）
- **故事4**：移植残疾人管理模块（disability_person → @d8d/allin-disability-module）
- **故事5**：移植订单管理模块（order → @d8d/allin-order-module）
- **故事6**：移植平台管理模块（platform → @d8d/allin-platform-module）
- **故事7**：移植薪资管理模块（salary → @d8d/allin-salary-module）
- **故事8**：将Allin系统模块集成到packages/server

**每个故事的关键要求**：
1. **技术栈转换**：必须完成实体、服务、路由、验证的完整转换
2. **API集成测试**：必须编写`tests/integration/{module}.integration.test.ts`文件
3. **测试覆盖**：必须覆盖所有路由端点，验证CRUD操作
4. **遵循现有模式**：参考advertisements-module的集成测试模式
5. **验证要求**：认证、授权、数据验证、错误处理

**执行顺序建议**：
1. ✅ 先执行**故事6**（platform-module）：基础依赖模块 - **已完成**
2. ✅ 然后执行**故事1**（channel-module）：独立模块 - **已完成**
3. ✅ 然后执行**故事3**（allin-enums）：枚举常量包 - **已完成**
4. ✅ 接着执行**故事2**（company-module）：依赖platform - **已完成**
5. ✅ 然后执行**故事7**（salary-module）：独立模块 - **已完成**
6. ✅ 最后执行**故事4、5**（disability、order）：处理循环依赖 - **已完成**
7. ✅ 最后执行**故事8**（集成到server）：将所有模块集成到packages/server - **已完成**

**技术栈转换关键点**：
- **NestJS控制器 → Hono路由**：使用OpenAPIHono
- **class-validator DTO → Zod Schema**：使用z.object()定义
- **自定义Service → GenericCrudService继承**：复用现有CRUD模式
- **下划线命名 → 驼峰命名**：实体字段名转换
- **模块间依赖**：通过workspace依赖管理

**API集成测试模板参考**：
参考`/packages/advertisements-module/tests/integration/advertisements.integration.test.ts`
- 使用`testClient`创建测试客户端
- 使用`setupIntegrationDatabaseHooksWithEntities`设置测试数据库
- 包含认证测试、数据验证测试、错误处理测试
- 每个端点都要有成功和失败场景测试

**文件实体集成方案**：
发现allin_system-master中有文件相关实体（DisabledPhoto、OrderPersonAsset），需要与现有file-module集成。

**采用方案**：实体重构 + UI层统一上传
1. **架构原则**：文件上传在UI层通过文件选择器组件处理，后端只接收文件ID
2. **故事4（disability-module）**：
   - 修改`DisabledPhoto`实体，添加`fileId`字段引用`File`实体
   - API接口接收`fileId`参数（而非文件内容）
   - 服务层验证文件存在性并关联业务
3. **故事5（order-module）**：
   - 修改`OrderPersonAsset`实体，添加`fileId`字段引用`File`实体
   - API接口接收`fileId`参数
   - 服务层处理资产文件关联逻辑
4. **数据库**：需要迁移现有URL数据到files表

**实施要点**：
- UI层使用现有文件选择器组件上传文件
- 后端API只接收文件ID，不处理文件上传
- 保持API兼容性，制定数据迁移方案
- 注意N+1查询性能，合理使用数据加载策略

**新增故事8说明**：
- **故事8**：将Allin系统模块集成到packages/server
- **目标**：将所有移植的Allin模块集成到主server中，配置路由和依赖
- **关键任务**：
  1. 在server的package.json中添加所有Allin模块的workspace依赖
  2. 在server/src/index.ts中导入并注册所有Allin模块的路由
  3. 在数据库初始化中注册所有Allin模块的实体
  4. 验证所有API端点可以通过server访问
- **集成路径**：所有模块使用`/api/v1/{module}`路径前缀
- **认证要求**：所有端点通过现有认证中间件保护

史诗应在保持系统完整性的同时实现将有实体模块从NestJS架构移植到Hono架构的标准化独立包，每个模块都要有完整的API集成测试验证，并完成与现有file-module的文件集成，最后将所有模块集成到packages/server中。"