From 54cbfe2adfcaedd5b4dc7efd4234ef3dd930d0a9 Mon Sep 17 00:00:00 2001 From: Audit Bot Date: Sun, 28 Jun 2026 21:28:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E5=BA=93=E6=96=B0=E5=BB=BA/=E7=BC=96=E8=BE=91=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20=E2=80=94=20=E5=89=8D=E5=90=8E=E7=AB=AF=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=90=8D=E4=B8=8D=E5=8C=B9=E9=85=8D=E5=AF=BC=E8=87=B4?= =?UTF-8?q?422?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:前端发送 { content, category } 但后端 CreateTitleLibraryRequest 期望 { name (必填), text (必填), category }。字段名不匹配导致 Pydantic 校验失败返回 422,modal 不关闭,数据不保存。 修复: - api/titles.ts: 请求时将 content 映射为 text + 自动截取 name - api/titles.ts: 响应时将后端 text 字段映射回前端 content - api/titles.ts: updateTitle 改用 PUT(后端实际方法) - 页面代码无需改动,映射在 API 层透明处理 Co-Authored-By: Claude Fable 5 --- apps/web/src/api/titles.ts | 84 +++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/apps/web/src/api/titles.ts b/apps/web/src/api/titles.ts index cdd60d931..376d21e0c 100644 --- a/apps/web/src/api/titles.ts +++ b/apps/web/src/api/titles.ts @@ -1,10 +1,11 @@ /** * 标题相关 API * Phase 1 新增:全局标题库 + * 注意:后端 schema 使用 name + text 字段,前端 UI 用 content 展示 */ import apiClient from './client'; -/** 标题条目 */ +/** 标题条目(前端展示用) */ export interface TitleItem { id: string; content: string; @@ -16,7 +17,50 @@ export interface TitleItem { updated_at?: string; } -/** 创建标题请求 */ +/** 后端标题响应格式 */ +interface BackendTitleResponse { + id: string; + user_id: string; + name: string; + text: string; + category: string; + description: string; + tags: string[]; + usage_count: number; + is_active: boolean; + created_at: string; + updated_at: string; +} + +/** 后端创建标题请求格式 */ +interface BackendCreateTitleRequest { + name: string; + text: string; + category: string; + description?: string; + tags?: string[]; +} + +/** 后端更新标题请求格式 */ +interface BackendUpdateTitleRequest { + name?: string; + text?: string; + category?: string; + description?: string; + tags?: string[]; +} + +/** 将后端响应映射为前端 TitleItem */ +const toTitleItem = (item: BackendTitleResponse): TitleItem => ({ + id: item.id, + content: item.text, + category: item.category, + word_count: item.text?.length || 0, + created_at: item.created_at, + updated_at: item.updated_at, +}); + +/** 创建标题请求(前端接口,保持向后兼容) */ export interface CreateTitleRequest { content: string; category?: string; @@ -24,25 +68,43 @@ export interface CreateTitleRequest { /** 获取当前用户的所有标题 */ export const getTitles = async (): Promise => { - const response = await apiClient.get('/titles'); - return response.data.items || response.data || []; + const response = await apiClient.get<{ items: BackendTitleResponse[] }>('/titles'); + return (response.data.items || []).map(toTitleItem); }; /** 创建标题 */ export const createTitle = async ( - data: CreateTitleRequest + data: CreateTitleRequest, ): Promise => { - const response = await apiClient.post('/titles', data); - return response.data; + // 后端要求 name(≤255)和 text(≤500),name 从 content 截取 + const payload: BackendCreateTitleRequest = { + name: data.content.slice(0, 255), + text: data.content.slice(0, 500), + category: data.category || 'default', + }; + const response = await apiClient.post('/titles', payload); + return toTitleItem(response.data); }; /** 更新标题 */ export const updateTitle = async ( titleId: string, - data: Partial + data: Partial, ): Promise => { - const response = await apiClient.patch(`/titles/${titleId}`, data); - return response.data; + const payload: BackendUpdateTitleRequest = {}; + if (data.content !== undefined) { + payload.name = data.content.slice(0, 255); + payload.text = data.content.slice(0, 500); + } + if (data.category !== undefined) { + payload.category = data.category; + } + // 后端用 PUT,非 PATCH + const response = await apiClient.put( + `/titles/${titleId}`, + payload, + ); + return toTitleItem(response.data); }; /** 删除标题 */ @@ -52,7 +114,7 @@ export const deleteTitle = async (titleId: string): Promise => { /** 批量导入标题 */ export const batchImportTitles = async ( - titles: string[] + titles: string[], ): Promise<{ imported_count: number }> => { const response = await apiClient.post('/titles/batch-import', { titles }); return response.data; -- 2.54.0