Files
Arkie-Library-Frontend/.unipi/docs/specs/2026-05-25-posts-api-contract.md
2026-05-26 07:36:53 +08:00

8.1 KiB
Raw Blame History

title, type, date, audience, status
title type date audience status
Posts API Contract (for backend) api-contract 2026-05-25 backend draft

Posts API Contract

这份文档是从 2026-05-25-telegram-style-resource-stream-design.md §1§2 抽出,供后端实现使用。前端已用 mock data 完成视觉;上线时把 VITE_USE_MOCK_POSTS=false 即可切真接口。

1. 数据模型

type AttachmentKind = "image" | "video" | "document";

type Attachment = {
  id: string; // 唯一 id
  kind: AttachmentKind; // 三大类,前端按此分支渲染
  url: string; // 原始文件地址
  mime: string; // image/jpeg, application/pdf, video/mp4, ...
  filename: string; // 显示用文件名,含扩展名
  sizeBytes: number; // 字节数;前端格式化为 "3.5 MB"
  width?: number; // image/video 用于占位比例CLS 优化)
  height?: number;
  durationSec?: number; // video 专用
  posterUrl?: string; // video 海报缩略图
  thumbnailUrl?: string; // image 缩略,列表用减少流量
};

type Post = {
  id: string;
  categoryId: number;
  categorySlug: string;
  language: string; // "zh" | "en" | "ja" | "ko" | "vi" | "id" | "ms"
  text?: string; // 可选,纯文本/图说;前端做 https → 链接自动识别
  attachments: Attachment[]; // 0~Ntext-only post 时为 []
  isRecommended: boolean;
  publishedAt: string; // ISO 8601用于排序 + 日期分组
  updatedAt: string;
};

type PostListResponse = {
  items: Post[];
  nextCursor?: string; // 不透明 cursorundefined = 没有下一页
};

关键约定

  • 图片当文档(在前端显示为「文件下载卡」):kind === "document"mime.startsWith("image/")。Admin 上传时通过开关决定走 image 还是 document 通道。
  • 图片当图片(前端显示为图片预览):kind === "image"
  • 多图相册:一个 Post 带多个 kind === "image" 的 attachments。前端会在 2-4 grid 中渲染attachments.length > 4 时第 4 格模糊 + +N
  • 图片 + 文字Post 同时有 text 与 attachments。
  • 纯文本 / 链接Post 仅有 textattachments: []
  • 视频kind === "video" 单 attachment。posterUrl 用于预览,durationSec 用于角标。
  • Attachment 内不携带任何「上传者头像 / 管理员标签」等社交字段(前端已下线)。

2. Endpoints

2.1 列表(核心)

GET /api/posts

Query 参数:

参数 必填 说明
lang UI 语言;后端可据此选择不同语言版本的 text
category category slug不传 = 全部分类
type all / image / video / pdf / ppt / text / link / archive;语义见 §3
language 资源语言:zh / en / ja / ko / vi / id / ms
cursor 上一次返回的 nextCursor;不传 = 第一页
limit 默认 20最大 50

返回:PostListResponse

排序:publishedAt DESC

2.2 Home 用聚合接口(可选,沿用现状)

GET /api/posts/recommended?lang=&limit=
GET /api/posts/latest?lang=&limit=

返回:{ items: Post[] }(不分页)

2.3 单条(用于老链接 301 落地)

GET /api/posts/:id

返回:Post(或 404

前端 /resource/:id 现在是轻量重定向:拿到 categorySlug/category/<slug>#post-<id> 锚点滚动。

2.4 分类(不变)

GET /api/categories?lang=

返回:现有 Category[]

2.5 Admin CRUD

POST   /api/admin/posts
PUT    /api/admin/posts/:id
DELETE /api/admin/posts/:id
GET    /api/admin/posts?...  (含未发布草稿)

需求:

  • 支持多附件上传(一次 multipart 或先 POST /api/admin/upload 拿到 url 再创建 Post
  • Admin UI 需要一个开关:「图片以图片形式呈现 / 以文档形式呈现」,对应 attachment.kind 的 image vs document。
  • 支持发布/隐藏、置顶/官方推荐。

Admin UI 改造单独建 spec / plan本契约仅说明后端必须支持这些字段。

3. type 参数语义

一个 Post 命中某个 type,规则:

type 命中条件
all 全部
image attachments 中至少一个 kind === "image"mime.startsWith("image/")
video 至少一个 kind === "video"mime.startsWith("video/")
pdf 至少一个 mime === "application/pdf" 或扩展名为 pdf
ppt 至少一个扩展名为 ppt / pptx / key 或 mime 含 presentation
archive 至少一个扩展名为 zip / rar / 7z / tar / gz
text text 非空
link text 非空且匹配 https?://

前端 mock 已按此规则过滤,便于切真接口时口径一致。

4. 删除 / 废弃

处理
POST /api/resources/:id/favorite 删除
GET /api/favorites / 收藏列表 删除(前端 /favorites 路由已移除)
/r/:id 老前端路由 已合并到 /resource/:id 重定向
/api/resources* 系列 后端可保留过渡期。建议提供数据迁移脚本:每个老 Resource → 一个 Post带 1 个 attachment 或 text-onlyisRecommended / language / categorySlug 字段迁移;favorite count 字段丢弃。
Resource.coverImage 与 Resource.fileUrl 二选一 转为 attachments[0]kind 由后端判断 image vs document

GET /api/resources?q=... 当前仍被 SearchPage 使用(在新 schema 上线前过渡)。后端可视情况:

  • 短期:保留旧接口
  • 长期:新增 GET /api/posts/search?q=... 返回 PostListResponse,前端再切

6. 错误格式

沿用现状HTTP 状态码 + 文本 body。前端 getJSON 会把非 2xx 当作 Error(text) 抛出,MessageStream 显示红色横幅 + 重试按钮。

7. 兼容性 / 灰度

后端 ready 时步骤:

  1. 把示例数据导入到 Posts 表
  2. /api/posts 在 staging 通过
  3. 前端 staging 部署设 VITE_USE_MOCK_POSTS=false,跑通后再 prod
  4. 前端代码层面:删除 src/mocks/mockPosts.tsusePostStream.ts 中的 mock 分支,或保留 mock 用于本地离线开发

8. 联系

前端Terry。Spec 主文档:.unipi/docs/specs/2026-05-25-telegram-style-resource-stream-design.md