Files
smartmate/docs/frontend/计划广场与Token商店对接说明.md

23 KiB
Raw Blame History

计划广场与 Token 商店前端对接说明

1. 文档目标

本文面向前端页面设计和接口联调,先把 P0 页面所需接口、字段、状态和交互流程定成初版。

当前确认口径:

  1. 论坛展示名使用“计划广场”。
  2. 前端正常展示评论,评论支持多层回复。
  3. 用户可以删除自己的评论P0 暂不引入管理员删评、举报和审核流。
  4. 点赞奖励只给帖子作者。
  5. 同一用户对同一计划只允许导入一次,奖励随该次导入记录一次。
  6. Token 发放本轮不改 user/auth,先在 token-store 内封装 Token 获取途径和后续发放出口。

2. 通用约定

2.1 鉴权

P0 所有接口都需要登录态。

Authorization: Bearer <access_token>

2.2 统一响应壳

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {}
}

失败响应:

{
  "status": "40004",
  "info": "计划不存在或已下架",
  "data": null
}

前端处理建议:

  1. status === "10000" 视为成功。
  2. 非成功状态优先展示 info
  3. 表单类错误可按接口返回的 info 直接提示,不需要前端硬编码太多文案。

2.3 幂等请求头

以下写操作建议前端带 X-Idempotency-Key

  1. 发布计划。
  2. 发表评论。
  3. 一键导入。
  4. 创建订单。
  5. mock paid。
X-Idempotency-Key: plan_square_publish_1710000000000_abcd

2.4 时间格式

所有时间字段使用 ISO 8601 字符串,带时区。

"2026-05-04T20:30:00+08:00"

2.5 分页结构

分页接口统一返回:

{
  "items": [],
  "page": 1,
  "page_size": 20,
  "total": 125,
  "has_more": true
}

3. 页面总览

3.1 计划广场

前端建议页面:

  1. 计划广场列表页:展示公开计划卡片,支持排序、标签筛选和搜索。
  2. 计划详情页 / 详情抽屉:展示计划说明、任务条目预览、点赞、评论和导入按钮。
  3. 发布计划弹窗 / 页面:选择自己的 TaskClass填写标题、简介、标签后发布。
  4. 评论区:展示多层评论树,支持回复和删除自己的评论。

3.2 Token 商店

前端建议页面:

  1. Token 商品页:展示 Token 包商品卡片。
  2. 订单确认 / mock paid 页P0 可直接展示“确认支付”按钮。
  3. Token 获取记录页:展示购买、点赞奖励、导入奖励等账本记录。
  4. Token 概览组件:展示本服务记录的累计获取 Token。P0 不承诺已经同步到 user/auth 可用额度。

4. 路由总览

4.1 计划广场接口

功能 方法 路径
计划列表 GET /api/v1/plan-square/posts
热门标签 GET /api/v1/plan-square/tags
发布计划 POST /api/v1/plan-square/posts
计划详情 GET /api/v1/plan-square/posts/{post_id}
点赞 POST /api/v1/plan-square/posts/{post_id}/like
取消点赞 DELETE /api/v1/plan-square/posts/{post_id}/like
评论树 GET /api/v1/plan-square/posts/{post_id}/comments
发表评论 / 回复 POST /api/v1/plan-square/posts/{post_id}/comments
删除自己的评论 DELETE /api/v1/plan-square/comments/{comment_id}
一键导入 POST /api/v1/plan-square/posts/{post_id}/import

发布计划时TaskClass 下拉列表复用现有接口:

GET /api/v1/task-class/list

4.2 Token 商店接口

功能 方法 路径
Token 概览 GET /api/v1/token-store/summary
商品列表 GET /api/v1/token-store/products
创建订单 POST /api/v1/token-store/orders
订单列表 GET /api/v1/token-store/orders
订单详情 GET /api/v1/token-store/orders/{order_id}
mock paid POST /api/v1/token-store/orders/{order_id}/mock-paid
Token 获取记录 GET /api/v1/token-store/grants

5. 计划广场接口详情

5.1 计划列表

GET /api/v1/plan-square/posts?page=1&page_size=20&sort=latest&keyword=数学&tag=考研

查询参数:

