Explorar o código

📝 docs(故事007.008): 创建广告管理模块多租户复制故事

- 创建故事007.008:广告管理模块多租户复制
- 更新故事007.007:完善商户模块验收标准和回归测试结果
- 包含从前7个故事中学到的8个关键经验教训
- 明确广告模块特有的技术约束和关联关系处理
- 提供完整的测试要求和237个回归测试验证

🤖 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 hai 1 mes
pai
achega
85d0033679

+ 34 - 12
docs/stories/007.007.merchant-module-multi-tenant-replication.md

@@ -12,14 +12,15 @@
 
 ## 验收标准
 
-1. **AC 1**: 成功复制 `@d8d/merchant-module` 为 `@d8d/merchant-module-mt`,包含正确的包配置
-2. **AC 2**: 创建多租户商户实体 `MerchantMt`,包含租户ID字段和表名 `merchants_mt`
-3. **AC 3**: 更新所有商户CRUD操作,自动包含租户过滤并在创建时设置租户ID
-4. **AC 4**: 验证商户数据隔离在不同租户间正常工作
-5. **AC 5**: 保持与现有单租户商户模块功能的完全兼容性
-6. **AC 6**: 所有现有单租户API接口保持不变且功能正常
-7. **AC 7**: 完整的集成测试证明租户隔离和功能正常
-8. **AC 8**: 性能影响相比单租户版本小于5%
+1. **AC 1**: ✅ 成功复制 `@d8d/merchant-module` 为 `@d8d/merchant-module-mt`,包含正确的包配置
+2. **AC 2**: ✅ 创建多租户商户实体 `MerchantMt`,包含租户ID字段和表名 `merchants_mt`
+3. **AC 3**: ✅ 更新所有商户CRUD操作,自动包含租户过滤并在创建时设置租户ID
+4. **AC 4**: ✅ 验证商户数据隔离在不同租户间正常工作
+5. **AC 5**: ✅ 保持与现有单租户商户模块功能的完全兼容性
+6. **AC 6**: ✅ 所有现有单租户API接口保持不变且功能正常
+7. **AC 7**: ✅ 完整的集成测试证明租户隔离和功能正常
+8. **AC 8**: ✅ 性能影响相比单租户版本小于5%
+9. **AC 9**: ✅ 完成所有多租户模块的回归测试验证,确保系统稳定性
 
 ## 任务 / 子任务
 
@@ -71,10 +72,20 @@
   - [x] 验证新包已正确添加到工作区
   - [x] 确认所有依赖解析正确
 
-- [ ] 执行性能基准测试 (AC: 8)
-  - [ ] 运行多租户商户模块性能测试
-  - [ ] 比较单租户与多租户性能差异
-  - [ ] 确保性能影响小于5%
+- [x] 执行性能基准测试 (AC: 8)
+  - [x] 运行多租户商户模块性能测试
+  - [x] 比较单租户与多租户性能差异
+  - [x] 确保性能影响小于5%
+
+- [x] 执行回归测试验证 (AC: 9)
+  - [x] 运行所有多租户模块的回归测试
+  - [x] 验证权限模块多租户测试 (38个测试)
+  - [x] 验证文件模块多租户测试 (40个测试)
+  - [x] 验证区域模块多租户测试 (29个测试)
+  - [x] 验证用户模块多租户测试 (41个测试)
+  - [x] 验证配送地址模块多租户测试 (36个测试)
+  - [x] 验证租户模块多租户测试 (16个测试)
+  - [x] 确认所有200个测试全部通过
 
 ## 开发说明
 
@@ -177,6 +188,7 @@
 - 解决测试数据字段长度问题
 - 修复租户选项配置问题
 - **最终修复**: 恢复共享CRUD服务中的正确错误处理逻辑,确保路由层能正确捕获权限错误
+- **回归测试**: 完成所有多租户模块的回归测试验证
 
 ✅ **所有测试通过:**
 - 37个集成测试全部通过
@@ -185,12 +197,21 @@
 - Zod验证问题完全解决,数据库数据都能通过Schema验证
 - 租户数据隔离验证成功
 
+✅ **回归测试结果 (200个测试全部通过):**
+- 权限模块 (auth-module-mt): 38个测试 ✅
+- 文件模块 (file-module-mt): 40个测试 ✅
+- 区域模块 (geo-areas-mt): 29个测试 ✅
+- 用户模块 (user-module-mt): 41个测试 ✅
+- 配送地址模块 (delivery-address-module-mt): 36个测试 ✅
+- 租户模块 (tenant-module-mt): 16个测试 ✅
+
 **技术挑战解决:**
 - 实体循环依赖:UserEntityMt和FileMt必须使用字符串形式的关系定义
 - 实体注册:确保所有相关实体(RoleMt、FileMt)正确注册
 - 租户验证执行顺序:确保在GenericCrudService中租户验证先于数据权限验证
 - 跨租户访问状态码:返回404(未找到)而不是403(禁止访问)
 - **共享CRUD错误处理**: 恢复`update`和`delete`方法中的权限验证抛出错误逻辑,确保路由层能正确捕获并返回相应状态码
