Fork goose with custom MCP widgets, platform extensions (aider, git, web, search), MindSpace H5 backend/frontend, Plaza/Ops UIs, and deploy scripts for tkmind.cn. Co-authored-by: Cursor <cursoragent@cursor.com>
12 KiB
sidebar_position, title, sidebar_label, description
| sidebar_position | title | sidebar_label | description |
|---|---|---|---|
| 5 | 后端 API 契约 | 后端 API | Plaza 和运营后台所有 API 的路由、请求/响应结构、权限规则和错误码 |
后端 API 契约
通用规范
- 所有路由前缀:
/api/plaza/v1/ - 运营路由前缀:
/api/ops/v1/ - 请求体格式:
application/json - 响应格式:
{ "data": ... }或{ "error": { "code": "...", "message": "..." } } - 分页:游标分页,参数
cursor(上一页最后一条的id)和limit(默认 20,最大 50) - 所有写接口需要登录,读接口游客可访问
- 所有接口携带
X-Request-ID响应头
Plaza 公开 API
GET /api/plaza/v1/feed
获取广场 Feed 列表(热门或最新)。
权限:无需登录
Query 参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
sort |
hot | new |
hot |
排序方式 |
category |
string | - | 分类 slug,不传则全部 |
cursor |
string | - | 游标翻页 |
limit |
number | 20 | 每页数量 |
响应:
{
"data": {
"posts": [ ... ],
"featured": {
"homepage_banner": [ ... ],
"trending": [ ... ]
},
"next_cursor": "post_yyy",
"has_more": true
}
}
精选与 Feed 合并规则(Sprint 5 起):
featured.homepage_banner:单独返回,前端渲染在 Feed 上方轮播,不参与posts游标分页。featured.trending:在sort=hot且cursor为空时,插入posts列表前 10 位(按plaza_featured.sort_order),去重后其余位按hot_score填充。- Sprint 1–4:
featured字段为{ "homepage_banner": [], "trending": [] }。
单条 post 对象字段(posts 数组元素、GET /posts/:id 同构):
{
"id": "post_xxx",
"title": "2025年Q2项目复盘报告",
"summary": "本季度核心指标完成情况与下季度规划",
"cover_url": "https://assets.go.tkmind.cn/...",
"category": { "id": "cat_xxx", "name": "职场报告", "slug": "work-report" },
"tags": ["复盘", "季度报告"],
"author": {
"user_id": "user_xxx",
"slug": "zhangsan",
"display_name": "张三",
"avatar_url": "https://..."
},
"stats": {
"view_count": 1234,
"like_count": 89,
"collect_count": 23,
"comment_count": 15
},
"viewer_reacted": { "liked": false, "collected": true },
"published_at": "2025-06-12T10:30:00Z"
}
viewer_reacted 仅登录用户返回,游客为 null。
GET /api/plaza/v1/posts/:id
获取单个帖子详情。
权限:无需登录
响应:同 feed 中的单条 post 对象,额外包含:
{
"data": {
"post": {
...上方字段...,
"publication_url": "https://go.tkmind.cn/u/zhangsan/pages/q2-report",
"allow_comment": true
}
}
}
POST /api/plaza/v1/posts
将已发布的 MindSpace 页面发布到广场。
权限:需要登录,publication_id 必须归属当前用户
请求体:
{
"publication_id": "pub_xxx",
"category_id": "cat_xxx",
"tags": ["复盘", "季度报告"],
"cover_url": "https://...", // 可选,不传则用 publication 的封面
"allow_comment": true
}
业务校验:
publication_id对应的 publicationstatus = online,否则返回PUBLICATION_NOT_ONLINE。- 该
publication_id在plaza_posts中已存在 → 返回409 ALREADY_PUBLISHED(创建接口不支持更新,更新走PATCH)。 - 当前用户未被运营封禁发帖权限,否则返回
POST_PERMISSION_DENIED。 - 执行内容二次安全扫描(异步),帖子先以
status = pending_review入库;扫描通过且(人工或自动)审核通过后变为published。
Feed 可见性:仅 status = published 的帖子出现在公开 Feed;pending_review 仅作者本人在 MindSpace「我的广场帖」可见(Sprint 1 可暂不实现作者视图,但不得出现在 /plaza)。
PATCH /api/plaza/v1/posts/:id
更新已发布到广场的帖子元数据(分类、标签、封面、是否允许评论)。
权限:需要登录,只能操作自己的帖子
请求体(字段均可选,至少传一项):
{
"category_id": "cat_xxx",
"tags": ["复盘"],
"cover_url": "https://...",
"allow_comment": false
}
约束:不可更换 publication_id;若帖子 status = rejected,PATCH 后重新进入 pending_review。
响应:
{
"data": {
"post": {
"id": "post_xxx",
"status": "pending_review"
}
}
}
DELETE /api/plaza/v1/posts/:id
将帖子从广场撤回(仅 status 改为 hidden,不物理删除)。
权限:需要登录,只能操作自己的帖子
POST /api/plaza/v1/posts/:id/reports
举报帖子。
权限:需要登录
请求体:{ "reason": "spam", "detail": "可选说明" }(reason 枚举见 06-ops-platform)
响应:201
POST /api/plaza/v1/comments/:id/reports
举报评论。
权限:需要登录
请求体:同帖子举报 { "reason": "spam", "detail": "可选说明" }
响应:201
GET /api/plaza/v1/categories
获取所有分类列表。
权限:无需登录
响应:
{
"data": {
"categories": [
{
"id": "cat_xxx",
"name": "职场报告",
"slug": "work-report",
"icon": "📊",
"post_count": 1234
}
]
}
}
GET /api/plaza/v1/users/:slug
获取用户广场主页信息。
权限:无需登录
stats.total_likes:对该用户所有 status = published 帖子的 like_count 求和(见 02-data-model)。
响应:
{
"data": {
"user": {
"user_id": "user_xxx",
"slug": "zhangsan",
"display_name": "张三",
"avatar_url": "https://...",
"bio": "数据分析师,喜欢用 AI 做报告",
"stats": {
"post_count": 12,
"follower_count": 234,
"following_count": 56,
"total_likes": 1890
},
"viewer_following": false
},
"recent_posts": [ ...最新 6 篇帖子... ]
}
}
GET /api/plaza/v1/users/:slug/posts
获取用户发布的帖子列表(分页)。
权限:无需登录
互动 API
POST /api/plaza/v1/posts/:id/reactions
点赞、收藏或标记分享。
权限:需要登录
请求体:
{ "type": "like" } // like | collect | share
幂等:同一用户同一帖子同类型重复请求返回 200,不报错。
DELETE /api/plaza/v1/posts/:id/reactions/:type
取消点赞或收藏。
权限:需要登录,只能取消自己的 reaction
GET /api/plaza/v1/posts/:id/comments
获取帖子评论列表。
权限:无需登录
Query 参数:cursor、limit、parent_id(获取某条评论的回复时传入)
响应:
{
"data": {
"comments": [
{
"id": "cmt_xxx",
"content": "写得很好!",
"author": {
"user_id": "user_xxx",
"slug": "lisi",
"display_name": "李四",
"avatar_url": "https://..."
},
"like_count": 5,
"reply_count": 2,
"viewer_liked": false,
"created_at": "2025-06-12T11:00:00Z",
"status": "visible"
}
],
"next_cursor": "cmt_yyy",
"has_more": false
}
}
POST /api/plaza/v1/posts/:id/comments
发表评论。
权限:需要登录
请求体:
{
"content": "写得很好!",
"parent_id": null // 回复二级时传一级评论 ID
}
校验:
content不能为空,最长 500 字符。parent_id如果传入,必须是该帖子的一级评论(parent_id IS NULL),不允许三级嵌套。plaza_posts.allow_comment = 1才允许评论,否则返回COMMENT_DISABLED。
DELETE /api/plaza/v1/comments/:id
删除自己的评论(软删除)。
权限:需要登录,只能删除自己的评论
POST /api/plaza/v1/comments/:id/reactions
点赞评论。
权限:需要登录
关注 API
POST /api/plaza/v1/users/:slug/follow
关注用户。
权限:需要登录,不能关注自己
DELETE /api/plaza/v1/users/:slug/follow
取消关注。
权限:需要登录
运营 API(/api/ops/v1/)
所有运营 API 需要 ops_role 为 reviewer、editor 或 ops_admin 的用户。平台 role = admin 不等于 ops_admin,需在 h5_users.ops_role 单独分配。
双审核入口:Sprint 1 保留
/admin-api/plaza/posts/:id/review(requireAdmin);Sprint 5 起运营主路径为/api/ops/v1/review/*。两路径均写入ops_audit_log(admin 路径由服务端桥接reviewPostAsOps)。
GET /api/ops/v1/review/queue
获取待审核帖子队列。
Query 参数:status(默认 pending_review)、cursor、limit
POST /api/ops/v1/review/posts/:id
审核单个帖子。
请求体:
{
"action": "approve", // approve | reject | hide
"reason": "内容违规:含广告" // reject/hide 时必填
}
副作用:
approve→plaza_posts.status = published,写入ops_audit_logreject→plaza_posts.status = rejected,通知发布者,写入日志hide→plaza_posts.status = hidden,写入日志
POST /api/ops/v1/review/batch
批量审核帖子(editor 及以上)。
请求体:
{
"post_ids": ["post_a", "post_b"],
"action": "approve",
"reason": null
}
响应:{ "data": { "posts": [ { "id": "...", "status": "published" } ] } }
POST /api/ops/v1/featured
设置精选内容。
请求体:
{
"post_id": "post_xxx",
"position": "homepage_banner", // homepage_banner | category_top | trending
"expires_at": "2025-06-20T00:00:00Z"
}
GET /api/ops/v1/analytics/overview
广场数据概览(日/周数据)。
响应字段:新帖数、发布转化率、日活、分类分布、TOP 创作者、违规比率。
错误码
| 错误码 | HTTP 状态 | 含义 |
|---|---|---|
PUBLICATION_NOT_ONLINE |
422 | 发布源不是在线状态 |
ALREADY_PUBLISHED |
409 | 该 publication 已发布到广场 |
POST_NOT_FOUND |
404 | 帖子不存在或已隐藏 |
POST_PERMISSION_DENIED |
403 | 无发帖权限(被封禁) |
COMMENT_DISABLED |
422 | 该帖子已关闭评论 |
COMMENT_TOO_LONG |
422 | 评论超出 500 字符 |
REPLY_DEPTH_EXCEEDED |
422 | 不允许三级嵌套回复 |
SELF_FOLLOW |
422 | 不能关注自己 |
OPS_PERMISSION_DENIED |
403 | 非运营角色访问运营接口 |
与 MindSpace 的联动
publication 下线时自动隐藏广场帖子
在现有 MindSpace 下线 publication 的事务中,增加:
UPDATE plaza_posts
SET status = 'hidden', updated_at = :now_ms
WHERE publication_id = :publication_id
AND status != 'hidden';
实现位置:ui/h5/mindspace-publications.mjs 的 offline 流程,与现有事件写入同一连接事务。
广场互动数据回写 MindSpace
通过后台定时任务(每小时),在同一 MySQL 实例内将广场计数汇总到 h5_publish_records:
UPDATE h5_publish_records p
JOIN plaza_posts pp ON pp.publication_id = p.id
SET p.plaza_view_count = pp.view_count,
p.plaza_like_count = pp.like_count
WHERE pp.updated_at > :last_sync_time;
- 不调用 MindSpace service 函数;字段定义与迁移由 MindSpace/Plaza 共用
db.mjs维护。 - 回写失败不影响广场读写,下次任务重试。