Files
WonderQ-Project/docs/superpowers/plans/2026-08-26-admin-vue-formalization.md
T
duanshuwen efab6d1398 feat(admin): 新增全套运营管理功能,修复核心路由bug及后端seed问题
修复Vue Admin布局中RouterView未绑定唯一key的问题,解决RBAC系统资源页面切换后内容不刷新的ISSUE-001故障,新增routeViewKey工具函数区分路由实例
完善路由配置,新增admin空路径路由,修复路由命名空路径告警
新增媒体管理、运维发布/重置页面,补全全站路由注册与动态路由过滤逻辑
新增玩法、线索、管家、系统资源管理的全套工具函数、业务组件及单元测试用例
修复后端app/seed.py中媒体资源重复检测未考虑会话内pending资源的bug,新增对应测试验证修复逻辑
修正alembic迁移版本0015的依赖关系,删除废弃的旧迁移文件
优化后端系统接口的字典序列化逻辑,简化重复代码
添加QA验收报告与相关测试截图,完善项目测试覆盖
2026-08-26 18:48:48 +08:00

27 KiB
Raw Blame History

WonderQ Admin Vue 正式化实施计划

For agentic workers: REQUIRED SUB-SKILL: Use subagent-driven-development or executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在不删除 React 过渡端的前提下,将四组首页 CRUD 接入 Vue,补齐管理员踢下线、显式 403、五级数据范围和 /admin、/admin-legacy 正式代理,并完成可重复的三端验收。

Architecture: 保持 FastAPI + PostgreSQL + Redis 的现有模块化单体。后端继续以既有 Admin API 和 {code,msg,data} 契约为边界;Vue 通过独立的 API client、页面组件和静态路由注册实现新管理端;React 继续由独立 legacy 静态入口提供回滚能力。Redis 只保存会话记录、用户会话索引和权限缓存,不新增会话数据库表。

Tech Stack: Python 3、FastAPI、SQLAlchemy 2、Alembic、PostgreSQL、Redis、JWT、Vue 3、Element Plus、TypeScript、Pinia、Vue Router 4、React、Vite、Nginx、Docker Compose、pytest、Vitest。


文件边界与责任

后端

  • 修改 WonderQ-Admin/app/redis_session.py:扩展会话存储协议,实现按用户索引、列出和批量撤销会话;不改变 Token 明文存储策略。
  • 修改 WonderQ-Admin/app/auth.py:在登录创建和刷新轮换时维护用户会话索引;保留旧登录响应字段。
  • 修改 WonderQ-Admin/app/routers/system.py:新增踢下线接口,执行权限、数据范围、当前会话保护和审计。
  • 修改 WonderQ-Admin/app/routers/admin.py:为四组首页资源接入现有权限码和数据范围检查,保持既有路径与返回字段。
  • 修改 WonderQ-Admin/app/rbac.py:固定数据范围和权限缓存故障的安全行为,确保资源列表与单条写入使用同一范围规则。
  • 修改 WonderQ-Admin/tests/test_home.py、tests/test_admin_rbac.py:补充首页权限、范围和契约回归。
  • 新建 WonderQ-Admin/tests/test_admin_sessions.py:覆盖 Redis 会话索引、刷新轮换、批量撤销和故障行为。
  • 新建 WonderQ-Admin/tests/test_data_scope_matrix.py:覆盖五种数据范围的跨部门矩阵。

