23 KiB
23 KiB
计划广场与 Token 商店前端对接说明
1. 文档目标
本文面向前端页面设计和接口联调,先把 P0 页面所需接口、字段、状态和交互流程定成初版。
当前确认口径:
- 论坛展示名使用“计划广场”。
- 前端正常展示评论,评论支持多层回复。
- 用户可以删除自己的评论;P0 暂不引入管理员删评、举报和审核流。
- 点赞奖励只给帖子作者。
- 同一用户对同一计划只允许导入一次,奖励随该次导入记录一次。
- 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
}
前端处理建议:
status === "10000"视为成功。- 非成功状态优先展示
info。 - 表单类错误可按接口返回的
info直接提示,不需要前端硬编码太多文案。
2.3 幂等请求头
以下写操作建议前端带 X-Idempotency-Key:
- 发布计划。
- 发表评论。
- 一键导入。
- 创建订单。
- 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 计划广场
前端建议页面:
- 计划广场列表页:展示公开计划卡片,支持排序、标签筛选和搜索。
- 计划详情页 / 详情抽屉:展示计划说明、任务条目预览、点赞、评论和导入按钮。
- 发布计划弹窗 / 页面:选择自己的 TaskClass,填写标题、简介、标签后发布。
- 评论区:展示多层评论树,支持回复和删除自己的评论。
3.2 Token 商店
前端建议页面:
- Token 商品页:展示 Token 包商品卡片。
- 订单确认 / mock paid 页:P0 可直接展示“确认支付”按钮。
- Token 获取记录页:展示购买、点赞奖励、导入奖励等账本记录。
- 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
}
}
前端展示建议:
- 卡片主标题用
title。 - 副文案用
summary,最多展示两到三行。 - 标签 chips 用
tags。 - 统计区展示点赞、评论、导入次数。
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"
}
}
前端流程建议:
- 先调用
GET /api/v1/task-class/list获取用户自己的计划。 - 用户选择一个 TaskClass 后填写发布信息。
- 发布成功后跳转到详情页或刷新计划广场列表。
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 道综合题。"
}
]
}
}
}
说明:
items_preview是模板快照,不是原作者当前 TaskClass。- 不展示
embedded_time、schedule 绑定和用户私有排程状态。 - 评论区单独调用评论树接口,避免详情首屏过重。
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
}
}
}
说明:
- 同一用户对同一帖子只能点赞一次。
- 点赞奖励只给作者。
- 取消点赞不回滚已经记录的作者奖励,避免账本反复冲正。
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"
}
}
说明:
- 只能删除自己的评论。
- 删除是软删除,不删除子回复。
- 非本人删除返回失败,前端展示
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"
}
}
说明:
- 一键导入只创建当前用户自己的 TaskClass 副本。
- 不直接写 schedule。
- 同一用户同一帖子只允许导入一次;导入成功后前端根据返回结果刷新
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 |
已同步到权威额度的 Token,P0 通常为 0 |
pending_apply_token_total |
已记录但尚未同步到权威额度的 Token |
quota_sync_status |
not_connected / partial / synced |
前端展示建议:
- P0 可以展示“累计获取 Token”,不要写成“当前可用 Token”。
quota_sync_status === "not_connected"时,展示轻提示,不阻塞购买流程。
6.2 商品列表
GET /api/v1/token-store/products
说明:
- 商品从
token_products表读取。 - 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
}
]
}
}
前端展示建议:
- 只展示
status === "active"的商品。 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
}
}
}
说明:
- P0 不接真实支付网关。
status === "granted"表示已经写入token-store获取记录。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 计划广场列表页
- 首屏调用计划列表接口,默认
sort=latest。 - 顶部筛选可放搜索框、排序切换和热门标签。
- 卡片 CTA 建议包含“查看详情”和“一键导入”。
- 点赞按钮可在卡片上直接操作,成功后用接口返回的
like_count覆盖本地计数。
7.2 计划详情页
- 进入详情页先拉计划详情。
- 评论区单独拉评论树。
- 发表评论或删除评论成功后,建议重新拉评论树,避免多层结构局部更新出错。
- 一键导入成功后,可弹出成功反馈,并提供“查看我的 TaskClass”入口。
7.3 发布计划
- 打开发布弹窗时调用现有
GET /api/v1/task-class/list。 - 用户选择 TaskClass 后填写标题、简介、标签。
- 发布按钮需要防重复点击,并发送
X-Idempotency-Key。 - 发布成功后跳转详情页或刷新列表。
7.4 Token 商店
- 首屏并行调用 Token 概览和商品列表。
- 商品卡片点击“购买”后创建订单。
- P0 下单后展示 mock paid 确认按钮。
- mock paid 成功后刷新 Token 概览和获取记录。
- 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 不需要前端处理的内容
- 不做计划推荐算法,只做排序和筛选。
- 不做关注、私信、用户主页。
- 不做富文本,简介和评论都是纯文本。
- 不做管理员删评、举报和审核后台。
- 不接真实支付网关。
- 不直接展示
user/auth权威可用额度,除非后续接口已经切通。