Files
SoonDesign/docs/API-PAGINATION.md
24kycj 4ce4486b26 Web 端本地缓存、首页与会员弹窗体验优化
新增 IndexedDB 与最近文件 L1 缓存、云保存/打开链路;首页支持下载与清空本地缓存;修复清空 storage 后语言初始化报错;优化 design2 背景加载与侧栏标签样式;完善会员激活/支付弹层样式;后端补充文件缩略图字段与 thumb 接口。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 11:05:44 +08:00

242 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 列表分页 API 契约
本文描述 `backend-web` 公开端(`/api/v1`)与管理端(`/api/admin`)列表接口的分页约定。实现以仓库内 PHP 控制器为准。
## 通用响应包装
所有 JSON 接口统一:
```json
{
"ok": true,
"data": { }
}
```
失败时 `ok: false`,含 `error`(机器码)与 `message`(人类可读文案)。
## 推荐分页形态(page / size / total
**新接口与前端管理列表优先使用此形态。**
### 请求 Query
| 参数 | 类型 | 默认 | 上限 | 说明 |
|------|------|------|------|------|
| `page` | int | `1` | — | 从 1 开始 |
| `size` | int | 见各接口 | 见各接口 | 每页条数 |
### 响应 `data`
| 字段 | 类型 | 说明 |
|------|------|------|
| `items` | array | 当前页记录 |
| `total` | int | 符合条件的总条数(用于算总页数) |
| `page` | int | 当前页码(回显) |
| `size` | int | 当前每页条数(回显) |
总页数:`ceil(total / size)``total === 0` 时视为 0 条、1 页。
### 示例
```http
GET /api/admin/users?page=2&size=20&q=test%40local.test
Authorization: Bearer <admin_token>
```
```json
{
"ok": true,
"data": {
"items": [ { "id": 1, "email": "..." } ],
"total": 42,
"page": 2,
"size": 20
}
}
```
---
## 管理端分页列表(`/api/admin`
需管理员 JWT`role=admin`)。默认 `size` 与上限见下表。
| 方法 | 路径 | 默认 size | size 上限 | 额外筛选 Query |
|------|------|-----------|-----------|----------------|
| GET | `/api/admin/users` | 20 | 200 | `q` 邮箱模糊 |
| GET | `/api/admin/orders` | 20 | 200 | `q` 订单号或邮箱;`status` pending/paid/cancelled/refunded`refund_status` none/pending/approved/rejected`channel` alipay/wechat`from`/`to` 日期 YYYY-MM-DD |
| GET | `/api/admin/audits` | 50 | 200 | `q` 操作/目标/管理员邮箱;`action` 精确操作码;`from`/`to` 日期 |
实现参考:
- `backend-web/src/Admin/Controllers/UsersController.php`
- `backend-web/src/Admin/Controllers/OrdersController.php`
- `backend-web/src/Admin/Controllers/AuditsController.php`
### 管理端全量列表(无分页)
以下接口一次返回全部 `items`,数据量预期较小:
| 路径 | 说明 |
|------|------|
| `GET /api/admin/plans` | 套餐配置 |
| `GET/POST /api/admin/templates``PUT/DELETE /api/admin/templates/{id}` | 模板库 CRUD |
| `GET /api/admin/settings` | 系统键值 |
| `GET /api/admin/payment/status` | PEM 文件元数据 |
### 管理端固定条数嵌套列表
| 来源 | 条数 | 说明 |
|------|------|------|
| `GET /api/admin/stats``recent_orders` | 5 | 仪表盘最近订单,非翻页接口 |
| `GET /api/admin/users/{id}``recent_orders` | 5 | 用户详情内嵌 |
---
## 公开端分页列表(`/api/v1`
需用户 JWT(除 plans 列表可匿名,见各控制器)。
| 方法 | 路径 | 默认 size | size 上限 | 分页参数 | 响应 |
|------|------|-----------|-----------|----------|------|
| GET | `/api/v1/files` | 50legacy | 200 | **推荐** `page`+`size`**兼容** `limit`+`offset` | 见下文 |
| GET | `/api/v1/pay/orders` | 8 | 50 | `page`+`size` | page 形态 |
实现参考:
- `backend-web/src/Controllers/FileController.php`
- `backend-web/src/Controllers/PayController.php`
- `backend-web/src/Services/MembershipService.php``listOrders`
### `GET /api/v1/files` 双模式
**推荐(page 形态)** — 传 `page`≥1 时生效:
```http
GET /api/v1/files?page=1&size=12
```
```json
{
"ok": true,
"data": {
"items": [ { "id": 1, "name": "design.soon", "size": 1024, "version": 1 } ],
"total": 25,
"page": 1,
"size": 12
}
}
```
**兼容(offset 形态)** — 未传 `page``page=0` 时:
```http
GET /api/v1/files?limit=50&offset=0
```
```json
{
"ok": true,
"data": {
"items": [ ],
"total": 25,
"limit": 50,
"offset": 0
}
}
```
两种形态均含 `total`。新前端(首页文件列表、管理端)应使用 `page`+`size`
### `GET /api/v1/pay/orders`
会员中心订单历史,默认每页 8 条:
```http
GET /api/v1/pay/orders?page=1&size=8
```
### 公开端全量 / 非标准列表
| 路径 | 形态 |
|------|------|
| `GET /api/v1/plans` | `{ items: [...] }` 全量活跃套餐 |
| `GET /api/v1/auth/me` | 当前用户 + 会员摘要(`is_member` / `tier` / `name` |
| `GET /api/v1/plans/me` | 完整会员配额信息(云端用量等,按需调用) |
| `GET /api/v1/settings` | 扁平 key-value 对象,非 `items` 数组 |
| `GET /api/v1/templates` | 首页模板列表(仅 `id/name/type/updated_at`;缩略图按需拉取) |
| `GET /api/v1/templates/{id}` | 模板 JSON 数据(门户标准读取端点,见下) |
| `GET /api/v1/templates/{id}/thumb` | 模板缩略图(从 `.soon``frontDisplayPic` 提取) |
| `GET /api/v1/templates/{id}/file` | 裸 `.soon` 流(`Cache-Control: max-age=86400`);门户读缓存首选 |
| `GET /api/v1/files/{id}/thumb` | 云文件缩略图(需登录;从 `.soon``frontDisplayPic` 提取) |
| `GET /api/v1/soon-models` | 兼容别名,同 `GET /api/v1/templates` |
#### `GET /api/v1/templates/{id}`(门户读取模板数据)
`GET /api/v1/files/{id}` 对齐,返回 JSON 包装,**非**裸文件流:
```json
{
"ok": true,
"data": {
"id": 1,
"name": "示例模版",
"type": 1,
"updated_at": "2026-06-09 00:23:29",
"json": "{...}"
}
}
```
- `data.json`**字符串**(与 `files/{id}` 一致),前端 `JSON.parse(data.json)` 后加载画布
- 公开接口,无需登录
### Web 虚拟 key 前缀(门户设计页)
| 前缀 | 含义 | readJsonFile | 保存 writeFile |
|------|------|--------------|----------------|
| `soondesign_file:{id}:v{ver}` | 云端已登记文件 | `GET /files/{id}` | `PUT /files/{id}` |
| `soondesign_template:{id}` | 云端模板(只读源) | `GET /templates/{id}/file` | `POST /files`(首次保存新建) |
| `soondesign_session:...` | 本地/临时会话 | sessionStorage | 已登录 `POST /files`;未登录写 session |
- **保存/打开**走 JSON API**仅**首页「下载」走 `GET /files/{id}/download` 落盘 `.soon`
- 门户**读缓存**与最近文件本地优先策略见 [`WEB-LOCAL-CACHE.md`](WEB-LOCAL-CACHE.md);模板首次加载推荐 `GET /templates/{id}/file`(流式 `.soon`,避免 `GET /templates/{id}` 二次包装)
---
## 前端约定
### 管理后台(`frontend-web/pages/admin`
- 分页状态:`AdminState``users` / `orders` / `audits``page``size` 及筛选字段)
- 分页 UI`AdminPager` + `AdminUi.pagerHtml` / `bindPager`
- 请求:相对 admin base 的路径,如 `users?page=1&size=20`
### 门户 / 会员 / 首页
| 页面 | 接口 | 默认 size |
|------|------|-----------|
| 会员订单 | `GET /api/v1/pay/orders` | 8 |
| 首页云端文件 | `GET /api/v1/files?page=&size=` | 12 |
---
## 新增列表接口检查清单
1. 响应是否包含 `items` + `total`
2. 是否使用 `page`(从 1+ `size`,并在 `data` 中回显?
3. `size` 是否有合理默认值与上限(建议上限 200,管理端;用户订单建议 50)?
4. 筛选参数是否与分页独立(翻页时保留筛选)?
5. 是否在本文档「管理端/公开端」表中登记?
---
## 变更记录
| 日期 | 说明 |
|------|------|
| 2026-06-08 | 初版:统一 page/size/totalfiles 兼容 limit/offsetpay/orders 分页 |
| 2026-06-08 | 新增 GET /templates/{id} JSON 契约;Web 虚拟 key 前缀;/file 标 legacy |
| 2026-06-08 | 新增 GET /files/{id}/thumbfiles list 增 `has_thumb`;模板 /file 长缓存 |