|
@@ -0,0 +1,389 @@
|
|
|
|
|
+# Story 010.009: 创建统一文件后端模块 (unified-file-module)
|
|
|
|
|
+
|
|
|
|
|
+## Status
|
|
|
|
|
+Approved
|
|
|
|
|
+
|
|
|
|
|
+## Story
|
|
|
|
|
+
|
|
|
|
|
+**As a** 开发者,
|
|
|
|
|
+**I want** 从单租户文件模块复制并改造创建统一文件后端模块(unified-file-module),
|
|
|
|
|
+**so that** 统一广告模块可以使用无租户隔离的文件实体,确保架构一致性。
|
|
|
|
|
+
|
|
|
|
|
+## Acceptance Criteria
|
|
|
|
|
+
|
|
|
|
|
+1. 创建 `packages/unified-file-module` 包(**使用 `cp -r` 命令直接复制 `packages/file-module` 整个文件夹**)
|
|
|
|
|
+2. 修改包名和模块引用(从 `@d8d/file-module` 改为 `@d8d/unified-file-module`),**移除用户模块和认证模块依赖**
|
|
|
|
|
+3. 定义Entity(确认无tenant_id字段,单租户file-module本身就没有tenant_id)
|
|
|
|
|
+4. 实现Service层和文件上传逻辑(MinIO,保持与原模块一致)
|
|
|
|
|
+5. 实现管理员路由(**所有路由**都使用 `tenantAuthMiddleware`,只有超级管理员可访问,**不需要用户展示路由**)
|
|
|
|
|
+6. 编写完整的单元测试和集成测试
|
|
|
|
|
+7. 测试覆盖率达到70%以上
|
|
|
|
|
+
|
|
|
|
|
+## Tasks / Subtasks
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务1: 复制文件模块创建统一文件模块** (AC: 1)
|
|
|
|
|
+ - [ ] **使用 `cp -r packages/file-module packages/unified-file-module` 命令直接复制整个文件夹**
|
|
|
|
|
+ - [ ] 验证复制后的目录结构完整
|
|
|
|
|
+ - [ ] 验证所有文件都已复制
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务2: 修改包配置文件** (AC: 2)
|
|
|
|
|
+ - [ ] 修改 `package.json` 包名:`@d8d/file-module` → `@d8d/unified-file-module`
|
|
|
|
|
+ - [ ] 修改 `package.json` 描述:添加"unified"相关描述
|
|
|
|
|
+ - [ ] **删除依赖**:从 `package.json` 中移除 `@d8d/user-module` 和 `@d8d/auth-module`(本模块只在超级管理员后台使用,不需要用户和认证依赖)
|
|
|
|
|
+ - [ ] **添加依赖**:添加 `@d8d/tenant-module-mt`(需要使用 `tenantAuthMiddleware`)
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务3: 修改模块导出文件** (AC: 2)
|
|
|
|
|
+ - [ ] 修改 `src/index.ts` 导出的实体名:`File` → `UnifiedFile`
|
|
|
|
|
+ - [ ] 修改 `src/index.ts` 导出的服务名:`FileService` → `UnifiedFileService`,`MinioService` 保持不变
|
|
|
|
|
+ - [ ] 更新 `src/entities/index.ts` 导出
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务4: 修改Entity定义** (AC: 3)
|
|
|
|
|
+ - [ ] 重命名文件:`entities/file.entity.ts` → `entities/unified-file.entity.ts`
|
|
|
|
|
+ - [ ] 修改类名:`export class File` → `export class UnifiedFile`
|
|
|
|
|
+ - [ ] 确认无 `tenant_id` 字段(单租户模块本身就没有,验证确认即可)
|
|
|
|
|
+ - [ ] 修改表名:`@Entity('file')` → `@Entity('unified_file')`
|
|
|
|
|
+ - [ ] **删除 UserEntity 关联**:移除 `@ManyToOne('UserEntity')` 和 `uploadUser` 字段(因为不需要用户模块依赖)
|
|
|
|
|
+ - [ ] 保持 `uploadUserId` 字段不变(只保留ID,不关联UserEntity)
|
|
|
|
|
+ - [ ] 保持其他所有字段定义不变(id, name, type, size, path, description, uploadTime, lastUpdated, createdAt, updatedAt)
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务5: 修改Service层** (AC: 4)
|
|
|
|
|
+ - [ ] 重命名文件:`services/file.service.ts` → `services/unified-file.service.ts`
|
|
|
|
|
+ - [ ] 修改类名:`export class FileService` → `export class UnifiedFileService`
|
|
|
|
|
+ - [ ] 更新构造函数中的Entity引用:`File` → `UnifiedFile`
|
|
|
|
|
+ - [ ] `MinioService` 保持不变(复用原有实现)
|
|
|
|
|
+ - [ ] 保持所有方法逻辑不变
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务6: 修改Schema定义** (AC: 4)
|
|
|
|
|
+ - [ ] 修改 `schemas/file.schema.ts` 中所有Schema名称:`FileSchema` → `UnifiedFileSchema`,`CreateFileSchema` → `CreateUnifiedFileSchema`,等等
|
|
|
|
|
+ - [ ] 更新Schema中的描述,添加"unified"相关说明
|
|
|
|
|
+ - [ ] 保持所有字段验证规则不变
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务7: 修改路由层** (AC: 5)
|
|
|
|
|
+ - [ ] **所有路由都使用 `tenantAuthMiddleware`**(只有超级管理员ID=1可访问,**不需要用户展示路由**)
|
|
|
|
|
+ - [ ] 删除原有的用户展示路由(如果存在)
|
|
|
|
|
+ - [ ] 确认路由路径设置为相对路径(不包含 `/api/v1` 前缀)
|
|
|
|
|
+ - [ ] 添加 `request.params` 定义,使用 `z.coerce.number<number>()` 进行类型转换
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务8: 编写单元测试** (AC: 7, 8)
|
|
|
|
|
+ - [ ] 重命名测试文件:`tests/unit/file.service.test.ts` → `tests/unit/unified-file.service.test.ts`
|
|
|
|
|
+ - [ ] 更新测试中的实体名:`File` → `UnifiedFile`
|
|
|
|
|
+ - [ ] 更新测试中的服务名:`FileService` → `UnifiedFileService`
|
|
|
|
|
+ - [ ] 验证所有单元测试通过
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务9: 编写集成测试** (AC: 6, 7)
|
|
|
|
|
+ - [ ] 重命名测试文件:`tests/integration/file.integration.test.ts` → `tests/integration/unified-file.integration.test.ts`
|
|
|
|
|
+ - [ ] 更新测试中的实体名和服务名
|
|
|
|
|
+ - [ ] 添加管理员路由权限测试(验证只有超级管理员ID=1可访问)
|
|
|
|
|
+ - [ ] **删除用户路由测试**(不需要用户展示路由)
|
|
|
|
|
+ - [ ] 验证所有集成测试通过
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **任务10: 类型检查和代码质量** (AC: 8)
|
|
|
|
|
+ - [ ] 运行 `pnpm typecheck` 确保无TypeScript类型错误
|
|
|
|
|
+ - [ ] 运行 `pnpm test` 确保所有测试通过
|
|
|
|
|
+ - [ ] 运行 `pnpm test:coverage` 确保测试覆盖率达到70%以上
|
|
|
|
|
+
|
|
|
|
|
+## Dev Notes
|
|
|
|
|
+
|
|
|
|
|
+### 前一故事关键要点(来自 010.006, 010.007, 010.008)
|
|
|
|
|
+
|
|
|
|
|
+**史诗010的统一设计模式**:
|
|
|
|
|
+- **⚠️ 统一文件模块只在超级管理员后台使用,不需要用户展示路由**
|
|
|
|
|
+- **所有路由都使用 `tenantAuthMiddleware`**(只有超级管理员ID=1可访问)
|
|
|
|
|
+- **不需要 `authMiddleware`**(不需要用户展示路由)
|
|
|
|
|
+- **不需要依赖用户模块和认证模块**
|
|
|
|
|
+- 统一模块的Entity没有 `tenant_id` 字段
|
|
|
|
|
+- API路由路径使用相对路径(不包含 `/api/v1` 前缀)
|
|
|
|
|
+
|
|
|
|
|
+**路由规范** [Source: docs/prd/epic-010-unified-ad-management.md, docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+- 路由路径必须使用相对路径(如 `/` 和 `/:id`),不包含 `/api/v1` 前缀
|
|
|
|
|
+- 路由 `request.params` 必须明确定义,使用 `z.coerce.number<number>()` 进行类型转换
|
|
|
|
|
+- 使用 `c.req.valid('param')` 获取参数,而非 `parseInt(c.req.param('id'))`
|
|
|
|
|
+
|
|
|
|
|
+### 当前问题说明
|
|
|
|
|
+
|
|
|
|
|
+**架构不一致性** [Source: docs/prd/epic-010-unified-ad-management.md]:
|
|
|
|
|
+- 统一广告模块 (`unified-advertisements-module`) 当前使用 `@d8d/core-module-mt/file-module-mt` 的 `FileMt` 实体
|
|
|
|
|
+- `FileMt` 实体有 `tenant_id` 字段,是多租户隔离的
|
|
|
|
|
+- 统一广告本身是无租户隔离的,但关联的文件却是多租户隔离的
|
|
|
|
|
+- 这造成了架构不一致性
|
|
|
|
|
+
|
|
|
|
|
+### 源模块:单租户文件模块 (file-module)
|
|
|
|
|
+
|
|
|
|
|
+**模块结构** [Source: packages/file-module/]:
|
|
|
|
|
+```
|
|
|
|
|
+packages/file-module/
|
|
|
|
|
+├── package.json
|
|
|
|
|
+├── tsconfig.json
|
|
|
|
|
+├── vitest.config.ts
|
|
|
|
|
+├── src/
|
|
|
|
|
+│ ├── entities/
|
|
|
|
|
+│ │ └── file.entity.ts
|
|
|
|
|
+│ ├── services/
|
|
|
|
|
+│ │ ├── file.service.ts
|
|
|
|
|
+│ │ └── minio.service.ts
|
|
|
|
|
+│ ├── routes/
|
|
|
|
|
+│ │ ├── file.routes.ts
|
|
|
|
|
+│ │ ├── file-crud.routes.ts
|
|
|
|
|
+│ │ ├── file-custom.routes.ts
|
|
|
|
|
+│ │ └── index.ts
|
|
|
|
|
+│ ├── schemas/
|
|
|
|
|
+│ │ └── file.schema.ts
|
|
|
|
|
+│ └── index.ts
|
|
|
|
|
+└── tests/
|
|
|
|
|
+ ├── unit/
|
|
|
|
|
+ │ └── file.service.test.ts
|
|
|
|
|
+ └── integration/
|
|
|
|
|
+ └── file.integration.test.ts
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Entity定义** [Source: packages/file-module/src/entities/file.entity.ts]:
|
|
|
|
|
+```typescript
|
|
|
|
|
+@Entity('file')
|
|
|
|
|
+export class File {
|
|
|
|
|
+ @PrimaryGeneratedColumn({ name: 'id', type: 'int', unsigned: true })
|
|
|
|
|
+ id!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'name', type: 'varchar', length: 255 })
|
|
|
|
|
+ name!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'type', type: 'varchar', length: 50, nullable: true, comment: '文件类型' })
|
|
|
|
|
+ type!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'size', type: 'int', unsigned: true, nullable: true, comment: '文件大小,单位字节' })
|
|
|
|
|
+ size!: number | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'path', type: 'varchar', length: 512, comment: '文件存储路径' })
|
|
|
|
|
+ path!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'description', type: 'text', nullable: true, comment: '文件描述' })
|
|
|
|
|
+ description!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'upload_user_id', type: 'int', unsigned: true })
|
|
|
|
|
+ uploadUserId!: number;
|
|
|
|
|
+
|
|
|
|
|
+ // ⚠️ 统一文件模块删除此关联(不需要用户模块依赖)
|
|
|
|
|
+ // @ManyToOne('UserEntity')
|
|
|
|
|
+ // @JoinColumn({ name: 'upload_user_id', referencedColumnName: 'id' })
|
|
|
|
|
+ // uploadUser!: UserEntity;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'upload_time', type: 'timestamp' })
|
|
|
|
|
+ uploadTime!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'last_updated', type: 'timestamp', nullable: true, comment: '最后更新时间' })
|
|
|
|
|
+ lastUpdated!: Date | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'created_at', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
|
|
|
|
+ createdAt!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'updated_at',
|
|
|
|
|
+ type: 'timestamp',
|
|
|
|
|
+ default: () => 'CURRENT_TIMESTAMP',
|
|
|
|
|
+ onUpdate: 'CURRENT_TIMESTAMP'
|
|
|
|
|
+ })
|
|
|
|
|
+ updatedAt!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ // 注意:没有 tenant_id 字段(单租户模块)
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**包名**: `@d8d/file-module` [Source: packages/file-module/package.json]
|
|
|
|
|
+
|
|
|
|
|
+**依赖** [Source: packages/file-module/package.json]:
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "dependencies": {
|
|
|
|
|
+ "@d8d/shared-types": "workspace:*",
|
|
|
|
|
+ "@d8d/shared-utils": "workspace:*",
|
|
|
|
|
+ "@d8d/shared-crud": "workspace:*",
|
|
|
|
|
+ "@d8d/tenant-module-mt": "workspace:*",
|
|
|
|
|
+ "hono": "^4.8.5",
|
|
|
|
|
+ "@hono/zod-openapi": "1.0.2",
|
|
|
|
|
+ "minio": "^8.0.5",
|
|
|
|
|
+ "typeorm": "^0.3.20",
|
|
|
|
|
+ "uuid": "^11.1.0",
|
|
|
|
|
+ "zod": "^4.1.12"
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**⚠️ 依赖变更说明**:
|
|
|
|
|
+- **删除**: `@d8d/user-module` 和 `@d8d/auth-module`(本模块只在超级管理员后台使用,不需要这些依赖)
|
|
|
|
|
+- **添加**: `@d8d/tenant-module-mt`(需要使用 `tenantAuthMiddleware`)
|
|
|
|
|
+
|
|
|
|
|
+### 关键实施要点
|
|
|
|
|
+
|
|
|
|
|
+**⚠️ 重要:使用 CP 命令直接复制**
|
|
|
|
|
+
|
|
|
|
|
+根据用户明确要求,必须使用以下命令直接复制整个文件模块文件夹:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cp -r packages/file-module packages/unified-file-module
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**为什么使用 cp 命令直接复制**:
|
|
|
|
|
+1. **保证完整性**: 复制整个文件夹确保所有文件(包括测试、配置、源码)都被包含
|
|
|
|
|
+2. **减少错误**: 避免手动创建文件时遗漏某些文件或配置
|
|
|
|
|
+3. **保持一致性**: 确保新模块的结构与原模块完全一致
|
|
|
|
|
+4. **提高效率**: 一次性复制后只需修改必要的部分,而不是从头创建
|
|
|
|
|
+
|
|
|
|
|
+**复制后的修改清单**:
|
|
|
|
|
+
|
|
|
|
|
+1. **包名修改**: `@d8d/file-module` → `@d8d/unified-file-module`
|
|
|
|
|
+2. **依赖修改**: 删除 `@d8d/user-module` 和 `@d8d/auth-module`,添加 `@d8d/tenant-module-mt`
|
|
|
|
|
+3. **实体名修改**: `File` → `UnifiedFile`,表名 `file` → `unified_file`
|
|
|
|
|
+4. **实体关联修改**: 删除 `UserEntity` 关联,只保留 `uploadUserId` 字段
|
|
|
|
|
+5. **服务名修改**: `FileService` → `UnifiedFileService`
|
|
|
|
|
+6. **Schema名修改**: 所有Schema添加 `Unified` 前缀
|
|
|
|
|
+7. **文件名修改**: 测试文件和服务文件重命名
|
|
|
|
|
+8. **路由配置**: **所有路由**都使用 `tenantAuthMiddleware`,**不需要用户展示路由**
|
|
|
|
|
+
|
|
|
|
|
+### 后端模块包开发规范
|
|
|
|
|
+
|
|
|
|
|
+**包结构规范** [Source: docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+```
|
|
|
|
|
+packages/{module-name}/
|
|
|
|
|
+├── package.json
|
|
|
|
|
+├── tsconfig.json
|
|
|
|
|
+├── vitest.config.ts
|
|
|
|
|
+├── src/
|
|
|
|
|
+│ ├── entities/
|
|
|
|
|
+│ ├── services/
|
|
|
|
|
+│ ├── routes/
|
|
|
|
|
+│ ├── schemas/
|
|
|
|
|
+│ └── index.ts
|
|
|
|
|
+└── tests/
|
|
|
|
|
+ ├── integration/
|
|
|
|
|
+ └── utils/
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Entity规范** [Source: docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+- 使用 `@Entity()` 装饰器定义表名
|
|
|
|
|
+- 完整的列定义:包含 `type`, `length`, `nullable`, `comment` 等属性
|
|
|
|
|
+- 主键使用 `@PrimaryGeneratedColumn`,设置 `unsigned: true`
|
|
|
|
|
+- 时间戳使用 `timestamp` 类型,默认值为 `() => 'CURRENT_TIMESTAMP'`
|
|
|
|
|
+
|
|
|
|
|
+**Service规范** [Source: docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+- 继承 `GenericCrudService` 基类
|
|
|
|
|
+- 使用 `override` 关键字覆盖父类方法
|
|
|
|
|
+- 软删除使用 `status` 字段(如果需要)
|
|
|
|
|
+
|
|
|
|
|
+**路由规范** [Source: docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+- 使用 `OpenAPIHono` 和 `AuthContext` 泛型
|
|
|
|
|
+- 路由路径使用相对路径,不包含 `/api/v1` 前缀
|
|
|
|
|
+- 自定义路由使用 `createRoute` 定义
|
|
|
|
|
+- 响应数据使用 `parseWithAwait` 验证
|
|
|
|
|
+- `request.params` 使用 `z.coerce.number<number>()` 进行类型转换
|
|
|
|
|
+
|
|
|
|
|
+**Schema规范** [Source: docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+- 使用 `z.object()` 定义Schema
|
|
|
|
|
+- 使用 `.openapi()` 装饰器添加描述和示例
|
|
|
|
|
+- 使用 `z.coerce.date<Date>()` 和 `z.coerce.number<number>()` 泛型语法
|
|
|
|
|
+- 不导出推断类型(类型由RPC自动推断)
|
|
|
|
|
+
|
|
|
|
|
+### 核心模块引用规范
|
|
|
|
|
+
|
|
|
|
|
+**⚠️ 重要**: 新模块必须从正确路径引用基础模块
|
|
|
|
|
+
|
|
|
|
|
+| 模块类型 | ✅ 正确引用 | 说明 |
|
|
|
|
|
+|---------|-----------|------|
|
|
|
|
|
+| 租户模块(多租户) | `@d8d/tenant-module-mt` | 独立包,位于 `packages/tenant-module-mt/` |
|
|
|
|
|
+
|
|
|
|
|
+**⚠️ 本模块不需要的依赖**:
|
|
|
|
|
+- ❌ 不需要 `@d8d/user-module` 或 `@d8d/user-module-mt`(不在用户模块中关联用户信息)
|
|
|
|
|
+- ❌ 不需要 `@d8d/auth-module` 或 `@d8d/auth-module-mt`(不需要用户认证中间件)
|
|
|
|
|
+- ✅ 只需要 `@d8d/tenant-module-mt`(使用 `tenantAuthMiddleware`)
|
|
|
|
|
+
|
|
|
|
|
+### MinIO集成
|
|
|
|
|
+
|
|
|
|
|
+**MinIO配置** [Source: docs/architecture/tech-stack.md]:
|
|
|
|
|
+- 对象存储服务:MinIO
|
|
|
|
|
+- 客户端SDK:MinIO JavaScript SDK
|
|
|
|
|
+- 默认存储桶:`d8dai`
|
|
|
|
|
+
|
|
|
|
|
+**环境变量**:
|
|
|
|
|
+- `MINIO_HOST`: MinIO服务器地址
|
|
|
|
|
+- `MINIO_PORT`: MinIO端口
|
|
|
|
|
+- `MINIO_USE_SSL`: 是否使用SSL
|
|
|
|
|
+- `MINIO_BUCKET_NAME`: 存储桶名称
|
|
|
|
|
+
|
|
|
|
|
+### 项目位置
|
|
|
|
|
+
|
|
|
|
|
+**新模块位置**: `packages/unified-file-module/` [Source: docs/architecture/source-tree.md]
|
|
|
|
|
+
|
|
|
|
|
+**测试标准**: Vitest [Source: docs/architecture/tech-stack.md]
|
|
|
|
|
+
|
|
|
|
|
+### Testing
|
|
|
|
|
+
|
|
|
|
|
+**测试文件位置** [Source: docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+- 单元测试: `packages/unified-file-module/tests/unit/`
|
|
|
|
|
+- 集成测试: `packages/unified-file-module/tests/integration/`
|
|
|
|
|
+
|
|
|
|
|
+**测试框架**:
|
|
|
|
|
+- Vitest [Source: docs/architecture/tech-stack.md]
|
|
|
|
|
+- hono/testing (API端点测试)
|
|
|
|
|
+
|
|
|
|
|
+**测试配置** [Source: docs/architecture/backend-module-package-standards.md]:
|
|
|
|
|
+```typescript
|
|
|
|
|
+// vitest.config.ts
|
|
|
|
|
+export default defineConfig({
|
|
|
|
|
+ test: {
|
|
|
|
|
+ globals: true,
|
|
|
|
|
+ environment: 'node',
|
|
|
|
|
+ include: ['tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
|
|
|
|
+ fileParallelism: false // 避免数据库连接冲突
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**测试覆盖要求**:
|
|
|
|
|
+- 测试覆盖率达到70%以上
|
|
|
|
|
+- 单元测试:覆盖Service层核心逻辑
|
|
|
|
|
+- 集成测试:覆盖路由端点和权限控制
|
|
|
|
|
+
|
|
|
|
|
+**测试命令**:
|
|
|
|
|
+```bash
|
|
|
|
|
+# 进入模块目录
|
|
|
|
|
+cd packages/unified-file-module
|
|
|
|
|
+
|
|
|
|
|
+# 运行所有测试
|
|
|
|
|
+pnpm test
|
|
|
|
|
+
|
|
|
|
|
+# 运行单元测试
|
|
|
|
|
+pnpm test:unit
|
|
|
|
|
+
|
|
|
|
|
+# 运行集成测试
|
|
|
|
|
+pnpm test:integration
|
|
|
|
|
+
|
|
|
|
|
+# 生成覆盖率报告
|
|
|
|
|
+pnpm test:coverage
|
|
|
|
|
+
|
|
|
|
|
+# 类型检查
|
|
|
|
|
+pnpm typecheck
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Change Log
|
|
|
|
|
+
|
|
|
|
|
+| Date | Version | Description | Author |
|
|
|
|
|
+|------|---------|-------------|--------|
|
|
|
|
|
+| 2026-01-04 | 1.0 | 初始故事创建 | Bob (Scrum Master) |
|
|
|
|
|
+| 2026-01-04 | 1.1 | 修正:统一文件模块只在超级管理员后台使用,移除用户模块和认证模块依赖,所有路由使用 tenantAuthMiddleware | Bob (Scrum Master) |
|
|
|
|
|
+| 2026-01-04 | 1.2 | 修正:租户模块是独立包 `@d8d/tenant-module-mt`,不在 `core-module-mt` 里面 | Bob (Scrum Master) |
|
|
|
|
|
+| 2026-01-04 | 1.3 | 批准故事 | Bob (Scrum Master) |
|
|
|
|
|
+
|
|
|
|
|
+## Dev Agent Record
|
|
|
|
|
+
|
|
|
|
|
+### Agent Model Used
|
|
|
|
|
+_开发代理实施时填写_
|
|
|
|
|
+
|
|
|
|
|
+### Debug Log References
|
|
|
|
|
+_开发代理实施时填写_
|
|
|
|
|
+
|
|
|
|
|
+### Completion Notes List
|
|
|
|
|
+_开发代理实施时填写_
|
|
|
|
|
+
|
|
|
|
|
+### File List
|
|
|
|
|
+_开发代理实施时填写_
|
|
|
|
|
+
|
|
|
|
|
+## QA Results
|
|
|
|
|
+_QA代理待填写_
|