Files
WonderQ-Admin/docs/admin-ui-api-requirements.md
duanshuwen a47b4b5dd0 feat: Add WonderQ-MiniAPP Public API documentation
- Introduced a comprehensive API contract for the WonderQ-MiniAPP, detailing endpoints for site configuration, product listings, and lead submissions.
- Defined data types for various entities including HeroSlide, Destination, Theme, CtaBanner, PublicProduct, and more.
- Specified request and response formats, including error handling guidelines.

chore: Update requirements to include python-multipart

- Added python-multipart dependency to requirements.txt for handling file uploads.

test: Implement API contract tests

- Created test suite for API contracts, validating serializers and endpoints for public products and leads.
- Included tests for destination and product serializers, ensuring correct data handling and validation.

test: Add configuration tests for OSS settings

- Implemented tests to verify that OSS settings are correctly loaded from environment variables.
2026-07-01 16:55:00 +08:00

12 KiB
Raw Permalink Blame History

WonderQ-Admin-UI Admin API 接口需求

本文档用于指导 WonderQ-Admin 后端按当前 WonderQ-Admin-UI 管理端完成 Admin API 对接。接口需求来源于前端 src/api.tssrc/App.tsx 的实际类型、请求封装和页面调用。

范围

  • 本文只覆盖当前后台管理前端必需的 Admin API。
  • 不包含 H5 Public API、订单、媒体库、审计日志、活动专题等后续规划能力。
  • 后端现有 GET /api/admin/meGET /api/admin/media-assets 不在当前 UI 必需范围内。

通用约定

  • 基础路径:/api/admin
  • 请求与响应均使用 JSON。
  • POST /api/admin/auth/login 外,其余接口都需要后台登录态。
  • 登录成功后前端会把返回的 token 存入本地,并在后续请求头中发送:
Authorization: Bearer <token>
Content-Type: application/json
  • 日期时间字段返回 ISO 8601 字符串。
  • ID 字段按字符串处理。
  • 前端错误提示优先读取响应体的 message 字段;如后端沿用 FastAPI 默认 detail,建议同时兼容输出 message,避免管理端展示兜底错误。

当前对接状态

接口 前端依赖 后端现状 备注
POST /api/admin/auth/login 登录页 已覆盖 返回 token 和 user
GET /api/admin/dashboard 客户端已封装 已覆盖 当前 UI 暂未展示
GET /api/admin/products 商品维护、结构维护 已覆盖 UI 当前只传 keyword
POST /api/admin/products 新建商品 已覆盖 返回完整 Product
PATCH /api/admin/products/{id} 编辑商品 已覆盖 返回完整 Product
GET /api/admin/destinations 商品目的地下拉、目的地页 已覆盖 需要返回别名和商品数
GET /api/admin/site-config 首页/目的地/活动结构维护 已覆盖 需要包含未启用内容
PATCH /api/admin/site-config/{module}/{item_id} 模块内容编辑 已覆盖 模块名需保持一致
GET /api/admin/leads 需求线索页 已覆盖 UI 当前不传筛选参数
PATCH /api/admin/leads/{id}/status 线索状态流转 已覆盖 UI 更新后会重新拉列表
POST /api/admin/publish 结构维护发布 已覆盖 UI 使用 title 提示发布结果
POST /api/admin/reset-guizhou-content 贵州内容重置 已覆盖 高风险操作,需鉴权和审计

枚举

ProductStatus

type ProductStatus = "draft" | "published" | "archived";

LeadStatus

type LeadStatus = "new" | "assigned" | "contacted" | "planning" | "won" | "invalid";

SiteModule

type SiteModule = "heroSlides" | "destinations" | "themes" | "ctaBanners";

公共数据结构

Destination

type Destination = {
  id: string;
  name: string;
  slug: string;
  region?: string | null;
  image?: string | null;
  isHot: boolean;
  isActive: boolean;
  sortOrder: number;
  aliases?: Array<{ id: string; alias: string }>;
  _count?: { products: number };
};

Product