字段 类型 必填 说明
page number 默认 1
page_size number 默认 20,最大建议 50
sort string latest / likes / imports
keyword string 搜索标题和简介
tag string 标签筛选

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "items": [
      {
        "post_id": 10001,
        "title": "30 天高数强化复习计划",
        "summary": "适合期末前一个月快速过完重点题型。",
        "tags": ["高数", "期末", "30天"],
        "author": {
          "user_id": 88,
          "nickname": "小鹿同学",
          "avatar_url": ""
        },
        "template_summary": {
          "task_count": 24,
          "mode": "date_range",
          "start_date": "2026-05-05",
          "end_date": "2026-06-04",
          "strategy_labels": ["每日推进", "错题复盘"]
        },
        "counters": {
          "like_count": 128,
          "comment_count": 32,
          "import_count": 45
        },
        "viewer_state": {
          "liked": false,
          "imported_once": true
        },
        "status": "published",
        "created_at": "2026-05-04T20:30:00+08:00"
      }
    ],
    "page": 1,
    "page_size": 20,
    "total": 125,
    "has_more": true
  }
}

前端展示建议:

  1. 卡片主标题用 title
  2. 副文案用 summary,最多展示两到三行。
  3. 标签 chips 用 tags
  4. 统计区展示点赞、评论、导入次数。
  5. viewer_state.liked 控制点赞按钮初始态。

5.2 热门标签

GET /api/v1/plan-square/tags?limit=20

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "items": [
      { "tag": "考研", "post_count": 30 },
      { "tag": "高数", "post_count": 18 }
    ]
  }
}

5.3 发布计划

POST /api/v1/plan-square/posts
Content-Type: application/json
X-Idempotency-Key: plan_square_publish_1710000000000_abcd

请求体:

{
  "task_class_id": 42,
  "title": "30 天高数强化复习计划",
  "summary": "适合期末前一个月快速过完重点题型。",
  "tags": ["高数", "期末", "30天"]
}

字段限制:

字段 规则
task_class_id 必填,只能选择当前用户自己的 TaskClass
title 必填,建议 4 到 40 字
summary 可选,建议 0 到 300 字
tags 可选,最多 5 个,每个最多 12 字

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "post_id": 10001,
    "title": "30 天高数强化复习计划",
    "status": "published",
    "created_at": "2026-05-04T20:30:00+08:00"
  }
}

前端流程建议:

  1. 先调用 GET /api/v1/task-class/list 获取用户自己的计划。
  2. 用户选择一个 TaskClass 后填写发布信息。
  3. 发布成功后跳转到详情页或刷新计划广场列表。

5.4 计划详情

GET /api/v1/plan-square/posts/{post_id}

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "post": {
      "post_id": 10001,
      "title": "30 天高数强化复习计划",
      "summary": "适合期末前一个月快速过完重点题型。",
      "tags": ["高数", "期末", "30天"],
      "author": {
        "user_id": 88,
        "nickname": "小鹿同学",
        "avatar_url": ""
      },
      "counters": {
        "like_count": 128,
        "comment_count": 32,
        "import_count": 45
      },
      "viewer_state": {
        "liked": false,
        "imported_once": true
      },
      "created_at": "2026-05-04T20:30:00+08:00"
    },
    "template": {
      "mode": "date_range",
      "start_date": "2026-05-05",
      "end_date": "2026-06-04",
      "strategy_labels": ["每日推进", "错题复盘"],
      "task_count": 24,
      "items_preview": [
        {
          "item_id": 90001,
          "order": 1,
          "content": "复习极限与连续,完成基础例题。"
        },
        {
          "item_id": 90002,
          "order": 2,
          "content": "整理导数公式,完成 10 道综合题。"
        }
      ]
    }
  }
}

说明:

  1. items_preview 是模板快照,不是原作者当前 TaskClass。
  2. 不展示 embedded_time、schedule 绑定和用户私有排程状态。
  3. 评论区单独调用评论树接口,避免详情首屏过重。

5.5 点赞

POST /api/v1/plan-square/posts/{post_id}/like

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "post_id": 10001,
    "liked": true,
    "like_count": 129,
    "reward_hint": {
      "receiver": "author",
      "status": "recorded",
      "amount": 1
    }
  }
}

说明:

  1. 同一用户对同一帖子只能点赞一次。
  2. 点赞奖励只给作者。
  3. 取消点赞不回滚已经记录的作者奖励,避免账本反复冲正。

5.6 取消点赞

DELETE /api/v1/plan-square/posts/{post_id}/like

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "post_id": 10001,
    "liked": false,
    "like_count": 128
  }
}

5.7 评论树