Vue 管理端

  • 修改 WonderQ-Admin-UI-Vue/src/types.ts:增加四组首页资源类型、403 错误类型、会话撤销结果类型。
  • 修改 WonderQ-Admin-UI-Vue/src/api/client.ts:增加首页 CRUD、踢下线请求和显式 403 错误;不改变 API base、Cookie 或 Access Token 键。
  • 新建 WonderQ-Admin-UI-Vue/src/api/client-home.test.ts:验证四组首页 API 方法、HTTP 方法和路径。
  • 修改 WonderQ-Admin-UI-Vue/src/api/client-system.test.ts:验证踢下线 API。
  • 新建 WonderQ-Admin-UI-Vue/src/components/home/HomeCrudEditor.vue:共享首页卡片表单、校验、图片 URL/状态字段和提交态。
  • 新建 WonderQ-Admin-UI-Vue/src/components/home/HomeCrudTable.vue:共享列表、空态、错误态、重试、排序和权限按钮。
  • 新建 WonderQ-Admin-UI-Vue/src/lib/home-content.ts:定义四组资源的字段元数据、默认值和 payload 归一化。
  • 新建 WonderQ-Admin-UI-Vue/src/pages/HomeContentPage.vue:体验、团建、野奢三组 CRUD 页面。
  • 新建 WonderQ-Admin-UI-Vue/src/pages/HomeWanfaRecommendationPage.vue:玩法推荐关联 CRUD 页面。
  • 新建 WonderQ-Admin-UI-Vue/src/pages/ForbiddenPage.vue:登录态下的 403 页面。
  • 修改 WonderQ-Admin-UI-Vue/src/pages/HomePage.vue、src/router/index.ts:把首页站点配置与四组业务入口拆开,并注册 403 和首页业务路由。
  • 修改 WonderQ-Admin-UI-Vue/src/pages/SystemResourcePage.vue:在用户列表增加踢下线操作。
  • 新建 WonderQ-Admin-UI-Vue/src/router/router.test.ts:验证 401、403、无权限页面和未知路径的分流。

部署与契约

  • 修改 docs/home-api.md:补充 Vue 对接字段、统一错误和四组后台验收矩阵。
  • 修改 docs/admin-api-requirements.md:补充踢下线、403、数据范围和 Redis 故障契约。
  • 修改 docs/integration-workflow.md:补充双管理端、正式代理和回滚命令。
  • 修改 docs/admin-business-function-matrix.md:记录每个切片的实际测试和人工验收结果。
  • 新建 WonderQ-Admin-UI-Vue/Dockerfile、WonderQ-Admin-UI-Vue/nginx.conf:构建并提供 /admin/ 静态资源。
  • 修改 WonderQ-Admin-UI/vite.config.ts、WonderQ-Admin-UI/nginx.conf、WonderQ-Admin-UI/Dockerfile:支持 /admin-legacy/ base 和 legacy 静态入口。
  • 新建 deploy/nginx/wonderq-admin.conf:统一代理 /admin/、/admin-legacy/ 和 /api/;不写入任何真实环境变量。
  • 新建 scripts/verify-admin-proxy.ps1:检查两个构建产物和统一 Nginx 配置的入口、深链与 API 隔离。
  • 修改 docker-compose.yml(仅在现有 Compose 确认包含前端服务时):加入两个前端构建入口和健康检查;若现有 Compose 不负责前端,则只增加部署文档,不扩大 Compose 责任。

Task 1: 建立契约基线与失败测试入口

Files:

  • Modify: docs/home-api.md

  • Modify: docs/admin-api-requirements.md

  • Modify: docs/integration-workflow.md

  • Test: WonderQ-Admin/tests/test_home.py

  • Test: WonderQ-Admin-UI-Vue/src/api/client-home.test.ts

  • Step 1: 记录四组首页的现有字段与接口响应

从 WonderQ-Admin/app/schemas.py、WonderQ-Admin/app/routers/admin.py、WonderQ-Admin-UI/src/api.ts 和 docs/home-api.md 对照四组资源。契约表必须明确 GET/POST/PATCH/DELETE/PATCH reorder、成功状态码、空数组、排序完整 ID 列表和 403 响应,不改现有公共字段名。

  • Step 2: 先写 Vue API 失败测试

在 client-home.test.ts 中为四组资源各调用一次列表、创建、更新、删除、排序方法,断言 fetch 收到以下请求:

expect(fetchMock.mock.calls.map(([url, options]) => [url, options?.method])).toEqual([
  ["/api/admin/home/experiences", undefined],
  ["/api/admin/home/experiences", "POST"],
  ["/api/admin/home/experiences/experience-1", "PATCH"],
  ["/api/admin/home/experiences/experience-1", "DELETE"],
  ["/api/admin/home/experiences/reorder", "PATCH"],
  ["/api/admin/home/team-buildings", undefined],
  ["/api/admin/home/team-buildings", "POST"],
  ["/api/admin/home/team-buildings/team-1", "PATCH"],
  ["/api/admin/home/team-buildings/team-1", "DELETE"],
  ["/api/admin/home/team-buildings/reorder", "PATCH"],
  ["/api/admin/home/wild-archives", undefined],
  ["/api/admin/home/wild-archives", "POST"],
  ["/api/admin/home/wild-archives/archive-1", "PATCH"],
  ["/api/admin/home/wild-archives/archive-1", "DELETE"],
  ["/api/admin/home/wild-archives/reorder", "PATCH"],
  ["/api/admin/home/play-recommendations", undefined],
  ["/api/admin/home/play-recommendations", "POST"],
  ["/api/admin/home/play-recommendations/recommendation-1", "PATCH"],
  ["/api/admin/home/play-recommendations/recommendation-1", "DELETE"],
  ["/api/admin/home/play-recommendations/reorder", "PATCH"],
]);
  • Step 3: 运行失败测试,确认缺口真实存在

