You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
12 KiB
12 KiB
API 响应格式规范化 - 全面修复
日期: 2026-02-11
类型: Breaking Change
影响范围: 41+ 个接口
概述
完成对全后端 API 接口的响应格式规范化,将 SuccessResponse[dict] 替换为明确的 Pydantic Schema,确保类型安全和 API 文档的完整性。
变更统计
| 模块 | 修复接口数 | 状态 |
|---|---|---|
| Auth | 2 | ✅ |
| Folders | 13 | ✅ |
| Projects | 9 | ✅ |
| AI | 5 | ✅ |
| AI Prompts | 9 | ✅ |
| AI Conversations | 3 | ✅ |
| Health | 2 | ✅ |
| File Storage | 2 | ✅ |
| Shared Folders | 3 | ⏸️ 保持 dict(复杂联合类型) |
| Screenplays | 1 | ⏸️ 保持 dict(复杂结构) |
| 1 | ✅ | |
| 总计 | 45/47 | 96% 完成率 |
核心修改
1. 创建通用 Schema
MessageResponse (server/app/schemas/common.py)
class MessageResponse(BaseModel):
"""通用消息响应"""
message: str = Field(..., description="响应消息")
适用场景:简单的成功/删除操作
2. Authentication 模块
修改接口
POST /api/v1/auth/login/phonePOST /api/v1/auth/refresh
新增字段
expiresIn: Token 过期时间(秒)tokenType: 默认 "Bearer"(首字母大写)
Schema 更新
class LoginResponse(BaseModel):
access_token: str = Field(..., alias="accessToken")
refresh_token: str = Field(..., alias="refreshToken")
token_type: str = Field(default="Bearer", alias="tokenType")
expires_in: int = Field(..., alias="expiresIn") # ← 新增
user: UserResponse
3. Folders 模块(13 个接口)
修复接口
GET /api/v1/folders/tree→FolderTreeResponsePOST /api/v1/folders/batch/move→FolderBatchOperationResponsePOST /api/v1/folders/batch/delete→FolderBatchOperationResponseDELETE /api/v1/folders/{folder_id}→MessageResponsePOST /api/v1/folders/{folder_id}/members→FolderMemberResponseGET /api/v1/folders/{folder_id}/members→PaginatedResponse[FolderMemberResponse]PUT /api/v1/folders/{folder_id}/members/{user_id}→FolderMemberResponseDELETE /api/v1/folders/{folder_id}/members/{user_id}→MessageResponsePOST /api/v1/folders/{folder_id}/clone→FolderCloneResponseGET /api/v1/folders/{folder_id}/stats→FolderStatsResponsePOST /api/v1/folders/{folder_id}/export→FolderExportResponseGET /api/v1/folders/export/{job_id}→FolderExportResponsePOST /api/v1/folders/{folder_id}/share→ ⚠️ 保持 dict(联合类型:用户分享 vs 链接分享)
Schema 修复
FolderMemberResponse
class FolderMemberResponse(BaseModel):
# ...
role: str # ✅ 从 int 改为 str(匹配实际返回的 "viewer"/"editor"/"owner")
4. Projects 模块(9 个接口)
修复接口
GET /api/v1/projects/trash→ProjectTrashListResponseDELETE /api/v1/projects/{project_id}→ProjectDeleteResponsePOST /api/v1/projects/{project_id}/restore→ProjectRestoreResponseDELETE /api/v1/projects/{project_id}/permanent→ProjectDeleteResponsePOST /api/v1/projects/{project_id}/shares→ProjectShareResponseDELETE /api/v1/projects/{project_id}/shares/{share_id}→MessageResponseGET /api/v1/projects/{project_id}/members→ProjectMemberListResponsePOST /api/v1/projects/{project_id}/members→ProjectMemberResponseDELETE /api/v1/projects/{project_id}/members/{user_id}→MessageResponse
Schema 增强
ProjectTrashResponse
class ProjectTrashResponse(BaseModel):
# ...
parent_project_id: Optional[UUID] = Field(None, alias="parentProjectId") # ← 新增
5. AI 模块(5 个接口)
修复接口
GET /api/v1/ai/voices→VoiceListResponseGET /api/v1/ai/jobs→PaginatedResponse[AIJobListItemResponse]POST /api/v1/ai/jobs/{job_id}/cancel→MessageResponseGET /api/v1/ai/statistics→AIJobStatisticsResponseGET /api/v1/ai/queue/status→QueueStatusResponse
新增 Schema
VoiceListResponse
class VoiceItemResponse(BaseModel):
voice_id: str = Field(..., alias="voiceId")
name: str
name_cn: str = Field(..., alias="nameCn")
category: str
description: str
description_cn: str = Field(..., alias="descriptionCn")
labels: Dict[str, Any]
class VoiceListResponse(BaseModel):
voices: List[VoiceItemResponse]
QueueStatusResponse
class QueueStatusResponse(BaseModel):
workers: WorkerInfo
tasks: TaskInfo
jobs: JobQueueInfo
6. AI Prompts 模块(9 个接口)
修复接口
POST /api/v1/admin/ai-prompts→AIPromptSystemResponseGET /api/v1/admin/ai-prompts→AIPromptSystemListResponseGET /api/v1/admin/ai-prompts/versions→List[AIPromptSystemListItem]GET /api/v1/admin/ai-prompts/default/{prompt_type}→AIPromptSystemResponseGET /api/v1/admin/ai-prompts/name/{name}→AIPromptSystemResponseGET /api/v1/admin/ai-prompts/{prompt_id}→AIPromptSystemResponsePATCH /api/v1/admin/ai-prompts/{prompt_id}→AIPromptSystemResponsePOST /api/v1/admin/ai-prompts/{prompt_id}/set-default→AIPromptSystemResponsePOST /api/v1/admin/ai-prompts/{prompt_id}/versions→AIPromptSystemResponse
说明:复用已有 Schema,无需创建新 Schema
7. AI Conversations 模块(3 个接口)
修复接口
GET /api/v1/ai-conversations→PaginatedResponse[ConversationListItem]GET /api/v1/ai-conversations/{conversation_id}/messages→PaginatedResponse[MessageResponse]GET /api/v1/ai-conversations/{conversation_id}/mentionable-resources→MentionableResourceResponse
新增 Schema
MentionableResourceResponse
class MentionableResourceItem(BaseModel):
resource_id: UUID = Field(..., alias="resourceId")
resource_type: int = Field(..., alias="resourceType")
title: str
thumbnail: Optional[str]
class MentionableResourceResponse(BaseModel):
resources: List[MentionableResourceItem]
8. 其他模块
Health(2 个接口)
GET /api/v1/health→MessageResponseGET /api/v1/health/db→MessageResponse
File Storage(2 个接口)
GET /api/v1/file-storage/presigned-url→PresignedUrlResponsePOST /api/v1/file-storage/cleanup→CleanupResult
新增 Schema (server/app/schemas/file_storage.py)
class PresignedUrlResponse(BaseModel):
url: str
expires_in: int = Field(..., alias="expiresIn")
class CleanupResult(BaseModel):
deleted_count: int = Field(..., alias="deletedCount")
freed_space: int = Field(..., alias="freedSpace")
Breaking Changes
前端必须修改的字段
1. Authentication
// ❌ 旧代码
response.data.access_token
response.data.user.user_id
// ✅ 新代码
response.data.accessToken
response.data.expiresIn // ← 新增,需要处理 Token 刷新逻辑
response.data.tokenType // ← 现在是 "Bearer" 而非 "bearer"
response.data.user.userId
2. FolderMemberResponse
// ❌ 旧代码
member.role === 1 // VIEWER
member.role_name // "Viewer"
// ✅ 新代码
member.role === "viewer" // ← 现在是字符串
// role_name 字段已移除,直接使用 role
3. Pagination
// ❌ 旧代码(部分接口返回 dict)
response.data.items
response.data.total
// ✅ 新代码(统一使用 PaginatedResponse)
response.data.items
response.data.total
response.data.page
response.data.pageSize
response.data.totalPages
未修改的接口
Shared Folders(保持 dict)
GET /api/v1/public/shared-folders/f/{token}GET /api/v1/public/shared-folders/f/{token}/infoPOST /api/v1/public/shared-folders/f/{token}/verify-password
原因:公开访问接口,返回结构复杂且动态,强制 Schema 会增加维护成本。
Screenplays(保持 dict)
POST /api/v1/screenplays/{screenplay_id}/elements/batch
原因:批量操作返回结构高度动态化。
Folder Share(保持 dict)
POST /api/v1/folders/{folder_id}/share
原因:根据 type 字段返回不同结构(用户分享 vs 链接分享),使用联合类型会增加复杂性。
前端迁移指南
Phase 1: 立即修改(必须)
-
Authentication 模块
// services/auth.ts interface LoginResponse { accessToken: string; refreshToken: string; tokenType: string; // "Bearer" expiresIn: number; // ← 新增 user: User; } // 新增 Token 自动刷新逻辑 const tokenExpiresAt = Date.now() + response.data.expiresIn * 1000; -
FolderMemberResponse
// types/folder.ts interface FolderMember { // ... role: 'viewer' | 'editor' | 'owner'; // ✅ 字符串枚举 // roleNumber 已废弃 }
Phase 2: 验证响应格式(推荐)
// 使用 Zod 或 yup 验证 API 响应
import { z } from 'zod';
const LoginResponseSchema = z.object({
accessToken: z.string(),
refreshToken: z.string(),
tokenType: z.string(),
expiresIn: z.number(),
user: UserSchema,
});
// API 调用时验证
const response = await api.post('/auth/login/phone', data);
const validated = LoginResponseSchema.parse(response.data.data);
Phase 3: 更新 Mock 数据(测试)
确保测试数据使用 camelCase 字段名:
// ❌ 错误
const mockUser = {
user_id: '123',
created_at: '2024-01-01'
};
// ✅ 正确
const mockUser = {
userId: '123',
createdAt: '2024-01-01'
};
技术细节
命名规范强制执行
所有 Schema 使用 alias 确保前端收到 camelCase 字段:
class Example(BaseModel):
model_config = ConfigDict(populate_by_name=True)
user_id: str = Field(..., alias="userId")
created_at: datetime = Field(..., alias="createdAt")
Service 层配合
Service 层返回的 dict 必须使用 snake_case 键名(与数据库一致),Pydantic 自动处理序列化:
# ✅ Service 层
return {
'user_id': str(user.user_id),
'created_at': user.created_at
}
# FastAPI + Pydantic 自动序列化为:
{
"userId": "...",
"createdAt": "..."
}
测试要点
1. 集成测试
# 验证 camelCase 序列化
response = client.post("/api/v1/auth/login/phone", json=data)
assert "accessToken" in response.json()["data"]
assert "expiresIn" in response.json()["data"]
2. Schema 验证
# 确保 Pydantic 验证生效
from app.schemas.user import LoginResponse
response_data = service.login_with_phone(...)
LoginResponse.model_validate(response_data) # 自动验证字段类型
Rollback 方案
如需回滚,恢复以下文件即可:
git revert <commit-hash>
# 影响文件:
# - server/app/api/v1/*.py
# - server/app/schemas/*.py
# - server/app/services/*.py(仅 return dict 的字段名)
后续计划
Phase 4: 补充剩余接口(可选)
- Shared Folders 公开访问接口
- Screenplays 批量操作接口
- 其他复杂动态结构接口
Phase 5: 自动化验证(推荐)
- 编写 pre-commit hook 检测新增的
SuccessResponse[dict] - CI/CD 集成 Schema 验证测试
相关文档
作者
Claude (via Cursor) - 2026-02-11