GET /api/v1/plan-square/posts/{post_id}/comments?page=1&page_size=20&sort=oldest

查询参数:

字段 类型 必填 说明
page number 根评论分页,默认 1
page_size number 根评论分页大小,默认 20
sort string oldest / latest,默认 oldest

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "items": [
      {
        "comment_id": 50001,
        "post_id": 10001,
        "parent_comment_id": null,
        "content": "这个计划很适合期末冲刺。",
        "status": "visible",
        "author": {
          "user_id": 91,
          "nickname": "西瓜同学",
          "avatar_url": ""
        },
        "can_delete": true,
        "created_at": "2026-05-04T20:40:00+08:00",
        "deleted_at": null,
        "children": [
          {
            "comment_id": 50002,
            "post_id": 10001,
            "parent_comment_id": 50001,
            "content": "我也准备照这个导入一份。",
            "status": "visible",
            "author": {
              "user_id": 92,
              "nickname": "青柠同学",
              "avatar_url": ""
            },
            "can_delete": false,
            "created_at": "2026-05-04T20:42:00+08:00",
            "deleted_at": null,
            "children": []
          }
        ]
      }
    ],
    "page": 1,
    "page_size": 20,
    "total": 32,
    "has_more": true
  }
}

评论状态:

status 前端展示
visible 正常展示 content
deleted 展示“该评论已删除”,保留子回复

5.8 发表评论 / 回复

POST /api/v1/plan-square/posts/{post_id}/comments
Content-Type: application/json
X-Idempotency-Key: plan_square_comment_1710000000000_abcd

请求体:

{
  "content": "这个计划很适合期末冲刺。",
  "parent_comment_id": null
}

回复某条评论时:

{
  "content": "我也准备照这个导入一份。",
  "parent_comment_id": 50001
}

字段限制:

字段 规则
content 必填,建议 1 到 500 字
parent_comment_id 可选,传 null 表示根评论

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "comment_id": 50003,
    "post_id": 10001,
    "parent_comment_id": 50001,
    "content": "我也准备照这个导入一份。",
    "status": "visible",
    "can_delete": true,
    "created_at": "2026-05-04T20:45:00+08:00"
  }
}

5.9 删除自己的评论

DELETE /api/v1/plan-square/comments/{comment_id}

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "comment_id": 50003,
    "status": "deleted",
    "content": "",
    "deleted_at": "2026-05-04T20:50:00+08:00"
  }
}

说明:

  1. 只能删除自己的评论。
  2. 删除是软删除,不删除子回复。
  3. 非本人删除返回失败,前端展示 info 即可。

5.10 一键导入

POST /api/v1/plan-square/posts/{post_id}/import
Content-Type: application/json
X-Idempotency-Key: plan_square_import_1710000000000_abcd

请求体:

{
  "target_title": "我的 30 天高数强化计划"
}

字段说明:

字段 必填 说明
target_title 导入后的 TaskClass 名称;不传则后端生成默认名称

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "import_id": 70001,
    "post_id": 10001,
    "new_task_class_id": 430,
    "task_class_title": "我的 30 天高数强化计划",
    "import_count": 46,
    "reward_hint": {
      "receiver": "author",
      "status": "recorded",
      "amount": 2
    },
    "next_action": {
      "type": "open_task_class",
      "task_class_id": 430
    },
    "created_at": "2026-05-04T20:55:00+08:00"
  }
}

说明:

  1. 一键导入只创建当前用户自己的 TaskClass 副本。
  2. 不直接写 schedule。
  3. 同一用户同一帖子只允许导入一次;导入成功后前端根据返回结果刷新 imported_once 和按钮状态。

6. Token 商店接口详情

6.1 Token 概览

GET /api/v1/token-store/summary

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "recorded_token_total": 120,
    "applied_token_total": 0,
    "pending_apply_token_total": 120,
    "quota_sync_status": "not_connected",
    "tip": "当前为 Token 获取记录,后续会切换到 user/auth 权威额度。"
  }
}

字段说明:

字段 说明
recorded_token_total token-store 内已经记录的累计获取 Token
applied_token_total 已同步到权威额度的 TokenP0 通常为 0
pending_apply_token_total 已记录但尚未同步到权威额度的 Token
quota_sync_status not_connected / partial / synced

前端展示建议:

  1. P0 可以展示“累计获取 Token”不要写成“当前可用 Token”。
  2. quota_sync_status === "not_connected" 时,展示轻提示,不阻塞购买流程。

