API v1 重构与完善 Spec
Why
当前 API v1 存在 URL 设计反 RESTful(?action=login)、分页格式不统一、缺少 access_token/refresh_token 双令牌机制、缺少帖子互动端点(收藏/点赞/举报)、无字段过滤与批量操作、错误响应结构不一致等问题,需要全面重构以达到可直接用于开发的标准。
What Changes
BREAKING URL 规范化
POST /user?action=login→POST /auth/loginPOST /user?action=register→POST /auth/registerPOST /notify?action=read→PUT /notify/{id}/readPOST /notify?action=read-all→PUT /notify/read-allGET /thread?tid=1→GET /thread/1(统一路径参数)GET /post?pid=1→GET /post/1(统一路径参数)GET /forum?fid=1→GET /forum/1(统一路径参数)
BREAKING 分页响应统一
- 所有列表端点统一返回
pagination对象:{page, pagesize, total, total_pages} - 列表数据统一放在
list字段
BREAKING 错误响应结构增强
- 错误响应增加
errors字段(验证错误时返回字段级详情) code字段与 HTTP 状态码对齐(成功为 0,其余与 HTTP 状态码一致)
Token 机制升级
- 引入 access_token(短期,默认 2 小时)+ refresh_token(长期,默认 30 天)双令牌
- 新增
POST /auth/refresh端点 - 新增
POST /auth/logout端点(撤销 refresh_token) api_token表增加type字段区分 access/refresh
新增资源端点
POST /thread/{tid}/like/DELETE /thread/{tid}/like— 帖子点赞POST /thread/{tid}/favorite/DELETE /thread/{tid}/favorite— 帖子收藏POST /thread/{tid}/report— 帖子举报GET /user/{uid}/threads— 用户帖子列表GET /user/{uid}/posts— 用户回复列表GET /user/{uid}/favorites— 用户收藏列表GET /forum/{fid}/threads— 版块帖子列表(替代/thread?fid=)POST /auth/logout— 退出登录
字段过滤
- 所有 GET 列表/详情端点支持
fields查询参数 - 示例:
GET /thread/1?fields=tid,subject,uid,create_date
批量操作
DELETE /thread/batch— 批量删除帖子(body:{tids: [1,2,3]},需管理员权限)DELETE /post/batch— 批量删除回复(body:{pids: [1,2,3]},需管理员权限)PUT /thread/batch— 批量更新帖子类型/置顶/关闭(body:{tids: [1,2,3], update: {top: 1}},需管理员权限)
API 版本策略
- URL 路径版本:
/api/v1/ - 响应头包含
X-API-Version: 1.0 - 版本变更规则:补丁版本向后兼容,主版本可引入 BREAKING 变更
限流增强
- 限流响应增加
Retry-After头 - 认证用户与匿名用户分别限流
- 管理员豁免限流
文档自动生成
ApiDocService从硬编码改为基于注解/配置自动生成- 新增
GET /api/v1/openapi.json端点返回 OpenAPI 3.0 规范
Impact
- Affected specs: api-optimization-and-admin-fix(前序 spec,已完成)
- Affected code:
api/v1/bootstrap.php— 路由分发重构api/v1/user.php→ 拆分为api/v1/auth.php+api/v1/user.phpapi/v1/thread.php— 增加互动端点、路径参数、字段过滤api/v1/post.php— 路径参数、字段过滤api/v1/forum.php— 增加/forum/{fid}/threads子资源api/v1/notify.php— URL 规范化api/v1/attach.php— 路径参数api/v1/search.php— 分页格式统一api/v1/site.php— 无变更lib/ApiAuthService.php— 双令牌机制lib/ApiResponse.php— 错误响应结构增强lib/ApiDocService.php— 改为自动生成lib/RateLimitService.php— 限流增强service/UserService.php— 增加 getUserList/getUserCountservice/ThreadService.php— 增加互动相关方法docs/refactor/phase3/migration.sql— 增加 thread_like/thread_favorite/thread_report 表admin/view/htm/api_doc.htm— 适配新端点admin/view/htm/api_debug.htm— 适配新端点
ADDED Requirements
Requirement: RESTful URL 规范
所有 API 端点 SHALL 遵循 RESTful 资源路径设计,禁止使用 ?action= 查询参数表达操作语义
Scenario: 用户登录
- WHEN 客户端发送
POST /api/v1/auth/login,body 包含{email, password} - THEN 返回
{code: 0, msg: "ok", data: {access_token, refresh_token, expires_in, user}}
Scenario: 用户注册
- WHEN 客户端发送
POST /api/v1/auth/register,body 包含{email, username, password} - THEN 返回
{code: 0, msg: "ok", data: {uid, access_token, refresh_token, expires_in}}
Scenario: 刷新令牌
- WHEN 客户端发送
POST /api/v1/auth/refresh,body 包含{refresh_token} - THEN 返回
{code: 0, msg: "ok", data: {access_token, refresh_token, expires_in}}
Scenario: 退出登录
- WHEN 客户端发送
POST /api/v1/auth/logout,Header 包含 Bearer access_token,body 包含{refresh_token} - THEN 撤销该 refresh_token 及其关联的 access_token,返回
{code: 0, msg: "ok", data: null}
Scenario: 标记通知已读
- WHEN 客户端发送
PUT /api/v1/notify/{id}/read - THEN 标记该通知已读,返回
{code: 0, msg: "ok", data: null}
Scenario: 全部标记已读
- WHEN 客户端发送
PUT /api/v1/notify/read-all - THEN 标记当前用户所有通知已读,返回
{code: 0, msg: "ok", data: null}
Requirement: 统一分页响应格式
所有列表端点 SHALL 返回统一的分页结构
Scenario: 列表响应
- WHEN 客户端请求任何列表端点
- THEN 响应 data 包含
{list: [...], pagination: {page, pagesize, total, total_pages}}
Scenario: 分页参数
- WHEN 客户端传递
page和pagesize查询参数 - THEN
pagesize上限为 100,默认 20;page默认 1
Requirement: 双令牌认证机制
系统 SHALL 实现 access_token + refresh_token 双令牌机制
Scenario: 登录获取令牌
- WHEN 用户登录成功
- THEN 返回
access_token(有效期 2 小时)和refresh_token(有效期 30 天)
Scenario: access_token 过期
- WHEN access_token 过期后客户端使用 refresh_token 调用
/auth/refresh - THEN 返回新的 access_token 和 refresh_token,旧 refresh_token 失效(轮转机制)
Scenario: refresh_token 过期
- WHEN refresh_token 也过期
- THEN 返回 401,客户端需重新登录
Scenario: Token 表结构
- WHEN 数据库迁移执行
- THEN
bbs_api_token表增加type字段(枚举 access/refresh)和related_token字段(关联 token ID)
Requirement: 增强错误响应
错误响应 SHALL 包含结构化的错误详情
Scenario: 验证错误
- WHEN 请求参数验证失败
- THEN 返回
{code: 422, msg: "Validation Error", data: null, errors: [{field: "email", message: "Email is required"}, {field: "password", message: "Password is required"}]}
Scenario: 通用错误
- WHEN 请求发生非验证类错误
- THEN 返回
{code: <http_status>, msg: "<描述>", data: null}
Requirement: 帖子互动端点
系统 SHALL 提供帖子点赞、收藏、举报端点
Scenario: 点赞帖子
- WHEN 客户端发送
POST /api/v1/thread/{tid}/like(需认证) - THEN 记录点赞,返回
{code: 0, msg: "ok", data: {liked: true}}
Scenario: 取消点赞
- WHEN 客户端发送
DELETE /api/v1/thread/{tid}/like(需认证) - THEN 取消点赞,返回
{code: 0, msg: "ok", data: {liked: false}}
Scenario: 收藏帖子
- WHEN 客户端发送
POST /api/v1/thread/{tid}/favorite(需认证) - THEN 记录收藏,返回
{code: 0, msg: "ok", data: {favorited: true}}
Scenario: 取消收藏
- WHEN 客户端发送
DELETE /api/v1/thread/{tid}/favorite(需认证) - THEN 取消收藏,返回
{code: 0, msg: "ok", data: {favorited: false}}
Scenario: 举报帖子
- WHEN 客户端发送
POST /api/v1/thread/{tid}/report(需认证),body 包含{reason} - THEN 记录举报,返回
{code: 0, msg: "ok", data: null}
Requirement: 用户子资源端点
系统 SHALL 提供用户维度的子资源查询
Scenario: 用户帖子列表
- WHEN 客户端发送
GET /api/v1/user/{uid}/threads - THEN 返回该用户的帖子列表(含分页)
Scenario: 用户回复列表
- WHEN 客户端发送
GET /api/v1/user/{uid}/posts - THEN 返回该用户的回复列表(含分页)
Scenario: 用户收藏列表
- WHEN 客户端发送
GET /api/v1/user/{uid}/favorites(需认证,仅可查看自己的) - THEN 返回该用户的收藏帖子列表(含分页)
Requirement: 版块帖子子资源
系统 SHALL 提供版块下的帖子列表端点
Scenario: 版块帖子列表
- WHEN 客户端发送
GET /api/v1/forum/{fid}/threads - THEN 返回该版块的帖子列表(含分页),支持
orderby、order、keyword参数
Requirement: 字段过滤
GET 端点 SHALL 支持 fields 查询参数
Scenario: 指定返回字段
- WHEN 客户端发送
GET /api/v1/thread/1?fields=tid,subject,uid - THEN 仅返回 tid、subject、uid 三个字段
Scenario: 无效字段名
- WHEN 客户端请求的 fields 包含不存在的字段
- THEN 忽略无效字段,仅返回有效字段
Requirement: 批量操作
系统 SHALL 提供管理员批量操作端点
Scenario: 批量删除帖子
- WHEN 管理员发送
DELETE /api/v1/thread/batch,body 包含{tids: [1,2,3]} - THEN 删除指定帖子,返回
{code: 0, msg: "ok", data: {deleted: 3}}
Scenario: 批量删除回复
- WHEN 管理员发送
DELETE /api/v1/post/batch,body 包含{pids: [1,2,3]} - THEN 删除指定回复,返回
{code: 0, msg: "ok", data: {deleted: 3}}
Scenario: 批量更新帖子
- WHEN 管理员发送
PUT /api/v1/thread/batch,body 包含{tids: [1,2,3], update: {top: 1}} - THEN 批量更新帖子属性,返回
{code: 0, msg: "ok", data: {updated: 3}}
Scenario: 非管理员批量操作
- WHEN 非管理员用户尝试批量操作
- THEN 返回 403 Forbidden
Requirement: 限流增强
限流机制 SHALL 区分认证/匿名用户,并返回标准头
Scenario: 限流响应头
- WHEN 任何 API 请求
- THEN 响应包含
X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset头
Scenario: 触发限流
- WHEN 请求超过限流阈值
- THEN 返回 429 状态码,包含
Retry-After头
Scenario: 认证用户限流
- WHEN 认证用户请求 API
- THEN 限流阈值高于匿名用户(认证 120/min,匿名 60/min)
Scenario: 管理员豁免
- WHEN 管理员(gid=1)请求 API
- THEN 不受限流限制
Requirement: OpenAPI 3.0 规范端点
系统 SHALL 提供 OpenAPI 3.0 JSON 规范
Scenario: 获取 OpenAPI 规范
- WHEN 客户端发送
GET /api/v1/openapi.json - THEN 返回符合 OpenAPI 3.0 规范的 JSON 文档
Requirement: API 版本头
所有 API 响应 SHALL 包含版本信息
Scenario: 版本响应头
- WHEN 任何 API 请求
- THEN 响应包含
X-API-Version: 1.0头
MODIFIED Requirements
Requirement: ApiResponse 输出格式
ApiResponse 类 SHALL 支持增强的错误响应结构
success()方法保持不变:{code: 0, msg: "ok", data: ...}error()方法增加可选errors参数:{code: <int>, msg: <string>, data: null, errors: [...]}- 新增
validationError()方法支持字段级错误:validationError(string $msg, array $errors = [])
Requirement: ApiAuthService 令牌管理
ApiAuthService SHALL 支持双令牌机制
generateTokens(int $uid): array— 同时生成 access_token 和 refresh_tokenvalidateAccessToken(string $token): ?array— 验证 access_tokenvalidateRefreshToken(string $token): ?array— 验证 refresh_tokenrefreshTokens(string $refreshToken): ?array— 轮转刷新令牌revokeTokens(string $refreshToken): bool— 撤销令牌对- 保留
getBearerToken()静态方法
Requirement: bootstrap.php 路由分发
bootstrap.php SHALL 支持更细粒度的路由匹配
- 支持路径参数提取:
/thread/{tid}/like→$segments[0]='thread', $segments[1]={tid}, $segments[2]='like' - 支持
auth资源路由到auth.php - 支持
batch子路径路由到对应资源的批量操作
REMOVED Requirements
Requirement: ?action= 查询参数路由
Reason: 违反 RESTful 设计原则,改为资源路径 Migration: 客户端需将 ?action=login 改为 POST /auth/login,?action=read 改为 PUT /notify/{id}/read
Requirement: 查询参数获取单个资源
Reason: GET /thread?tid=1 不符合 REST 规范,应使用路径参数 GET /thread/1 Migration: 客户端需将 ?tid=1 改为路径参数 /thread/1
完整端点清单
认证 Auth
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| POST | /auth/login | 否 | 登录 |
| POST | /auth/register | 否 | 注册 |
| POST | /auth/refresh | 否 | 刷新令牌 |
| POST | /auth/logout | 是 | 退出登录 |
用户 User
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /user | 否 | 用户列表 |
| GET | /user/me | 是 | 当前用户 |
| GET | /user/{uid} | 否 | 用户详情 |
| PUT | /user/{uid} | 是 | 更新用户 |
| GET | /user/{uid}/threads | 否 | 用户帖子 |
| GET | /user/{uid}/posts | 否 | 用户回复 |
| GET | /user/{uid}/favorites | 是 | 用户收藏 |
帖子 Thread
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /thread | 否 | 帖子列表 |
| POST | /thread | 是 | 创建帖子 |
| GET | /thread/{tid} | 否 | 帖子详情 |
| PUT | /thread/{tid} | 是 | 更新帖子 |
| DELETE | /thread/{tid} | 是 | 删除帖子 |
| POST | /thread/{tid}/like | 是 | 点赞 |
| DELETE | /thread/{tid}/like | 是 | 取消点赞 |
| POST | /thread/{tid}/favorite | 是 | 收藏 |
| DELETE | /thread/{tid}/favorite | 是 | 取消收藏 |
| POST | /thread/{tid}/report | 是 | 举报 |
| DELETE | /thread/batch | 是(管理员) | 批量删除 |
| PUT | /thread/batch | 是(管理员) | 批量更新 |
回复 Post
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /post | 否 | 回复列表 |
| POST | /post | 是 | 创建回复 |
| GET | /post/{pid} | 否 | 回复详情 |
| PUT | /post/{pid} | 是 | 更新回复 |
| DELETE | /post/{pid} | 是 | 删除回复 |
| DELETE | /post/batch | 是(管理员) | 批量删除 |
版块 Forum
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /forum | 否 | 版块列表 |
| GET | /forum/{fid} | 否 | 版块详情 |
| GET | /forum/{fid}/threads | 否 | 版块帖子 |
附件 Attach
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /attach/{aid} | 否 | 附件详情 |
| POST | /attach | 是 | 上传附件 |
| DELETE | /attach/{aid} | 是 | 删除附件 |
通知 Notify
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /notify | 是 | 通知列表 |
| GET | /notify/unread | 是 | 未读数 |
| PUT | /notify/{id}/read | 是 | 标记已读 |
| PUT | /notify/read-all | 是 | 全部已读 |
搜索 Search
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /search | 否 | 全文搜索 |
站点 Site
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /site | 否 | 站点信息 |
| GET | /site/stats | 否 | 站点统计 |
元数据
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| GET | /openapi.json | 否 | OpenAPI 3.0 规范 |
数据库变更
-- 修改 api_token 表:增加 type 和 related_id 字段
ALTER TABLE `bbs_api_token` ADD COLUMN `type` enum('access','refresh') NOT NULL DEFAULT 'access' AFTER `uid`;
ALTER TABLE `bbs_api_token` ADD COLUMN `related_id` bigint(16) unsigned NOT NULL DEFAULT 0 AFTER `type`;
ALTER TABLE `bbs_api_token` ADD INDEX `uid_type` (`uid`, `type`);
-- 帖子点赞表
CREATE TABLE IF NOT EXISTS `bbs_thread_like` (
`id` bigint(16) unsigned NOT NULL AUTO_INCREMENT,
`tid` int(11) unsigned NOT NULL DEFAULT 0,
`uid` int(11) unsigned NOT NULL DEFAULT 0,
`create_date` int(11) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `tid_uid` (`tid`, `uid`),
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 帖子收藏表
CREATE TABLE IF NOT EXISTS `bbs_thread_favorite` (
`id` bigint(16) unsigned NOT NULL AUTO_INCREMENT,
`tid` int(11) unsigned NOT NULL DEFAULT 0,
`uid` int(11) unsigned NOT NULL DEFAULT 0,
`create_date` int(11) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `tid_uid` (`tid`, `uid`),
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 帖子举报表
CREATE TABLE IF NOT EXISTS `bbs_thread_report` (
`id` bigint(16) unsigned NOT NULL AUTO_INCREMENT,
`tid` int(11) unsigned NOT NULL DEFAULT 0,
`uid` int(11) unsigned NOT NULL DEFAULT 0,
`reason` varchar(500) NOT NULL DEFAULT '',
`create_date` int(11) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `tid` (`tid`),
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 谁有知乎的那个二开的知乎主题,网盘的那个 2024-10-23
- 秀尊云 给大家推荐一个香港优惠年付主机 活动只到2024年3月10号为止 2024-3-3
- 本站同款首页公告(支持多条)v1.1 - 让你的论坛公告更灵活! 3月前
- 求教文字修改 2020-10-23
|
15 主题数 |
15 帖子数 |
Xiuno BBS开源程序交流论坛