|
@@ -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代理待填写_
|