type Product = {
  id: string;
  sourceId?: number | null;
  title: string;
  subtitle?: string | null;
  priceAmount?: number | null;
  priceUnit: string;
  tags: string[];
  coverImage?: string | null;
  summary?: string | null;
  images?: Array<{ id?: string; url: string; alt?: string | null; sortOrder: number }>;
  detailSections?: ProductDetailSection[] | null;
  status: ProductStatus;
  sortWeight: number;
  updatedAt: string;
  destination?: Destination | null;
  destinationId?: string | null;
};

ProductDetailSection

type ProductDetailBlock =
  | { type: "text"; text: string }
  | { type: "image"; url: string; alt?: string | null };

type ProductDetailSection = {
  key: string;
  label: string;
  title?: string | null;
  blocks: ProductDetailBlock[];
};

ProductInput

POST /productsPATCH /products/{id} 复用该结构;PATCH 可以只提交需要修改的字段。

type ProductInput = {
  title: string;
  subtitle?: string;
  destinationId?: string | null;
  priceAmount?: number | null;
  priceUnit?: string;
  tags: string[];
  coverImage?: string | null;
  summary?: string | null;
  images?: Array<{ url: string; alt?: string | null; sortOrder: number }>;
  detailSections?: ProductDetailSection[];
  status: ProductStatus;
  sortWeight: number;
};

字段处理要求:

  • title 必填,后端至少应校验非空;当前后端 schema 为最少 2 个字符。
  • priceAmount 可为空;不为空时应为大于等于 0 的整数。
  • priceUnit 为空时后端默认使用 起/人
  • images 保存前按数组顺序重排 sortOrder
  • detailSections 中空 key、空 label、空 blocks 的模块不应保存为有效详情模块。
  • status 首次变为 published 时,后端可写入发布时间。

Lead

type Lead = {
  id: string;
  destination?: string | null;
  phone: string;
  note?: string | null;
  sourcePage?: string | null;
  status: LeadStatus;
  createdAt: string;
  sourceProduct?: { id: string; title: string } | null;
  assignedUser?: { id: string; name: string } | null;
};

后端可额外返回 travelDatepeopleCountbudgetMinbudgetMax 等字段,但以上字段是当前管理端展示所需的最小集合。手机号属于隐私信息,日志、错误和文档示例中不得输出真实号码。

SiteConfig

type SiteConfig = {
  heroSlides: Array<{
    id: string;
    title: string;
    kicker?: string | null;
    image: string;
    targetType?: string | null;
    targetValue?: string | null;
    isActive: boolean;
  }>;
  destinations: Destination[];
  themes: Array<{
    id: string;
    label: string;
    image: string;
    targetType?: string | null;
    targetValue?: string | null;
    isActive: boolean;
  }>;
  ctaBanners: Array<{
    id: string;
    alt: string;
    image: string;
    targetType: string;
    targetValue?: string | null;
    isActive: boolean;
  }>;
};

SiteItemPatch

type SiteItemPatch = {
  title?: string;
  kicker?: string;
  name?: string;
  label?: string;
  alt?: string;
  image?: string | null;
  targetType?: string | null;
  targetValue?: string | null;
  isActive?: boolean;
};

模块字段映射:

module 可编辑字段
heroSlides titlekickerimagetargetTypetargetValueisActive
destinations nameimageisActive
themes labelimagetargetTypetargetValueisActive
ctaBanners altimagetargetTypetargetValueisActive

接口明细

登录

POST /api/admin/auth/login

请求体:

{
  "email": "admin@example.com",
  "password": "example-password"
}

响应体:

{
  token: string;
  user: {
    id?: string;
    name: string;
    email: string;
    role: string;
  };
}

状态码要求:

  • 200:登录成功。
  • 401:账号不存在、密码错误或账号停用。

工作台统计

GET /api/admin/dashboard

当前前端客户端已封装该接口,但页面暂未展示。后端保持兼容即可。

响应体:

{
  stats: {
    productCount: number;
    publishedProductCount: number;
    destinationCount: number;
    newLeadCount: number;
    leadCount: number;
    campaignCount: number;
  };
  recentLeads: Lead[];
}

