Add TKMind platform extensions, H5/MindSpace stack, and deployment tooling.
Deploy Documentation / deploy (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / bundle-desktop-windows-cuda (push) Has been cancelled
Canary / Release (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
CI / Check MSRV (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
CI / H5 Plaza Tests and Build (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled

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>
This commit is contained in:
john
2026-06-14 21:30:20 +08:00
parent e5fd568e01
commit 4e21ca937a
359 changed files with 70658 additions and 56 deletions
@@ -0,0 +1,518 @@
---
sidebar_position: 5
title: 后端 API 契约
sidebar_label: 后端 API
description: 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 | 每页数量 |
**响应**
```json
{
"data": {
"posts": [ ... ],
"featured": {
"homepage_banner": [ ... ],
"trending": [ ... ]
},
"next_cursor": "post_yyy",
"has_more": true
}
}
```
**精选与 Feed 合并规则**Sprint 5 起):
1. `featured.homepage_banner`:单独返回,前端渲染在 Feed 上方轮播,**不参与** `posts` 游标分页。
2. `featured.trending`:在 `sort=hot``cursor` 为空时,插入 `posts` 列表前 10 位(按 `plaza_featured.sort_order`),去重后其余位按 `hot_score` 填充。
3. Sprint 14`featured` 字段为 `{ "homepage_banner": [], "trending": [] }`
**单条 post 对象字段**`posts` 数组元素、`GET /posts/:id` 同构):
```json
{
"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 对象,额外包含:
```json
{
"data": {
"post": {
......,
"publication_url": "https://go.tkmind.cn/u/zhangsan/pages/q2-report",
"allow_comment": true
}
}
}
```
---
### POST /api/plaza/v1/posts
将已发布的 MindSpace 页面发布到广场。
**权限**:需要登录,`publication_id` 必须归属当前用户
**请求体**
```json
{
"publication_id": "pub_xxx",
"category_id": "cat_xxx",
"tags": ["复盘", "季度报告"],
"cover_url": "https://...", // 可选,不传则用 publication 的封面
"allow_comment": true
}
```
**业务校验**
1. `publication_id` 对应的 publication `status = online`,否则返回 `PUBLICATION_NOT_ONLINE`
2.`publication_id``plaza_posts` 中已存在 → 返回 `409 ALREADY_PUBLISHED`**创建接口不支持更新**,更新走 `PATCH`)。
3. 当前用户未被运营封禁发帖权限,否则返回 `POST_PERMISSION_DENIED`
4. 执行内容二次安全扫描(异步),帖子先以 `status = pending_review` 入库;扫描通过且(人工或自动)审核通过后变为 `published`
**Feed 可见性**:仅 `status = published` 的帖子出现在公开 Feed`pending_review` 仅作者本人在 MindSpace「我的广场帖」可见(Sprint 1 可暂不实现作者视图,但不得出现在 `/plaza`)。
---
### PATCH /api/plaza/v1/posts/:id
更新已发布到广场的帖子元数据(分类、标签、封面、是否允许评论)。
**权限**:需要登录,只能操作自己的帖子
**请求体**(字段均可选,至少传一项):
```json
{
"category_id": "cat_xxx",
"tags": ["复盘"],
"cover_url": "https://...",
"allow_comment": false
}
```
**约束**:不可更换 `publication_id`;若帖子 `status = rejected`PATCH 后重新进入 `pending_review`
**响应**
```json
{
"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](./06-ops-platform)
**响应**`201`
---
### POST /api/plaza/v1/comments/:id/reports
举报评论。
**权限**:需要登录
**请求体**:同帖子举报 `{ "reason": "spam", "detail": "可选说明" }`
**响应**`201`
---
### GET /api/plaza/v1/categories
获取所有分类列表。
**权限**:无需登录
**响应**
```json
{
"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](./02-data-model))。
**响应**
```json
{
"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
点赞、收藏或标记分享。
**权限**:需要登录
**请求体**
```json
{ "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`(获取某条评论的回复时传入)
**响应**
```json
{
"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
发表评论。
**权限**:需要登录
**请求体**
```json
{
"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
审核单个帖子。
**请求体**
```json
{
"action": "approve", // approve | reject | hide
"reason": "内容违规:含广告" // reject/hide 时必填
}
```
**副作用**
- `approve``plaza_posts.status = published`,写入 `ops_audit_log`
- `reject``plaza_posts.status = rejected`,通知发布者,写入日志
- `hide``plaza_posts.status = hidden`,写入日志
---
### POST /api/ops/v1/review/batch
批量审核帖子(`editor` 及以上)。
**请求体**
```json
{
"post_ids": ["post_a", "post_b"],
"action": "approve",
"reason": null
}
```
**响应**`{ "data": { "posts": [ { "id": "...", "status": "published" } ] } }`
---
### POST /api/ops/v1/featured
设置精选内容。
**请求体**
```json
{
"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 的事务中,增加:
```sql
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`
```sql
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` 维护。
- 回写失败不影响广场读写,下次任务重试。