浏览代码

📝 docs(epic-007): 添加文件实体集成方案

- 新增文件实体集成方案章节,详细分析现有file-module结构和allin_system-master中的文件相关实体
- 提出两种集成方案:方案A(实体重构,推荐)和方案B(服务层集成,备选)
- 详细说明方案A的实施步骤,包括数据库迁移、实体重构和服务层调整
- 提供推荐方案A的理由:标准化管理、功能复用、扩展性、一致性和维护性
- 明确影响的故事:故事4(disability-module)和故事5(order-module)
- 列出实施注意事项:数据迁移、兼容性、性能和错误处理
- 更新兼容性要求,增加文件功能与现有file-module集成
- 在史诗总结部分补充文件实体集成方案概述和实施要点
yourname 3 周之前
父节点
当前提交
769613f892
共有 1 个文件被更改,包括 170 次插入2 次删除
  1. 170 2
      docs/epic-007.md

+ 170 - 2
docs/epic-007.md

@@ -442,12 +442,165 @@ export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
 - 验证:数值计算、等级规则
 - 包含整体集成测试:验证所有模块协同工作
 
+## 文件实体集成方案
+
+### 现状分析
+
+#### 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`字段
+
+### 集成方案(推荐:统一使用file-module)
+
+#### 方案A:实体重构(推荐)
+**核心思想**:将文件URL存储转换为引用`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. 创建files表记录(可选,根据现有URL数据)
+   -- 3. 建立外键关系
+   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);
+   ```
+
+2. **实体重构**:
+   ```typescript
+   // 原DisabledPhoto实体
+   @Entity('disabled_photo')
+   export class DisabledPhoto {
+     @Column({ name: 'photo_url' })
+     photoUrl: string; // 直接存储URL
+   }
+
+   // 重构后
+   @Entity('disabled_photo')
+   export class DisabledPhoto {
+     @Column({ name: 'file_id' })
+     fileId: number;
+
+     @ManyToOne(() => File)
+     @JoinColumn({ name: 'file_id' })
+     file: File; // 引用File实体
+
+     // 保持原有字段
+     @Column({ name: 'photo_type' })
+     photoType: string;
+
+     @Column({ name: 'can_download' })
+     canDownload: number;
+
+     // 兼容性属性
+     get photoUrl(): Promise<string> {
+       return this.file?.fullUrl || Promise.resolve('');
+     }
+   }
+   ```
+
+3. **服务层调整**:
+   ```typescript
+   // 使用FileService处理文件上传
+   import { FileService } from '@d8d/file-module';
+
+   export class DisabilityService {
+     async uploadPhoto(personId: number, fileData: Buffer, filename: string): Promise<DisabledPhoto> {
+       // 1. 使用FileService上传文件
+       const file = await this.fileService.uploadFile({
+         name: filename,
+         data: fileData,
+         uploadUserId: currentUser.id
+       });
+
+       // 2. 创建DisabledPhoto记录
+       const photo = this.photoRepository.create({
+         personId,
+         fileId: file.id,
+         photoType: this.getPhotoType(filename),
+         canDownload: 1
+       });
+
+       return this.photoRepository.save(photo);
+     }
+   }
+   ```
+
+#### 方案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. **标准化管理**:统一使用项目现有的文件管理架构
+2. **功能复用**:利用file-module的完整功能(权限、元数据、存储)
+3. **扩展性**:便于未来添加文件版本、缩略图、水印等功能
+4. **一致性**:与项目其他模块保持相同的技术栈和模式
+5. **维护性**:集中文件逻辑,减少重复代码
+
+### 影响的故事
+
+需要调整以下故事的文件实体处理:
+
+1. **故事4(disability-module)**:
+   - 修改`DisabledPhoto`实体,引用`File`实体
+   - 更新照片上传/下载服务,使用`FileService`
+   - 调整API集成测试,验证文件功能
+
+2. **故事5(order-module)**:
+   - 修改`OrderPersonAsset`实体,引用`File`实体
+   - 更新资产上传服务,使用`FileService`
+   - 调整测试用例
+
+### 实施注意事项
+
+1. **数据迁移**:需要制定现有URL数据的迁移方案
+2. **兼容性**:保持API兼容,逐步迁移
+3. **性能**:注意N+1查询问题,合理使用数据加载策略
+4. **错误处理**:处理文件服务不可用的情况
+
 ## 兼容性要求
 
 - [ ] 现有API接口保持不变
-- [ ] 数据库schema保持向后兼容
+- [ ] 数据库schema保持向后兼容(通过迁移方案)
 - [ ] 遵循现有TypeScript配置和构建模式
 - [ ] 性能影响最小化
+- [ ] 文件功能与现有file-module集成
 
 ## 风险缓解
 
@@ -533,4 +686,19 @@ export type CreateChannelDto = z.infer<typeof CreateChannelSchema>;
 - 包含认证测试、数据验证测试、错误处理测试
 - 每个端点都要有成功和失败场景测试
 
-史诗应在保持系统完整性的同时实现将有实体模块从NestJS架构移植到Hono架构的标准化独立包,每个模块都要有完整的API集成测试验证。"
+**文件实体集成方案**:
+发现allin_system-master中有文件相关实体(DisabledPhoto、OrderPersonAsset),需要与现有file-module集成。
+
+**推荐方案**:统一使用file-module,重构文件实体引用
+1. **故事4(disability-module)**:修改`DisabledPhoto`实体,添加`fileId`字段引用`File`实体
+2. **故事5(order-module)**:修改`OrderPersonAsset`实体,添加`fileId`字段引用`File`实体
+3. **服务层**:使用`FileService`处理文件上传/下载
+4. **数据库**:需要迁移现有URL数据到files表
+
+**实施要点**:
+- 保持API兼容性
+- 制定数据迁移方案
+- 注意N+1查询性能问题
+- 处理文件服务错误情况
+
+史诗应在保持系统完整性的同时实现将有实体模块从NestJS架构移植到Hono架构的标准化独立包,每个模块都要有完整的API集成测试验证,并完成与现有file-module的文件集成。"