Run: Set-Location WonderQ-Admin-UI-Vue; yarn test --run src/api/client-home.test.ts

Expected: FAIL because the four Vue client methods and corresponding types do not exist yet.

  • Step 4: 同步契约文档并检查差异

Run: git diff --check -- docs/home-api.md docs/admin-api-requirements.md docs/integration-workflow.md

Expected: no whitespace errors;文档中没有真实 Token、密码、OSS 凭证或生产地址。

Task 2: 接入四组首页 API 与后端权限/范围

Files:

  • Modify: WonderQ-Admin-UI-Vue/src/types.ts

  • Modify: WonderQ-Admin-UI-Vue/src/api/client.ts

  • Modify: WonderQ-Admin/app/routers/admin.py

  • Modify: WonderQ-Admin/tests/test_home.py

  • Modify: WonderQ-Admin-UI-Vue/src/api/client-home.test.ts

  • Step 1: 写后端首页鉴权和范围失败测试

为四组列表和至少一个更新接口增加测试:普通管理员没有 admin:site-config 时返回 403;有该权限时只能读取或更新 is_within_data_scope 为真的记录;完整排序请求缺 ID 或重复 ID 时返回 400。测试继续使用现有依赖覆盖方式,不创建真实生产数据。

  • Step 2: 运行后端失败测试

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_home.py -q

Expected: new permission/scope assertions fail while existing home CRUD compatibility tests remain visible in the output。

  • Step 3: 增加 TypeScript 资源类型和 API 方法

在 src/types.ts 中以 React 已有类型和后端 schema 为准定义 HomeExperience、HomeTeamBuilding、HomeWildArchive、HomeWanfaRecommendation 及对应 Create/Patch 类型;在 src/api/client.ts 中提供以下函数:

getHomeExperiences(); createHomeExperience(input); updateHomeExperience(id, input);
deleteHomeExperience(id); reorderHomeExperiences(itemIds);
getHomeTeamBuildings(); createHomeTeamBuilding(input); updateHomeTeamBuilding(id, input);
deleteHomeTeamBuilding(id); reorderHomeTeamBuildings(itemIds);
getHomeWildArchives(); createHomeWildArchive(input); updateHomeWildArchive(id, input);
deleteHomeWildArchive(id); reorderHomeWildArchives(itemIds);
getHomePlayRecommendations(); createHomePlayRecommendation(input);
updateHomePlayRecommendation(id, input); deleteHomePlayRecommendation(id);
reorderHomePlayRecommendations(itemIds);

每个方法只调用既有 /api/admin/home/... 路径,并由 request<T> 统一解包响应。

  • Step 4: 将后端四组路由切换到已有权限依赖并复用数据范围

把当前四组首页路由的 Depends(require_admin) 替换为 Depends(require_admin_permission("admin:site-config")),列表通过 apply_data_scope,单条更新、删除和排序前通过 is_within_data_scope;保留超级管理员兼容行为、既有审计事件和返回结构。写入记录继续使用现有创建人/部门回填逻辑。

  • Step 5: 运行后端与 Vue API 测试

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_home.py -q

Run: Set-Location ..\WonderQ-Admin-UI-Vue; yarn test --run src/api/client-home.test.ts

Expected: new assertions PASS;如全量后端仍出现已知 Alembic 兼容失败,记录其具体测试名,不将其归因于首页切片。

Task 3: 迁移首页三组内容 CRUD 页面

Files:

  • Create: WonderQ-Admin-UI-Vue/src/lib/home-content.ts

  • Create: WonderQ-Admin-UI-Vue/src/components/home/HomeCrudEditor.vue

  • Create: WonderQ-Admin-UI-Vue/src/components/home/HomeCrudTable.vue

  • Create: WonderQ-Admin-UI-Vue/src/pages/HomeContentPage.vue

  • Modify: WonderQ-Admin-UI-Vue/src/router/index.ts

  • Modify: WonderQ-Admin-UI-Vue/src/pages/HomePage.vue

  • Test: WonderQ-Admin-UI-Vue/src/lib/home-content.test.ts

  • Step 1: 写字段元数据和表单归一化失败测试

