diff --git a/WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts b/WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts index d73efc3..abc9bdc 100644 --- a/WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts +++ b/WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts @@ -8,6 +8,20 @@ export type HomeExperience = { demandKeyword: string; }; +export 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; +}; + +export type HomeExperiences = HomeExperienceApiItem[]; + export const homeExperienceMocks: HomeExperience[] = [ { id: "waterfall-descent", @@ -28,3 +42,37 @@ export const homeExperienceMocks: HomeExperience[] = [ demandKeyword: "地心探险", }, ]; + +export const homeExperienceFallback = homeExperienceMocks; + +export function normalizeHomeExperiences( + items: HomeExperiences | null | undefined, +): HomeExperience[] { + const fallback = homeExperienceFallback.map((item) => ({ ...item })); + const activeItems = (items ?? []) + .map((item, index) => ({ item, index })) + .filter(({ item }) => item.isActive !== false && Boolean(item.title?.trim())) + .sort( + (left, right) => + (left.item.sortOrder ?? left.index) - (right.item.sortOrder ?? right.index), + ); + + if (activeItems.length === 0) { + return fallback; + } + + return activeItems.map(({ item }, index) => { + const fallbackItem = homeExperienceFallback[index] ?? homeExperienceFallback[0]; + const title = item.title?.trim() || fallbackItem.title; + + return { + id: item.id?.trim() || fallbackItem.id, + badge: item.badge?.trim() || fallbackItem.badge, + category: item.category?.trim() || fallbackItem.category, + title, + englishTitle: item.englishTitle?.trim() || fallbackItem.englishTitle, + image: item.image?.trim() || fallbackItem.image, + demandKeyword: item.demandKeyword?.trim() || title, + }; + }); +} diff --git a/docs/README.md b/docs/README.md index 01bab8f..7b095fd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,24 +6,22 @@ 后端开发: -1. `backend-api-service.md` -2. `backend-plan.md` -3. `admin-api-requirements.md` -4. `wanfa-api.md` -5. `concierge-api.md` -6. `detail-api.md` -7. `module-config-api.md` -8. `public-api.md` +1. `admin-api-requirements.md` +2. `home-api.md` +3. `wanfa-api.md` +4. `concierge-api.md` +5. `detail-api.md` +6. `public-api.md` 管理前端开发: 1. `integration-workflow.md` 2. `admin-api-requirements.md` -3. `wanfa-api.md` -4. `concierge-api.md` -5. `detail-api.md` -6. `module-config-api.md` -7. `development-status.md` +3. `home-api.md` +4. `wanfa-api.md` +5. `concierge-api.md` +6. `detail-api.md` +7. `module-config-api.md` MiniAPP 前台开发: @@ -33,24 +31,26 @@ MiniAPP 前台开发: ## 文档清单 -| 文档 | 作用 | 主要读者 | -| --- | --- | --- | -| `integration-workflow.md` | 三端本地启动、联调顺序、接口变更流程和验证命令 | 全部 | -| `development-status.md` | 三端能力对接状态矩阵和优先联调路径 | 全部 | -| `decisions.md` | 当前有效技术和文档决策 | 全部 | -| `backend-api-service.md` | 后端 API 服务运行与前端联调说明 | 后端 | -| `backend-plan.md` | 当前 FastAPI 后端定位、业务模块、近期优先级和安全部署原则 | 后端 | -| `admin-api-requirements.md` | Admin UI 必需的 Admin API 主契约 | 后端、管理前端 | -| `module-config-api.md` | 页面模块配置 CRUD 的唯一细节契约 | 后端、管理前端 | -| `wanfa-api.md` | 玩法分类和路线管理 API 的补充契约 | 后端、管理前端 | -| `concierge-api.md` | 管家顾问资料管理 API 的补充契约 | 后端、管理前端 | -| `detail-api.md` | 详情展示内容管理 API 的补充契约 | 后端、管理前端 | -| `public-api.md` | MiniAPP 对接后端的 Public API 契约 | 后端、MiniAPP | +| 文档 | 作用 | 主要读者 | +| --------------------------- | --------------------------------------------------------- | -------------- | +| `integration-workflow.md` | 三端本地启动、联调顺序、接口变更流程和验证命令 | 全部 | +| `development-status.md` | 三端能力对接状态矩阵和优先联调路径 | 全部 | +| `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 补充契约 | 后端、管理前端 | +| `wanfa-api.md` | 玩法分类和路线管理 API 的补充契约 | 后端、管理前端 | +| `concierge-api.md` | 管家顾问资料管理 API 的补充契约 | 后端、管理前端 | +| `detail-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 补充契约。 diff --git a/docs/admin-api-requirements.md b/docs/admin-api-requirements.md index be6fc8e..60c7b29 100644 --- a/docs/admin-api-requirements.md +++ b/docs/admin-api-requirements.md @@ -8,6 +8,8 @@ 详情展示内容的字段、图片、排序与商品领域隔离约束见 [detail-api.md](./detail-api.md)。该文档是本主契约的详情领域补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI`。 +首页体验、团队共创和极境视界内容的字段、图片、排序与商品领域隔离约束见 [home-api.md](./home-api.md)。该文档是本主契约的首页内容补充,适用端为 `WonderQ-Admin` 和 `WonderQ-Admin-UI`。 + ## 通用约定 - API 前缀:`/api/admin`。 @@ -18,22 +20,22 @@ ## 接口清单 -| 方法 | 路径 | 用途 | -| --- | --- | --- | -| `POST` | `/api/admin/auth/login` | 后台登录 | -| `GET` | `/api/admin/me` | 当前后台用户 | -| `GET` | `/api/admin/dashboard` | 工作台统计和最近线索 | -| `GET` | `/api/admin/site-config` | 获取全部站点配置 | -| `POST` | `/api/admin/site-config/{module}` | 新增模块项 | -| `PATCH` | `/api/admin/site-config/{module}/{id}` | 更新模块项 | -| `DELETE` | `/api/admin/site-config/{module}/{id}` | 删除模块项 | -| `PATCH` | `/api/admin/site-config/{module}/reorder` | 调整排序 | -| `GET` | `/api/admin/leads` | 线索列表 | -| `PATCH` | `/api/admin/leads/{id}/status` | 更新线索状态 | -| `GET` | `/api/admin/media-assets` | 素材列表 | -| `POST` | `/api/admin/media-assets/upload` | 上传图片 | -| `POST` | `/api/admin/reset-guizhou-content` | 重置站点内容 | -| `POST` | `/api/admin/publish` | 发布站点快照 | +| 方法 | 路径 | 用途 | +| -------- | ----------------------------------------- | -------------------- | +| `POST` | `/api/admin/auth/login` | 后台登录 | +| `GET` | `/api/admin/me` | 当前后台用户 | +| `GET` | `/api/admin/dashboard` | 工作台统计和最近线索 | +| `GET` | `/api/admin/site-config` | 获取全部站点配置 | +| `POST` | `/api/admin/site-config/{module}` | 新增模块项 | +| `PATCH` | `/api/admin/site-config/{module}/{id}` | 更新模块项 | +| `DELETE` | `/api/admin/site-config/{module}/{id}` | 删除模块项 | +| `PATCH` | `/api/admin/site-config/{module}/reorder` | 调整排序 | +| `GET` | `/api/admin/leads` | 线索列表 | +| `PATCH` | `/api/admin/leads/{id}/status` | 更新线索状态 | +| `GET` | `/api/admin/media-assets` | 素材列表 | +| `POST` | `/api/admin/media-assets/upload` | 上传图片 | +| `POST` | `/api/admin/reset-guizhou-content` | 重置站点内容 | +| `POST` | `/api/admin/publish` | 发布站点快照 | ## 站点模块 @@ -46,19 +48,19 @@ type SiteModule = | "demandHero" | "demandFeatureCards" | "demandForm" - | "vehicleOptions" + | "vehicleOptions"; ``` 模块职责: -| 模块 | 主要字段 | 约束 | -| --- | --- | --- | -| `heroSlides` | `title`、`kicker`、`image`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | -| `destinationHero` | `title`、`kicker`、`image`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | -| `demandHero` | `title`、`kicker`、`description`、`steps`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | -| `demandFeatureCards` | `title`、`description`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | -| `demandForm` | 表单标签、占位文案、`chips`、`isActive` | 单例,不支持排序 | -| `vehicleOptions` | `title`、`description`、`image`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | +| 模块 | 主要字段 | 约束 | +| -------------------- | ------------------------------------------------------------------ | ------------------------ | +| `heroSlides` | `title`、`kicker`、`image`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | +| `destinationHero` | `title`、`kicker`、`image`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | +| `demandHero` | `title`、`kicker`、`description`、`steps`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | +| `demandFeatureCards` | `title`、`description`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | +| `demandForm` | 表单标签、占位文案、`chips`、`isActive` | 单例,不支持排序 | +| `vehicleOptions` | `title`、`description`、`image`、`isActive`、`sortOrder` | 可新增、编辑、删除、排序 | `GET /api/admin/site-config` 返回以上全部模块,包含停用内容,空模块返回 `[]`。 @@ -74,43 +76,6 @@ type SiteModule = 成功响应包含 `token` 和 `{ id, email, name, role }`。 -### 工作台统计 - -`GET /api/admin/dashboard` 返回: - -```ts -{ - stats: { - newLeadCount: number; - leadCount: number; - }; - recentLeads: Lead[]; -} -``` - -## 线索 - -### `GET /api/admin/leads` - -Query 参数:`status`、`sourcePage`、`keyword`、`createdFrom`、`createdTo`、`take`。`take` 范围为 1-200,默认 100。关键词只搜索联系方式、目的地和备注。 - -### `PATCH /api/admin/leads/{id}/status` - -请求: - -```json -{ "status": "contacted" } -``` - -状态值:`new`、`assigned`、`contacted`、`planning`、`won`、`invalid`。 - -## 媒体与发布 - -- 图片上传字段为 multipart `file` 和 `group`。 -- 允许 JPG、PNG、WebP、GIF,单文件最大 5MB。 -- `POST /api/admin/publish` 保存当前启用内容快照并返回版本记录。 -- `POST /api/admin/reset-guizhou-content` 只重置站点内容模块,不创建已移除领域的数据。 - ## 兼容边界 - 当前 Admin UI 不应调用未列出的领域接口。 diff --git a/docs/backend-api-service.md b/docs/backend-api-service.md deleted file mode 100644 index 749a087..0000000 --- a/docs/backend-api-service.md +++ /dev/null @@ -1,36 +0,0 @@ -# 后台 API 服务 - -`WonderQ-Admin` 当前是独立的 Python + FastAPI API 服务,使用 PostgreSQL 保存业务数据,通过 Docker Compose 启动 `api`、`postgres` 和 `redis`。 - -## 本地 API 启动 - -从 `WonderQ-Project` 根目录启动时,先确认 Docker Desktop 已运行,再进入后端目录: - -```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 -``` - -首次缺少虚拟环境或依赖时再执行: - -```powershell -Set-Location .\WonderQ-Admin -python -m venv .venv -.\.venv\Scripts\Activate.ps1 -python -m pip install -r requirements.txt -python -m alembic upgrade head -python -m app.seed --no-reset -``` - -健康检查:`http://localhost:4000/health` - -## 前端联调 - -后台管理前端位于 `D:\www\znkj\WonderQ-Project\WonderQ-Admin-UI`。如需连接本服务,在前端 `.env` 或环境变量中配置: - -```text -VITE_API_BASE_URL="http://localhost:4000" -``` diff --git a/docs/backend-plan.md b/docs/backend-plan.md deleted file mode 100644 index 38fabce..0000000 --- a/docs/backend-plan.md +++ /dev/null @@ -1,34 +0,0 @@ -# WonderQ-Admin 后端规划摘要 - -## 当前定位 - -`WonderQ-Admin` 是 WonderQ 的独立后端 API 服务,当前技术栈已调整为 Python + FastAPI + SQLAlchemy 2 + Alembic + PostgreSQL,通过 Docker Compose 部署 API、PostgreSQL 和 Redis。 - -## 核心目标 - -- 为 H5 前台提供稳定的 Public API。 -- 为后台管理端提供 JWT 鉴权的 Admin API。 -- 保留现有 PostgreSQL 数据和主要接口路径,降低前端联调成本。 -- 使用 Alembic 管理后续数据库迁移,已有数据库通过 `alembic stamp head` 接入 baseline。 - -## 当前业务模块 - -- 首页配置:轮播、目的地、发布版本。 -- 线路产品:列表、详情、图片、详情区块、状态和排序。 -- 线索:前台提交、后台列表、状态流转。 -- 媒体:媒体资源登记。 -- 审计:后台关键变更写入 `AuditLog`。 - -## 近期优先级 - -1. 保持 Public/Admin API 与现有前端调用兼容。 -2. 补充更多 PostgreSQL 集成测试,覆盖迁移、seed 和核心接口。 -3. 建立生产迁移流程:备份、`alembic stamp head`、后续增量迁移。 -4. 按实际业务继续扩展权限、订单、客户和消息模块。 - -## 安全与部署原则 - -- 生产环境必须替换 `JWT_SECRET`,禁止使用示例值。 -- 不提交 `.env`、日志、数据库备份和任何真实密钥。 -- `python -m app.seed` 会重置内容数据,生产环境执行前必须明确确认。 -- Docker Compose 适合本地和单机部署;生产可按相同环境变量拆分到托管数据库或容器平台。 diff --git a/docs/decisions.md b/docs/decisions.md deleted file mode 100644 index 451366a..0000000 --- a/docs/decisions.md +++ /dev/null @@ -1,108 +0,0 @@ -# WonderQ 当前技术与文档决策 - -本文档只记录当前有效决策,避免非当前技术路径干扰后续开发。 - -## DEC-001:后端采用 Python + FastAPI 技术栈 - -状态:已接受 - -当前后端服务位于 `WonderQ-Admin`,技术栈为 Python、FastAPI、SQLAlchemy 2、Alembic、PostgreSQL、JWT 和 Pydantic。 - -原因: - -- 当前代码和文档已经围绕该技术栈落地。 -- Alembic 负责数据库迁移,适合继续演进 PostgreSQL 结构。 -- FastAPI 与 Pydantic 能清晰表达 Public API 和 Admin API 的请求响应契约。 - -影响: - -- 后端文档以 `backend-api-service.md` 和 `backend-plan.md` 为准。 -- 不在当前文档中保留其它后端技术栈规划。 - -## DEC-002:Public API 与 Admin API 分离 - -状态:已接受 - -`WonderQ-MiniAPP` 只对接 `/api/public/...`,不依赖后台鉴权;`WonderQ-Admin-UI` 只对接 `/api/admin/...`,除登录外均需要后台登录态。 - -原因: - -- 前台展示和后台运营权限边界不同。 -- Public API 需要过滤未发布或未启用内容。 -- Admin API 需要返回运营维护所需的完整配置和草稿数据。 - -影响: - -- `public-api.md` 是 MiniAPP 对接后端的唯一 Public API 契约。 -- `admin-api-requirements.md` 是 Admin UI 对接后端的主契约。 - -## DEC-003:页面模块 CRUD 细节以 `module-config-api.md` 为准 - -状态:已接受 - -首页轮播、目的地和万趣用车的新增、更新、删除、排序细节,统一维护在 `module-config-api.md`。 - -原因: - -- 页面模块字段多、规则细,放在 Admin API 主文档里会造成重复。 -- 管理前端和后端实现都需要同一份细粒度契约。 - -影响: - -- `admin-api-requirements.md` 只保留站点配置主接口和模块入口说明。 -- 任何页面模块字段、状态码、排序、媒体上传规则变化,都先更新 `module-config-api.md`。 - -## DEC-004:文档保持扁平结构 - -状态:已接受 - -`docs/` 目录不再按子项目分目录,所有当前文档直接放在 `docs/` 根目录。 - -原因: - -- 当前项目只有少量关键文档,扁平结构查找更快。 -- 三端联调需要跨项目阅读,按子项目分目录会增加跳转成本。 - -影响: - -- `docs/README.md` 是唯一文档入口。 -- 新文档必须在 `docs/README.md` 登记。 - -## DEC-005:当前文档只服务现行架构 - -状态:已接受 - -当前文档集中只保留服务现行架构和三端联调的内容。 - -原因: - -- 非当前技术路径会干扰三端当前开发判断。 -- 当前任务目标是提高后端、管理前端和 MiniAPP 的开发进度,文档需要服务现行架构。 - -影响: - -- 新成员只需要阅读当前文档即可理解现行协作方式。 -- 如未来需要记录重大调整,直接新增当前决策或更新本文件。 - -## DEC-006:MiniAPP 采用简约、内容优先的移动端视觉风格 - -状态:已接受 - -`WonderQ-MiniAPP` 的 H5 与微信小程序前台统一采用扁平、低装饰的简约风格。内容图片和信息层级优先于装饰效果,交互状态通过颜色、边框和轻量动效表达。 - -原因: - -- 旅行内容页需要让用户快速扫描目的地、线路和服务入口,复杂背景和重装饰会削弱信息层级。 -- H5 与小程序运行设备性能差异较大,减少渐变、模糊和重阴影可以降低渲染负担并提高跨端一致性。 -- 固定导航采用稳定布局,避免浮动徽章和缩放动画造成视觉干扰或布局跳动。 - -约定: - -- 基础色使用白色、浅灰和低饱和绿色;主操作使用绿色,价格等业务强调色保持低饱和暖色。 -- 卡片、输入框和按钮优先使用 8-12px 圆角、细边框和轻阴影;不新增大面积渐变、装饰性光晕或重阴影。 -- 复用 `WonderQ-MiniAPP/src/app.css` 的 `--wq-*` 设计变量及共享组件样式,页面局部样式不得重新建立一套颜色和阴影体系。 - -影响: - -- 新增或调整 MiniAPP 页面时,应先复用共享样式,再补充页面特有布局。 -- 如确需引入明显装饰效果,需要在页面需求中说明其内容价值,并同步更新本决策。 diff --git a/docs/development-status.md b/docs/development-status.md deleted file mode 100644 index 504bb83..0000000 --- a/docs/development-status.md +++ /dev/null @@ -1,56 +0,0 @@ -# WonderQ 开发状态矩阵 - -本文档记录三端当前对接范围,不替代实际构建、测试和联调结果。 - -| 能力 | 后端 | 管理前端 | MiniAPP | 入口 | -| --- | --- | --- | --- | --- | -| 健康检查 | 已覆盖 | 需实测 | 需实测 | `GET /health` | -| Public 站点配置 | 已覆盖 | 不直接使用 | 当前使用 | `GET /api/public/site-config` | -| Public 线索提交 | 已覆盖 | 不直接使用 | 当前使用 | `POST /api/public/leads` | -| Public 客户登录 | 已覆盖 | 不使用 | 当前使用 | `/api/public/auth/*` | -| Admin 登录 | 已覆盖 | 当前使用 | 不使用 | `POST /api/admin/auth/login` | -| Admin 站点配置 | 已覆盖 | 当前使用 | 不使用 | `GET /api/admin/site-config` | -| 页面模块 CRUD | 已覆盖 | 当前使用 | 不使用 | `module-config-api.md` | -| Admin 线索跟进 | 已覆盖 | 当前使用 | 不使用 | `/api/admin/leads` | -| 媒体上传 | 已覆盖 | 当前使用 | 不使用 | `/api/admin/media-assets/upload` | -| 发布站点配置 | 已覆盖 | 当前使用 | 不使用 | `POST /api/admin/publish` | -| 贵州内容重置 | 已覆盖 | 当前使用 | 不使用 | `POST /api/admin/reset-guizhou-content` | - -## 当前页面联调路径 - -1. 后端启动后检查 `/health` 和 `/api/public/site-config`。 -2. Admin UI 登录并读取站点配置。 -3. 在首页、玩法页和需求页模块中新增、编辑、删除、排序或启停配置。 -4. MiniAPP 启动并验证首页、玩法、需求和我的页面。 -5. MiniAPP 提交线索,Admin UI 在“需求线索”中查询并更新状态。 -6. Admin UI 发布站点配置,MiniAPP 刷新后验证启用内容。 - -## 验证命令 - -```powershell -Set-Location D:\www\znkj\WonderQ-Project\WonderQ-Admin -.\.venv\Scripts\python.exe -m pytest -``` - -```powershell -Set-Location D:\www\znkj\WonderQ-Project\WonderQ-Admin-UI -yarn build -``` - -```powershell -Set-Location D:\www\znkj\WonderQ-Project\WonderQ-MiniAPP -yarn test -yarn build:h5 -yarn build:mp-weixin -``` - -## 本次领域清理 - -- MiniAPP 只保留站点内容、需求提交和客户登录相关逻辑。 -- Public API 只保留 `site-config`、登录、客户和线索接口。 -- Admin UI 只保留站点结构、素材、发布和线索管理入口。 -- 后端通过 `0009_remove_product_domain` 迁移移除已退役表、关联表以及线索和订单中的旧外键列。 -- 后端通过 `0010_remove_destination_module` 迁移移除省内目的地及别名表。 -- 后端通过 `0012_remove_offer_modules` 迁移移除特价优惠、特色酒店及相关表。 -- 后端通过 `0013_remove_map_theme_modules` 迁移移除贵州地图和主题甄选相关表。 -- 未执行现有数据库的破坏性迁移;部署时应先备份,再按迁移流程执行。 diff --git a/docs/home-api.md b/docs/home-api.md index e69de29..fe16b77 100644 --- a/docs/home-api.md +++ b/docs/home-api.md @@ -0,0 +1,362 @@ +# 首页内容管理 Admin API + +> 适用端:`WonderQ-Admin`、`WonderQ-Admin-UI`。 +> +> 状态:待实现契约。本文件参考 `WonderQ-MiniAPP/src/pages/home/components/homeExperienceData.ts`、`homeTeamBuildingData.ts` 和 `homeWildArchivesData.ts` 定义首页卡片内容,以及管理端需要的查询、维护和排序接口。它是首页内容的 Admin API 补充契约,不是 MiniAPP Public API 文档。 + +## 领域边界 + +首页内容管理只维护三类首页卡片: + +- 体验推荐:徽标、分类、标题、英文标题、封面和需求关键词。 +- 团队共创:标签、标题、描述、封面和需求关键词。 +- 极境视界:案例标题、封面和需求关键词。 + +本领域不负责: + +- 顶部轮播、车辆选项、需求表单或其他站点模块;这些内容遵循 [Admin API 主契约](./admin-api-requirements.md)。 +- 商品、Product、ProductImage、详情、价格、库存、订单或预订。 +- 线索创建和线索跟进。 + +`demandKeyword` 只是点击卡片后预填需求页的普通字符串,不是商品 ID、路线 ID、订单 ID,也不建立数据库外键。 + +当前首页仍直接使用三个文件中的 mock 数组。Admin API 接入前,这些数组继续作为前台 fallback;本文件不代表接口已经在 `WonderQ-Admin` 或 `WonderQ-Admin-UI` 中实现。 + +## 接口清单 + +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` | 调整极境视界案例顺序 | + +## 通用约定 + +- 请求和响应使用 JSON,字段使用 camelCase。 +- 所有管理接口需要 `Authorization: Bearer `。 +- `GET` 返回启用和停用的全部记录,按 `sortOrder` 升序返回,供 Admin UI 完整维护。 +- 创建、编辑、删除和排序成功后写入审计日志,再提交事务。 +- 创建和编辑返回最新记录;排序接口返回排序后的 `items`。 +- ID 由后端生成并作为非空字符串返回。初始化迁移时应优先保留三个 mock 文件中的稳定 ID。 +- 空集合返回 `[]`,不能返回 `null` 或省略字段。 +- 所有资源的 `sortOrder` 从 `0` 开始,数值越小越靠前;新增未传排序时追加到末尾。 +- 失败响应沿用 Admin API 主契约,包含 `message`、`code` 和可选的 `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; +}; + +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; + 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` | 是 | 点击后预填需求页的关键词。 | +| 极境视界 | `title` | `string` | 是 | 案例标题,去除首尾空白后不得为空。 | +| 极境视界 | `image` | `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 +{ + "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` 和新建记录;未传 `sortOrder` 时追加到末尾。 +- `PATCH /api/admin/home/{resource}/{id}`:请求体为对应资源的 `Patch` 类型,只更新提交字段;成功返回更新后的记录,不存在返回 `404`。 +- `DELETE /api/admin/home/{resource}/{id}`:成功返回 `{ "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"] } +``` + +成功响应为 `{ "items": [] }`,其中 `items` 是更新 `sortOrder` 后的完整记录列表。缺少 ID、包含未知 ID 或出现重复 ID 时返回 `400 HOME_REORDER_INVALID`。 + +## 当前 fallback 与迁移映射 + +迁移初始数据时应保留以下稳定 ID、字段值和当前数组顺序: + +### 体验推荐 + +| 顺序 | 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`。 +4. 极境视界表单维护 `title`、`image` 和 `demandKeyword`。 +5. 三类资源都提供启用/停用、编辑、删除和上移/下移操作;排序时提交完整 ID 列表。 +6. 每次变更成功后以接口返回的记录或列表更新本地状态,不直接假设本地修改已经保存。 +7. 删除前要求二次确认;删除成功后接受服务端返回的重新排序结果。 +8. 处理 `401`、`404`、`409`、`422` 和 `5xx`,保存、上传和排序进行中禁用重复提交。 +9. 不在首页表单中出现 Product ID、ProductImage ID、价格、库存、订单或预订字段。 +10. 预览点击行为使用 `demandKeyword`;该字段为空时禁止提交,而不是由前端猜测或拼接商品标识。 + +建议的 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[]); +``` + +当前首页的“查看更多”按钮固定跳转需求关键词“极境视界”,不属于 `HomeWildArchive` 记录字段;如果未来需要后台配置该按钮,应另行增加首页 CTA 配置契约。 + +## 前后台数据边界 + +- Admin API 返回启用和停用的完整记录,供管理端维护。 +- 未来 Public API 只返回已发布且启用的首页内容;Public API 的响应字段应与本契约的渲染字段兼容,但不应暴露 `createdAt`、`updatedAt` 等管理元数据。 +- MiniAPP 接入时再同步更新 `src/lib/types.ts`、`src/lib/data.ts`、首页组件和 `docs/public-api.md`;本次文档不改变现有 mock 消费路径。 +- 后端不得把 `demandKeyword` 解析成 Product 或订单关联;如需线路、详情或预订能力,应另立领域契约。 + +## 后端落地边界 + +本契约落地时需要由 `WonderQ-Admin` 补充对应 ORM 模型、迁移、schema、路由、序列化和审计日志;由 `WonderQ-Admin-UI` 补充 API 类型、请求封装、首页内容列表、表单、图片上传和排序交互。当前后端没有 `/api/admin/home/*` 路由,Admin UI 也未接入这三类首页数据。 + +相关文档: + +- [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) +- [极境视界数据](../WonderQ-MiniAPP/src/pages/home/components/homeWildArchivesData.ts) diff --git a/docs/integration-workflow.md b/docs/integration-workflow.md index cd59f16..9197d10 100644 --- a/docs/integration-workflow.md +++ b/docs/integration-workflow.md @@ -4,11 +4,11 @@ ## 三端职责 -| 端 | 目录 | 职责 | 主要契约 | -| --- | --- | --- | --- | -| 后端 API | `WonderQ-Admin` | 提供 Public API、Admin API、鉴权、数据库、迁移和 seed | `backend-api-service.md`、`backend-plan.md`、`public-api.md`、`admin-api-requirements.md` | -| 管理前端 | `WonderQ-Admin-UI` | 维护首页结构、目的地、线索和页面模块配置 | `admin-api-requirements.md`、`module-config-api.md` | -| 前台 MiniAPP | `WonderQ-MiniAPP` | H5 与微信小程序前台展示、咨询和线索提交 | `public-api.md` | +| 端 | 目录 | 职责 | 主要契约 | +| ------------ | ------------------ | ----------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| 后端 API | `WonderQ-Admin` | 提供 Public API、Admin API、鉴权、数据库、迁移和 seed | `backend-api-service.md`、`backend-plan.md`、`public-api.md`、`admin-api-requirements.md` | +| 管理前端 | `WonderQ-Admin-UI` | 维护首页结构、目的地、线索和页面模块配置 | `admin-api-requirements.md`、`module-config-api.md` | +| 前台 MiniAPP | `WonderQ-MiniAPP` | H5 与微信小程序前台展示、咨询和线索提交 | `public-api.md` | ## 本地启动顺序 @@ -90,11 +90,9 @@ MiniAPP 联调重点: 1. 先更新契约文档。 - Public API 变更:更新 `public-api.md`。 - Admin API 变更:更新 `admin-api-requirements.md`。 - - 页面模块 CRUD 变更:更新 `module-config-api.md`。 2. 后端实现或调整接口,并补充对应验证。 3. 管理前端或 MiniAPP 按契约调整调用和类型。 4. 三端分别运行对应验证命令。 -5. 若变更影响启动、部署或架构边界,同步更新 `backend-api-service.md`、`backend-plan.md` 或 `decisions.md`。 ## 验证命令 diff --git a/docs/module-config-api.md b/docs/module-config-api.md deleted file mode 100644 index ff81758..0000000 --- a/docs/module-config-api.md +++ /dev/null @@ -1,133 +0,0 @@ -# 页面模块配置 Admin API 契约 - -本文档约束 `WonderQ-Admin-UI` 对站点结构模块的 CRUD、排序和字段白名单。 - -## 适用模块 - -| 模块 | 用途 | -| --- | --- | -| `heroSlides` | 首页顶部轮播 | -| `destinationHero` | 目的地页主视觉 | -| `demandHero` | 需求页主视觉 | -| `demandFeatureCards` | 需求页能力卡片 | -| `demandForm` | 需求提交表单 | -| `vehicleOptions` | 万趣用车卡片 | - -## 通用规则 - -- 路径:`/api/admin/site-config/{module}`。 -- 所有模块项都使用字符串 `id`;排序项使用整数 `sortOrder`。 -- 创建、更新、删除和排序均需要后台 JWT。 -- 后端只接受模块字段白名单,未知字段会被忽略。 -- `demandForm` 是单例模块,重复创建返回 `409`。 -- `demandForm` 不支持排序;其他模块支持排序。 -- 所有变更写入审计日志,并在成功提交后返回最新模块项。 - -## 类型 - -```ts -type SiteModule = - | "heroSlides" - | "destinationHero" - | "demandHero" - | "demandFeatureCards" - | "demandForm" - | "vehicleOptions" - -type SiteItemPatch = { - title?: string; - kicker?: string | null; - image?: string | null; - description?: string | null; - steps?: string[]; - destinationLabel?: string; - destinationPlaceholder?: string | null; - phoneLabel?: string; - phonePlaceholder?: string | null; - noteLabel?: string; - notePlaceholder?: string | null; - submitLabel?: string; - chips?: string[]; - targetType?: string | null; - targetValue?: string | null; - isActive?: boolean; - sortOrder?: number; -}; -``` - -## 获取完整配置 - -### `GET /api/admin/site-config` - -返回所有模块数组,包括停用项: - -```ts -{ - heroSlides: unknown[]; - destinationHero: unknown[]; - demandHero: unknown[]; - demandFeatureCards: unknown[]; - demandForm: unknown[]; - vehicleOptions: unknown[]; -} -``` - -空模块必须返回 `[]`,不能返回 `null` 或省略字段。 - -## CRUD - -### 新增 - -```http -POST /api/admin/site-config/{module} -Authorization: Bearer -Content-Type: application/json -``` - -请求体使用 `SiteItemPatch`。成功返回 `201` 和新建模块项。各模块的主字段如下: - -| 模块 | 主字段 | -| --- | --- | -| `heroSlides` | `title` | -| `destinationHero` | `title` | -| `demandHero` | `title` | -| `demandFeatureCards` | `title` | -| `demandForm` | `submitLabel` | -| `vehicleOptions` | `title` | - -缺少主字段返回 `422 MODULE_CONFIG_VALIDATION_ERROR`。模块不存在返回 `400 MODULE_CONFIG_FORBIDDEN`。 - -### 更新 - -```http -PATCH /api/admin/site-config/{module}/{id} -``` - -只更新该模块允许的字段;不存在的 ID 返回 `404 MODULE_CONFIG_NOT_FOUND`。 - -### 删除 - -```http -DELETE /api/admin/site-config/{module}/{id} -``` - -成功返回 `{ "id": "..." }`。删除后,排序模块会重新从 0 开始编号。 - -### 排序 - -```http -PATCH /api/admin/site-config/{module}/reorder -Content-Type: application/json - -{ "itemIds": ["id-2", "id-1"] } -``` - -`itemIds` 必须刚好包含当前模块的全部 ID,且不能重复。否则返回 `400 MODULE_CONFIG_REORDER_INVALID`。不支持排序的模块返回 `400 MODULE_CONFIG_REORDER_UNSUPPORTED`。 - -## 模块字段补充 - -- `demandForm` 的 `chips` 会过滤空白值;表单只保留一条配置。 - -## 删除范围边界 - -模块配置只维护站点内容,不负责客户、线索或订单数据。接口变更必须同步 `src/api.ts`、`src/types/admin.ts` 和本文档。 diff --git a/docs/public-api.md b/docs/public-api.md index 064752d..6682965 100644 --- a/docs/public-api.md +++ b/docs/public-api.md @@ -11,13 +11,13 @@ ## 接口清单 -| 方法 | 路径 | 鉴权 | 用途 | -| --- | --- | --- | --- | -| `GET` | `/health` | 否 | 服务健康检查 | -| `GET` | `/api/public/site-config` | 否 | 获取启用的站点内容 | -| `POST` | `/api/public/auth/phone-login` | 否 | 微信手机号登录 | -| `GET` | `/api/public/auth/me` | Customer JWT | 获取当前客户 | -| `POST` | `/api/public/leads` | 否 | 提交出行需求 | +| 方法 | 路径 | 鉴权 | 用途 | +| ------ | ------------------------------ | ------------ | ------------------ | +| `GET` | `/health` | 否 | 服务健康检查 | +| `GET` | `/api/public/site-config` | 否 | 获取启用的站点内容 | +| `POST` | `/api/public/auth/phone-login` | 否 | 微信手机号登录 | +| `GET` | `/api/public/auth/me` | Customer JWT | 获取当前客户 | +| `POST` | `/api/public/leads` | 否 | 提交出行需求 | ## 站点配置 @@ -103,46 +103,3 @@ type DemandForm = { ```json { "id": "customer-id", "phoneMasked": "138****0000" } ``` - -## 出行需求 - -### `POST /api/public/leads` - -请求字段: - -| 字段 | 类型 | 必填 | 说明 | -| --- | --- | --- | --- | -| `destination` | `string` | 否 | 目的地或玩法 | -| `phone` | `string` | 是 | 联系方式,长度 2-64 | -| `travelDate` | `datetime` | 否 | 支持 `YYYY-MM-DD` | -| `peopleCount` | `number` | 否 | 大于 0 | -| `budgetMin` | `number` | 否 | 不小于 0 | -| `budgetMax` | `number` | 否 | 不小于 0 | -| `note` | `string` | 否 | 最长 1000 字符 | -| `sourcePage` | `string` | 否 | 来源页面标识 | - -成功响应: - -```json -{ "id": "lead-id", "status": "new" } -``` - -## 错误约定 - -- 未登录访问客户接口:`401`,消息为“请先登录”。 -- 参数校验失败:`422`。 -- 微信登录未配置:`503`。 -- 微信登录凭证无效:`400`。 - -## MiniAPP 依赖 - -- 启动时只请求 `GET /api/public/site-config`。 -- 首页使用 `heroSlides` 与 `vehicleOptions`。 -- 需求提交只通过 `POST /api/public/leads`,失败时显示统一错误状态并保留本地表单内容。 - -## 验证建议 - -- 验证 `site-config` 的模块字段始终为数组。 -- 验证 Public 内容只返回启用状态的数据。 -- 验证需求请求不接受未知关联字段,并覆盖日期、联系方式和预算校验。 -- 验证旧内容路径返回 `404`,避免客户端继续依赖已撤下的接口。