diff --git a/README.md b/README.md index eb3a990..0ee70c9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ | 子项目 | 定位 | 技术栈 | 默认地址 | | ------------------ | ----------------------- | ----------------------------------------------------------------- | ----------------------- | | `WonderQ-MiniAPP` | H5 与微信小程序前台 | uni-app、Vue 3、TypeScript、Tailwind CSS | `http://localhost:5173` | -| `WonderQ-Admin-UI` | 运营管理后台前端 | Vite、React、TypeScript、Tailwind CSS 4 | `http://localhost:5602` | +| `WonderQ-Admin-UI-Vue` | 运营管理后台前端 | Vite、Vue 3、TypeScript、Element Plus、Pinia | `http://localhost:5604/admin/` | | `WonderQ-Admin` | Public API 与 Admin API | Python、FastAPI、SQLAlchemy 2、Alembic、PostgreSQL、JWT、Pydantic | `http://localhost:4000` | 项目采用页面驱动开发:前台页面定义用户流程,管理后台维护运营内容,后端负责鉴权、接口、数据持久化和发布能力。 @@ -20,7 +20,7 @@ WonderQ-Project/ ├─ README.md # 项目入口文档 ├─ docs/ # 接口契约、联调流程和技术决策 ├─ WonderQ-MiniAPP/ # 前台 H5 / 微信小程序 -├─ WonderQ-Admin-UI/ # 运营管理后台前端 +├─ WonderQ-Admin-UI-Vue/ # Vue 运营管理后台前端 └─ WonderQ-Admin/ # 后端 API 服务 ``` @@ -40,16 +40,25 @@ WonderQ-Project/ 建议按后端、管理后台、前台的顺序启动。 -### 1. 启动后端 +### 1. 启动后端 `WonderQ-Admin` 要求:Python `3.12 - 3.14`、Docker Desktop。 +首次安装: + ```powershell Set-Location .\WonderQ-Admin Copy-Item .env.example .env python -m venv .venv .\.venv\Scripts\Activate.ps1 python -m pip install -r requirements.txt +``` + +日常启动: + +```powershell +Set-Location .\WonderQ-Admin +.\.venv\Scripts\Activate.ps1 docker-compose up -d postgres redis python -m alembic upgrade head python -m uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload @@ -62,15 +71,15 @@ python -m uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload 首次初始化空库时才执行 `python -m app.seed`。该命令可能重置站点、商品、目的地和媒体内容,已有开发数据时不要重复执行。 -### 2. 启动管理后台 +### 2. 启动管理后台 `WonderQ-Admin-UI-Vue` ```powershell -Set-Location .\WonderQ-Admin-UI +Set-Location .\WonderQ-Admin-UI-Vue yarn install yarn dev ``` -访问 `http://localhost:5602`。 +访问 `http://localhost:5604/admin/`。管理后台通过 Vite `/api` 代理访问 `http://localhost:4000`;如需修改后端地址,可在 `.env.local` 中设置 `VITE_API_PROXY_TARGET`。 ### 3. 启动前台 @@ -90,24 +99,20 @@ yarn dev:mp-weixin ### 本地 API 端口说明 -后端文档和默认启动命令使用 `4000`;当前 `WonderQ-MiniAPP/vite.config.ts` 与 `WonderQ-Admin-UI/vite.config.ts` 的 `/api` 代理目标为 `4001`。三端联调前必须统一端口: - -- 让后端监听 `4001`;或 -- 将两个前端 Vite 配置中的代理目标同步改为 `4000`。 - -不要同时使用不一致的端口,否则页面会出现接口连接失败或空数据。 +后端默认监听 `4000`,Vue 管理后台默认运行在 `5604`,并将 `/api` 请求代理到 `http://localhost:4000`。如果修改后端端口,需要同步设置 `WonderQ-Admin-UI-Vue/.env.local` 的 `VITE_API_PROXY_TARGET`。 ## 接口与文档 `docs/README.md` 是详细文档入口。推荐阅读顺序: 1. [`docs/integration-workflow.md`](docs/integration-workflow.md):三端启动、联调顺序和接口变更流程 -2. [`docs/development-status.md`](docs/development-status.md):当前能力对接状态和联调路径 +2. [`docs/api-response-contract.md`](docs/api-response-contract.md):三端统一响应和错误契约 3. [`docs/public-api.md`](docs/public-api.md):MiniAPP 使用的 Public API 契约 -4. [`docs/admin-api-requirements.md`](docs/admin-api-requirements.md):Admin UI 使用的 Admin API 契约 -5. [`docs/module-config-api.md`](docs/module-config-api.md):页面模块 CRUD 细节契约 -6. [`docs/backend-api-service.md`](docs/backend-api-service.md):后端运行和集成说明 -7. [`docs/decisions.md`](docs/decisions.md):当前技术与文档决策 +4. [`docs/admin-api-requirements.md`](docs/admin-api-requirements.md):Admin UI Vue 使用的 Admin API 契约 +5. [`docs/module-config-api.md`](docs/module-config-api.md):首页与用车站点模块 CRUD 契约 +6. [`docs/wanfa-api.md`](docs/wanfa-api.md):玩法分类和路线管理契约 +7. [`docs/detail-api.md`](docs/detail-api.md):路线详情管理契约 +8. [`docs/concierge-api.md`](docs/concierge-api.md):管家顾问管理契约 接口边界保持如下: @@ -127,7 +132,8 @@ python -m pytest 管理后台: ```powershell -Set-Location .\WonderQ-Admin-UI +Set-Location .\WonderQ-Admin-UI-Vue +yarn test yarn build ``` diff --git a/docs/README.md b/docs/README.md index e5fbfa9..3fbb4ad 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,78 +1,48 @@ # WonderQ 文档索引 -本目录只保留当前有效文档,用于支撑 `WonderQ-Admin`、`WonderQ-Admin-UI`、`WonderQ-MiniAPP` 三端并行开发和联调。非当前技术路径不放在当前文档集中。 +本目录只维护当前三端实际使用的契约和联调规则:`WonderQ-Admin`、`WonderQ-Admin-UI-Vue`、`WonderQ-MiniAPP`。历史需求、旧管理端方案和重复接口说明不作为当前文档。 ## 推荐阅读路径 -后端开发: +后端或管理前端: -1. `api-response-contract.md` -2. `admin-api-requirements.md` -3. `home-api.md` -4. `wanfa-api.md` -5. `concierge-api.md` -6. `detail-api.md` -7. `team-building-api.md` -8. `public-api.md` +1. [`api-response-contract.md`](./api-response-contract.md):统一响应包裹、错误和 ID 规则 +2. [`admin-api-requirements.md`](./admin-api-requirements.md):Admin API 主契约 +3. [`module-config-api.md`](./module-config-api.md):首页与用车站点模块 CRUD 契约 +4. [`wanfa-api.md`](./wanfa-api.md):玩法分类和路线管理契约 +5. [`detail-api.md`](./detail-api.md):路线详情管理契约 +6. [`concierge-api.md`](./concierge-api.md):管家顾问管理契约 -管理前端开发: +MiniAPP 或 Public API 联调: -1. `api-response-contract.md` -2. `integration-workflow.md` -3. `admin-api-requirements.md` -4. `home-api.md` -5. `wanfa-api.md` -6. `concierge-api.md` -7. `detail-api.md` -8. `team-building-api.md` -9. `module-config-api.md`(用车服务配置与需求线索) - -MiniAPP 前台开发: - -1. `api-response-contract.md` -2. `integration-workflow.md` -3. `wanfa-api.md` -4. `detail-api.md` -5. `team-building-api.md` -6. `public-api.md` -7. `development-status.md` +1. [`api-response-contract.md`](./api-response-contract.md) +2. [`public-api.md`](./public-api.md):唯一 Public API 契约 +3. [`integration-workflow.md`](./integration-workflow.md):启动、联调和验证流程 ## 文档清单 -| 文档 | 作用 | 主要读者 | -| --------------------------- | --------------------------------------------------------- | -------------- | -| `api-response-contract.md` | 三端统一 JSON 响应包裹、错误和客户端解包规则 | 全部 | -| `integration-workflow.md` | 三端本地启动、联调顺序、接口变更流程和验证命令 | 全部 | -| `development-status.md` | 三端能力对接状态矩阵和优先联调路径 | 全部 | -| `admin-business-function-matrix.md` | Admin A+B 重构业务覆盖、隐藏接口和删除闸门矩阵 | 全部 | -| `decisions.md` | 当前有效技术和文档决策 | 全部 | -| `backend-api-service.md` | 后端 API 服务运行与前端联调说明 | 后端 | -| `backend-plan.md` | 当前 FastAPI 后端定位、业务模块、近期优先级和安全部署原则 | 后端 | -| `admin-api-requirements.md` | Admin UI 必需的 Admin API 主契约 | 后端、管理前端 | -| `module-config-api.md` | 页面模块配置 CRUD 的唯一细节契约 | 后端、管理前端 | -| `home-api.md` | 首页内容和玩法推荐关联的 Admin API 补充契约 | 后端、管理前端 | -| `wild-archives-api.md` | 客片案例列表、详情和图片字段契约 | 三端 | -| `wanfa-api.md` | 玩法分类和路线管理 API 的补充契约 | 后端、管理前端 | -| `concierge-api.md` | 管家顾问资料管理 API 的补充契约 | 后端、管理前端 | -| `detail-api.md` | 详情展示内容管理 API 的补充契约 | 后端、管理前端 | -| `team-building-api.md` | 团队共创详情字段、CRUD 与 Public 详情接口契约 | 三端 | -| `public-api.md` | MiniAPP 对接后端的 Public API 契约 | 后端、MiniAPP | +| 文档 | 作用 | 主要读者 | +| --- | --- | --- | +| `api-response-contract.md` | 三端统一 JSON 响应、错误、ID 和客户端解包规则 | 全部 | +| `integration-workflow.md` | 三端本地启动、联调顺序、接口变更流程和验证命令 | 全部 | +| `admin-api-requirements.md` | 当前 Admin API 主契约 | 后端、管理前端 | +| `module-config-api.md` | 站点页面模块 CRUD 唯一细节契约 | 后端、管理前端 | +| `wanfa-api.md` | 玩法分类和路线管理 API | 后端、管理前端 | +| `detail-api.md` | 路线详情管理 API | 后端、管理前端 | +| `concierge-api.md` | 管家顾问管理 API | 后端、管理前端 | +| `public-api.md` | MiniAPP 使用的 Public API 唯一契约 | 后端、MiniAPP | ## 文档边界 - `public-api.md` 是 MiniAPP 对接后端的唯一 Public API 契约。 -- `admin-api-requirements.md` 是 Admin UI 对接后端的主契约。 -- `home-api.md` 是首页内容管理的 Admin API 补充契约。 -- `wanfa-api.md` 是玩法分类和路线管理的 Admin API 补充契约。 -- `concierge-api.md` 是管家顾问资料管理的 Admin API 补充契约。 -- `detail-api.md` 是详情展示内容管理的 Admin API 补充契约。 -- `module-config-api.md` 是页面模块 CRUD 细节的唯一权威文档。 -- `integration-workflow.md` 只写联调流程,不重复接口字段。 -- `development-status.md` 只记录当前状态,不替代测试结果。 -- `decisions.md` 只记录当前有效决策。 +- `admin-api-requirements.md` 是 Admin API 主契约;首页站点模块的字段和 CRUD 以 `module-config-api.md` 为准。 +- 玩法、详情和管家领域分别以对应补充契约为准,不在联调文档中重复维护字段。 +- `integration-workflow.md` 只记录流程、验证和风险,不定义新的接口字段。 +- 首页站点模块的 Admin API 统一以 `module-config-api.md` 为准。 +- `docs/superpowers/` 仅保存仍在使用的设计参考,不作为接口契约入口。 ## 安全约束 - 文档示例不得写入真实 Token、JWT secret、客服链接、企业 ID、手机号或生产环境变量值。 - `.env`、`.env.local` 和生产配置不进入文档目录。 -- 涉及重置数据、发布、回滚、迁移或生产操作的文档,需要明确风险和验证方式。 +- 涉及重置数据、发布、回滚、迁移或生产操作的文档,必须明确风险和验证方式。 diff --git a/docs/admin-api-requirements.md b/docs/admin-api-requirements.md index 26a68ac..62cc4b6 100644 --- a/docs/admin-api-requirements.md +++ b/docs/admin-api-requirements.md @@ -1,14 +1,14 @@ # WonderQ Admin API 接口需求 -本文档描述 `WonderQ-Admin-UI` 当前使用的 Admin API。接口负责站点内容维护、素材、发布和需求线索管理。 +本文档描述 `WonderQ-Admin-UI-Vue` 当前使用的 Admin API。接口负责站点内容维护、素材、发布、权限和需求线索管理。 -玩法分类和路线管理的字段、嵌套路由、排序与删除约束见 [wanfa-api.md](./wanfa-api.md)。该文档是本主契约的玩法领域补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI`。 +玩法分类和路线管理的字段、嵌套路由、排序与删除约束见 [wanfa-api.md](./wanfa-api.md)。该文档是本主契约的玩法领域补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI-Vue`。 -管家顾问资料管理的字段、图片、排序与删除约束见 [concierge-api.md](./concierge-api.md)。该文档是本主契约的管家领域补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI`。 +管家顾问资料管理的字段、图片、排序与删除约束见 [concierge-api.md](./concierge-api.md)。该文档是本主契约的管家领域补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI-Vue`。 -详情展示内容的字段、图片、排序与商品领域隔离约束见 [detail-api.md](./detail-api.md)。该文档是本主契约的详情领域补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI`。 +详情展示内容的字段、图片、排序与商品领域隔离约束见 [detail-api.md](./detail-api.md)。该文档是本主契约的详情领域补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI-Vue`。 -首页体验、团队共创和极境视界内容的字段、图片、排序与商品领域隔离约束见 [home-api.md](./home-api.md)。该文档是本主契约的首页内容补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI`。 +首页和用车站点模块的字段、单例、排序与删除约束见 [module-config-api.md](./module-config-api.md)。 所有 Admin JSON 接口遵循 [三端统一 API 响应契约](./api-response-contract.md)。 @@ -31,10 +31,18 @@ | `POST` | `/api/admin/auth/logout` | 撤销当前后台会话 | | `GET` | `/api/admin/me` | 当前后台用户 | | `GET` | `/api/admin/system/profile` | 当前用户、角色、权限码和动态菜单 | -| `GET/POST/PATCH` | `/api/admin/system/users` | 管理后台用户 | -| `GET/POST/PATCH` | `/api/admin/system/roles` | 管理角色和数据范围 | -| `GET/POST/PATCH` | `/api/admin/system/menus` | 管理目录、页面和按钮 | -| `GET/POST/PATCH` | `/api/admin/system/depts` | 管理部门 | +| `GET` | `/api/admin/system/users` | 查询后台用户 | +| `POST` | `/api/admin/system/users` | 新增后台用户 | +| `PATCH` | `/api/admin/system/users/{userId}` | 更新后台用户 | +| `GET` | `/api/admin/system/roles` | 查询管理角色和数据范围 | +| `POST` | `/api/admin/system/roles` | 新增管理角色 | +| `PATCH` | `/api/admin/system/roles/{roleId}` | 更新管理角色 | +| `GET` | `/api/admin/system/menus` | 查询目录、页面和按钮 | +| `POST` | `/api/admin/system/menus` | 新增目录、页面或按钮 | +| `PATCH` | `/api/admin/system/menus/{menuId}` | 更新目录、页面或按钮 | +| `GET` | `/api/admin/system/depts` | 查询部门 | +| `POST` | `/api/admin/system/depts` | 新增部门 | +| `PATCH` | `/api/admin/system/depts/{deptId}` | 更新部门 | | `GET` | `/api/admin/dashboard` | 工作台统计和最近线索 | | `GET` | `/api/admin/site-config` | 获取全部站点配置 | | `POST` | `/api/admin/site-config/{module}` | 新增模块项 | @@ -95,13 +103,13 @@ type SiteModule = { "email": "admin@example.test", "password": "" } ``` -成功响应包裹为 `data: { token, accessToken, expiresIn, user: { id, email, name, role } }`。`token` 保留给过渡期 React 管理端,`accessToken` 为短时访问令牌;Refresh Token 只通过同域 HttpOnly Cookie 返回,不进入 JSON。 +成功响应包裹为 `data: { token, accessToken, expiresIn, user: { id, email, name, role } }`。`accessToken` 是短时访问令牌,`token` 是当前响应中的同值兼容字段;Refresh Token 只通过同域 HttpOnly Cookie 返回,不进入 JSON。 管理员登录、刷新和退出依赖 Redis 会话存储。Refresh Token 轮换后旧令牌立即失效;Redis 不可用时认证接口返回 `503`,不降级为无会话校验。 `/api/admin/system/profile` 返回 `roles`、`permissions`、`menus`、`dataScopes` 和 `deptIds`。菜单只返回启用且可见的目录/页面,按钮菜单保留在页面节点的 `children` 中;前端组件只能从预注册组件白名单加载 `component`。 -角色数据范围使用以下五个编码:`all`(全部)、`dept`(当前部门)、`dept_and_children`(当前部门及子部门)、`custom_dept`(自定义部门)、`self`(本人)。运营资源通过 `deptId` 和 `createdById` 归属字段执行查询过滤;资源归属迁移完成前不得在生产环境切换非管理员角色。 +角色数据范围使用以下五个编码:`all`(全部)、`dept`(当前部门)、`dept_and_children`(当前部门及子部门)、`custom_dept`(自定义部门)、`self`(本人)。运营资源通过 `deptId` 和 `createdById` 归属字段执行查询过滤。 登录按 IP 与账号组合执行 Redis 限流,默认 60 秒最多 5 次;权限菜单缓存默认 300 秒。Redis 故障不能放行权限检查,缓存不可用时只能重新读取数据库,认证会话和限流不可用时返回 `503`。 diff --git a/docs/admin-business-function-matrix.md b/docs/admin-business-function-matrix.md deleted file mode 100644 index 6351dba..0000000 --- a/docs/admin-business-function-matrix.md +++ /dev/null @@ -1,48 +0,0 @@ -# WonderQ Admin 业务功能矩阵 - -本文档是 A+B 并行重构的第一阶段基线。矩阵以当前后端路由、契约文档、React 页面和 MiniAPP 公共接口为准;“已接入”只表示代码已具备,不代表已经完成人工验收。 - -## 端与路径 - -| 端 | 目录 | 当前路径 | 迁移策略 | -| --- | --- | --- | --- | -| 后端 | `WonderQ-Admin` | `/api` | 保持 Docker Compose、PostgreSQL、Redis、JWT 和 `{code,msg,data}` 契约 | -| Vue 管理端 | `WonderQ-Admin-UI-Vue` | 本地 `/admin/`,生产 `/admin` | 新权限壳和业务切片逐项迁移 | -| React 管理端 | `WonderQ-Admin-UI` | 本地 `5602`,生产 `/admin-legacy` | 过渡运行,已接入 Refresh Token 自动刷新 | -| 前台 | `WonderQ-MiniAPP` | H5/微信小程序 | 不整体重写,只验证 Public API 兼容 | - -## 业务覆盖矩阵 - -| 业务切片 | 后端接口/数据 | React 过渡端 | Vue 迁移端 | MiniAPP 依赖 | 当前验收状态 | -| --- | --- | --- | --- | --- | --- | -| 登录、刷新、退出 | `/api/admin/auth/login`、`refresh`、`logout`、`/api/admin/me` | 兼容旧 `token/user` 字段,401 自动刷新 | 登录页、内存 Access Token、HttpOnly Refresh Cookie | 无 | 自动化通过,待浏览器人工验收 | -| 仪表盘 | `/api/admin/dashboard` | 已有 | `DashboardPage` 已接入 | 无 | 读流程已接入 | -| 首页与站点配置 | `/api/admin/site-config` 及模块 CRUD、排序 | 已有 | `HomePage` 已迁移全部 7 类站点模块的读写、删除和可用模块排序;单例模块禁止重复创建 | `/api/public/home`、`/api/public/site-config` | 契约、表单校验、接口回归和页面入口已通过,真实数据写入待人工确认 | -| 玩法分类与路线 | `/api/admin/wanfa/categories` 及分类/路线 CRUD | 已有 | `WanfaPage` 已迁移分类/路线新增、编辑、删除、排序和非空分类删除保护 | `/api/public/wanfa/categories` | 契约、接口回归和页面入口已通过,真实数据写入待人工确认 | -| 路线详情 | `/api/admin/details` 及详情 CRUD | React 现有玩法流程依赖 | 已集成到 Vue 路线编辑器,支持详情字段、数组文案、管家关联、启用状态和图片上传/排序 | `/api/public/details/{key}` | 契约、接口回归和页面入口已通过,真实数据写入待人工确认 | -| 管家 | `/api/admin/concierge/advisors` 及 CRUD | 已有 | `ConciergePage` 已接入新增、编辑、启停、排序、删除和头像/二维码上传 | `/api/public/concierge/advisors`、详情顾问信息 | 契约、表单校验和页面入口已通过,真实写入待人工确认 | -| 需求线索 | `/api/admin/leads`、状态更新 | 已有筛选和状态流转 | `LeadsPage` 已接入类型、状态、关键词筛选、详情查看和六态状态写入 | `/api/public/leads` | 契约、有效会话下的列表/筛选/详情页面验收已通过,真实状态写入待人工确认 | -| 媒体 | `/api/admin/media-assets`、上传 | React 已有上传 | `MediaPage` 已接入素材列表、分组、批量上传和图片校验;玩法/管家继续复用上传入口 | 通过站点配置间接依赖 | 契约、列表和上传入口已通过,真实上传待人工确认 | -| 发布、重置 | `/api/admin/publish`、`reset-guizhou-content` | React 现有入口/能力 | `OperationsToolsPage` 已接入发布和重置,均有权限控制、进行中禁用和二次确认 | 发布结果影响 Public API | 入口和确认闸门已通过,执行前必须人工确认 | -| RBAC 用户 | `/api/admin/system/users` | 旧角色字段继续兼容 | 系统资源入口已接入读取 | 无 | 后端/读页面已接入,编辑待迁移 | -| RBAC 角色、菜单、部门 | `/api/admin/system/roles`、`menus`、`depts` | 旧端不删除 | Vue 动态菜单、组件白名单和系统资源读取已接入 | 无 | 后端/读页面已接入,完整按钮授权待验收 | - -## 隐藏和横向能力清单 - -- Admin API 还包括模块新增、更新、删除、排序,以及详情、玩法、管家、首页内容的审计日志。 -- 迁移不能只检查可见菜单;`media-assets`、`publish`、`reset-guizhou-content`、仪表盘统计和所有排序接口必须在验收记录中逐项确认。 -- Public API 的首页、玩法、详情、管家、线索和用车需求依赖继续由 MiniAPP 访问;Admin API 不向 MiniAPP 暴露。 -- 新 RBAC 迁移为 `0025_admin_rbac`,资源归属迁移为 `0026_admin_ownership`。两者只提交迁移文件,不在本阶段自动升级数据库。 - -## 删除 React 的闸门 - -只有当矩阵中 Vue 列全部变为“已接入并人工验收”,并且登录刷新、权限、数据范围、Redis 故障、Public API 回归和 Docker 验证记录齐全后,才可以征求确认切换 `/admin` 与删除 `/admin-legacy`。 - -## 阶段验收记录(2026-08-26) - -- 线索状态:已在 Vue 管理端将真实线索从“待处理”改为“已联系”,再恢复为“待处理”,写入和恢复均成功。 -- 发布与重置:发布成功;重置成功,Public API 和 MiniAPP 已读取重置后的站点内容。 -- RBAC:用户、角色、菜单、部门均完成真实编辑写入并恢复原值,编辑成功提示和最终列表值正常。 -- 媒体上传:按当前验收顺序暂缓,放到最后;此前验证发现 OSS 返回 403,尚未形成素材记录。 -- 自动化回归:Vue 15 个测试文件、25 个测试通过,TypeScript 检查和生产构建通过;后端 134 个测试通过,7 个既有 Alembic 兼容性测试失败,未涉及本轮站点重置和 RBAC 改动。 -- React 删除:未执行。需待媒体上传、登录刷新、权限/数据范围、Redis 异常、Public API 和 Docker 验收全部完成,并经人工确认后再处理。 diff --git a/docs/api-response-contract.md b/docs/api-response-contract.md index 3867faa..cccfa04 100644 --- a/docs/api-response-contract.md +++ b/docs/api-response-contract.md @@ -1,6 +1,6 @@ # 三端统一 API 响应契约 -本文档是 `WonderQ-Admin`、`WonderQ-Admin-UI`、`WonderQ-MiniAPP` 的统一 JSON 响应规范。适用于 `/health`、`/api/public/**` 和 `/api/admin/**`,新接口必须直接遵守本契约。 +本文档是 `WonderQ-Admin`、`WonderQ-Admin-UI-Vue`、`WonderQ-MiniAPP` 的统一 JSON 响应规范。适用于 `/health`、`/api/public/**` 和 `/api/admin/**`,新接口必须直接遵守本契约。 ## 基本结构 @@ -28,9 +28,8 @@ - 所有持久化资源的 `id` 都是服务端生成的稳定不透明字符串,当前实现统一使用 UUID v4 格式。 - ID 只在记录创建时生成,后续列表、详情、排序、编辑和删除响应必须保持不变;禁止在序列化或每次请求时重新随机生成。 -- 玩法路线详情的 `DetailRecord.key` 等于对应的 `WanfaRoute.id`,因此路线 ID 迁移后详情 `key` 必须同步更新。 +- 玩法路线详情的 `DetailRecord.key` 等于对应的 `WanfaRoute.id`,详情、列表、排序和跳转必须复用同一个路线 ID。 - 本地 fallback/mock 数据可以继续使用便于阅读的语义 ID,但这些 ID 不代表服务端正式 ID;接口成功后应以 API 返回的 UUID 为准。 -- `0022_opaque_ids` 迁移只转换历史非 UUID ID,并同步外键、详情 key 和审计实体引用;新建记录继续由 ORM 默认生成 UUID。 ## 成功响应 @@ -106,7 +105,7 @@ ## 客户端处理 - `WonderQ-Admin` 负责所有路由和全局异常处理,不能把内部异常、SQL、堆栈、Token 或环境配置写入 `msg`、`details` 或响应日志。 -- `WonderQ-Admin-UI` 的公共 `request` 校验包裹结构,成功只返回 `data`;失败使用 `msg` 提示,并保留 `errorCode`、`details`。 +- `WonderQ-Admin-UI-Vue` 的公共 `request` 校验包裹结构,成功只返回 `data`;失败使用 `msg` 提示,并保留 `errorCode`、`details`。 - `WonderQ-MiniAPP` 的公共请求层执行同样校验,页面、Store 和归一化函数继续只接收业务类型,不重复读取 `data`。 - HTTP 错误是服务端返回了合法错误包;网络错误是请求未获得 HTTP 响应;协议错误是响应缺少必填字段或字段类型不正确,三者应分别进入现有错误、重试和 fallback 流程。 - 未包裹的旧响应不再兼容,客户端必须将其识别为协议错误。 @@ -121,4 +120,4 @@ 4. 列表、详情、删除和排序的原业务字段只出现在 `data` 内。 5. `400`、`401`、`404`、`409`、`422`、`500`(适用时)都保持相同包裹结构。 -相关领域字段和路径以 [public-api.md](./public-api.md)、[admin-api-requirements.md](./admin-api-requirements.md)、[home-api.md](./home-api.md)、[wanfa-api.md](./wanfa-api.md)、[detail-api.md](./detail-api.md)、[concierge-api.md](./concierge-api.md)、[team-building-api.md](./team-building-api.md) 和 [wild-archives-api.md](./wild-archives-api.md) 为准。 +相关领域字段和路径以 [public-api.md](./public-api.md)、[admin-api-requirements.md](./admin-api-requirements.md)、[module-config-api.md](./module-config-api.md)、[wanfa-api.md](./wanfa-api.md)、[detail-api.md](./detail-api.md) 和 [concierge-api.md](./concierge-api.md) 为准。 diff --git a/docs/concierge-api.md b/docs/concierge-api.md index 2cf15ee..7defd4c 100644 --- a/docs/concierge-api.md +++ b/docs/concierge-api.md @@ -1,8 +1,8 @@ # 管家管理 Admin API -> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。 +> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI-Vue`。 > -> 状态:已实现。本文档约定 `WonderQ-Admin`、`WonderQ-Admin-UI` 的管家管理接口,并记录 MiniAPP 使用的对应 Public API。 +> 状态:已实现。本文档约定 `WonderQ-Admin`、`WonderQ-Admin-UI-Vue` 的管家管理接口,并记录 MiniAPP 使用的对应 Public API。 所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。 @@ -111,7 +111,7 @@ type ConciergeReorderRequest = { | 字段 | 类型 | 必填 | 约束和用途 | | --- | --- | --- | --- | -| `id` | `string` | 响应必填 | 顾问稳定标识,由后端生成。Admin UI 不使用姓名作为编辑、删除或 React `key`。 | +| `id` | `string` | 响应必填 | 顾问稳定标识,由后端生成。管理端不使用姓名作为编辑、删除或列表 `key`。 | | `avatar` | `string` | 是 | 顾问头像 URL,前台按圆形头像展示。 | | `name` | `string` | 是 | 顾问姓名,去除首尾空白后不得为空。 | | `role` | `string` | 是 | 顾问职位或英文职称,去除首尾空白后不得为空。 | @@ -336,7 +336,7 @@ const advisor: ConciergeAdvisor = { ## 后端落地边界 -当前实现由 `WonderQ-Admin` 提供 ORM 模型、`0016_concierge_advisors` 迁移、schema、Admin/Public 路由、序列化和审计日志;由 `WonderQ-Admin-UI` 提供 API 类型、请求封装、管家列表、表单、图片上传、启停、排序和删除确认;由 `WonderQ-MiniAPP` 调用 Public API 并归一化顾问数据。Hero 和服务原则仍不在管家表中维护。 +当前实现由 `WonderQ-Admin` 提供 ORM 模型、schema、Admin/Public 路由、序列化和审计日志;由 `WonderQ-Admin-UI-Vue` 提供 API 类型、请求封装、管家列表、表单、图片上传、启停、排序和删除确认;由 `WonderQ-MiniAPP` 调用 Public API 并归一化顾问数据。Hero 和服务原则仍不在管家表中维护。 相关文档: diff --git a/docs/detail-api.md b/docs/detail-api.md index 7cbfee1..35679f6 100644 --- a/docs/detail-api.md +++ b/docs/detail-api.md @@ -1,8 +1,8 @@ # 详情展示管理 Admin API -> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。 +> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI-Vue`。 > -> 状态:已实现契约。本文件定义独立路线详情展示模型,供 `WonderQ-Admin`、`WonderQ-Admin-UI` 和 `WonderQ-MiniAPP` 三端联调使用。详情不恢复 Product、ProductImage 或订单关联。 +> 状态:已实现契约。本文件定义独立路线详情展示模型,供 `WonderQ-Admin`、`WonderQ-Admin-UI-Vue` 和 `WonderQ-MiniAPP` 三端联调使用。详情不恢复 Product、ProductImage 或订单关联。 ## 领域边界 @@ -33,7 +33,7 @@ - 当前实现优先消费 Public API 返回的最终展示字段。 - 接口失败、字段不完整或详情未配置时,MiniAPP 按路线 ID 使用本地网络图片和模拟文案兜底。 -新的 Admin API 应直接维护最终展示字段;Admin UI 不应复刻这些推导逻辑,也不应依赖已移除的 Product 字段。 +新的 Admin API 应直接维护最终展示字段;`WonderQ-Admin-UI-Vue` 不应复刻这些推导逻辑,也不应依赖已移除的 Product 字段。 ## 接口清单 @@ -347,7 +347,7 @@ const presentation: DetailPresentation = { ## 后端落地边界 -当前实现由 `WonderQ-Admin` 的 `DetailRecord` 模型、`0021_detail_records`、`0022_opaque_ids` 和 `0023_detail_concierge_advisor` 迁移、Admin/Public 路由和审计日志提供能力;由 `WonderQ-Admin-UI` 在玩法路线编辑抽屉中维护详情及顾问 ID;由 `WonderQ-MiniAPP` 调用 Public 详情接口并按路线 ID fallback。迁移文件只负责生成和初始化详情记录,不自动执行数据库升级。顾问资料始终从管家领域实时读取,不复制到详情表。 +当前实现由 `WonderQ-Admin` 的 `DetailRecord` 模型、Admin/Public 路由和审计日志提供能力;由 `WonderQ-Admin-UI-Vue` 在玩法路线编辑抽屉中维护详情及顾问 ID;由 `WonderQ-MiniAPP` 调用 Public 详情接口并按路线 ID fallback。顾问资料始终从管家领域实时读取,不复制到详情表。 相关文档: diff --git a/docs/guanjia-api.md b/docs/guanjia-api.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/home-api.md b/docs/home-api.md deleted file mode 100644 index 3fdb075..0000000 --- a/docs/home-api.md +++ /dev/null @@ -1,478 +0,0 @@ -# 首页内容管理 Admin API - -> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。 -> -> 状态:已实现。本文件约定首页内容的 Admin API,并记录 MiniAPP 使用的对应 Public API。首页玩法推荐不复制玩法文案,而是关联 `WanfaCategory`。 - -## 领域边界 - -首页内容管理维护四类首页内容: - -- 体验推荐:徽标、分类、标题、英文标题、封面和需求关键词。 -- 团队共创:标签、标题、描述、封面、需求关键词、详情副标题和详情正文段落。 -- 极境视界:案例标题、封面、详情图片和需求关键词;首页卡片可进入客片案例详情。 -- 玩法推荐:已关联的玩法分类、启用状态和首页展示顺序;分类名称及路线由玩法领域维护。 - -本领域不负责: - -- 顶部轮播、车辆选项、需求表单或其他站点模块;这些内容遵循 [Admin API 主契约](./admin-api-requirements.md)。 -- 商品、Product、ProductImage、详情、价格、库存、订单或预订。 -- 线索创建和线索跟进。 - -`demandKeyword` 只是点击卡片后预填需求页的普通字符串,不是商品 ID、路线 ID、订单 ID,也不建立数据库外键。 - -首页正式接口返回的所有资源 `id` 都是稳定 UUID 字符串。不要把下方 fallback 文件中的语义 ID 当作服务端 ID,也不要在接口序列化时重新生成 ID;首页卡片跳转详情、排序和删除必须复用接口返回的同一 ID。 - -当前首页通过 `GET /api/public/home` 消费三类内容;团队共创和极境视界 mock 数组仍作为接口失败、空响应或字段缺失时的前台 fallback,`experiences` 中的玩法推荐无本地模拟数据时保持空态。Admin UI 仍通过本文件列出的 Admin API 分别维护体验推荐和玩法推荐关联。 - -本文件所有 JSON 示例的业务对象均位于统一响应的 `data` 字段内,完整包裹格式见 [api-response-contract.md](./api-response-contract.md)。 - -## 接口清单 - -API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。 - -| 方法 | 路径 | 用途 | -| --- | --- | --- | -| `GET` | `/api/admin/home/experiences` | 获取全部体验推荐卡片 | -| `POST` | `/api/admin/home/experiences` | 新增体验推荐卡片 | -| `PATCH` | `/api/admin/home/experiences/{experienceId}` | 编辑体验推荐卡片 | -| `DELETE` | `/api/admin/home/experiences/{experienceId}` | 删除体验推荐卡片 | -| `PATCH` | `/api/admin/home/experiences/reorder` | 调整体验推荐卡片顺序 | -| `GET` | `/api/admin/home/team-buildings` | 获取全部团队共创卡片 | -| `POST` | `/api/admin/home/team-buildings` | 新增团队共创卡片 | -| `PATCH` | `/api/admin/home/team-buildings/{teamBuildingId}` | 编辑团队共创卡片 | -| `DELETE` | `/api/admin/home/team-buildings/{teamBuildingId}` | 删除团队共创卡片 | -| `PATCH` | `/api/admin/home/team-buildings/reorder` | 调整团队共创卡片顺序 | -| `GET` | `/api/admin/home/wild-archives` | 获取全部极境视界案例 | -| `POST` | `/api/admin/home/wild-archives` | 新增极境视界案例 | -| `PATCH` | `/api/admin/home/wild-archives/{archiveId}` | 编辑极境视界案例 | -| `DELETE` | `/api/admin/home/wild-archives/{archiveId}` | 删除极境视界案例 | -| `PATCH` | `/api/admin/home/wild-archives/reorder` | 调整极境视界案例顺序 | -| `GET` | `/api/admin/home/play-recommendations` | 获取首页已关联的玩法分类 | -| `POST` | `/api/admin/home/play-recommendations` | 关联一个玩法分类到首页 | -| `PATCH` | `/api/admin/home/play-recommendations/{recommendationId}` | 修改关联的启用状态或玩法分类 | -| `DELETE` | `/api/admin/home/play-recommendations/{recommendationId}` | 移除首页玩法分类关联,不删除玩法分类 | -| `PATCH` | `/api/admin/home/play-recommendations/reorder` | 调整首页玩法推荐顺序 | - -MiniAPP Public API: - -| 方法 | 路径 | 用途 | -| --- | --- | --- | -| `GET` | `/api/public/home` | 获取已启用的首页内容和玩法推荐 | -| `GET` | `/api/public/home/team-buildings/{teamBuildingId}` | 获取单个团队共创详情 | -| `GET` | `/api/public/home/wild-archives` | 获取客片案例更多列表 | -| `GET` | `/api/public/home/wild-archives/{archiveId}` | 获取单个客片案例详情 | - -## 通用约定 - -- 请求和响应使用 JSON,字段使用 camelCase。 -- 所有管理接口需要 `Authorization: Bearer `。 -- `GET` 返回启用和停用的全部记录,按 `sortOrder` 升序返回,供 Admin UI 完整维护。 -- 创建、编辑、删除和排序成功后写入审计日志,再提交事务。 -- 创建和编辑返回最新记录;排序接口返回排序后的 `items`。 -- ID 由后端生成并作为非空字符串返回。初始化迁移时应优先保留三个 mock 文件中的稳定 ID。 -- 空集合返回 `[]`,不能返回 `null` 或省略字段。 -- 所有资源的 `sortOrder` 从 `0` 开始,数值越小越靠前;新增未传排序时追加到末尾。 -- 成功结果放入 `data`;失败返回数字 `code`、`msg`、`data: null`,可选 `errorCode` 和 `details`。 - -## 数据类型 - -### 体验推荐 - -`homeExperienceData.ts` 已包含前台渲染类型和 API 过渡类型。Admin API 的完整记录应补充管理元数据: - -```ts -type HomeExperience = { - id: string; - badge: string; - category: string; - title: string; - englishTitle: string; - image: string; - demandKeyword: string; -}; - -type HomeExperienceApiItem = { - id?: string | null; - badge?: string | null; - category?: string | null; - title?: string | null; - englishTitle?: string | null; - image?: string | null; - demandKeyword?: string | null; - isActive?: boolean | null; - sortOrder?: number | null; -}; - -type HomeExperienceRecord = HomeExperience & { - isActive: boolean; - sortOrder: number; - createdAt: string; - updatedAt: string; -}; - -type HomeExperienceCreate = Omit & { - isActive?: boolean; - sortOrder?: number; -}; - -type HomeExperiencePatch = Partial; -``` - -### 团队共创 - -`homeTeamBuildingData.ts` 当前只包含前台渲染字段,管理端记录增加状态、排序和审计时间: - -```ts -type HomeTeamBuilding = { - id: string; - tag: string; - title: string; - description: string; - image: string; - demandKeyword: string; - detailSubtitle: string; - detailParagraphs: string[]; -}; - -type HomeTeamBuildingRecord = HomeTeamBuilding & { - isActive: boolean; - sortOrder: number; - createdAt: string; - updatedAt: string; -}; - -type HomeTeamBuildingCreate = Omit & { - isActive?: boolean; - sortOrder?: number; -}; - -type HomeTeamBuildingPatch = Partial; -``` - -### 极境视界 - -`homeWildArchivesData.ts` 当前只包含前台渲染字段,管理端记录增加状态、排序和审计时间: - -```ts -type HomeWildArchive = { - id: string; - title: string; - image: string; - images: string[]; - demandKeyword: string; -}; - -type HomeWildArchiveRecord = HomeWildArchive & { - isActive: boolean; - sortOrder: number; - createdAt: string; - updatedAt: string; -}; - -type HomeWildArchiveCreate = Omit & { - isActive?: boolean; - sortOrder?: number; -}; - -type HomeWildArchivePatch = Partial; -``` - -列表响应和排序请求统一使用以下结构: - -```ts -type HomeListResponse = { - items: T[]; -}; - -type HomeReorderRequest = { - itemIds: string[]; -}; -``` - -## 字段约束 - -| 资源 | 字段 | 类型 | 必填 | 约束和用途 | -| --- | --- | --- | --- | --- | -| 体验推荐 | `badge` | `string` | 是 | 卡片徽标,去除首尾空白后不得为空。 | -| 体验推荐 | `category` | `string` | 是 | 体验分类文案,去除首尾空白后不得为空。 | -| 体验推荐 | `title` | `string` | 是 | 卡片主标题,去除首尾空白后不得为空。 | -| 体验推荐 | `englishTitle` | `string` | 是 | 卡片英文标题,去除首尾空白后不得为空。 | -| 体验推荐 | `image` | `string` | 是 | 可直接用于图片组件的封面 URL。 | -| 体验推荐 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 | -| 团队共创 | `tag` | `string` | 是 | 卡片标签,去除首尾空白后不得为空。 | -| 团队共创 | `title` | `string` | 是 | 卡片标题,去除首尾空白后不得为空。 | -| 团队共创 | `description` | `string` | 是 | 卡片描述,去除首尾空白后不得为空。 | -| 团队共创 | `image` | `string` | 是 | 可直接用于图片组件的封面 URL。 | -| 团队共创 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 | -| 团队共创 | `detailSubtitle` | `string` | 是 | 详情页首屏副标题;旧数据回退为 `description`。 | -| 团队共创 | `detailParagraphs` | `string[]` | 是 | 详情页正文段落;至少一段,旧数据回退为 `[description]`。 | -| 极境视界 | `title` | `string` | 是 | 案例标题,去除首尾空白后不得为空。 | -| 极境视界 | `image` | `string` | 是 | 案例封面图片 URL。 | -| 极境视界 | `images` | `string[]` | 是 | 案例详情图片 URL 列表,至少一张;第一张建议与封面一致。 | -| 极境视界 | `demandKeyword` | `string` | 是 | 点击后预填需求页的关键词。 | -| 全部资源 | `isActive` | `boolean` | 响应必填 | 是否进入已发布前台内容,创建默认 `true`。 | -| 全部资源 | `sortOrder` | `number` | 响应必填 | 非负整数,数值越小越靠前。 | -| 全部资源 | `createdAt` | `string` | 响应必填 | ISO 8601 创建时间。 | -| 全部资源 | `updatedAt` | `string` | 响应必填 | ISO 8601 最后更新时间。 | - -服务端应校验所有必填文本、URL 格式和非负整数排序值;具体文本最大长度由后端 schema 统一定义,并同步到 Admin UI 表单校验。图片字段只保存最终 URL,不接受 base64,不在首页内容记录中保存图片二进制。 - -## 接口详情 - -以下规则适用于三类资源;路径中的资源名和 ID 参数以接口清单为准。 - -### 获取列表 - -```http -GET /api/admin/home/experiences -Authorization: Bearer -``` - -团队共创和极境视界分别使用 `/api/admin/home/team-buildings`、`/api/admin/home/wild-archives`。 - -成功响应示例(统一响应包裹): - -```json -{ - "code": 200, - "msg": "success", - "data": { - "items": [ - { - "id": "waterfall-descent", - "badge": "玩过推荐", - "category": "瀑降体验", - "title": "悬崖瀑降", - "englishTitle": "WATERFALL DESCENT", - "image": "https://example.test/assets/waterfall-descent.jpg", - "demandKeyword": "悬崖瀑降", - "isActive": true, - "sortOrder": 0, - "createdAt": "2026-01-01T00:00:00Z", - "updatedAt": "2026-01-01T00:00:00Z" - } - ] - } -} -``` - -接口返回启用和停用的全部记录;Admin UI 负责显示状态,不能让后端默认隐藏停用记录。 - -### 新增、编辑与删除 - -- `POST /api/admin/home/{resource}`:请求体为对应资源的 `Create` 类型,成功返回 `201` 和 `data` 内的新建记录;未传 `sortOrder` 时追加到末尾。 -- `PATCH /api/admin/home/{resource}/{id}`:请求体为对应资源的 `Patch` 类型,只更新提交字段;成功返回 `data` 内的更新记录,不存在返回 `404`。 -- `DELETE /api/admin/home/{resource}/{id}`:成功返回 `data: { "id": "..." }`,删除后重新规范化同一资源剩余记录的 `sortOrder`。 - -其中 `{resource}` 只能是 `experiences`、`team-buildings` 或 `wild-archives`;对应路径参数分别为 `experienceId`、`teamBuildingId`、`archiveId`。删除不触发商品、订单、预订或线索级联操作。 - -### 调整顺序 - -```http -PATCH /api/admin/home/experiences/reorder -Authorization: Bearer -Content-Type: application/json -``` - -团队共创和极境视界分别使用 `/api/admin/home/team-buildings/reorder`、`/api/admin/home/wild-archives/reorder`。请求必须完整包含当前资源的全部 ID,不能重复: - -```json -{ "itemIds": ["cave-exploration", "waterfall-descent"] } -``` - -成功响应为 `data: { "items": [] }`,其中 `items` 是更新 `sortOrder` 后的完整记录列表。缺少 ID、包含未知 ID 或出现重复 ID 时返回 `400`,业务码为 `HOME_REORDER_INVALID`。 - -### MiniAPP Public API - -```http -GET /api/public/home -``` - -无需鉴权。接口只返回 `isActive === true` 的记录,并移除管理端状态、排序和审计时间字段。为统一三端首页消费模型,玩法推荐数据放入 `experiences` 字段,响应中不再返回 `playRecommendations`: - -```ts -type PublicHomeResponse = { - experiences: HomeWanfaRecommendation[]; - teamBuildings: HomeTeamBuilding[]; - wildArchives: HomeWildArchive[]; -}; - -type HomeWanfaRecommendation = { - id: string; - categoryId: string; - label: string; - routes: WanfaRoute[]; -}; - -type WanfaRoute = { - id: string; - title: string; - subtitle: string; - image: string; - routeCount: number; - demandKeyword: string; -}; -``` - -注意:`/api/admin/home/experiences` 仍是后台体验推荐 CRUD 资源;Public `/api/public/home` 的 `experiences` 字段已统一承载玩法推荐数据,不能按后台体验推荐字段解读。 - -三组列表始终返回数组;没有可用内容时返回空数组。`experiences` 只返回启用的玩法推荐关联记录,并展开关联分类当前的路线。MiniAPP 应在数据层将 `experiences` 归一化为玩法推荐,不再读取或维护 `playRecommendations` 字段。 - -首页玩法推荐点击行为:当 `routes` 存在第一条路线时,MiniAPP 使用该路线的 `id` 调用 `goWanfaRouteDetail`,进入 `/pages/detail/index?routeId={route.id}`;没有关联路线时才使用 `demandKeyword` 或分类名称进入需求页。路线详情字段和 Public 详情接口以 [详情展示契约](./detail-api.md) 为准。 - -### 玩法推荐关联 - -`POST /api/admin/home/play-recommendations` 请求体: - -```ts -type HomeWanfaRecommendationCreate = { - categoryId: string; - isActive?: boolean; - sortOrder?: number; -}; - -type HomeWanfaRecommendationPatch = Partial; -``` - -Admin 响应记录包含 `id`、`categoryId`、`categoryLabel`、`routeCount`、`isActive`、`sortOrder`、`createdAt` 和 `updatedAt`。同一个玩法分类只能关联一次;分类不存在或已关联时分别返回 `404` 或 `409 HOME_WANFA_CATEGORY_DUPLICATE`。移除关联不会删除 `WanfaCategory` 或其路线。被首页推荐关联的玩法分类不能直接删除,需先移除首页关联。 - -## 当前 fallback 与迁移映射 - -以下语义 ID 仅用于 MiniAPP 本地 fallback 和迁移前的内容识别;执行 `0022_opaque_ids` 后,正式 API 返回对应记录的稳定 UUID,字段值和当前数组顺序保持不变: - -### 体验推荐 - -| 顺序 | ID | 标题 | 需求关键词 | -| --- | --- | --- | --- | -| 0 | `waterfall-descent` | 悬崖瀑降 | 悬崖瀑降 | -| 1 | `cave-exploration` | 森林&探洞 | 地心探险 | - -`homeExperienceData.ts` 当前的 `normalizeHomeExperiences` 具有以下过渡行为: - -- `isActive === false` 的 API 项被过滤。 -- 缺少有效 `title` 的 API 项被过滤。 -- 其余项目按 `sortOrder` 升序排列,未传排序时保持 API 原顺序。 -- 文案和图片字段为空时使用当前 fallback 对应位置的值。 -- API 没有有效项目时返回当前 `homeExperienceMocks` 的副本。 - -这些规则用于前台接入过渡,不应替代服务端校验。后端返回正式记录后,Admin UI 应提交完整字段,避免依赖位置 fallback。 - -### 团队共创 - -| 顺序 | ID | 标题 | 需求关键词 | -| --- | --- | --- | --- | -| 0 | `wild-challenge` | 山野挑战,共创极境 | 户外团建 | -| 1 | `canyon-teamwork` | 峡谷溯溪,默契同行 | 峡谷团建 | -| 2 | `village-gathering` | 苗寨共聚,认识彼此 | 贵州团建 | - -### 极境视界 - -| 顺序 | ID | 标题 | 需求关键词 | -| --- | --- | --- | --- | -| 0 | `hundred-meter-descent` | 百米自降 | 悬崖瀑降 | -| 1 | `shilong-cave` | 石龙洞 | 地心探险 | -| 2 | `cliff-current` | 绝壁迎流 | 峡谷探险 | -| 3 | `canyon-streaming` | 峡谷溯溪 | 峡谷溯溪 | - -三个 mock 文件中的图片 URL 只作为初始化内容来源。正式数据应通过媒体上传接口获得最终 URL;不要把 mock 文件中的远程图片地址当成图片存储协议。 - -## 图片与素材 - -Admin UI 使用现有素材上传接口获取图片 URL: - -```http -POST /api/admin/media-assets/upload -``` - -建议首页内容使用 `group=home`,上传成功后将返回的 `url` 写入对应的 `image` 字段。接口只保存 URL,不接受 base64,也不创建 ProductImage 或商品图片关联。 - -## Admin UI 对接要求 - -1. 进入首页内容管理时请求三类首页卡片列表,并同时请求玩法分类和首页玩法推荐关联,按 `sortOrder` 渲染对应分组。 -2. 体验推荐表单维护 `badge`、`category`、`title`、`englishTitle`、`image` 和 `demandKeyword`。 -3. 团队共创表单维护 `tag`、`title`、`description`、`image`、`demandKeyword`、`detailSubtitle` 和 `detailParagraphs`;正文使用空行分隔多个段落。 -4. 极境视界表单维护 `title`、`image`、`images` 和 `demandKeyword`;详情图片按一行一个 URL 编辑。 -5. 三类首页卡片提供启用/停用、编辑、删除和上移/下移操作;玩法推荐提供启用/停用、移除和上移/下移操作;排序时提交完整 ID 列表。 -6. 每次变更成功后以接口返回的记录或列表更新本地状态,不直接假设本地修改已经保存。 -7. 删除前要求二次确认;删除成功后接受服务端返回的重新排序结果。 -8. 处理 `401`、`404`、`409`、`422` 和 `5xx`,保存、上传和排序进行中禁用重复提交。 -9. 不在首页表单中出现 Product ID、ProductImage ID、价格、库存、订单或预订字段。 -10. 预览点击行为使用 `demandKeyword`;该字段为空时禁止提交,而不是由前端猜测或拼接商品标识。 -11. 玩法推荐管理应先加载玩法分类,再加载首页关联;新增只能从未关联分类中选择,移除只取消关联,不能删除玩法分类。 - -建议的 Admin UI API 封装函数: - -```ts -getHomeExperiences(); -createHomeExperience(input: HomeExperienceCreate); -updateHomeExperience(experienceId: string, input: HomeExperiencePatch); -deleteHomeExperience(experienceId: string); -reorderHomeExperiences(itemIds: string[]); - -getHomeTeamBuildings(); -createHomeTeamBuilding(input: HomeTeamBuildingCreate); -updateHomeTeamBuilding(teamBuildingId: string, input: HomeTeamBuildingPatch); -deleteHomeTeamBuilding(teamBuildingId: string); -reorderHomeTeamBuildings(itemIds: string[]); - -getHomeWildArchives(); -createHomeWildArchive(input: HomeWildArchiveCreate); -updateHomeWildArchive(archiveId: string, input: HomeWildArchivePatch); -deleteHomeWildArchive(archiveId: string); -reorderHomeWildArchives(itemIds: string[]); - -getHomeWanfaRecommendations(); -createHomeWanfaRecommendation(input: HomeWanfaRecommendationCreate); -updateHomeWanfaRecommendation(recommendationId: string, input: HomeWanfaRecommendationPatch); -deleteHomeWanfaRecommendation(recommendationId: string); -reorderHomeWanfaRecommendations(itemIds: string[]); -``` - -当前首页的“查看更多”按钮跳转客片案例列表;首页案例卡片跳转 `/pages/wild-archives/detail?id={archiveId}`,不再跳转需求页。 - -## 前后台数据边界 - -- Admin API 返回启用和停用的完整记录,供管理端维护。 -- 未来 Public API 只返回已发布且启用的首页内容;Public API 的响应字段应与本契约的渲染字段兼容,但不应暴露 `createdAt`、`updatedAt` 等管理元数据。 -- MiniAPP 已在 `src/lib/api.ts`、`src/lib/types.ts` 和 `src/lib/store.ts` 接入 Public API;首页组件通过共享状态消费归一化后的三类内容,`experiences` 按关联分类展示玩法推荐。 -- 后端不得把 `demandKeyword` 解析成 Product 或订单关联;如需线路、详情或预订能力,应另立领域契约。 - -## 团队共创详情 - -团队共创首页卡片点击后跳转 MiniAPP `/pages/team-buildings/detail?id={teamBuildingId}`,详情页再按 ID 请求 Public API。首页接口只返回卡片摘要,不携带正文段落。 - -```http -GET /api/public/home/team-buildings/{teamBuildingId} -``` - -成功响应在首页摘要基础上增加详情字段: - -```ts -type PublicHomeTeamBuildingDetail = HomeTeamBuilding & { - detailSubtitle: string; - detailParagraphs: string[]; -}; -``` - -规则: - -- 仅返回 `isActive === true` 的团队共创;不存在或已停用返回 `404`。 -- `detailSubtitle` 为空时回退为 `description`。 -- `detailParagraphs` 为空时回退为 `[description]`。 -- MiniAPP 请求失败时按 `teamBuildingId` 使用本地模拟详情,并提示当前为模拟数据;找不到对应 fallback 时展示未找到状态。 -- 详情封面复用 `image` 字段,不新增独立 hero 图片或详情表。 - -## 后端落地边界 - -当前实现由 `WonderQ-Admin` 提供三张首页内容表、`HomeWanfaRecommendation` 关联表、`0017_home_content`、`0018_home_wanfa_recommendations`、`0019_home_wild_archive_images` 与 `0020_home_team_building_details` 迁移、schema、Admin/Public 路由、序列化和审计日志;由 `WonderQ-Admin-UI` 提供首页玩法推荐入口、分类关联、启停、移除、排序、客片详情图片和团队共创详情维护;由 `WonderQ-MiniAPP` 调用 `/api/public/home`、团队共创详情和客片案例列表/详情接口。 - -相关文档: - -- [Admin API 主契约](./admin-api-requirements.md) -- [页面模块配置契约](./module-config-api.md) -- [Public API 契约](./public-api.md) -- [体验推荐数据](../WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts) -- [团队共创数据](../WonderQ-MiniAPP/src/pages/home/components/homeTeamBuildingData.ts) -- [团队共创详情接口](./team-building-api.md) -- [极境视界数据](../WonderQ-MiniAPP/src/pages/home/components/homeWildArchivesData.ts) diff --git a/docs/integration-workflow.md b/docs/integration-workflow.md index 15f3b6b..c2ea63c 100644 --- a/docs/integration-workflow.md +++ b/docs/integration-workflow.md @@ -1,21 +1,32 @@ # WonderQ 三端联调流程 -本文档定义 `WonderQ-Admin` 后端、`WonderQ-Admin-UI` 管理前端、`WonderQ-MiniAPP` 前台的本地联调顺序和接口变更流程。 +本文档定义 `WonderQ-Admin` 后端、`WonderQ-Admin-UI-Vue` 管理前端和 `WonderQ-MiniAPP` 前台的本地启动、联调顺序与接口变更流程。 ## 三端职责 -| 端 | 目录 | 职责 | 主要契约 | -| ------------ | ------------------ | ----------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| 后端 API | `WonderQ-Admin` | 提供 Public API、Admin API、鉴权、数据库、迁移和 seed | `backend-api-service.md`、`backend-plan.md`、`public-api.md`、`admin-api-requirements.md` | -| 管理前端(Vue 迁移端) | `WonderQ-Admin-UI-Vue` | 新权限壳、动态菜单和业务切片迁移,生产路径 `/admin` | `admin-api-requirements.md`、`module-config-api.md` | -| 管理前端(React 过渡端) | `WonderQ-Admin-UI` | 旧管理端,过渡路径 `/admin-legacy` | `admin-api-requirements.md`、`module-config-api.md` | -| 前台 MiniAPP | `WonderQ-MiniAPP` | H5 与微信小程序前台展示、咨询和线索提交 | `public-api.md` | +| 端 | 目录 | 职责 | 主要契约 | +| --- | --- | --- | --- | +| 后端 API | `WonderQ-Admin` | 提供 Public API、Admin API、鉴权、数据库、迁移和 seed | `admin-api-requirements.md`、`public-api.md` | +| 管理前端 | `WonderQ-Admin-UI-Vue` | 管理登录、权限、站点模块、玩法、详情、管家和线索 | `admin-api-requirements.md`、`module-config-api.md`、各领域契约 | +| 前台 MiniAPP | `WonderQ-MiniAPP` | H5 与微信小程序展示、咨询和线索提交 | `public-api.md` | -三端所有 JSON 接口还必须遵守 [api-response-contract.md](./api-response-contract.md):成功业务数据位于 `data`,失败为 `data: null`,`code` 必须等于 HTTP 状态码。 +三端所有 JSON 接口还必须遵守 [`api-response-contract.md`](./api-response-contract.md):成功业务数据位于 `data`,失败时 `data: null`,`code` 等于 HTTP 状态码。 ## 本地启动顺序 -1. 确认 Docker Desktop 已运行,然后启动后端依赖和数据库迁移。 +### 1. 启动后端 + +首次安装: + +```powershell +Set-Location .\WonderQ-Admin +Copy-Item .env.example .env +python -m venv .venv +.\.venv\Scripts\Activate.ps1 +python -m pip install -r requirements.txt +``` + +日常启动: ```powershell Set-Location .\WonderQ-Admin @@ -25,28 +36,11 @@ python -m alembic upgrade head python -m uvicorn app.main:app --host 0.0.0.0 --port 4000 --reload ``` -首次缺少虚拟环境或依赖时,先在 `WonderQ-Admin` 目录执行 `python -m venv .venv` 和 `python -m pip install -r requirements.txt`。 -健康检查: +健康检查:`http://localhost:4000/health`。 -```text -http://localhost:4000/health -``` +首次空库初始化内容才执行 `python -m app.seed`;该命令可能重置站点、玩法和媒体内容,已有开发数据时不要重复执行。 -2. 启动管理前端。 - -```powershell -Set-Location .\WonderQ-Admin-UI -yarn install -yarn dev -``` - -默认访问: - -```text -http://localhost:5602 -``` - -Vue 迁移端独立启动: +### 2. 启动管理前端 ```powershell Set-Location .\WonderQ-Admin-UI-Vue @@ -54,11 +48,9 @@ yarn install yarn dev ``` -默认访问 `http://localhost:5604/admin/`。生产反向代理使用 `/admin`,旧 React 使用 `/admin-legacy`;两端共用 `/api` 和 HttpOnly Refresh Cookie。 +访问 `http://localhost:5604/admin/`。Vite `/api` 代理默认指向 `http://localhost:4000`;修改后端地址时,在 `.env.local` 设置 `VITE_API_PROXY_TARGET`。 -管理端优先通过 `VITE_API_BASE_URL` 或本地 Vite `/api` 代理访问 `http://localhost:4000`。 - -3. 启动 MiniAPP H5。 +### 3. 启动 MiniAPP H5 ```powershell Set-Location .\WonderQ-MiniAPP @@ -66,78 +58,40 @@ yarn install yarn dev ``` -默认访问: +访问 `http://localhost:5173`。微信小程序开发构建使用: -```text -http://localhost:5173 -``` - -微信小程序开发构建: - -```bash +```powershell yarn dev:mp-weixin ``` ## 联调检查清单 -后端启动后先检查: +后端启动后: -- `GET /health` 返回服务健康状态。 -- `GET /api/public/site-config` 返回前台所需数组字段。 -- `POST /api/admin/auth/login` 能返回 token 和 user。 -- 使用客户端或 curl 检查响应顶层包含数字 `code`、字符串 `msg` 和 `data`;不能继续接受旧的未包裹响应。 +- `GET /health` 返回健康状态。 +- `GET /api/public/site-config` 返回前台站点配置。 +- `POST /api/admin/auth/login` 返回统一包裹的登录结果。 +- 所有成功响应包含数字 `code`、`msg: "success"` 和 `data`;失败响应的 `data` 必须为 `null`。 -管理端联调重点: +管理端重点检查: -- 登录后请求头包含 `Authorization: Bearer `。 -- 站点配置、线索、发布和重置接口按 `admin-api-requirements.md` 返回。 -- 页面模块新增、更新、删除、排序按 `module-config-api.md` 返回 JSON。 +- 登录后请求带 `Authorization: Bearer `,Refresh Token 只通过 HttpOnly Cookie 传递。 +- 站点模块、玩法、详情、管家、线索、媒体、发布和重置接口按当前契约返回。 +- 新增、更新、删除和排序成功后,页面使用接口返回的数据更新状态,不自行生成 ID 或排序结果。 -MiniAPP 联调重点: +MiniAPP 重点检查: -- `site-config` 失败时仍能回退本地内容。 -- 首页模块按 Public API 字段渲染,不依赖后台未发布或未启用数据。 -- 线索提交调用 `POST /api/public/leads`,失败时显示可理解错误。 -- 首页、玩法、路线详情、管家、团队共创和客片案例请求都由公共 API 层解包 `data`,页面不重复解包。 - -## 用车需求三端联调 - -1. 在 Admin UI 首页模块的“用车服务”中维护板块简介、服务说明、优势和使用流程;在“万趣用车”中维护可选车型。 -2. 启动 MiniAPP 后点击车型卡片,确认进入 `/pages/vehicle-demand?vehicleOptionId={id}`;未登录时走现有微信登录并回跳原页面。 -3. 登录后提交用车需求,确认 `POST /api/public/leads` 携带 `leadType=vehicle`、`vehicleDemand` 和客户 JWT;后端从 JWT 写入 `customerId`,并归一化目的地、日期和人数摘要。 -4. Admin UI 进入“需求线索”,默认筛选 `leadType=vehicle`,确认联系人、日期、地点、人数、行李、车型快照和备注可查看,并可更新既有线索状态。 -5. 停止 Public API 时确认 MiniAPP 仍显示本地用车服务文案,并保留表单错误和重试提示;不展示实时库存、价格、支付或订单状态。 - -## 路线详情三端联调 - -路线详情使用独立 `DetailRecord`,不修改 `WanfaRoute` 表结构,也不建立商品、订单或预订关联。联调顺序如下: - -1. 执行数据库迁移,确认 `0021_detail_records` 已创建详情表并为已有路线生成基础记录;确认 `0022_opaque_ids` 已将历史语义 ID 转换为稳定 UUID,并同步玩法外键、详情 `key` 和审计引用。生产环境执行前按迁移规范单独确认。 -2. 在 Admin UI 进入“玩法”,编辑路线摘要、详情字段和联系管家,保存时先保存路线,再以路线 ID 作为 `DetailRecord.key` 创建或更新详情;顾问选项来自 `GET /api/admin/concierge/advisors`。 -3. 检查 `GET /api/public/wanfa/categories` 仍只返回路线摘要;检查 `GET /api/public/home` 的玩法推荐携带关联路线摘要。 -4. 在 MiniAPP 首页玩法推荐或玩法页点击路线,确认跳转 `/pages/detail/index?routeId={routeId}`,并请求 `GET /api/public/details/{routeId}`。 -5. 修改 Admin UI 的详情内容或联系管家后刷新 MiniAPP,确认标题、正文、费用说明、注意事项、画廊和顾问资料更新;停用或删除详情时确认 Public API 返回 `404`。 -6. 在管家页面停用或删除已关联顾问,再刷新路线详情,确认详情仍可读取且 `conciergeAdvisor` 为 `null`,联系入口隐藏;重新启用顾问后确认入口和弹窗资料恢复。 -6. 关闭后端接口,确认 MiniAPP 按路线 ID 展示本地网络图片和模拟文案,并提示当前为模拟数据;未知路线展示未找到和重试状态。 - -## 稳定 ID 联调检查 - -1. 执行 `0022_opaque_ids` 后,检查首页、玩法、管家、团队共创和客片案例列表返回的 `id` 均为 UUID 字符串。 -2. 从列表复制一个 ID 请求对应详情、排序或删除接口,确认同一 ID 可连续复用,不能每次响应变化。 -3. 检查首页玩法推荐的 `categoryId`、路线 `id` 与 `GET /api/public/details/{key}` 的 `key` 关联正确。 -4. MiniAPP 本地 fallback 的语义 ID 只在接口失败时使用,不得覆盖接口成功返回的 UUID。 - -路线详情页不包含在线订阅、收藏、预订或订单动作;联系管家入口由 `conciergeAdvisor` 是否有效决定。字段和错误约定以 `detail-api.md`、`public-api.md` 和 `concierge-api.md` 为准。 +- Public API 失败时显示错误、重试或空态,并按现有本地 fallback 规则处理。 +- 首页、玩法、路线详情、管家、团队共创和客片案例都由公共 API 层解包 `data`,页面不重复解包。 +- `POST /api/public/leads` 的用车需求携带客户 JWT;后端从 JWT 写入 `customerId`,客户端不提交该字段。 ## 接口变更流程 -1. 先更新契约文档。 - - 响应包裹、错误结构或状态码变化:更新 `api-response-contract.md`。 - - Public API 变更:更新 `public-api.md`。 - - Admin API 变更:更新 `admin-api-requirements.md`。 -2. 后端实现或调整接口,并补充对应验证。 -3. 管理前端或 MiniAPP 按契约调整调用和类型。 -4. 三端分别运行对应验证命令。 +1. 响应包裹、错误结构或 ID 规则变化:先更新 `api-response-contract.md`。 +2. Admin API 路径、字段、权限或状态码变化:更新 `admin-api-requirements.md` 或对应领域契约。 +3. 站点模块字段、排序、单例或删除规则变化:更新 `module-config-api.md`。 +4. Public API 变化:更新 `public-api.md`,再同步后端序列化和 MiniAPP 类型。 +5. 完成后端实现、前端调用和影响范围内的测试;不得在流程文档中复制接口字段。 ## 验证命令 @@ -146,13 +100,13 @@ MiniAPP 联调重点: ```powershell Set-Location .\WonderQ-Admin python -m pytest -python -m alembic upgrade head ``` 管理前端: ```powershell -Set-Location .\WonderQ-Admin-UI +Set-Location .\WonderQ-Admin-UI-Vue +yarn test yarn build ``` @@ -160,13 +114,13 @@ MiniAPP: ```powershell Set-Location .\WonderQ-MiniAPP +yarn test yarn build:h5 yarn build:mp-weixin -yarn test ``` ## 安全边界 - 不读取、展示或提交 `.env`、`.env.local` 和生产环境变量值。 -- 文档示例只写变量名,不写真实 Token、JWT secret、客服链接、企业 ID 或手机号。 -- `python -m app.seed`、内容重置、发布、回滚和数据库迁移都属于高风险动作,生产环境执行前必须明确确认。 +- 文档示例只写占位值,不写真实 Token、JWT secret、客服链接、企业 ID 或手机号。 +- `python -m app.seed`、内容重置、发布和数据库迁移属于高风险动作,生产环境执行前必须明确确认。 diff --git a/docs/module-config-api.md b/docs/module-config-api.md index 1dfd515..f98e03e 100644 --- a/docs/module-config-api.md +++ b/docs/module-config-api.md @@ -1,6 +1,6 @@ # 页面模块配置 API 契约 -本文档补充 `WonderQ-Admin` 和 `WonderQ-Admin-UI` 对站点页面模块的维护约定。接口路径保持现有实现不变,所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md)。 +本文档补充 `WonderQ-Admin` 和 `WonderQ-Admin-UI-Vue` 对站点页面模块的维护约定。接口路径保持现有实现不变,所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md)。 ## 接口清单 diff --git a/docs/public-api.md b/docs/public-api.md index 9884748..c28b12d 100644 --- a/docs/public-api.md +++ b/docs/public-api.md @@ -114,7 +114,7 @@ type PublicWanfaRoute = { }; ``` -MiniAPP 使用 `demandKeyword` 作为需求页预填关键词;接口失败或响应为空时,玩法页展示对应的错误、重试或空态,不再读取 `playData.ts` 模拟数据。 +MiniAPP 使用 `demandKeyword` 作为需求页预填关键词;接口失败或响应为空时,玩法页展示对应的错误、重试或空态。 ### 路线详情 diff --git a/docs/superpowers/plans/2026-08-26-admin-vue-formalization.md b/docs/superpowers/plans/2026-08-26-admin-vue-formalization.md deleted file mode 100644 index 3e75494..0000000 --- a/docs/superpowers/plans/2026-08-26-admin-vue-formalization.md +++ /dev/null @@ -1,468 +0,0 @@ -# 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` 收到以下请求: - -```ts -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` 中提供以下函数: - -```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` 统一解包响应。 - -- [ ] **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` 验证: - -```python -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 配置** - -统一配置明确以下映射: - -```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`、测试输出和业务矩阵记录作为检查点。 diff --git a/docs/superpowers/specs/2026-08-26-admin-vue-formalization-design.md b/docs/superpowers/specs/2026-08-26-admin-vue-formalization-design.md deleted file mode 100644 index c538dff..0000000 --- a/docs/superpowers/specs/2026-08-26-admin-vue-formalization-design.md +++ /dev/null @@ -1,220 +0,0 @@ -# WonderQ Admin Vue 正式化与三端联调设计规格 - -## 1. 文档状态 - -- 日期:2026-08-26 -- 状态:待用户评审 -- 适用范围:WonderQ-Admin、WonderQ-Admin-UI-Vue、WonderQ-Admin-UI、WonderQ-MiniAPP 的本阶段正式化工作 -- 目标:在保留 React 过渡端的前提下,补齐 Vue 管理端未覆盖的首页 CRUD,完成管理员会话踢下线、显式 403、五级数据范围与正式反向代理验收。 - -本阶段不删除 React,不切换生产默认入口,不处理 OSS/媒体上传验收。媒体上传仍作为最后一项验收内容。 - -## 2. 当前基线与缺口 - -现有后端已经提供以下首页管理接口,但 Vue 管理端尚未接入: - -| 功能组 | 接口前缀 | Vue 现状 | React 现状 | -| --- | --- | --- | --- | -| 体验内容 | `/api/admin/home/experiences` | 未接入 | 已有列表、创建、编辑、删除、排序 | -| 团建内容 | `/api/admin/home/team-buildings` | 未接入 | 已有列表、创建、编辑、删除、排序 | -| 野奢档案 | `/api/admin/home/wild-archives` | 未接入 | 已有列表、创建、编辑、删除、排序 | -| 玩法推荐 | `/api/admin/home/play-recommendations` | 未接入 | 已有列表、创建、编辑、删除、排序 | - -现有 Vue 首页只覆盖站点配置模块,不能据此判断旧 React 业务已经全部迁移。 - -当前还存在以下验收缺口: - -1. Redis 会话只支持当前会话退出,没有按用户撤销全部会话的管理能力。 -2. Vue 路由权限失败会落到 Not Found,API 客户端也没有统一的 403 反馈路径。 -3. 五级数据范围已有后端基础模型与角色编辑入口,但缺少覆盖五种范围的完整自动化矩阵和非超级管理员真实验收记录。 -4. React 与 Vue 目前没有统一的生产反向代理入口;React 仍按根路径构建,不能直接挂载到 `/admin-legacy/`。 - -## 3. 目标架构 - -```text -浏览器 - ├─ /admin/ -> Vue3 静态资源,正式管理端 - ├─ /admin-legacy/ -> React 静态资源,过渡管理端 - └─ /api/ -> FastAPI,保持现有 Public API 与 Admin API - -FastAPI - ├─ PostgreSQL:业务数据、RBAC、审计 - └─ Redis:管理员会话、会话索引、权限菜单缓存、限流 -``` - -Vue 继续使用预注册组件加载动态菜单,不允许由后端返回任意前端组件路径。React 保持原有 API 响应格式和登录字段兼容,直到正式删除闸门满足并得到用户确认。 - -## 4. 分阶段实施设计 - -### 阶段一:四组首页 CRUD 垂直切片 - -每个功能组都同时完成 API 客户端、Vue 页面、权限按钮、错误态与测试,不先做一个只读总览页。 - -#### 4.1 API 与数据契约 - -在 Vue API 层增加四组资源的类型和请求函数,沿用后端既有路径、字段、`{code,msg,data}` 包裹和排序语义: - -- `GET /api/admin/home/experiences` -- `POST /api/admin/home/experiences` -- `PATCH /api/admin/home/experiences/{id}` -- `DELETE /api/admin/home/experiences/{id}` -- `POST /api/admin/home/experiences/reorder` -- `GET/POST/PATCH/DELETE/POST /reorder` 对应 `team-buildings` -- `GET/POST/PATCH/DELETE/POST /reorder` 对应 `wild-archives` -- `GET/POST/PATCH/DELETE/POST /reorder` 对应 `play-recommendations` - -实际字段以现有后端 schema 和 `docs/home-api.md` 为准;如果发现契约缺失,先补文档,再同步 TypeScript 类型和后端测试,不擅自改动旧字段名。 - -#### 4.2 Vue 页面拆分 - -不继续把四组业务逻辑堆进 `HomePage.vue`。按资源拆出可维护的页面或业务组件,并复用列表、编辑表单、排序操作、确认弹窗和权限按钮组件。每组至少覆盖: - -- 列表加载、空态、错误态、重试; -- 新建、编辑、删除及提交中禁用态; -- 排序保存与失败回滚提示; -- 图片/链接/状态等现有字段的校验和展示; -- `home:*` 权限码控制按钮显示与接口 403 反馈; -- 桌面端和窄屏布局。 - -验收以 React 页面和后端路由为基线逐项核对,不因 Vue 页面视觉重排而遗漏业务字段或操作。 - -### 阶段二:踢下线 - -#### 4.3 Redis 会话索引 - -在现有会话存储基础上增加按管理员用户索引的 Redis Set,索引只保存随机会话 ID,不保存明文 Refresh Token。创建、刷新轮换、主动退出、过期清理和批量撤销都必须维护索引;Redis 不可用时不得绕过会话校验。 - -会话存储协议增加: - -- 按用户列出会话 ID; -- 按用户撤销全部会话; -- 清理已不存在或已撤销的索引项。 - -不新增数据库表,不把 Refresh Token 写入 PostgreSQL,不在日志中记录 Token 或其完整哈希。 - -#### 4.4 管理接口与 UI - -新增: - -`POST /api/admin/system/users/{user_id}/sessions/revoke` - -要求: - -- 权限码:`system:user:update`; -- 只能由具备该按钮权限且通过数据范围校验的管理员调用; -- 默认撤销目标用户的全部管理员会话; -- 禁止通过该接口撤销当前操作会话,避免误操作造成管理流程中断; -- 目标用户不存在、无数据范围权限或 Redis 不可用时返回明确错误,不返回内部异常; -- 写入审计日志,记录操作者、目标用户、撤销数量和结果,不记录 Token; -- Vue 用户管理页提供按钮、确认提示、提交中状态和成功/失败反馈; -- 被踢出的浏览器在下一次 API 请求或刷新时收到 401,并回到登录页。 - -### 阶段三:显式 403 - -新增 Vue `ForbiddenPage.vue` 和 `/forbidden` 路由。路由守卫在用户已登录但无菜单/页面权限时进入 403 页面;未登录仍进入登录页,未知路径继续进入 Not Found。 - -API 客户端对 403 不刷新 Token、不清除有效登录态,统一转换为可识别的权限错误。页面层显示“无权限”与返回上一页/首页操作;按钮级权限仍由现有权限指令控制,但后端 403 始终视为最终边界。 - -至少验证: - -- 无页面权限访问页面 URL; -- 有页面权限但无按钮权限; -- 直接调用被禁止的 Admin API; -- 401 与 403 不混淆; -- 刷新后权限缓存和动态路由行为一致。 - -### 阶段四:五级数据范围 - -保留现有范围编码和角色编辑能力,补充自动化测试和真实验收,不另造一套权限模型。五种范围必须分别验证: - -1. 全部:可见全部归属资源; -2. 当前部门:只见当前部门资源; -3. 当前部门及子部门:可见当前部门和后代部门资源; -4. 自定义部门:只见角色明确配置的部门资源; -5. 本人:只见创建人/负责人为自己的资源。 - -测试使用至少两个部门、一个子部门、两个管理员和跨部门资源,覆盖列表、详情、更新、删除/状态写入等接口。每个切片都检查数据范围过滤在服务端生效,不能只依赖前端隐藏行。 - -权限或部门变更后必须使权限菜单缓存和相关数据范围缓存失效。Redis 不可用时拒绝需要会话或权限缓存的请求,不降级为全量数据。 - -### 阶段五:Docker 与正式反向代理 - -新增部署级 Nginx 配置和 Vue 静态镜像入口,统一提供: - -- `/admin/`:Vue 正式管理端,使用 Vue 的 `/admin/` base; -- `/admin-legacy/`:React 过渡端,React 构建必须使用 `/admin-legacy/` base; -- `/api/`:反向代理到 FastAPI; -- 两个前端入口都支持 SPA history fallback,但不能把 `/api/` 错误回退到 `index.html`。 - -React 通过构建环境或独立 legacy 构建配置注入 base,不改变现有开发端口和 API 合同。正式代理切换采用可回滚配置:先同时发布两个入口,再切换默认链接到 `/admin/`;切换期间 `/admin-legacy/` 保持可访问。 - -Docker 验收包括前端镜像构建、PostgreSQL/Redis/API 启动、Alembic 升级、健康检查、两个管理入口、API 代理和刷新深链路。生产环境变量、密钥和现有部署配置不写入仓库或文档示例。 - -## 5. 文档同步 - -实现前或接口确认时同步以下契约文档: - -- `docs/home-api.md`:四组首页 CRUD 的 Vue 对接字段、排序和错误语义; -- `docs/admin-api-requirements.md`:踢下线、403、数据范围和响应契约; -- `docs/integration-workflow.md`:三端启动、正式代理和验收顺序; -- `docs/admin-business-function-matrix.md`:每个垂直切片的迁移、测试和人工验收记录。 - -不修改 `docs/public-api.md`,除非实际发现 Public API 字段发生兼容性变化。MiniAPP 本阶段只做契约回归检查,不整体重写。 - -## 6. 测试与验收闸门 - -采用测试先行和垂直切片交付。每一片先增加会失败的测试,再写实现,最后运行与影响范围匹配的验证。 - -### 后端 - -- 四组 CRUD 的鉴权、参数校验、响应格式、排序和审计测试; -- 会话创建、刷新轮换、退出、按用户踢下线、重复撤销、Redis 不可用测试; -- 401/403 区分测试; -- 五级数据范围的跨部门/子部门/本人矩阵测试; -- 旧 React 登录字段、Public API、既有 Admin API 兼容测试; -- Alembic 空库初始化、现有数据库升级与 Docker 健康检查。 - -### Vue 与 React - -- Vue TypeScript 检查、单元测试和生产构建; -- 四组首页 CRUD 的列表、表单、删除、排序、错误态和按钮权限; -- Vue 刷新、退出、踢下线、403、动态菜单; -- React 原有构建和登录刷新回归; -- 正式代理下 `/admin/`、`/admin-legacy/`、深链刷新和 `/api/` 转发。 - -### 人工验收顺序 - -1. 四组首页 CRUD; -2. 踢下线与 403; -3. 五级数据范围; -4. Docker/Redis/正式反向代理; -5. Public API 与 MiniAPP 回归; -6. 最后单独验收 OSS/媒体上传。 - -每个阶段完成后记录实际命令、结果、失败项和人工操作证据。任何一项未通过都不能把 Vue 标记为正式替代端。 - -## 7. 回滚与删除闸门 - -- 四组业务切片异常时,可保持 React 入口继续承载原页面,Vue 只回滚对应菜单/路由,不回滚数据库既有业务数据。 -- 反向代理异常时保留 `/admin-legacy/`,恢复默认入口到 legacy 配置即可回退。 -- 会话索引异常时拒绝管理请求并修复 Redis 索引,不允许切换到绕过会话校验的降级模式。 -- 本阶段不删除 React 源码、依赖、构建入口或 legacy 代理。 - -只有同时满足以下条件,并由用户明确确认,才启动 React 删除阶段: - -1. Vue 覆盖功能矩阵全部业务能力; -2. Public API 和现有 Admin API 无回归; -3. 角色、菜单、按钮权限、五级数据范围测试通过; -4. 登录刷新、退出、踢下线、Redis 异常通过; -5. Docker 和正式反向代理通过; -6. OSS/媒体上传通过; -7. 用户完成人工验收并明确确认删除旧 React。 - -## 8. 非目标 - -- 不整体重写 MiniAPP; -- 不引入 RuoYi 的代码生成、定时任务、监控、表单设计器等非核心模块; -- 不直接删除或重置业务数据; -- 不恢复 Alembic 已清理的无用 schema; -- 不把媒体上传提前到本阶段的首页 CRUD 验收之前; -- 不在本规格确认前改动生产环境变量、密钥和部署数据。 diff --git a/docs/team-building-api.md b/docs/team-building-api.md deleted file mode 100644 index 1d67bd7..0000000 --- a/docs/team-building-api.md +++ /dev/null @@ -1,161 +0,0 @@ -# 团队共创详情接口契约 - -> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`、`WonderQ-MiniAPP`。 -> 目标:复用 `HomeTeamBuilding` 表,维护首页团队共创卡片和沉浸式详情页内容。 - -所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。 - -团队共创记录的正式 `id` 为服务端生成的稳定 UUID 字符串;MiniAPP 本地 fallback 可以保留语义 ID,但接口成功后必须以 UUID 作为详情请求参数。 - -## 领域边界 - -- 团队共创卡片和详情共用一条 `HomeTeamBuilding` 记录。 -- 详情封面复用 `image`,不新增独立详情表或第二张 hero 图片。 -- `detailSubtitle` 是详情首屏副标题。 -- `detailParagraphs` 是按阅读顺序保存的正文段落数组。 -- Admin UI 使用正文 textarea 的“空行分隔段落”约定;后端保存前会清洗首尾空白和空段落。 -- 首页卡片仍保留 `demandKeyword`,但点击行为改为详情页;该字段仅作为其他需求入口的普通文案,不建立商品或订单外键。 - -## 数据类型 - -```ts -type HomeTeamBuilding = { - id: string; - tag: string; - title: string; - description: string; - image: string; - demandKeyword: string; - detailSubtitle: string; - detailParagraphs: string[]; - isActive: boolean; - sortOrder: number; - createdAt: string; - updatedAt: string; -}; - -type HomeTeamBuildingCreate = { - tag: string; - title: string; - description: string; - image: string; - demandKeyword: string; - detailSubtitle?: string; - detailParagraphs?: string[]; - isActive?: boolean; - sortOrder?: number; -}; - -type HomeTeamBuildingPatch = Partial; -``` - -兼容规则:旧客户端不提交详情字段时,后端使用模型默认值;迁移 `0020_home_team_building_details` 会把已有记录的副标题回填为 `description`,正文回填为 `[description]`。Public 序列化时仍会对空值做相同 fallback。 - -## Admin API - -前缀为 `/api/admin`,需要 `Authorization: Bearer `。既有列表、创建、编辑、删除和排序路径保持不变: - -| 方法 | 路径 | 用途 | -| --- | --- | --- | -| `GET` | `/api/admin/home/team-buildings` | 获取全部团队共创记录 | -| `POST` | `/api/admin/home/team-buildings` | 新增团队共创记录 | -| `PATCH` | `/api/admin/home/team-buildings/{teamBuildingId}` | 编辑团队共创记录和详情字段 | -| `DELETE` | `/api/admin/home/team-buildings/{teamBuildingId}` | 删除团队共创记录 | -| `PATCH` | `/api/admin/home/team-buildings/reorder` | 调整团队共创顺序 | - -创建或编辑请求示例: - -```json -{ - "tag": "户外挑战", - "title": "山野挑战,共创极境", - "description": "洞穴、瀑降与协作,适合 10-30 人。", - "image": "https://example.test/assets/team-building.jpg", - "demandKeyword": "户外团建", - "detailSubtitle": "越过山丘,向来处去", - "detailParagraphs": [ - "真正的贵州,从未被写进流水线的攻略里。", - "把会议室换成山野,让团队重新认识彼此。" - ], - "isActive": true -} -``` - -约束: - -- 文本字段去除首尾空白后不得为空;详情副标题最大 160 字,正文最多 30 段。 -- `detailParagraphs` 保存时过滤空段落;编辑接口显式传空正文会返回 `422`。 -- 图片只接受 HTTP(S) URL;排序值为非负整数。 -- 列表接口返回启用和停用记录,按 `sortOrder` 升序;删除和排序继续写审计日志。 -- 迁移只新增两列,不改变旧字段和现有接口路径;本次实现不自动执行迁移。 - -## Public API - -### 首页摘要 - -```http -GET /api/public/home -``` - -首页响应中的 `teamBuildings` 只返回卡片摘要: - -```ts -type PublicHomeTeamBuildingSummary = { - id: string; - tag: string; - title: string; - description: string; - image: string; - demandKeyword: string; -}; -``` - -### 详情 - -```http -GET /api/public/home/team-buildings/{teamBuildingId} -``` - -无需鉴权。成功响应: - -```ts -type PublicHomeTeamBuildingDetail = PublicHomeTeamBuildingSummary & { - detailSubtitle: string; - detailParagraphs: string[]; -}; -``` - -上面的 `PublicHomeTeamBuildingDetail` 是统一响应 `data` 内的业务对象,不是完整 HTTP 响应包。失败响应使用 `code`、`msg`、`data: null`,可选 `errorCode` 和 `details`。 - -错误响应: - -| 状态码 | 场景 | -| --- | --- | -| `404` | ID 不存在或团队共创已停用 | -| `5xx` | 服务端异常;MiniAPP 进入本地 fallback | - -响应中的 `detailSubtitle` 为空时返回 `description`;`detailParagraphs` 为空时返回 `[description]`。Public API 不返回管理端状态、排序和审计字段。 - -## MiniAPP 联调约定 - -1. 首页通过 `/api/public/home` 加载团队共创卡片。 -2. `openTeamBuilding(item)` 调用 `goTeamBuildingDetail(item.id)`,跳转 `/pages/team-buildings/detail?id={teamBuildingId}`。 -3. 详情页调用 `fetchPublicTeamBuildingDetail(teamBuildingId)`,成功后通过 `normalizeHomeTeamBuildingDetail` 归一化。 -4. 请求失败时按 ID 查找 `homeTeamBuildingFallback`;命中则展示模拟网络图片、标题、副标题和正文,并提示“接口暂不可用,当前展示模拟数据”。 -5. 无 ID、ID 不存在且无 fallback 时展示未找到状态,并提供返回首页操作。 -6. 页面必须覆盖 loading、接口失败、空正文和未找到状态;详情布局使用 TailwindCSS,不新增页面级自定义样式。 - -## 三端联调顺序 - -1. 在 `WonderQ-Admin` 执行 `python -m alembic upgrade head`,仅在确认目标数据库后执行。 -2. 启动后端并验证 `GET /health`。 -3. 在 Admin UI 创建或编辑团队共创,填写详情副标题和正文;正文 textarea 使用空行分段。 -4. 验证 Admin API 返回详情字段,Public 首页只返回摘要。 -5. 在 MiniAPP 首页点击团队共创卡片,确认 URL 携带正确 ID。 -6. 验证详情接口成功展示最新内容;停止后端后确认按 ID fallback 并显示提示。 - -## 变更文件 - -- 后端:`WonderQ-Admin/app/models.py`、`app/schemas.py`、`app/serializers.py`、`app/routers/public.py`、`alembic/versions/0020_home_team_building_details.py`。 -- 管理端:`WonderQ-Admin-UI/src/api.ts`、`src/pages/structure/HomeContentPage.tsx`、`src/components/admin/HomeContentEditor.tsx`、`src/components/admin/HomeContentCardRail.tsx`。 -- 前台:`WonderQ-MiniAPP/src/pages/home/components/homeTeamBuildingData.ts`、`src/lib/types.ts`、`src/lib/api.ts`、`src/lib/navigation.ts`、`src/pages/home/index.vue`、`src/pages/team-buildings/detail.vue`、`src/pages.json`。 diff --git a/docs/wanfa-api.md b/docs/wanfa-api.md index 479275f..2094277 100644 --- a/docs/wanfa-api.md +++ b/docs/wanfa-api.md @@ -1,16 +1,16 @@ # 玩法管理 Admin API -> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。 +> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI-Vue`。 > -> 状态:已实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/play/components/playData.ts` 定义玩法分类和路线的数据结构,以及管理端需要的查询、维护和排序接口。它不是 MiniAPP Public API 文档。 +> 状态:已实现契约。本文件定义玩法分类和路线的 Admin API,不替代 MiniAPP Public API 文档。 -当前实现:`WonderQ-Admin` 通过迁移 `0015_wanfa` 创建 `WanfaCategory`、`WanfaRoute` 表并导入初始数据;`0022_opaque_ids` 将历史语义 ID 转换为稳定 UUID;`WonderQ-Admin-UI` 已接入分类和路线的查询、新增、编辑、删除及排序操作。 +当前实现:`WonderQ-Admin` 提供 `WanfaCategory`、`WanfaRoute` 的查询、新增、编辑、删除及排序接口;`WonderQ-Admin-UI-Vue` 已接入对应管理操作。 所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。 路线接口只维护分类和路线摘要字段。路线详情不写入 `WanfaRoute`,由独立 `DetailRecord` 通过 `docs/detail-api.md` 管理,详情记录的 `key` 等于路线 ID。首页玩法推荐和玩法页路线点击后统一跳转 `/pages/detail/index?routeId={route.id}`;无关联路线时才回退到需求页。 -分类和路线的正式 `id` 均为服务端生成的稳定 UUID 字符串。下方本地数据映射中的语义 ID 只用于 MiniAPP fallback 和迁移前数据识别,不作为正式接口响应 ID;不要在序列化时临时随机生成 ID。 +分类和路线的正式 `id` 均为服务端生成的稳定 UUID 字符串;不要在序列化时临时生成 ID,也不要用标题或数组下标代替 ID。 ## 领域边界 @@ -52,7 +52,7 @@ API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。 - 所有管理接口需要 `Authorization: Bearer `。 - 创建、编辑、删除和排序成功后写入审计日志,再提交事务。 - 变更接口返回最新变更对象;排序接口返回排序后的 `items`。 -- ID 由后端生成并作为非空字符串返回。初始化迁移时应优先保留 `playData.ts` 中已有的稳定 ID。 +- ID 由后端生成并作为稳定 UUID 返回;管理端必须保存并复用接口返回的 ID。 - 分类和路线的数组顺序就是管理端和前台的展示顺序,接口不要求前端依赖 `sortOrder` 字段。 - 空集合返回 `[]`,不能返回 `null` 或省略字段。 - 成功结果放入 `data`;失败返回数字 `code`、`msg`、`data: null`,可选 `errorCode` 和 `details`。 @@ -60,7 +60,7 @@ API 前缀为 `/api/admin`,除登录接口外均需要后台 JWT。 ## 数据类型 -以下类型与 `playData.ts` 保持字段兼容。管理端接口不应向前台模型强制增加商品 ID、预订 ID 或数据库关联字段。 +以下类型与 Public API 的前台展示模型保持字段兼容。管理端接口不应向前台模型增加商品 ID、预订 ID 或数据库关联字段。 ```ts type WanfaConfig = { @@ -299,20 +299,7 @@ Content-Type: application/json 分类不存在返回 `404`,业务码为 `WANFA_CATEGORY_NOT_FOUND`;路线 ID 不完整、重复或不属于该分类时返回 `400`,业务码为 `WANFA_ROUTE_REORDER_INVALID`。 -## 当前本地数据映射 - -迁移初始数据时,分类和路线应按以下 ID 与顺序导入: - -| 分类 ID | 分类名称 | 路线 ID(当前顺序) | -| --- | --- | --- | -| `family-route` | 亲子路线 | `family-water`、`family-village`、`family-grassland` | -| `photo-route` | 旅拍路线 | `miao-photo`、`peak-photo`、`terrace-photo` | -| `healing-route` | 疗愈路线 | `mountain-healing`、`hot-spring-healing`、`river-healing` | -| `team-building` | 团建 | `team-challenge`、`team-stream`、`team-culture` | -| `guizhou-panorama` | 贵州全景 | `classic-panorama`、`mountain-panorama`、`wild-panorama` | -| `private-custom` | 私人定制 | `private-family`、`private-business`、`private-wild` | - -图片别名的当前解析规则位于 `playData.ts` 的 `routeImageByAsset` 和 `resolveRouteImage`:已配置别名解析为完整远程 URL,完整 `http` URL 直接使用,其他值按 `/assets/guizhou/{image}.jpg` 解析。Admin API 建议保存最终 URL,Admin UI 通过现有媒体上传接口获取 URL 后再提交 `image`。 +图片字段保存最终 URL;管理端通过媒体上传接口获取 URL 后再提交 `image`。本地 fallback 数据不属于 Admin API 契约。 ## Admin UI 对接要求 @@ -342,10 +329,9 @@ reorderWanfaRoutes(categoryId: string, itemIds: string[]); ## 后端落地边界 -玩法数据由 `WonderQ-Admin` 的 `WanfaCategory`、`WanfaRoute` ORM 模型和 Admin API 负责持久化;`WonderQ-Admin-UI` 负责 API 类型、请求封装、表单、删除确认和排序交互。`playData.ts` 仅作为迁移初始数据来源,不能视为数据库或 API 数据。 +玩法数据由 `WonderQ-Admin` 的 `WanfaCategory`、`WanfaRoute` ORM 模型和 Admin API 负责持久化;`WonderQ-Admin-UI-Vue` 负责 API 类型、请求封装、表单、删除确认和排序交互。本地 fallback 数据不能视为数据库或 API 数据。 相关文档: - [Admin API 主契约](./admin-api-requirements.md) - [页面模块配置契约](./module-config-api.md) -- [玩法本地数据](../WonderQ-MiniAPP/src/pages/play/components/playData.ts) diff --git a/docs/wild-archives-api.md b/docs/wild-archives-api.md deleted file mode 100644 index 7cf5714..0000000 --- a/docs/wild-archives-api.md +++ /dev/null @@ -1,159 +0,0 @@ -# 客片案例 API 契约 - -> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`、`WonderQ-MiniAPP`。 -> -> 目标:维护“极境视界”首页卡片,并提供客片案例更多列表和案例详情页面。 - -所有 JSON 响应遵循 [三端统一 API 响应契约](./api-response-contract.md),成功业务对象位于 `data`,失败时 `data` 为 `null`。 - -客片案例记录的正式 `id` 为服务端生成的稳定 UUID 字符串;列表、详情和删除使用同一 ID。本地 fallback 的语义 ID 仅用于接口失败时定位模拟内容。 - -## 领域边界 - -客片案例是首页内容领域的一类展示内容,不属于商品、Product、ProductImage、订单或预订领域。 - -- `image` 是首页卡片封面。 -- `images` 是详情页纵向展示的图片 URL 列表。 -- `demandKeyword` 仅为兼容已有需求入口的普通文案,本次案例卡片和详情不跳转需求页。 -- 图片只保存 URL,不保存 base64,不建立商品图片关联。 - -## 数据类型 - -```ts -type WildArchiveRecord = { - id: string; - title: string; - image: string; - images: string[]; - demandKeyword: string; - isActive: boolean; - sortOrder: number; - createdAt: string; - updatedAt: string; -}; - -type PublicWildArchiveSummary = { - id: string; - title: string; - image: string; - demandKeyword: string; - photoCount: number; -}; - -type PublicWildArchiveDetail = PublicWildArchiveSummary & { - images: string[]; -}; - -type WildArchiveCreate = { - title: string; - image: string; - images?: string[]; - demandKeyword: string; - isActive?: boolean; - sortOrder?: number; -}; - -type WildArchivePatch = Partial; -``` - -`images` 未传时服务端兼容为 `[image]`;传入后至少包含一张合法 `http` 或 `https` 图片 URL,最多 30 张。`photoCount` 始终由服务端根据详情图片列表返回。 - -## Admin API - -所有 Admin API 需要 `Authorization: Bearer `。 - -| 方法 | 路径 | 用途 | -| --- | --- | --- | -| `GET` | `/api/admin/home/wild-archives` | 获取全部案例,包含启用和停用记录及详情图片 | -| `POST` | `/api/admin/home/wild-archives` | 新增案例 | -| `PATCH` | `/api/admin/home/wild-archives/{archiveId}` | 修改标题、封面、详情图片、需求关键词、启用状态或排序 | -| `DELETE` | `/api/admin/home/wild-archives/{archiveId}` | 删除案例 | -| `PATCH` | `/api/admin/home/wild-archives/reorder` | 按完整 ID 列表调整顺序 | - -新增示例: - -```json -{ - "title": "石龙洞——客片案例", - "image": "https://example.test/cover.jpg", - "images": [ - "https://example.test/cover.jpg", - "https://example.test/cave-1.jpg" - ], - "demandKeyword": "地心探险", - "isActive": true -} -``` - -Admin UI 的“极境视界”表单必须同时维护封面和详情图片,详情图片使用一行一个 URL;删除需要二次确认,排序提交完整 `itemIds`。 - -## Public API - -### 获取更多列表 - -```http -GET /api/public/home/wild-archives -``` - -无需鉴权。只返回 `isActive === true` 的记录,按 `sortOrder` 升序: - -```json -{ - "code": 200, - "msg": "success", - "data": { - "items": [ - { - "id": "shilong-cave", - "title": "石龙洞——客片案例", - "image": "https://example.test/cover.jpg", - "demandKeyword": "地心探险", - "photoCount": 5 - } - ] - } -} -``` - -### 获取案例详情 - -```http -GET /api/public/home/wild-archives/{archiveId} -``` - -无需鉴权。停用或不存在的案例返回 `404`,成功返回: - -```json -{ - "code": 200, - "msg": "success", - "data": { - "id": "shilong-cave", - "title": "石龙洞——客片案例", - "image": "https://example.test/cover.jpg", - "demandKeyword": "地心探险", - "photoCount": 5, - "images": [ - "https://example.test/cover.jpg", - "https://example.test/cave-1.jpg" - ] - } -} -``` - -## MiniAPP 页面约定 - -- `pages/wild-archives/index`:客片案例更多列表。 -- `pages/wild-archives/detail?id={archiveId}`:客片案例详情。 -- 首页“极境视界”卡片调用详情页;“查看更多”调用列表页。 -- 页面数据层优先调用 Public API;接口失败时列表和详情允许使用 `homeWildArchivesData.ts` 中的网络图片 mock 数据。 -- 详情页按 `images` 顺序纵向展示,保留图片原始比例;空列表时展示空态,不渲染破损图片。 - -## 迁移与验证 - -- 数据模型:`HomeWildArchive.images` 使用 JSONB 保存 URL 数组。 -- 迁移:`WonderQ-Admin/alembic/versions/0019_home_wild_archive_images.py`。 -- 迁移会将历史 `image` 自动转为第一张详情图片,保证旧数据可打开详情页。 -- 三端联调顺序:执行迁移 → 启动 Admin API → 配置 Admin UI 案例 → 访问 MiniAPP 列表和详情。 - -相关契约:[首页内容 API](./home-api.md)、[Public API](./public-api.md)。