测试必须分别检查三组资源的必填字段、空字符串裁剪、数字字段转换、sortOrder 默认值、isActive 默认值和错误文案。测试输入只使用测试文本和本地图片 URL。

  • Step 2: 运行失败测试

Run: Set-Location WonderQ-Admin-UI-Vue; yarn test --run src/lib/home-content.test.ts

Expected: FAIL because the metadata module does not exist。

  • Step 3: 实现元数据模块

home-content.ts 导出资源联合类型、资源定义、createEmptyHomeDraft、toHomePayload 和 validateHomePayload。所有字段名必须与 docs/home-api.md 和后端 Pydantic schema 一致;不把 base64 文件内容写入 CRUD payload,只保存媒体接口返回的 URL。

  • Step 4: 实现共享表格与编辑器

HomeCrudTable.vue 接收 items/loading/error/permission,提供空态、错误重试、编辑、删除、上下移和提交中禁用;HomeCrudEditor.vue 接收资源定义和 draft,提供 Element Plus 表单校验、取消、提交中状态和 save 事件。按钮使用 v-permission="'admin:site-config'",后端 403 仍由页面捕获显示错误。

  • Step 5: 实现三组 Vue 页面

HomeContentPage.vue 根据路由参数选择 experiences、team-buildings 或 wild-archives,加载、创建、更新、删除和排序均调用 Task 2 的 API 方法。列表、编辑器和请求状态按资源隔离,避免修改一组数据时污染另外两组。页面提供加载骨架、空态、错误重试和窄屏表格滚动。

  • Step 6: 将首页入口与新页面注册到静态路由

保留 /admin/home 作为站点配置页,在该页增加三个明确入口;在 router/index.ts 增加 /home/content/:resource 静态路由并绑定 HomeContentPage,权限使用 admin:site-config。组件仍必须来自本地预注册表,不接受后端任意组件路径。

  • Step 7: 运行 Vue 单元、类型和构建验证

Run: Set-Location WonderQ-Admin-UI-Vue; yarn test --run src/lib/home-content.test.ts src/api/client-home.test.ts

Run: yarn build

Expected: tests and TypeScript build PASS;Vite 已有 chunk 大小或 __dirname warning 可记录,但不得出现编译错误。

Task 4: 迁移玩法推荐关联 CRUD

Files:

  • Create: WonderQ-Admin-UI-Vue/src/pages/HomeWanfaRecommendationPage.vue

  • Modify: WonderQ-Admin-UI-Vue/src/router/index.ts

  • Modify: WonderQ-Admin-UI-Vue/src/pages/HomePage.vue

  • Test: WonderQ-Admin-UI-Vue/src/api/client-home.test.ts

  • Test: WonderQ-Admin/tests/test_home.py

  • Step 1: 写玩法推荐列表、关联和排序失败测试

后端测试验证关联不存在的玩法分类返回 404/400、删除关联不删除分类、状态更新只改变关联、排序必须包含当前全部关联 ID。Vue API 测试验证 play-recommendations 的五个方法路径。

  • Step 2: 运行失败测试

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_home.py -q

Run: Set-Location ..\WonderQ-Admin-UI-Vue; yarn test --run src/api/client-home.test.ts

Expected: new Vue methods and missing edge-case assertions fail。

  • Step 3: 实现 Vue 玩法推荐页面

页面加载现有玩法分类作为选择项,单独显示已关联推荐;创建和编辑只提交后端允许的分类 ID、状态和排序字段;删除操作明确提示“只移除首页关联,不删除玩法分类”;复用 HomeCrudTable 的空态、错误态、排序和权限按钮。

  • Step 4: 运行玩法推荐切片验收

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_home.py -q

Run: Set-Location ..\WonderQ-Admin-UI-Vue; yarn test --run; yarn build

Expected: 后端首页测试、Vue 现有 25 项基线测试和构建全部通过;失败项记录到业务矩阵后再进入下一阶段。

Task 5: 实现管理员踢下线