6.2 商品列表

GET /api/v1/token-store/products

说明:

  1. 商品从 token_products 表读取。
  2. P0 商品由后端 seed 初始化,不做商品管理后台。

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "items": [
      {
        "product_id": 1,
        "name": "基础 Token 包",
        "description": "适合轻量使用 Agent。",
        "token_amount": 100,
        "price_cent": 990,
        "price_text": "¥9.90",
        "currency": "CNY",
        "badge": "入门",
        "status": "active",
        "sort_order": 10
      },
      {
        "product_id": 2,
        "name": "进阶 Token 包",
        "description": "适合高频规划和复盘。",
        "token_amount": 300,
        "price_cent": 1990,
        "price_text": "¥19.90",
        "currency": "CNY",
        "badge": "推荐",
        "status": "active",
        "sort_order": 20
      }
    ]
  }
}

前端展示建议:

  1. 只展示 status === "active" 的商品。
  2. badge 可作为角标,不保证一定有值。

6.3 创建订单

POST /api/v1/token-store/orders
Content-Type: application/json
X-Idempotency-Key: token_order_1710000000000_abcd

请求体:

{
  "product_id": 1,
  "quantity": 1
}

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "order_id": 80001,
    "order_no": "TS202605042055000001",
    "status": "pending",
    "product_snapshot": {
      "product_id": 1,
      "name": "基础 Token 包",
      "token_amount": 100
    },
    "quantity": 1,
    "token_amount": 100,
    "amount_cent": 990,
    "price_text": "¥9.90",
    "currency": "CNY",
    "payment_mode": "mock",
    "payment_action": {
      "type": "mock_paid",
      "label": "确认支付"
    },
    "created_at": "2026-05-04T20:55:00+08:00"
  }
}

6.4 订单列表

GET /api/v1/token-store/orders?page=1&page_size=20&status=pending

查询参数:

字段 类型 必填 说明
page number 默认 1
page_size number 默认 20
status string pending / paid / granted / closed

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "items": [
      {
        "order_id": 80001,
        "order_no": "TS202605042055000001",
        "status": "pending",
        "product_name": "基础 Token 包",
        "token_amount": 100,
        "price_text": "¥9.90",
        "created_at": "2026-05-04T20:55:00+08:00",
        "paid_at": null,
        "granted_at": null
      }
    ],
    "page": 1,
    "page_size": 20,
    "total": 1,
    "has_more": false
  }
}

订单状态:

status 前端含义
pending 待支付
paid 已支付,等待写入获取记录
granted 已写入 Token 获取记录
closed 已关闭

6.5 订单详情

GET /api/v1/token-store/orders/{order_id}

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "order_id": 80001,
    "order_no": "TS202605042055000001",
    "status": "pending",
    "product_snapshot": {
      "product_id": 1,
      "name": "基础 Token 包",
      "token_amount": 100
    },
    "quantity": 1,
    "token_amount": 100,
    "amount_cent": 990,
    "price_text": "¥9.90",
    "currency": "CNY",
    "payment_mode": "mock",
    "grant": null,
    "created_at": "2026-05-04T20:55:00+08:00",
    "paid_at": null,
    "granted_at": null
  }
}

6.6 mock paid

POST /api/v1/token-store/orders/{order_id}/mock-paid
Content-Type: application/json
X-Idempotency-Key: token_mock_paid_1710000000000_abcd

请求体:

{
  "mock_channel": "dev"
}

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "order_id": 80001,
    "order_no": "TS202605042055000001",
    "status": "granted",
    "paid_at": "2026-05-04T21:00:00+08:00",
    "granted_at": "2026-05-04T21:00:01+08:00",
    "grant": {
      "grant_id": 90001,
      "event_id": "order:80001:paid",
      "source": "purchase",
      "amount": 100,
      "status": "recorded",
      "quota_applied": false
    }
  }
}

说明:

  1. P0 不接真实支付网关。
  2. status === "granted" 表示已经写入 token-store 获取记录。
  3. quota_applied === false 表示本轮未同步到 user/auth 权威额度。

6.7 Token 获取记录

GET /api/v1/token-store/grants?page=1&page_size=20&source=purchase

查询参数:

字段 类型 必填 说明
page number 默认 1
page_size number 默认 20
source string purchase / forum_like / forum_import / manual

成功响应:

{
  "status": "10000",
  "info": "success",
  "data": {
    "items": [
      {
        "grant_id": 90001,
        "event_id": "order:80001:paid",
        "source": "purchase",
        "source_label": "购买 Token 包",
        "amount": 100,
        "status": "recorded",
        "quota_applied": false,
        "description": "购买基础 Token 包",
        "created_at": "2026-05-04T21:00:01+08:00"
      },
      {
        "grant_id": 90002,
        "event_id": "forum.post.liked:10001:91",
        "source": "forum_like",
        "source_label": "计划被点赞",
        "amount": 1,
        "status": "recorded",
        "quota_applied": false,
        "description": "你的计划《30 天高数强化复习计划》获得点赞",
        "created_at": "2026-05-04T21:05:00+08:00"
      }
    ],
    "page": 1,
    "page_size": 20,
    "total": 2,
    "has_more": false
  }
}

获取记录状态:

status 前端含义
recorded 已记录获取事实P0 默认状态
applied 已同步到权威额度,后续切 user/auth 后使用
skipped 幂等重复或规则不发放
failed 写入或后续发放失败

7. 页面交互建议

7.1 计划广场列表页

  1. 首屏调用计划列表接口,默认 sort=latest
  2. 顶部筛选可放搜索框、排序切换和热门标签。
  3. 卡片 CTA 建议包含“查看详情”和“一键导入”。
  4. 点赞按钮可在卡片上直接操作,成功后用接口返回的 like_count 覆盖本地计数。

7.2 计划详情页

  1. 进入详情页先拉计划详情。
  2. 评论区单独拉评论树。
  3. 发表评论或删除评论成功后,建议重新拉评论树,避免多层结构局部更新出错。
  4. 一键导入成功后,可弹出成功反馈,并提供“查看我的 TaskClass”入口。

7.3 发布计划

  1. 打开发布弹窗时调用现有 GET /api/v1/task-class/list
  2. 用户选择 TaskClass 后填写标题、简介、标签。
  3. 发布按钮需要防重复点击,并发送 X-Idempotency-Key
  4. 发布成功后跳转详情页或刷新列表。

7.4 Token 商店

  1. 首屏并行调用 Token 概览和商品列表。
  2. 商品卡片点击“购买”后创建订单。
  3. P0 下单后展示 mock paid 确认按钮。
  4. mock paid 成功后刷新 Token 概览和获取记录。
  5. P0 文案建议使用“累计获取 Token”暂不写“可用 Token 已到账”。

8. 前端类型参考

type ApiResponse<T> = {
  status: string
  info: string
  data: T
}

type PageData<T> = {
  items: T[]
  page: number
  page_size: number
  total: number
  has_more: boolean
}

type UserBrief = {
  user_id: number
  nickname: string
  avatar_url: string
}

type PlanSquarePostCard = {
  post_id: number
  title: string
  summary: string
  tags: string[]
  author: UserBrief
  template_summary: {
    task_count: number
    mode: string
    start_date: string
    end_date: string
    strategy_labels: string[]
  }
  counters: {
    like_count: number
    comment_count: number
    import_count: number
  }
  viewer_state: {
    liked: boolean
    imported_once: boolean
  }
  status: 'published'
  created_at: string
}

type CommentNode = {
  comment_id: number
  post_id: number
  parent_comment_id: number | null
  content: string
  status: 'visible' | 'deleted'
  author: UserBrief
  can_delete: boolean
  created_at: string
  deleted_at: string | null
  children: CommentNode[]
}

type TokenProduct = {
  product_id: number
  name: string
  description: string
  token_amount: number
  price_cent: number
  price_text: string
  currency: 'CNY'
  badge: string
  status: 'active' | 'inactive'
  sort_order: number
}

type TokenGrant = {
  grant_id: number
  event_id: string
  source: 'purchase' | 'forum_like' | 'forum_import' | 'manual'
  source_label: string
  amount: number
  status: 'recorded' | 'applied' | 'skipped' | 'failed'
  quota_applied: boolean
  description: string
  created_at: string
}

9. P0 不需要前端处理的内容

  1. 不做计划推荐算法,只做排序和筛选。
  2. 不做关注、私信、用户主页。
  3. 不做富文本,简介和评论都是纯文本。
  4. 不做管理员删评、举报和审核后台。
  5. 不接真实支付网关。
  6. 不直接展示 user/auth 权威可用额度,除非后续接口已经切通。