# Folder API 查询参数格式修复 **日期**: 2026-01-29 **类型**: Bug 修复 **影响范围**: 前端 Folder API 服务 ## 问题描述 前端调用 Folder API 时,查询参数使用了 `snake_case` 格式(如 `folder_category`),但后端 API 规范要求使用 `camelCase` 格式(如 `folderCategory`),导致请求失败。 **错误示例**: ``` GET /api/v1/folders?folder_category=1 ❌ ``` **错误响应**: ```json { "success": false, "code": 400, "message": "查询根文件夹时必须指定 folder_category", "data": null } ``` ## 根本原因 `client/src/services/api/folders.ts` 中的 API 调用使用了 `keysToSnakeCase()` 工具函数转换查询参数,将 camelCase 参数名转换为 snake_case,与后端 API 规范不符。 ## 解决方案 ### 修改文件 - `client/src/services/api/folders.ts` ### 具体变更 #### 1. `getAll()` 方法 **修改前**: ```typescript async getAll(params?: { parentId?: string | null; folderCategory?: FolderCategory; page?: number; pageSize?: number; }): Promise { const queryParams = keysToSnakeCase(params || {}); // ❌ 转换为 snake_case const response = await apiClient.get('/folders', { params: queryParams, }); return keysToCamelCase(response); } ``` **修改后**: ```typescript async getAll(params?: { parentId?: string | null; folderCategory?: FolderCategory; page?: number; pageSize?: number; }): Promise { // 查询参数保持 camelCase,符合 API 规范 const response = await apiClient.get('/folders', { params: params, // ✅ 保持 camelCase }); return keysToCamelCase(response); } ``` #### 2. `getTree()` 方法 **修改前**: ```typescript async getTree(params?: { maxDepth?: number; includeProjects?: boolean; }): Promise<{ tree: FolderTreeNode[] }> { const queryParams: Record = {}; if (params?.maxDepth) queryParams.max_depth = params.maxDepth; // ❌ snake_case if (params?.includeProjects) queryParams.include_projects = params.includeProjects; // ❌ snake_case const response = await apiClient.get<{ tree: FolderTreeNode[] }>('/folders/tree', { params: Object.keys(queryParams).length > 0 ? queryParams : undefined, }); return keysToCamelCase(response); } ``` **修改后**: ```typescript async getTree(params?: { maxDepth?: number; includeProjects?: boolean; }): Promise<{ tree: FolderTreeNode[] }> { // 查询参数保持 camelCase,符合 API 规范 const response = await apiClient.get<{ tree: FolderTreeNode[] }>('/folders/tree', { params: params, // ✅ 保持 camelCase }); return keysToCamelCase(response); } ``` #### 3. `getById()` 方法 **修改前**: ```typescript async getById(id: string, includeBreadcrumbs = true): Promise { const response = await apiClient.get(`/folders/${id}`, { params: { include_breadcrumbs: includeBreadcrumbs }, // ❌ snake_case }); return keysToCamelCase(response); } ``` **修改后**: ```typescript async getById(id: string, includeBreadcrumbs = true): Promise { const response = await apiClient.get(`/folders/${id}`, { params: { includeBreadcrumbs }, // ✅ camelCase }); return keysToCamelCase(response); } ``` ## API 规范说明 根据 `docs/requirements/api-design-specification.md` 和 `.claude/skills/jointo-tech-stack/references/api-design.md`: ### 查询参数命名规范 > 参数命名使用 **camelCase**: > - `folderId` > - `pageSize` > - `sortBy` > - `sortOrder` > - `folderCategory` > - `parentId` ### 正确的 API 调用示例 ```http GET /api/v1/folders?folderCategory=1&parentId=null&page=1&pageSize=20 GET /api/v1/folders/tree?maxDepth=3&includeProjects=true GET /api/v1/folders/{id}?includeBreadcrumbs=true ``` ## 影响范围 ### 修复的功能 ✅ 获取文件夹列表(根文件夹和子文件夹) ✅ 获取文件夹树形结构 ✅ 获取文件夹详情(包含面包屑) ### 不受影响的功能 - 创建文件夹(POST 请求体使用 snake_case,符合后端 Pydantic 模型) - 更新文件夹(PUT 请求体使用 snake_case,符合后端 Pydantic 模型) - 删除文件夹 - 移动文件夹 ## 测试验证 ### 手动测试 ```bash # 1. 获取根文件夹列表(我的项目) curl -X GET "http://localhost:6160/api/v1/folders?folderCategory=1" \ -H "Authorization: Bearer " # 2. 获取文件夹树 curl -X GET "http://localhost:6160/api/v1/folders/tree?maxDepth=3&includeProjects=true" \ -H "Authorization: Bearer " # 3. 获取文件夹详情 curl -X GET "http://localhost:6160/api/v1/folders/{id}?includeBreadcrumbs=true" \ -H "Authorization: Bearer " ``` ### 预期结果 所有请求应返回 `200 OK` 和正确的数据,不再出现 `400 Bad Request` 错误。 ## 相关文档 - API 设计规范: `docs/requirements/api-design-specification.md` - Jointo Tech Stack Skill: `.claude/skills/jointo-tech-stack/references/api-design.md` - 后端 Folder API: `server/app/api/v1/folders.py` ## 经验教训 1. **查询参数 vs 请求体**: - 查询参数(GET):使用 **camelCase**,直接传递,不转换 - 请求体(POST/PUT):使用 **snake_case**,需要 `keysToSnakeCase()` 转换 2. **API 规范一致性**: - 前后端必须遵循统一的命名规范 - 查询参数应与后端 FastAPI 的 `alias` 参数保持一致 3. **工具函数使用场景**: - `keysToSnakeCase()`:仅用于 POST/PUT 请求体 - `keysToCamelCase()`:用于所有响应数据 - 查询参数:保持原始 camelCase 格式 ## 后续优化建议 1. 在 `api-utils.ts` 中添加注释,明确说明各工具函数的使用场景 2. 考虑创建专门的查询参数类型,确保类型安全 3. 添加单元测试验证查询参数格式