商品列表

GET /api/admin/products?keyword=<keyword>

查询参数:

参数 类型 当前 UI 是否使用 说明
keyword string 搜索商品标题、短标题或标签
status ProductStatus 后端可支持状态筛选
take number 后端当前可限制返回条数

响应体:

{
  items: Product[];
}

排序建议:sortWeight 升序,再按 updatedAt 倒序。结构维护页和商品维护页都会读取该接口。

新建商品

POST /api/admin/products

请求体:ProductInput

响应体:Product

状态码要求:

  • 201:创建成功。
  • 422:字段校验失败。

更新商品

PATCH /api/admin/products/{id}

请求体:Partial<ProductInput>

响应体:Product

状态码要求:

  • 200:更新成功。
  • 404:商品不存在。
  • 422:字段校验失败。

当前 UI 保存商品后会使用响应体刷新编辑状态,因此后端需要返回完整 Product而不是只返回成功标记。

目的地列表

GET /api/admin/destinations

响应体:

{
  items: Destination[];
}

要求:

  • 返回所有目的地,包括未启用项,便于后台维护。
  • sortOrder 升序。
  • 每个目的地包含 aliases
  • 每个目的地建议包含 _count.products,用于后台判断关联商品数量。

站点配置

GET /api/admin/site-config

响应体:SiteConfig

要求:

  • 返回 heroSlidesdestinationsthemesctaBanners 四个模块。
  • Admin API 需要返回未启用内容Public API 才按发布/启用状态过滤。
  • 各模块按 sortOrder 升序。

更新站点配置项

PATCH /api/admin/site-config/{module}/{item_id}

路径参数:

参数 类型 说明
module SiteModule 只能为 heroSlidesdestinationsthemesctaBanners
item_id string 对应模块内容项 ID

请求体:SiteItemPatch

响应体:更新后的内容项对象。

状态码要求:

  • 200:更新成功。
  • 400:模块不存在。
  • 404:内容项不存在。
  • 422:字段校验失败。

当前 UI 保存后会重新调用 GET /api/admin/site-config 刷新页面,响应体只需保证是合法 JSON。

线索列表

GET /api/admin/leads

查询参数:

参数 类型 当前 UI 是否使用 说明
status LeadStatus 后端可支持状态筛选
take number 后端当前可限制返回条数

响应体:

{
  items: Lead[];
}

排序建议:createdAt 倒序。当前 UI 展示客户手机号、创建时间、目的地/备注、来源商品/来源页面和状态。

更新线索状态

PATCH /api/admin/leads/{id}/status

请求体:

{
  status: LeadStatus;
}

响应体:更新后的 Lead,至少需要包含 idstatus

状态码要求:

  • 200:更新成功。
  • 404:线索不存在。
  • 422:状态值非法。

当前 UI 更新后会重新调用 GET /api/admin/leads,因此响应体不会直接用于渲染列表。

发布站点配置

POST /api/admin/publish

请求体:空 JSON 对象或无请求体均可兼容。

响应体:

{
  id: string;
  title: string;
  publishedAt: string;
}

当前 UI 只读取 title 展示发布结果。后端可额外返回 statussnapshot 等字段。

重置贵州内容

POST /api/admin/reset-guizhou-content

请求体:空 JSON 对象或无请求体均可兼容。

响应体:

{
  heroSlides: number;
  destinations: number;
  themes: number;
  ctaBanners: number;
  products: number;
}

要求:

  • 该接口会重置内容数据,必须走后台鉴权。
  • 后端需要记录审计日志。
  • 生产环境调用前应通过部署流程或权限控制额外确认。

后端实现注意事项

  • Admin API 默认使用 require_admin,登录接口除外。
  • 所有外部输入通过 Pydantic schema 校验。
  • 变更类接口需要继续写入 AuditLog
  • 响应字段使用 camelCase以匹配当前前端类型。
  • 允许后端返回额外字段,但不要移除本文列出的前端依赖字段。
  • 当前管理端不会直接上传图片,只维护图片 URL媒体库接口暂不属于本需求范围。