Files:

  • Modify: WonderQ-Admin/app/redis_session.py

  • Modify: WonderQ-Admin/app/auth.py

  • Modify: WonderQ-Admin/app/routers/system.py

  • Modify: WonderQ-Admin/tests/test_admin_rbac.py

  • Create: WonderQ-Admin/tests/test_admin_sessions.py

  • Modify: WonderQ-Admin-UI-Vue/src/types.ts

  • Modify: WonderQ-Admin-UI-Vue/src/api/client.ts

  • Modify: WonderQ-Admin-UI-Vue/src/api/client-system.test.ts

  • Modify: WonderQ-Admin-UI-Vue/src/pages/SystemResourcePage.vue

  • Step 1: 写会话索引失败测试

在 test_admin_sessions.py 验证:

store = InMemoryAdminSessionStore()
store.create(session_id="s-1", user_id="u-1", access_jti="a-1", refresh_hash="r-1", access_expires_at=future, refresh_expires_at=future)
store.create(session_id="s-2", user_id="u-1", access_jti="a-2", refresh_hash="r-2", access_expires_at=future, refresh_expires_at=future)
assert store.revoke_user_sessions("u-1", except_session_id="s-1") == 1
assert store.is_access_active("s-1", "a-1")
assert not store.is_access_active("s-2", "a-2")

同时验证 rotate 保留同一会话索引、revoke 删除索引、重复批量撤销返回 0、Redis 异常抛出 RedisUnavailableError。

  • Step 2: 运行会话失败测试

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_admin_sessions.py -q

Expected: FAIL because the protocol and stores do not expose revoke_user_sessions。

  • Step 3: 扩展协议和两种存储实现

在 AdminSessionStore 增加 list_user_sessions(user_id) 和 revoke_user_sessions(user_id, except_session_id=None)。InMemory 使用 dict[user_id, set[session_id]] 或从现有记录派生并在 create/rotate/revoke 中保持一致;Redis 使用 wonderq:admin:user-sessions:{user_id} Set,成员仅为 session ID,批量删除 session key、refresh key 和 Set 成员。所有 Redis 客户端异常统一为 RedisUnavailableError。

  • Step 4: 运行会话存储测试并修复轮换回归

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_admin_sessions.py tests/test_admin_rbac.py -q

Expected: session index, JWT rotation, logout and existing legacy login fields all PASS。

  • Step 5: 写踢下线路由失败测试

使用 FastAPI TestClient 和依赖覆盖验证:没有 system:user:update 返回 403;目标用户不存在返回 404;目标为当前用户返回 400;成功时排除当前 session、返回撤销数量并写入审计记录;Redis 不可用返回服务错误而不是继续放行。

  • Step 6: 实现 system 路由与前端 API

新增 POST /api/admin/system/users/{user_id}/sessions/revoke,依赖 require_permission("system:user:update"),使用当前 JWT 的 sid 作为排除项,调用 revoke_user_sessions,审计内容只记录操作者、目标用户和数量。Vue 增加 revokeSystemUserSessions(userId),系统用户行增加“踢下线”确认按钮、提交中禁用和结果提示。

  • Step 7: 运行踢下线全链路测试

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_admin_sessions.py tests/test_admin_rbac.py -q

Run: Set-Location ..\WonderQ-Admin-UI-Vue; yarn test --run src/api/client-system.test.ts

Expected: backend and API client tests PASS;浏览器验收时用两个登录会话确认被踢会话的下一次请求收到 401 并回到登录页。

Task 6: 增加显式 403 页面与错误分流

Files:

  • Modify: WonderQ-Admin-UI-Vue/src/api/client.ts

  • Create: WonderQ-Admin-UI-Vue/src/pages/ForbiddenPage.vue

  • Modify: WonderQ-Admin-UI-Vue/src/router/index.ts

  • Create: WonderQ-Admin-UI-Vue/src/router/router.test.ts

  • Step 1: 写 403 与 401 分流失败测试

测试 request 收到 403 时抛出 PermissionDeniedError,不调用 /auth/refresh;收到 401 时仍只刷新一次;路由守卫对已登录无权限页面返回 forbidden,未登录返回 login,未知路径返回 not-found。

  • Step 2: 运行失败测试

Run: Set-Location WonderQ-Admin-UI-Vue; yarn test --run src/router/router.test.ts

Expected: FAIL because 403 error class、forbidden route and explicit guard branch do not exist。

  • Step 3: 实现错误类与 403 页面

