feat: align frontend languages with posts api
This commit is contained in:
@@ -15,7 +15,7 @@ status: draft
|
||||
- API base:前端通过 `VITE_API_URL` 指向后端;本地可同源 `/api`。
|
||||
- 上传文件可通过 `/uploads/...` 或完整 URL 返回;前端会用 `assetUrl()` 处理相对路径。
|
||||
- 所有时间字段使用 ISO 8601 字符串,例如 `2026-05-24T14:42:00.000Z`。
|
||||
- 语言字段:`zh-TW` / `zh-CN` / `en`。
|
||||
- 语言字段:`zh` / `en` / `ja` / `ko` / `vi` / `id` / `ms`;默认语言为 `en`。中文只有简体 `zh`,没有繁体中文。
|
||||
- 错误格式:非 2xx + text/plain 或 JSON 均可;前端会显示错误文本。
|
||||
- Admin 接口需要 `Authorization: Bearer <token>`。
|
||||
|
||||
@@ -26,10 +26,10 @@ status: draft
|
||||
```ts
|
||||
type Category = {
|
||||
id: number;
|
||||
slug: string; // 用于 /category/:slug 和 GET /api/posts?category=<slug>
|
||||
name: string; // 已按 lang 返回本地化名称
|
||||
slug: string; // 用于 /category/:slug 和 GET /api/posts?category=<slug>
|
||||
name: string; // 已按 lang 返回本地化名称
|
||||
description?: string;
|
||||
iconKey: string; // folder/calendar/megaphone/video/image 等,前端已有 icon map
|
||||
iconKey: string; // folder/calendar/megaphone/video/image 等,前端已有 icon map
|
||||
sortOrder: number;
|
||||
};
|
||||
```
|
||||
@@ -42,22 +42,22 @@ type AttachmentKind = "image" | "video" | "document";
|
||||
type Attachment = {
|
||||
id: string;
|
||||
kind: AttachmentKind;
|
||||
url: string; // 原始文件或可访问文件 URL
|
||||
mime: string; // image/jpeg, application/pdf, video/mp4, ...
|
||||
url: string; // 原始文件或可访问文件 URL
|
||||
mime: string; // image/jpeg, application/pdf, video/mp4, ...
|
||||
filename: string;
|
||||
sizeBytes: number;
|
||||
width?: number; // image/video 建议提供
|
||||
width?: number; // image/video 建议提供
|
||||
height?: number;
|
||||
durationSec?: number; // video 建议提供
|
||||
posterUrl?: string; // video preview
|
||||
thumbnailUrl?: string; // image/document preview
|
||||
durationSec?: number; // video 建议提供
|
||||
posterUrl?: string; // video preview
|
||||
thumbnailUrl?: string; // image/document preview
|
||||
};
|
||||
|
||||
type Post = {
|
||||
id: string;
|
||||
categoryId: number;
|
||||
categorySlug: string;
|
||||
language: "zh-TW" | "zh-CN" | "en";
|
||||
language: "zh" | "en" | "ja" | "ko" | "vi" | "id" | "ms";
|
||||
text?: string;
|
||||
attachments: Attachment[];
|
||||
isRecommended: boolean;
|
||||
@@ -88,7 +88,7 @@ type PostListResponse = {
|
||||
### 2.1 分类列表
|
||||
|
||||
```http
|
||||
GET /api/categories?lang=zh-CN
|
||||
GET /api/categories?lang=en
|
||||
```
|
||||
|
||||
Response:
|
||||
@@ -104,29 +104,30 @@ Category[]
|
||||
### 2.2 全部资料 / 分类资料流
|
||||
|
||||
```http
|
||||
GET /api/posts?lang=zh-CN&limit=20&cursor=<cursor>&type=all&language=&category=<slug>
|
||||
GET /api/posts?lang=en&limit=20&cursor=<cursor>&type=all&language=&category=<slug>
|
||||
```
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|---|---:|---|
|
||||
| `lang` | 是 | UI 语言 |
|
||||
| `limit` | 否 | 默认 20,最大建议 50 |
|
||||
| `cursor` | 否 | 后端返回的不透明 cursor |
|
||||
| `category` | 否 | 不传 = 全部资料;传 slug = 单分类 |
|
||||
| `type` | 否 | `all/image/video/ppt/pdf/text/link/archive` |
|
||||
| `language` | 否 | 资料语言:`zh-TW/zh-CN/en` |
|
||||
| 参数 | 必填 | 说明 |
|
||||
| ---------- | ---: | ------------------------------------------- |
|
||||
| `lang` | 是 | UI 语言 |
|
||||
| `limit` | 否 | 默认 20,最大建议 50 |
|
||||
| `cursor` | 否 | 后端返回的不透明 cursor |
|
||||
| `category` | 否 | 不传 = 全部资料;传 slug = 单分类 |
|
||||
| `type` | 否 | `all/image/video/ppt/pdf/text/link/archive` |
|
||||
| `language` | 否 | 资料语言:`zh/en/ja/ko/vi/id/ms` |
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
PostListResponse
|
||||
PostListResponse;
|
||||
```
|
||||
|
||||
排序:`publishedAt DESC`。
|
||||
|
||||
用途:
|
||||
|
||||
- `/browse`:不传 `category`
|
||||
- `/category/:slug`:传 `category=<slug>`
|
||||
|
||||
@@ -135,7 +136,7 @@ PostListResponse
|
||||
### 2.3 Home 推荐资料
|
||||
|
||||
```http
|
||||
GET /api/posts/recommended?lang=zh-CN&limit=12
|
||||
GET /api/posts/recommended?lang=en&limit=12
|
||||
```
|
||||
|
||||
Response:
|
||||
@@ -153,7 +154,7 @@ Response:
|
||||
### 2.4 Home 最新资料
|
||||
|
||||
```http
|
||||
GET /api/posts/latest?lang=zh-CN&limit=8
|
||||
GET /api/posts/latest?lang=en&limit=8
|
||||
```
|
||||
|
||||
Response:
|
||||
@@ -177,7 +178,7 @@ GET /api/posts/:id
|
||||
Response:
|
||||
|
||||
```ts
|
||||
Post
|
||||
Post;
|
||||
```
|
||||
|
||||
用途:旧 `/resource/:id` 前端重定向:拿 `categorySlug` 后跳到 `/category/<slug>#post-<id>`。
|
||||
@@ -189,13 +190,13 @@ Post
|
||||
建议新接口:
|
||||
|
||||
```http
|
||||
GET /api/posts/search?q=<keyword>&lang=zh-CN&type=all&language=&cursor=&limit=20
|
||||
GET /api/posts/search?q=<keyword>&lang=en&type=all&language=&cursor=&limit=20
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```ts
|
||||
PostListResponse
|
||||
PostListResponse;
|
||||
```
|
||||
|
||||
搜索范围建议:`text`、`filename`、`categoryName`、tags。
|
||||
@@ -245,16 +246,16 @@ Response:204 或 `{ ok: true }`。
|
||||
|
||||
`GET /api/posts` 的 `type` 参数建议按以下规则命中:
|
||||
|
||||
| type | 命中条件 |
|
||||
|---|---|
|
||||
| `all` | 全部 |
|
||||
| `image` | 任一 attachment `kind === "image"` |
|
||||
| `video` | 任一 attachment `kind === "video"` 或 `mime.startsWith("video/")` |
|
||||
| `pdf` | 任一 attachment 扩展名 `pdf` 或 `mime === "application/pdf"` |
|
||||
| `ppt` | 任一 attachment 扩展名 `ppt/pptx/key` 或 mime 含 `presentation` |
|
||||
| `archive` | 任一 attachment 扩展名 `zip/rar/7z/tar/gz` |
|
||||
| `text` | `text` 非空 |
|
||||
| `link` | `text` 中包含 `https?://` |
|
||||
| type | 命中条件 |
|
||||
| --------- | ----------------------------------------------------------------- |
|
||||
| `all` | 全部 |
|
||||
| `image` | 任一 attachment `kind === "image"` |
|
||||
| `video` | 任一 attachment `kind === "video"` 或 `mime.startsWith("video/")` |
|
||||
| `pdf` | 任一 attachment 扩展名 `pdf` 或 `mime === "application/pdf"` |
|
||||
| `ppt` | 任一 attachment 扩展名 `ppt/pptx/key` 或 mime 含 `presentation` |
|
||||
| `archive` | 任一 attachment 扩展名 `zip/rar/7z/tar/gz` |
|
||||
| `text` | `text` 非空 |
|
||||
| `link` | `text` 中包含 `https?://` |
|
||||
|
||||
## 4. Wallet Auth API
|
||||
|
||||
@@ -270,7 +271,9 @@ Content-Type: application/json
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ message: string }
|
||||
{
|
||||
message: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 验证签名并签发 token
|
||||
@@ -289,7 +292,9 @@ Content-Type: application/json
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ token: string }
|
||||
{
|
||||
token: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 验证当前 wallet session
|
||||
@@ -302,7 +307,9 @@ Authorization: Bearer <wallet-token>
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ wallet: string }
|
||||
{
|
||||
wallet: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Admin API
|
||||
@@ -319,7 +326,9 @@ Content-Type: application/json
|
||||
Response:
|
||||
|
||||
```ts
|
||||
{ token: string }
|
||||
{
|
||||
token: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
@@ -335,18 +344,18 @@ Response:
|
||||
|
||||
```ts
|
||||
type AdminDashboard = {
|
||||
totalResources: number; // 若迁移到 Post,可理解为 totalPosts
|
||||
totalResources: number; // 若迁移到 Post,可理解为 totalPosts
|
||||
published: number;
|
||||
todayNew: number;
|
||||
totalViews: number;
|
||||
totalDownloads: number;
|
||||
totalFavorites: number; // 收藏下线后可返回 0,避免旧 admin UI 崩
|
||||
totalFavorites: number; // 收藏下线后可返回 0,避免旧 admin UI 崩
|
||||
totalShares: number;
|
||||
hotResources: {
|
||||
id: string;
|
||||
title: string;
|
||||
downloads: number;
|
||||
favorites: number; // 可返回 0
|
||||
favorites: number; // 可返回 0
|
||||
views: number;
|
||||
}[];
|
||||
};
|
||||
@@ -367,7 +376,9 @@ file=<File>
|
||||
最低 Response:
|
||||
|
||||
```ts
|
||||
{ url: string }
|
||||
{
|
||||
url: string;
|
||||
}
|
||||
```
|
||||
|
||||
建议 Response(更方便前端自动建 Attachment):
|
||||
@@ -438,7 +449,7 @@ Request:
|
||||
```ts
|
||||
type UpsertPostPayload = {
|
||||
categoryId: number;
|
||||
language: "zh-TW" | "zh-CN" | "en";
|
||||
language: "zh" | "en" | "ja" | "ko" | "vi" | "id" | "ms";
|
||||
text?: string;
|
||||
attachments: Attachment[];
|
||||
isPublic: boolean;
|
||||
|
||||
@@ -16,34 +16,34 @@ status: draft
|
||||
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 优化)
|
||||
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 缩略,列表用减少流量
|
||||
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 时为 []
|
||||
language: string; // "zh" | "en" | "ja" | "ko" | "vi" | "id" | "ms"
|
||||
text?: string; // 可选,纯文本/图说;前端做 https → 链接自动识别
|
||||
attachments: Attachment[]; // 0~N;text-only post 时为 []
|
||||
isRecommended: boolean;
|
||||
publishedAt: string; // ISO 8601;用于排序 + 日期分组
|
||||
publishedAt: string; // ISO 8601;用于排序 + 日期分组
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
type PostListResponse = {
|
||||
items: Post[];
|
||||
nextCursor?: string; // 不透明 cursor;undefined = 没有下一页
|
||||
nextCursor?: string; // 不透明 cursor;undefined = 没有下一页
|
||||
};
|
||||
```
|
||||
|
||||
@@ -67,14 +67,14 @@ 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 |
|
||||
| 参数 | 必填 | 说明 |
|
||||
| ---------- | ---- | ---------------------------------------------------------------------------------- |
|
||||
| `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`
|
||||
|
||||
@@ -117,6 +117,7 @@ GET /api/admin/posts?... (含未发布草稿)
|
||||
```
|
||||
|
||||
需求:
|
||||
|
||||
- 支持多附件上传(一次 multipart 或先 `POST /api/admin/upload` 拿到 url 再创建 Post)。
|
||||
- Admin UI 需要一个开关:「图片以图片形式呈现 / 以文档形式呈现」,对应 attachment.kind 的 image vs document。
|
||||
- 支持发布/隐藏、置顶/官方推荐。
|
||||
@@ -127,28 +128,28 @@ GET /api/admin/posts?... (含未发布草稿)
|
||||
|
||||
一个 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?://` |
|
||||
| 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)|
|
||||
| 项 | 处理 |
|
||||
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user