6.3 KiB
6.3 KiB
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-TW" | "zh-CN" | "en"
text?: string; // 可选,纯文本/图说;前端做 https → 链接自动识别
attachments: Attachment[]; // 0~N;text-only post 时为 []
isRecommended: boolean;
publishedAt: string; // ISO 8601;用于排序 + 日期分组
updatedAt: string;
};
type PostListResponse = {
items: Post[];
nextCursor?: string; // 不透明 cursor;undefined = 没有下一页
};
关键约定
- 图片当文档(在前端显示为「文件下载卡」):
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 仅有
text,attachments: []。 - 视频:
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-TW / zh-CN / en |
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-only)。isRecommended / language / categorySlug 字段迁移;favorite count 字段丢弃。 |
| Resource.coverImage 与 Resource.fileUrl 二选一 | 转为 attachments[0](kind 由后端判断 image vs document) |
5. Search
GET /api/resources?q=... 当前仍被 SearchPage 使用(在新 schema 上线前过渡)。后端可视情况:
- 短期:保留旧接口
- 长期:新增
GET /api/posts/search?q=...返回PostListResponse,前端再切
6. 错误格式
沿用现状(HTTP 状态码 + 文本 body)。前端 getJSON 会把非 2xx 当作 Error(text) 抛出,MessageStream 显示红色横幅 + 重试按钮。
7. 兼容性 / 灰度
后端 ready 时步骤:
- 把示例数据导入到 Posts 表
/api/posts在 staging 通过- 前端 staging 部署设
VITE_USE_MOCK_POSTS=false,跑通后再 prod - 前端代码层面:删除
src/mocks/mockPosts.ts与usePostStream.ts中的 mock 分支,或保留 mock 用于本地离线开发
8. 联系
前端:Terry。Spec 主文档:.unipi/docs/specs/2026-05-25-telegram-style-resource-stream-design.md。