Jelajahi Sumber

docs: 完成故事010.005并创建故事010.006 - Web集成和Server模块替换

- 更新故事010.005状态为Done(测试覆盖率达到87.33%)
- 创建故事010.006:Web集成和Server模块替换
  - 租户后台集成统一广告管理UI
  - Admin后台移除广告管理功能
  - Server包替换模块引用(advertisements-module-mt → unified-advertisements-module)
  - 保持API路径100%兼容(/api/v1/advertisements)
  - E2E测试验证小程序端API兼容性
  - 集成测试验证管理员权限控制

🤖 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 2 minggu lalu
induk
melakukan
6836847ba3
2 mengubah file dengan 326 tambahan dan 1 penghapusan
  1. 1 1
      docs/stories/010.005.story.md
  2. 325 0
      docs/stories/010.006.story.md

+ 1 - 1
docs/stories/010.005.story.md

@@ -1,7 +1,7 @@
 # Story 010.005: 补充统一广告管理UI包测试覆盖度
 
 ## Status
-Ready for Review
+Done
 
 ## Story
 

+ 325 - 0
docs/stories/010.006.story.md

@@ -0,0 +1,325 @@
+# Story 010.006: Web集成和Server模块替换
+
+## Status
+Approved
+
+## Story
+
+**As a** 超级管理员,
+**I want** 将统一广告管理集成到租户后台,并从各租户admin后台移除广告管理功能,Server包切换到统一广告模块,
+**so that** 可以在租户后台统一管理所有租户的广告,所有租户用户端展示相同的广告数据,且小程序端无需任何修改。
+
+## Acceptance Criteria
+
+1. 租户后台(超级管理员专用)添加广告管理菜单项和路由
+2. 租户后台API客户端初始化,连接到统一广告模块的管理员API
+3. Admin后台移除广告管理和广告类型管理菜单项和路由
+4. **Server包替换模块**: 将 `@d8d/advertisements-module-mt` 替换为 `@d8d/unified-advertisements-module`
+5. **保持API兼容性**: `/api/v1/advertisements` 和 `/api/v1/advertisement-types` 路由保持不变
+6. **数据源切换**: 注册 `UnifiedAdvertisement` 和 `UnifiedAdvertisementType` 实体
+7. **E2E测试验证**: 验证租户后台可管理广告,验证小程序端API兼容性100%
+8. 权限控制正确(只有超级管理员ID=1可管理统一广告)
+
+## Tasks / Subtasks
+
+- [ ] **任务1: 租户后台集成统一广告管理UI** (AC: 1, 2, 8)
+  - [ ] 在 `web/src/client/tenant/routes.tsx` 添加广告管理路由
+  - [ ] 在 `web/src/client/tenant/layouts/MainLayout.tsx` 添加菜单项(广告管理、广告类型管理)
+  - [ ] 确认 `web/src/client/tenant/api_init.ts` 中API客户端正确配置
+  - [ ] 测试租户后台广告管理功能可访问
+
+- [ ] **任务2: Admin后台移除广告管理功能** (AC: 3)
+  - [ ] 从 `web/src/client/admin/routes.tsx` 移除广告管理路由
+  - [ ] 从 `web/src/client/admin/layouts/MainLayout.tsx` 移除菜单项
+  - [ ] 移除 `@d8d/advertisement-management-ui-mt` 和 `@d8d/advertisement-type-management-ui-mt` 的导入
+  - [ ] 验证Admin后台不再显示广告管理入口
+
+- [ ] **任务3: Server包替换模块引用** (AC: 4, 5, 6)
+  - [ ] 在 `packages/server/src/index.ts` 中:
+    - [ ] 替换导入:`@d8d/advertisements-module-mt` → `@d8d/unified-advertisements-module`
+    - [ ] 替换实体:`Advertisement, AdvertisementType` → `UnifiedAdvertisement, UnifiedAdvertisementType`
+    - [ ] 替换路由:`advertisementRoutes, advertisementTypeRoutes` → `unifiedAdvertisementRoutes, unifiedAdvertisementTypeRoutes`
+    - [ ] 添加管理员路由导入:`unifiedAdvertisementAdminRoutes, unifiedAdvertisementTypeAdminRoutes`
+    - [ ] 在 `initializeDataSource` 中注册新实体
+  - [ ] 在 `packages/server/src/data-source.ts` 中:
+    - [ ] 替换实体导入和注册
+  - [ ] 验证类型检查通过:`cd packages/server && pnpm typecheck`
+
+- [ ] **任务4: Server包注册统一广告管理员路由** (AC: 1, 5, 8)
+  - [ ] 在 `packages/server/src/index.ts` 添加管理员路由注册:
+    - [ ] `/api/v1/admin/unified-advertisements` → `unifiedAdvertisementAdminRoutes`
+    - [ ] `/api/v1/admin/unified-advertisement-types` → `unifiedAdvertisementTypeAdminRoutes`
+  - [ ] 验证管理员路由使用 `tenantAuthMiddleware`(仅超级管理员可访问)
+
+- [ ] **任务5: E2E测试验证API兼容性** (AC: 5, 7)
+  - [ ] 创建 `web/tests/e2e/unified-advertisement-api.spec.ts` 测试文件
+  - [ ] 测试 `/api/v1/advertisements` 端点返回统一广告数据
+  - [ ] 测试 `/api/v1/advertisement-types` 端点返回统一广告类型数据
+  - [ ] 验证响应结构与原模块完全一致(字段名、类型、格式)
+  - [ ] 运行E2E测试:`cd web && pnpm test:e2e:chromium`
+
+- [ ] **任务6: 集成测试验证管理员权限** (AC: 8)
+  - [ ] 创建 `packages/server/tests/integration/unified-advertisement-auth.integration.test.ts`
+  - [ ] 测试普通租户用户无法访问管理员API(应返回403)
+  - [ ] 测试超级管理员(ID=1)可以访问管理员API
+  - [ ] 测试认证用户可以访问用户端API(获取统一广告数据)
+  - [ ] 运行集成测试:`cd packages/server && pnpm test`
+
+- [ ] **任务7: 更新史诗010文档** (AC: 完成)
+  - [ ] 在 `docs/prd/epic-010-unified-ad-management.md` 中标记故事010.006为完成
+  - [ ] 记录模块切换的详细信息和验证结果
+  - [ ] 更新兼容性验证结果(小程序端API 100%兼容)
+
+## Dev Notes
+
+### 前一故事关键要点(来自 010.005)
+
+**测试成果**:
+- 创建了 51 个通过的集成测试,覆盖率87.33% statements
+- 统一广告管理UI包已完成完整的功能测试
+- 所有测试使用 `data-testid` 进行可靠的元素选择
+
+**技术要点**:
+- **RPC客户端**: UI包使用 `@hono/zod-openapi` 的类型推断,通过 `hc.rpc()` 获取类型安全的客户端
+- **API路径**: 统一广告模块的管理员API端点为 `/api/v1/admin/unified-advertisements`
+- **权限中间件**: 管理员路由使用 `tenantAuthMiddleware`,只有超级管理员(ID=1)可访问
+
+### 项目结构映射
+
+**Server包集成**:
+```
+packages/server/src/
+├── index.ts                      # 主入口 - 需要修改
+│   ├── 导入模块: @d8d/advertisements-module-mt → @d8d/unified-advertisements-module
+│   ├── 实体注册: Advertisement → UnifiedAdvertisement
+│   └── 路由注册: advertisementRoutes → unifiedAdvertisementRoutes
+└── data-source.ts                # 数据源配置 - 需要修改
+    └── 实体列表: Advertisement → UnifiedAdvertisement
+```
+
+**租户后台集成**:
+```
+web/src/client/tenant/
+├── routes.tsx                    # 路由配置 - 需要添加广告管理路由
+└── layouts/MainLayout.tsx        # 布局组件 - 需要添加菜单项
+```
+
+**Admin后台移除**:
+```
+web/src/client/admin/
+├── routes.tsx                    # 路由配置 - 需要移除广告管理路由
+└── layouts/MainLayout.tsx        # 布局组件 - 需要移除菜单项
+```
+
+### API端点映射(保持兼容)
+
+**用户端API(小程序使用,保持不变)**:
+```typescript
+// 当前: advertisements-module-mt
+// 切换后: unified-advertisements-module
+// 路由路径和响应结构100%兼容
+
+GET    /api/v1/advertisements          // 获取广告列表
+GET    /api/v1/advertisements/:id      // 获取广告详情
+GET    /api/v1/advertisement-types     // 获取广告类型列表
+```
+
+**管理员API(新增,租户后台使用)**:
+```typescript
+// 使用 tenantAuthMiddleware,仅超级管理员ID=1可访问
+
+GET    /api/v1/admin/unified-advertisements           // 广告列表
+POST   /api/v1/admin/unified-advertisements           // 创建广告
+PUT    /api/v1/admin/unified-advertisements/:id       // 更新广告
+DELETE /api/v1/admin/unified-advertisements/:id       // 删除广告
+
+GET    /api/v1/admin/unified-advertisement-types          // 广告类型列表
+POST   /api/v1/admin/unified-advertisement-types          // 创建广告类型
+PUT    /api/v1/admin/unified-advertisement-types/:id      // 更新广告类型
+DELETE /api/v1/admin/unified-advertisement-types/:id      // 删除广告类型
+```
+
+### 模块导入路径
+
+**统一广告模块导出** [Source: packages/unified-advertisements-module/src/index.ts]:
+```typescript
+// 实体
+export { UnifiedAdvertisement } from './entities/unified-advertisement.entity';
+export { UnifiedAdvertisementType } from './entities/unified-advertisement-type.entity';
+
+// 管理员路由(新增,租户后台使用)
+export { default as unifiedAdvertisementAdminRoutes } from './routes/admin/unified-advertisements.admin.routes';
+export { default as unifiedAdvertisementTypeAdminRoutes } from './routes/admin/unified-advertisement-types.admin.routes';
+
+// 用户路由(与原模块API兼容)
+export { default as unifiedAdvertisementRoutes } from './routes/unified-advertisements.routes';
+export { default as unifiedAdvertisementTypeRoutes } from './routes/unified-advertisement-types.routes';
+```
+
+**Server包修改示例**:
+```typescript
+// packages/server/src/index.ts
+
+// ===== 旧代码(删除)=====
+import { Advertisement, AdvertisementType } from '@d8d/advertisements-module-mt';
+import { advertisementRoutes, advertisementTypeRoutes } from '@d8d/advertisements-module-mt';
+
+// ===== 新代码(使用)=====
+import {
+  UnifiedAdvertisement,
+  UnifiedAdvertisementType
+} from '@d8d/unified-advertisements-module';
+import {
+  unifiedAdvertisementRoutes,
+  unifiedAdvertisementTypeRoutes,
+  unifiedAdvertisementAdminRoutes,      // 新增:管理员路由
+  unifiedAdvertisementTypeAdminRoutes    // 新增:管理员类型路由
+} from '@d8d/unified-advertisements-module';
+
+// ===== 数据源注册 =====
+initializeDataSource([
+  // ...其他实体
+  // 旧: Advertisement, AdvertisementType,
+  新: UnifiedAdvertisement, UnifiedAdvertisementType,
+]);
+
+// ===== 路由注册 - 用户端(保持路径不变)=====
+export const advertisementApiRoutes = api.route('/api/v1/advertisements', unifiedAdvertisementRoutes);
+export const advertisementTypeApiRoutes = api.route('/api/v1/advertisement-types', unifiedAdvertisementTypeRoutes);
+
+// ===== 路由注册 - 管理员端(新增)=====
+export const adminUnifiedAdApiRoutes = api.route('/api/v1/admin/unified-advertisements', unifiedAdvertisementAdminRoutes);
+export const adminUnifiedAdTypeApiRoutes = api.route('/api/v1/admin/unified-advertisement-types', unifiedAdvertisementTypeAdminRoutes);
+```
+
+### 租户后台集成示例
+
+**路由配置** [Source: web/src/client/tenant/routes.tsx]:
+```typescript
+// 添加广告管理路由
+import { UnifiedAdvertisementManagement } from '@d8d/unified-advertisement-management-ui';
+
+export const router = createBrowserRouter([
+  // ...
+  {
+    path: '/tenant',
+    element: <ProtectedRoute><MainLayout /></ProtectedRoute>,
+    children: [
+      // ...
+      {
+        path: 'unified-advertisements',
+        element: <UnifiedAdvertisementManagement />,
+        errorElement: <ErrorPage />
+      },
+      // ...
+    ],
+  },
+]);
+```
+
+### Admin后台移除示例
+
+**路由配置** [Source: web/src/client/admin/routes.tsx]:
+```typescript
+// 移除以下导入
+// import { AdvertisementManagement } from '@d8d/advertisement-management-ui-mt';
+// import { AdvertisementTypeManagement } from '@d8d/advertisement-type-management-ui-mt';
+
+// 移除以下路由配置
+// {
+//   path: 'advertisements',
+//   element: <AdvertisementManagement />,
+// },
+// {
+//   path: 'advertisement-types',
+//   element: <AdvertisementTypeManagement />,
+// },
+```
+
+### 权限控制验证
+
+**tenantAuthMiddleware** [Source: docs/prd/epic-010-unified-ad-management.md]:
+- 只有超级管理员(tenantId=1, userId=1)可以访问管理员API
+- 管理员路由必须在server包中正确注册中间件
+- 用户端路由使用 `authMiddleware` 进行多租户认证,但返回统一的广告数据(无tenant_id过滤)
+
+### 测试策略
+
+**E2E测试重点** [Source: docs/architecture/testing-strategy.md]:
+- 验证API端点路径保持不变
+- 验证响应结构100%兼容
+- 验证小程序端无需任何修改
+
+**集成测试重点** [Source: docs/architecture/web-server-testing-standards.md]:
+- 验证管理员权限控制
+- 验证数据源切换正确
+- 验证新实体可以正常操作
+
+### 关键注意事项
+
+1. **API兼容性优先**: 小程序端使用的API端点必须保持100%兼容,路由路径、请求参数、响应结构都不能变化
+2. **权限控制严格**: 管理员API必须使用 `tenantAuthMiddleware`,确保只有超级管理员可访问
+3. **数据源切换**: 确保TypeORM实体正确注册,避免运行时错误
+4. **回滚准备**: 保留 `@d8d/advertisements-module-mt` 包不动,便于回滚
+5. **测试验证**: E2E测试必须验证小程序端API兼容性
+
+### 参考
+
+- [后端模块包开发规范](../architecture/backend-module-package-standards.md)
+- [UI包开发规范](../architecture/ui-package-standards.md)
+- [Web Server测试规范](../architecture/web-server-testing-standards.md)
+- [测试策略概述](../architecture/testing-strategy.md)
+- [API设计和集成](../architecture/api-design-integration.md)
+- [源码树和文件组织](../architecture/source-tree.md)
+
+## Testing
+
+### 测试文件位置
+- E2E测试: `web/tests/e2e/unified-advertisement-api.spec.ts`
+- 集成测试: `packages/server/tests/integration/unified-advertisement-auth.integration.test.ts`
+
+### 测试框架
+- **E2E**: Playwright (chromium)
+- **集成测试**: Vitest + hono/testing
+
+### 测试标准
+
+| 测试类型 | 覆盖率要求 | 重点验证 |
+|----------|------------|----------|
+| E2E测试 | 关键流程100% | API兼容性 |
+| 集成测试 | ≥60% | 权限控制、数据源 |
+
+### 测试执行命令
+```bash
+# E2E测试(验证API兼容性)
+cd web && pnpm test:e2e:chromium
+
+# 集成测试(验证权限控制)
+cd packages/server && pnpm test
+
+# 类型检查
+cd packages/server && pnpm typecheck
+```
+
+## Change Log
+
+| Date | Version | Description | Author |
+|------|---------|-------------|--------|
+| 2026-01-03 | 1.0 | 初始故事创建 | James (Claude Code) |
+
+## Dev Agent Record
+
+### Agent Model Used
+_待开发者填写_
+
+### Debug Log References
+_待开发者填写_
+
+### Completion Notes List
+_待开发者填写_
+
+### File List
+_待开发者填写_
+
+## QA Results
+_QA代理待填写_