epic-007-allin-system-transplant.md 52 KB

史诗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模块直接依赖
  • companychannel_info模块被orderdisability_person模块引用
  • orderdisability_person模块相互引用实体,形成循环依赖
  • dict_managementsalary模块完全独立

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. 实体层转换示例
// 原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. 服务层转换示例
// 原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. 路由层转换示例
// 原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. 验证系统转换
// 原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-moduleallin-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,覆盖createupdate方法实现业务逻辑
  • ✅ 已完成路由层转换:实现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 enumconst enum定义
  • 值映射:保持与原始数据库值一致(如disability_type='vision'DisabilityType.VISION = 'vision'

对后续模块的影响

  1. disability-module:使用DisabilityTypeDisabilityLevel枚举
  2. order-module:使用OrderStatusWorkStatus枚举
  3. 其他模块:根据需要使用相应枚举

优势

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

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

背景分析

经过对Allin系统模块的分析,发现以下模块使用行政区划数据:

  1. 残疾人管理模块 (disability_person):

    • 存储provincecitydistrict字段(字符串类型)
    • 用于记录残疾人的户籍地址和居住地址
  2. 薪资管理模块 (salary):

    • 存储provincecitydistrict字段(字符串类型)
    • 用于按地区管理薪资水平,有唯一性约束(province, city)

问题分析

当前实现存在以下问题:

  1. 数据不一致:字符串存储,容易输入错误或格式不一致
  2. 缺乏验证:没有验证区域数据的有效性
  3. 维护困难:区域变更需要手动更新所有相关记录
  4. 重复功能:与已有的@d8d/geo-areas包功能重叠

设计决策

采用方案A:完全依赖区域包,将所有区域数据改为引用@d8d/geo-areas包中的AreaEntity

改造方案

  1. 残疾人管理模块改造

    • 移除provincecitydistrict字符串字段
    • 添加provinceIdcityIddistrictId字段(外键引用AreaEntity
    • 使用AreaService验证区域数据的有效性和层级关系
  2. 薪资管理模块改造

    • 移除provincecitydistrict字符串字段
    • 添加provinceIdcityIddistrictId字段(外键引用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包中的DisabilityTypeDisabilityLevel枚举
  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个实体转换:DisabledPersonDisabledBankCardDisabledPhotoDisabledRemarkDisabledVisit
  • ✅ 已完成文件模块集成:照片和银行卡实体使用fileId字段引用File实体
  • ✅ 已完成服务层转换:DisabledPersonService继承GenericCrudServiceAggregatedService处理聚合业务逻辑
  • ✅ 已完成路由层转换:实现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包中的OrderStatusWorkStatus枚举
  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个实体转换:EmploymentOrderOrderPersonOrderPersonAsset
  • ✅ 已完成文件模块集成: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,覆盖createupdate方法实现业务逻辑,包含区域验证和唯一性检查
  • ✅ 已完成路由层转换:实现6个API端点,支持区域ID参数和查询
  • ✅ 已完成验证系统转换:从class-validator DTO转换为Zod Schema,包含区域ID验证
  • ✅ 已编写16个集成测试,全部通过,特别测试与geo-areas包的集成和区域唯一性约束
  • ✅ 类型检查通过,修复了Schema验证问题、Decimal字段处理、路由冲突等问题
  • ✅ 修复了测试数据问题:添加第二个省份和城市测试数据,避免唯一性约束冲突
  • ✅ 修复了计算逻辑问题:在calculateTotalSalary方法中添加字符串转换逻辑,正确处理数据库返回的decimal字符串格式
  • ✅ 修复了Schema验证问题:将basicSalarytotalSalaryallowanceinsurancehousingFund字段改为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模块的路由:

    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. 端点名称错误getAllOrdersgetAllSalaryLevels端点不存在,修复为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. DisabledPhotodisabled_photo表):残疾人照片管理

    • 字段:photo_id, person_id, photo_type, photo_url, upload_time, can_download
    • 直接存储文件URL在photo_url字段
  2. OrderPersonAssetorder_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. 数据库迁移

    -- 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. 实体重构

    // 重构后的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接口设计

    // 创建残疾人照片的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. 服务层实现

    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_photoorder_person_asset
  2. 服务层集成

    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.fullUrlfile.name等)
    • 不需要在业务实体中定义重复的文件URL属性
    • 使用TypeORM的relations选项加载关联数据
  5. 错误处理
    • 验证fileId对应的文件是否存在
    • 处理文件服务不可用的情况
    • 处理关联实体加载失败的情况

兼容性要求

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

风险缓解

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

完成定义

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

验证清单

范围验证

  • 史诗可在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中。"