新增 PermissionDeniedError extends ApiRequestError,状态码固定为 403;request 保持 403 原样解包并抛出该错误。新增 /forbidden 公共渲染页面,显示无权限、返回上一页和回到首页操作,不清除 Pinia 登录态。

  • Step 4: 修改路由守卫

把当前 return { name: "not-found" } 的权限分支改为 return { name: "forbidden", query: { from: to.fullPath } };保留动态路由只从 componentRegistry 注册。系统资源路由仍根据用户、角色、菜单、部门分别检查权限码。

  • Step 5: 运行 403、现有测试和构建

Run: Set-Location WonderQ-Admin-UI-Vue; yarn test --run; yarn build

Expected: 403 不触发刷新、不误导为 404,现有测试和构建 PASS。

Task 7: 完成五级数据范围自动化矩阵

Files:

  • Modify: WonderQ-Admin/app/rbac.py

  • Modify: WonderQ-Admin/app/routers/admin.py

  • Modify: WonderQ-Admin/tests/test_data_scope_matrix.py

  • Modify: WonderQ-Admin/tests/test_admin_rbac.py

  • Modify: docs/admin-api-requirements.md

  • Step 1: 建立隔离测试数据

测试 fixture 创建部门 dept-a、子部门 dept-a-child、dept-b,管理员 user-a、user-b,并创建带 deptId 和 createdById 的线索、玩法详情、管家、媒体和首页资源。fixture 每次测试独立回滚,不使用开发数据库真实业务数据。

  • Step 2: 写五种范围失败测试

分别为 all、dept、dept_and_children、custom_dept、self 配置角色,断言列表只返回预期 ID;同时调用详情、更新、删除或状态写入,确认跨范围资源返回 404/403,不能只在前端隐藏。

  • Step 3: 运行范围矩阵失败测试

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_data_scope_matrix.py -q

Expected: 对未覆盖资源或错误的异常状态出现失败,输出具体资源和范围编码。

  • Step 4: 统一范围过滤与写入检查

确保 apply_data_scope 对目录列表使用部门/创建人 OR 规则;is_within_data_scope 用于详情、更新、删除、状态和排序;缺少归属字段的旧记录由既有回填规则处理,不把无归属资源错误暴露给非超级管理员。保留 admin/super_admin 的全量兼容行为。

  • Step 5: 固定 Redis 权限缓存故障语义

权限缓存读取或写入失败时,允许数据库重新计算权限但不得把异常转换成放行;需要 Redis 会话校验的接口继续拒绝。为 RedisUnavailableError 增加测试,确认不能因 Redis 断开返回全量菜单或全量业务数据。

  • Step 6: 运行范围和后端回归

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest tests/test_data_scope_matrix.py tests/test_admin_rbac.py tests/test_home.py -q

Expected: five-level matrix and affected business tests PASS;与本切片无关的既有 Alembic bootstrap 失败单独记录,不跳过新测试。

Task 8: 建立 Docker 与正式双入口代理

Files:

  • Create: WonderQ-Admin-UI-Vue/Dockerfile

  • Create: WonderQ-Admin-UI-Vue/nginx.conf

  • Modify: WonderQ-Admin-UI/Dockerfile

  • Modify: WonderQ-Admin-UI/nginx.conf

  • Modify: WonderQ-Admin-UI/vite.config.ts

  • Create: deploy/nginx/wonderq-admin.conf

  • Modify: docs/integration-workflow.md

  • Step 1: 写代理静态检查

新建 scripts/verify-admin-proxy.ps1,断言 Vue 构建产物引用 /admin/,React legacy 构建产物引用 /admin-legacy/,Nginx 配置包含 /api/ proxy、两个 SPA fallback 和独立 /healthz,且不存在把 /api/ fallback 到前端 index 的规则。

  • Step 2: 运行代理失败检查

Run: rg -n "admin-legacy|/admin/|proxy_pass|try_files|location /api" WonderQ-Admin-UI WonderQ-Admin-UI-Vue deploy/nginx

Expected: 当前 React 入口和 Vue Docker 入口缺少至少一项,检查失败并列出缺失路径。

  • Step 3: 配置 Vue 静态镜像

Vue Dockerfile 使用 Node 构建 yarn build,Nginx 配置把 / 的容器根目录作为 Vue /admin/ 资源根,并对 /admin/ history fallback 到 /admin/index.html。健康检查使用 /healthz,不暴露环境变量。

  • Step 4: 配置 React legacy base 和镜像