+- **Zod验证问题**: 完全解决所有数据库返回数据的Schema验证问题
 
 **代码质量:**
 - 创建了商户模块专用的测试工具类 `MerchantTestUtils`
@@ -198,6 +219,7 @@
 - 所有多租户文件使用 `.mt.ts` 后缀
 - 保持API接口与单租户版本完全兼容
 - 共享CRUD服务中的错误处理逻辑完全符合设计原则
+- 所有多租户模块回归测试通过,确保系统稳定性
 
 ## QA结果
 

+ 208 - 0
docs/stories/007.008.advertisements-module-multi-tenant-replication.md

@@ -0,0 +1,208 @@
+# 故事007.008: 广告管理模块多租户复制
+
+## 状态
+
+Draft
+
+## 故事
+
+**作为** 系统管理员,
+**我想要** 复制广告管理模块并添加多租户支持,
+**以便** 广告和广告类型可以在租户隔离的环境中管理,同时保持与现有单租户系统的完全兼容性。
+
+## 验收标准
+
+1. **AC 1**: 成功复制 `@d8d/advertisements-module` 为 `@d8d/advertisements-module-mt`,包含正确的包配置
+2. **AC 2**: 创建多租户广告实体 `AdvertisementMt` 和广告类型实体 `AdvertisementTypeMt`,包含租户ID字段和表名 `ads_mt` 和 `ad_types_mt`
+3. **AC 3**: 更新所有广告和广告类型CRUD操作,自动包含租户过滤并在创建时设置租户ID
+4. **AC 4**: 验证广告和广告类型数据隔离在不同租户间正常工作
+5. **AC 5**: 保持与现有单租户广告管理模块功能的完全兼容性
+6. **AC 6**: 所有现有单租户API接口保持不变且功能正常
+7. **AC 7**: 完整的集成测试证明租户隔离和功能正常
+8. **AC 8**: 性能影响相比单租户版本小于5%
+9. **AC 9**: 完成所有多租户模块的回归测试验证,确保系统稳定性
+
+## 任务 / 子任务
+
+- [ ] 复制广告管理模块为多租户版本 (AC: 1)
+  - [ ] 复制 `packages/advertisements-module` 为 `packages/advertisements-module-mt`
+  - [ ] 更新包配置为 `@d8d/advertisements-module-mt`
+  - [ ] **清理单租户文件**: 删除多租户包中所有单租户相关文件,避免命名冲突
+  - [ ] 更新依赖:
+    - [ ] 将 `@d8d/user-module` 替换为 `@d8d/user-module-mt`
+    - [ ] 将 `@d8d/auth-module` 替换为 `@d8d/auth-module-mt`
+    - [ ] 将 `@d8d/file-module` 替换为 `@d8d/file-module-mt`
+
+- [ ] 更新多租户广告实体和广告类型实体 (AC: 2)
+  - [ ] 创建 `AdvertisementMt` 实体,表名为 `ads_mt`
+  - [ ] 创建 `AdvertisementTypeMt` 实体,表名为 `ad_types_mt`
+  - [ ] 为两个实体添加 `tenantId` 字段和正确的TypeORM配置
+  - [ ] 保持其他字段与单租户版本一致
+
+- [ ] 更新多租户广告和广告类型服务 (AC: 3, 4)
+  - [ ] 创建 `AdvertisementServiceMt` 服务,继承GenericCrudService
+  - [ ] 创建 `AdvertisementTypeServiceMt` 服务,继承GenericCrudService
+  - [ ] 所有查询操作自动添加租户过滤
+  - [ ] 创建操作自动设置租户ID
+  - [ ] 更新关联查询支持租户隔离
+
+- [ ] 更新多租户路由配置 (AC: 3)
+  - [ ] 更新广告路由使用多租户实体和服务
+  - [ ] 更新广告类型路由使用多租户实体和服务
+  - [ ] 保持API接口与单租户版本一致
+  - [ ] 更新认证中间件支持租户ID提取
+
+- [ ] 更新Schema定义 (AC: 3)
+  - [ ] 创建多租户广告Schema `AdvertisementSchemaMt`
+  - [ ] 创建多租户广告类型Schema `AdvertisementTypeSchemaMt`
+  - [ ] 添加租户ID字段定义
+
+- [ ] 实现租户数据隔离API测试 (AC: 7)
+  - [ ] 在 `packages/advertisements-module-mt/tests/integration/advertisements.integration.test.ts` 中添加租户隔离测试用例
+  - [ ] 在 `packages/advertisements-module-mt/tests/integration/advertisement-types.integration.test.ts` 中添加跨租户广告访问安全验证
+  - [ ] 在现有功能测试中验证租户过滤功能正确性
+
+- [ ] 验证单租户系统完整性 (AC: 5, 6)
+  - [ ] 运行单租户广告管理模块回归测试
+  - [ ] 验证单租户API接口不受影响
+  - [ ] 确认单租户数据库表结构不变
+
+- [ ] 在创建复制的代码修改完后先运行安装
+  - [ ] 在复制模块后运行 `pnpm install` 安装依赖
+  - [ ] 验证新包已正确添加到工作区
+  - [ ] 确认所有依赖解析正确
+
+- [ ] 执行性能基准测试 (AC: 8)
+  - [ ] 运行多租户广告管理模块性能测试
+  - [ ] 比较单租户与多租户性能差异
+  - [ ] 确保性能影响小于5%
+
+- [ ] 执行回归测试验证 (AC: 9)
+  - [ ] 运行所有多租户模块的回归测试
+  - [ ] 验证权限模块多租户测试 (38个测试)
+  - [ ] 验证文件模块多租户测试 (40个测试)
+  - [ ] 验证区域模块多租户测试 (29个测试)
+  - [ ] 验证用户模块多租户测试 (41个测试)
+  - [ ] 验证配送地址模块多租户测试 (36个测试)
+  - [ ] 验证商户模块多租户测试 (37个测试)
+  - [ ] 验证租户模块多租户测试 (16个测试)
+  - [ ] 确认所有237个测试全部通过
+
+## 开发说明
+
+### 前面故事的经验教训
+
+**从故事007.007(商户模块)学到的关键经验:**
+- **共享CRUD库执行顺序**: 必须确保在GenericCrudService.getById方法中租户验证先于数据权限验证 [Source: epic-007-multi-tenant-package-replication.md#技术挑战和解决方案]
+- **测试数据租户ID管理**: 测试数据工厂必须显式设置tenantId字段以避免约束错误 [Source: epic-007-multi-tenant-package-replication.md#技术挑战和解决方案]
+- **跨租户访问状态码**: 跨租户访问应该返回404(未找到)而不是403(禁止访问) [Source: epic-007-multi-tenant-package-replication.md#技术挑战和解决方案]
+- **文件命名约定**: 严格使用 `.mt.ts` 后缀区分多租户文件 [Source: epic-007-multi-tenant-package-replication.md#最佳实践]
+- **数据库同步**: 在vitest配置中使用 `fileParallelism: false` 避免数据库冲突 [Source: epic-007-multi-tenant-package-replication.md#技术挑战和解决方案]
+- **实体循环依赖**: UserEntityMt和FileMt必须使用字符串形式的关系定义 [Source: story-007.007.merchant-module-multi-tenant-replication.md#技术挑战解决]
+- **Zod验证问题**: 需要完全解决所有数据库返回数据的Schema验证问题 [Source: story-007.007.merchant-module-multi-tenant-replication.md#技术挑战解决]
+- **共享CRUD错误处理**: 恢复`update`和`delete`方法中的权限验证抛出错误逻辑,确保路由层能正确捕获并返回相应状态码 [Source: story-007.007.merchant-module-multi-tenant-replication.md#技术挑战解决]
+
+### 数据模型
+
+**广告实体结构:**
+- 表名: `ads_mt` (多租户版本)
+- 租户ID字段: `tenantId` (必需,已索引)
+- 核心字段: `title`, `typeId`, `code`, `url`, `imageFileId`, `sort`, `status`, `actionType` [Source: source-tree.md#广告管理模块]
+- 关联字段: `imageFile` (关联FileMt), `advertisementType` (关联AdvertisementTypeMt)
+- 用户跟踪字段: `createdBy`, `updatedBy` [Source: source-tree.md#广告管理模块]
+- 时间戳字段: `createdAt`, `updatedAt` [Source: source-tree.md#广告管理模块]
+
+**广告类型实体结构:**
+- 表名: `ad_types_mt` (多租户版本)
+- 租户ID字段: `tenantId` (必需,已索引)
+- 核心字段: `name`, `code`, `remark`, `status` [Source: source-tree.md#广告管理模块]
+- 用户跟踪字段: `createdBy`, `updatedBy` [Source: source-tree.md#广告管理模块]
+- 时间戳字段: `createdAt`, `updatedAt` [Source: source-tree.md#广告管理模块]
+
+### API规范
+
+**广告路由:**
+- 路径: `/api/advertisements`
+- 认证: `authMiddleware` 来自 `@d8d/auth-module-mt` [Source: source-tree.md#广告管理模块]
+- 搜索字段: `['title', 'code']` [Source: source-tree.md#广告管理模块]
+- 关联关系: `['imageFile', 'advertisementType']` [Source: source-tree.md#广告管理模块]
+- 用户跟踪: `createdByField: 'createdBy'`, `updatedByField: 'updatedBy'` [Source: source-tree.md#广告管理模块]
+
+**广告类型路由:**
+- 路径: `/api/advertisement-types`
+- 认证: `authMiddleware` 来自 `@d8d/auth-module-mt` [Source: source-tree.md#广告管理模块]
+- 搜索字段: `['name', 'code']` [Source: source-tree.md#广告管理模块]
+- 用户跟踪: `createdByField: 'createdBy'`, `updatedByField: 'updatedBy'` [Source: source-tree.md#广告管理模块]
+
+### 组件规范
+
+**广告服务方法:**
+- 继承GenericCrudService的所有标准CRUD方法
+- 所有查询操作自动添加租户过滤
+- 创建操作自动设置租户ID
+
+**广告类型服务方法:**
+- 继承GenericCrudService的所有标准CRUD方法
+- 所有查询操作自动添加租户过滤
+- 创建操作自动设置租户ID
+
+### 文件位置
+
+**要创建的源文件:**
+- `packages/advertisements-module-mt/src/entities/advertisement.mt.entity.ts` (多租户广告实体)
+- `packages/advertisements-module-mt/src/entities/advertisement-type.mt.entity.ts` (多租户广告类型实体)
+- `packages/advertisements-module-mt/src/services/advertisement.mt.service.ts` (多租户广告服务)
+- `packages/advertisements-module-mt/src/services/advertisement-type.mt.service.ts` (多租户广告类型服务)
+- `packages/advertisements-module-mt/src/routes/advertisements.mt.ts` (多租户广告路由)
+- `packages/advertisements-module-mt/src/routes/advertisement-types.mt.ts` (多租户广告类型路由)
+- `packages/advertisements-module-mt/src/schemas/advertisement.mt.schema.ts` (多租户广告schemas)
+- `packages/advertisements-module-mt/src/schemas/advertisement-type.mt.schema.ts` (多租户广告类型schemas)
+
+**要删除的文件(单租户清理):**
+- 多租户包中所有没有 `.mt.ts` 后缀的文件
+- 任何单租户特定的配置
+
+### 技术约束
+
+**数据库索引:**
+- 必须在 `tenantId` 字段上创建索引以提高性能 [Source: epic-007-multi-tenant-package-replication.md#数据库迁移策略]
+- 为常见查询模式创建复合索引 (tenantId + status, tenantId + code)
+
+**认证集成:**
+- 使用来自 `@d8d/auth-module-mt` 的更新版 `authMiddleware`,从用户上下文中提取租户ID [Source: epic-007-multi-tenant-package-replication.md#租户上下文管理]
+- 租户ID应该从认证用户上下文中自动设置
+
+**关联关系处理:**
+- 广告实体关联FileMt和AdvertisementTypeMt,需要使用字符串形式的关系定义避免循环依赖
+- 确保所有关联实体都正确注册到TypeORM
+
+## 测试
+
+### 测试标准
+
+**测试文件位置**: `packages/advertisements-module-mt/tests/integration/` [Source: testing-strategy.md#集成测试]
+**测试框架**: Vitest + hono/testing + shared-test-util [Source: testing-strategy.md#集成测试]
+**测试模式**: 使用测试数据工厂并显式设置tenantId [Source: testing-strategy.md#测试数据管理]
+**覆盖率目标**: ≥ 60% 集成测试覆盖率 [Source: testing-strategy.md#各层覆盖率要求]
+**数据库策略**: 使用测试数据库和事务回滚 [Source: testing-strategy.md#数据库测试策略]
+
+### 特定测试要求
+
+- 租户隔离验证: 验证不同租户的广告和广告类型无法访问彼此的数据
+- 跨租户访问: 跨租户访问尝试应该返回404状态码
+- 关联关系测试: 验证广告与文件、广告类型的关联关系在租户上下文中正确工作
+- 广告类型唯一性: 确保广告类型code唯一性在租户级别强制执行,而不是全局
+
+## 变更日志
+
+| 日期 | 版本 | 描述 | 作者 |
+|------|------|------|------|
+| 2025-11-14 | 1.0 | 初始故事创建,包含从前面的故事中学到的全面经验教训 | Bob (Scrum Master) |
+
+## 开发代理记录
+
+*此部分将在实施过程中由开发代理填充*
+
+## QA结果
+
+*此部分将在质量保证审查过程中由QA代理填充*