在 React Vite 配置中读取 VITE_BASE_PATH,默认开发仍为 /,正式 legacy 构建传入 /admin-legacy/;React Nginx 资源根、history fallback 和静态资源链接与该 base 一致。原有 API proxy 开发行为保持不变。

  • Step 5: 编写统一部署 Nginx 配置

统一配置明确以下映射:

location /api/ { proxy_pass http://wonderq-api:4000; }
location /admin/ { alias /srv/wonderq-vue/; try_files $uri $uri/ /admin/index.html; }
location /admin-legacy/ { alias /srv/wonderq-react/; try_files $uri $uri/ /admin-legacy/index.html; }

实际文件路径按镜像挂载目录调整,但必须保留三条逻辑边界,且 /api/ 位于 SPA fallback 之前并单独转发。

  • Step 6: 构建并验证容器

Run: docker compose config

Run: docker build -t wonderq-admin-ui-vue:acceptance .\WonderQ-Admin-UI-Vue

Run: docker build --build-arg VITE_BASE_PATH=/admin-legacy/ -t wonderq-admin-ui-react-legacy:acceptance .\WonderQ-Admin-UI

Run: docker compose up -d postgres redis

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m alembic upgrade head

Expected: 两个前端镜像、PostgreSQL、Redis 构建/启动成功;http://localhost:4000/health 和两个静态入口可访问。若当前 Compose 不包含 API 服务,只执行现有 API 启动命令并记录边界。

Task 9: 三端回归、人工验收与 React 删除闸门

Files:

  • Modify: docs/admin-business-function-matrix.md

  • Modify: docs/integration-workflow.md

  • Read-only verification: WonderQ-MiniAPP/src/lib/api.ts, WonderQ-Admin-UI/src/api.ts

  • Step 1: 运行后端完整回归

Run: Set-Location WonderQ-Admin; .\.venv\Scripts\python.exe -m pytest -q

Expected: 新增切片全部通过;既有 Alembic 兼容失败必须保留完整测试名和原因,不能用“部分通过”掩盖。

  • Step 2: 运行两套管理端回归

Run: Set-Location ..\WonderQ-Admin-UI-Vue; yarn test --run; yarn build

Run: Set-Location ..\WonderQ-Admin-UI; yarn build

Expected: Vue 测试/构建和 React 构建通过;旧 React API 路径、登录响应字段和刷新逻辑没有被删改。

  • Step 3: 做 MiniAPP Public API 兼容检查

确认 WonderQ-MiniAPP/src/lib/api.ts 仍只访问 /api/public/...,首页 experiences、团队共创和极境视界字段与 docs/public-api.md 一致。只有 Public API 或字段发生变化时才运行 MiniAPP 类型检查和测试。

  • Step 4: 人工验收顺序

使用独立测试账号逐项完成:

  1. 四组首页新建、编辑、删除、排序、启停和错误重试;
  2. 两个登录会话验证踢下线;
  3. 无页面权限 403、无按钮权限和直接 API 403;
  4. 五种数据范围跨部门列表与写入;
  5. Redis 异常、Docker、/admin/、/admin-legacy/ 和深链刷新;
  6. Public API/MiniAPP 回归;
  7. 最后进行 OSS/媒体上传验收。

每项将实际 URL、操作结果和失败证据写入 docs/admin-business-function-matrix.md,不记录账号密码、Token 或密钥。

  • Step 5: 判断 React 删除闸门

只有 Vue 功能矩阵、Public/Admin API 回归、RBAC 五级范围、会话/Redis、Docker/代理、OSS/媒体和人工验收全部通过,并收到用户明确“删除旧 React”确认,才另开删除计划。当前计划结束时保留 React 源码、依赖、构建入口和 /admin-legacy/。

验证与交付规则

  • 每个 Task 完成后先运行其局部测试,再运行受影响端的构建;失败时先定位根因,不以扩大改动规避测试。
  • 不执行 git reset、git checkout --、批量删除或覆盖用户现有改动。
  • 不修改 .env、.env.local、真实 Docker secrets、OSS 凭证、生产数据库连接或生产部署变量。
  • 不新增 Alembic 迁移;会话索引只使用 Redis。若实现过程中发现确需数据库结构变化,暂停并单独征求确认。
  • 不自动提交或删除 React;阶段结果以 git diff --check、测试输出和业务矩阵记录作为检查点。