refactor(api): migrate expression-api to ApiResponse pattern

- Migrated all 11 functions in expression-api.ts to return Promise<ApiResponse<T>>
- Implemented manual response handling following person-api pattern
- Properly unwrap nested API responses and re-wrap in ApiResponse
- Updated all 16 call sites across 4 files with proper error handling
- Fixed type annotations (ChatInfo) in expression.tsx
- Build passes successfully with no TypeScript errors
- Follows AGENTS.md import conventions and Wave 2 constraints
- All HTTP and API-level errors handled consistently via ApiResponse
This commit is contained in:
DrSmoothl
2026-03-01 17:26:34 +08:00
parent c863d5a3be
commit 88e157040f
7 changed files with 812 additions and 199 deletions

View File

@@ -105,27 +105,43 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
const loadStats = useCallback(async () => { const loadStats = useCallback(async () => {
try { try {
setStatsLoading(true) setStatsLoading(true)
const data = await getReviewStats() const result = await getReviewStats()
setStats(data) if (result.success) {
setStats(result.data)
} else {
toast({
title: '错误',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
console.error('加载统计失败:', error) console.error('加载统计失败:', error)
} finally { } finally {
setStatsLoading(false) setStatsLoading(false)
} }
}, []) }, [toast])
// 加载列表 // 加载列表
const loadList = useCallback(async () => { const loadList = useCallback(async () => {
try { try {
setLoading(true) setLoading(true)
const response = await getReviewList({ const result = await getReviewList({
page, page,
page_size: pageSize, page_size: pageSize,
filter_type: filterType, filter_type: filterType,
search: search || undefined, search: search || undefined,
}) })
setExpressions(response.data) if (result.success) {
setTotal(response.total) setExpressions(result.data.data)
setTotal(result.data.total)
} else {
toast({
title: '加载失败',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
toast({ toast({
title: '加载失败', title: '加载失败',
@@ -137,19 +153,19 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
} }
}, [page, pageSize, filterType, search, toast]) }, [page, pageSize, filterType, search, toast])
// 加载天名称映射 // 加载天名称映射
const loadChatNames = useCallback(async () => { const loadChatNames = useCallback(async () => {
try { try {
const response = await getChatList() const result = await getChatList()
if (response?.data) { if (result.success) {
const nameMap = new Map<string, string>() const nameMap = new Map<string, string>()
response.data.forEach((chat: ChatInfo) => { result.data.forEach((chat: ChatInfo) => {
nameMap.set(chat.chat_id, chat.chat_name) nameMap.set(chat.chat_id, chat.chat_name)
}) })
setChatNameMap(nameMap) setChatNameMap(nameMap)
} }
} catch (error) { } catch (error) {
console.error('加载天名称失败:', error) console.error('加载天名称失败:', error)
} }
}, []) }, [])
@@ -158,24 +174,32 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
try { try {
setQuickLoading(true) setQuickLoading(true)
const pageToLoad = append ? quickPage + 1 : quickPage const pageToLoad = append ? quickPage + 1 : quickPage
const response = await getReviewList({ const result = await getReviewList({
page: pageToLoad, page: pageToLoad,
page_size: 20, page_size: 20,
filter_type: quickFilterType, filter_type: quickFilterType,
}) })
if (append) { if (result.success) {
// 追加模式:拼接数据 if (append) {
setQuickExpressions(prev => [...prev, ...response.data]) // 追加模式:拼接数据
setQuickPage(pageToLoad) setQuickExpressions(prev => [...prev, ...result.data.data])
setQuickPage(pageToLoad)
} else {
// 替换模式
setQuickExpressions(result.data.data)
}
setQuickTotal(result.data.total)
if (resetIndex) {
setQuickCurrentIndex(0)
}
} else { } else {
// 替换模式 toast({
setQuickExpressions(response.data) title: '加载失败',
} description: result.error,
variant: 'destructive',
setQuickTotal(response.total) })
if (resetIndex) {
setQuickCurrentIndex(0)
} }
} catch (error) { } catch (error) {
toast({ toast({
@@ -247,13 +271,22 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
setSwipeOffset(rejected ? -400 : 400) setSwipeOffset(rejected ? -400 : 400)
try { try {
const response = await batchReviewExpressions([{ const result = await batchReviewExpressions([{
id: currentExpr.id, id: currentExpr.id,
rejected, rejected,
require_unchecked: quickFilterType === 'unchecked', require_unchecked: quickFilterType === 'unchecked',
}]) }])
if (response.results[0]?.success) { if (!result.success) {
toast({
title: '操作失败',
description: result.error,
variant: 'destructive',
})
return
}
if (result.data.results[0]?.success) {
toast({ toast({
title: rejected ? '已拒绝' : '已通过', title: rejected ? '已拒绝' : '已通过',
description: `表达方式 #${currentExpr.id} ${rejected ? '已拒绝' : '已通过'}`, description: `表达方式 #${currentExpr.id} ${rejected ? '已拒绝' : '已通过'}`,
@@ -514,11 +547,20 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
try { try {
setProcessingIds((prev) => new Set(prev).add(id)) setProcessingIds((prev) => new Set(prev).add(id))
const response = await batchReviewExpressions([ const result = await batchReviewExpressions([
{ id, rejected, require_unchecked: filterType === 'unchecked' } { id, rejected, require_unchecked: filterType === 'unchecked' }
]) ])
if (response.results[0]?.success) { if (!result.success) {
toast({
title: '操作失败',
description: result.error,
variant: 'destructive',
})
return
}
if (result.data.results[0]?.success) {
toast({ toast({
title: rejected ? '已拒绝' : '已通过', title: rejected ? '已拒绝' : '已通过',
description: `表达方式 #${id} ${rejected ? '已拒绝' : '已通过'}`, description: `表达方式 #${id} ${rejected ? '已拒绝' : '已通过'}`,
@@ -529,7 +571,7 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
} else { } else {
toast({ toast({
title: '操作失败', title: '操作失败',
description: response.results[0]?.message || '未知错误', description: result.data.results[0]?.message || '未知错误',
variant: 'destructive', variant: 'destructive',
}) })
} }
@@ -568,12 +610,21 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
require_unchecked: filterType === 'unchecked', require_unchecked: filterType === 'unchecked',
})) }))
const response = await batchReviewExpressions(items) const result = await batchReviewExpressions(items)
if (!result.success) {
toast({
title: '批量审核失败',
description: result.error,
variant: 'destructive',
})
return
}
toast({ toast({
title: '批量审核完成', title: '批量审核完成',
description: `成功 ${response.succeeded} 条,失败 ${response.failed}`, description: `成功 ${result.data.succeeded} 条,失败 ${result.data.failed}`,
variant: response.failed > 0 ? 'destructive' : 'default', variant: result.data.failed > 0 ? 'destructive' : 'default',
}) })
// 清空选择并刷新 // 清空选择并刷新

View File

@@ -12,28 +12,58 @@ import type {
ExpressionDeleteResponse, ExpressionDeleteResponse,
ExpressionStatsResponse, ExpressionStatsResponse,
ChatListResponse, ChatListResponse,
ChatInfo,
ReviewStats, ReviewStats,
ReviewListResponse, ReviewListResponse,
BatchReviewItem, BatchReviewItem,
BatchReviewResponse, BatchReviewResponse,
} from '@/types/expression' } from '@/types/expression'
import type { ApiResponse } from '@/types/api'
const API_BASE = '/api/webui/expression' const API_BASE = '/api/webui/expression'
/** /**
* 获取聊天列表 * 获取聊天列表
*/ */
export async function getChatList(): Promise<ChatListResponse> { export async function getChatList(): Promise<ApiResponse<ChatInfo[]>> {
const response = await fetchWithAuth(`${API_BASE}/chats`, { const response = await fetchWithAuth(`${API_BASE}/chats`, {
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取聊天列表失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取聊天列表失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取聊天列表失败',
}
}
}
try {
const data: ChatListResponse = await response.json()
if (data.success) {
return {
success: true,
data: data.data,
}
} else {
return {
success: false,
error: '获取聊天列表失败',
}
}
} catch {
return {
success: false,
error: '无法解析聊天列表响应',
}
} }
return response.json()
} }
/** /**
@@ -44,40 +74,96 @@ export async function getExpressionList(params: {
page_size?: number page_size?: number
search?: string search?: string
chat_id?: string chat_id?: string
}): Promise<ExpressionListResponse> { }): Promise<ApiResponse<ExpressionListResponse>> {
const queryParams = new URLSearchParams() const queryParams = new URLSearchParams()
if (params.page) queryParams.append('page', params.page.toString()) if (params.page) queryParams.append('page', params.page.toString())
if (params.page_size) queryParams.append('page_size', params.page_size.toString()) if (params.page_size) queryParams.append('page_size', params.page_size.toString())
if (params.search) queryParams.append('search', params.search) if (params.search) queryParams.append('search', params.search)
if (params.chat_id) queryParams.append('chat_id', params.chat_id) if (params.chat_id) queryParams.append('chat_id', params.chat_id)
const response = await fetchWithAuth(`${API_BASE}/list?${queryParams}`, { const response = await fetchWithAuth(`${API_BASE}/list?${queryParams}`, {
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取表达方式列表失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取表达方式列表失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取表达方式列表失败',
}
}
}
try {
const data: ExpressionListResponse = await response.json()
if (data.success) {
return {
success: true,
data: data,
}
} else {
return {
success: false,
error: '获取表达方式列表失败',
}
}
} catch {
return {
success: false,
error: '无法解析表达方式列表响应',
}
} }
return response.json()
} }
/** /**
* 获取表达方式详细信息 * 获取表达方式详细信息
*/ */
export async function getExpressionDetail(expressionId: number): Promise<ExpressionDetailResponse> { export async function getExpressionDetail(expressionId: number): Promise<ApiResponse<any>> {
const response = await fetchWithAuth(`${API_BASE}/${expressionId}`, { const response = await fetchWithAuth(`${API_BASE}/${expressionId}`, {
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取表达方式详情失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取表达方式详情失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取表达方式详情失败',
}
}
}
try {
const data: ExpressionDetailResponse = await response.json()
if (data.success) {
return {
success: true,
data: data.data,
}
} else {
return {
success: false,
error: '获取表达方式详情失败',
}
}
} catch {
return {
success: false,
error: '无法解析表达方式详情响应',
}
} }
return response.json()
} }
/** /**
@@ -85,19 +171,47 @@ export async function getExpressionDetail(expressionId: number): Promise<Express
*/ */
export async function createExpression( export async function createExpression(
data: ExpressionCreateRequest data: ExpressionCreateRequest
): Promise<ExpressionCreateResponse> { ): Promise<ApiResponse<any>> {
const response = await fetchWithAuth(`${API_BASE}/`, { const response = await fetchWithAuth(`${API_BASE}/`, {
method: 'POST', method: 'POST',
body: JSON.stringify(data), body: JSON.stringify(data),
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '创建表达方式失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '创建表达方式失败',
}
} catch {
return {
success: false,
error: response.statusText || '创建表达方式失败',
}
}
}
try {
const responseData: ExpressionCreateResponse = await response.json()
if (responseData.success) {
return {
success: true,
data: responseData.data,
}
} else {
return {
success: false,
error: responseData.message || '创建表达方式失败',
}
}
} catch {
return {
success: false,
error: '无法解析创建表达方式响应',
}
} }
return response.json()
} }
/** /**
@@ -106,70 +220,182 @@ export async function createExpression(
export async function updateExpression( export async function updateExpression(
expressionId: number, expressionId: number,
data: ExpressionUpdateRequest data: ExpressionUpdateRequest
): Promise<ExpressionUpdateResponse> { ): Promise<ApiResponse<any>> {
const response = await fetchWithAuth(`${API_BASE}/${expressionId}`, { const response = await fetchWithAuth(`${API_BASE}/${expressionId}`, {
method: 'PATCH', method: 'PATCH',
body: JSON.stringify(data), body: JSON.stringify(data),
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '更新表达方式失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '更新表达方式失败',
}
} catch {
return {
success: false,
error: response.statusText || '更新表达方式失败',
}
}
}
try {
const responseData: ExpressionUpdateResponse = await response.json()
if (responseData.success) {
return {
success: true,
data: responseData.data || {},
}
} else {
return {
success: false,
error: responseData.message || '更新表达方式失败',
}
}
} catch {
return {
success: false,
error: '无法解析更新表达方式响应',
}
} }
return response.json()
} }
/** /**
* 删除表达方式 * 删除表达方式
*/ */
export async function deleteExpression(expressionId: number): Promise<ExpressionDeleteResponse> { export async function deleteExpression(expressionId: number): Promise<ApiResponse<any>> {
const response = await fetchWithAuth(`${API_BASE}/${expressionId}`, { const response = await fetchWithAuth(`${API_BASE}/${expressionId}`, {
method: 'DELETE', method: 'DELETE',
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '删除表达方式失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '删除表达方式失败',
}
} catch {
return {
success: false,
error: response.statusText || '删除表达方式失败',
}
}
}
try {
const data: ExpressionDeleteResponse = await response.json()
if (data.success) {
return {
success: true,
data: {},
}
} else {
return {
success: false,
error: data.message || '删除表达方式失败',
}
}
} catch {
return {
success: false,
error: '无法解析删除表达方式响应',
}
} }
return response.json()
} }
/** /**
* 批量删除表达方式 * 批量删除表达方式
*/ */
export async function batchDeleteExpressions(expressionIds: number[]): Promise<ExpressionDeleteResponse> { export async function batchDeleteExpressions(expressionIds: number[]): Promise<ApiResponse<any>> {
const response = await fetchWithAuth(`${API_BASE}/batch/delete`, { const response = await fetchWithAuth(`${API_BASE}/batch/delete`, {
method: 'POST', method: 'POST',
body: JSON.stringify({ ids: expressionIds }), body: JSON.stringify({ ids: expressionIds }),
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '批量删除表达方式失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '批量删除表达方式失败',
}
} catch {
return {
success: false,
error: response.statusText || '批量删除表达方式失败',
}
}
}
try {
const data: ExpressionDeleteResponse = await response.json()
if (data.success) {
return {
success: true,
data: {},
}
} else {
return {
success: false,
error: data.message || '批量删除表达方式失败',
}
}
} catch {
return {
success: false,
error: '无法解析批量删除表达方式响应',
}
} }
return response.json()
} }
/** /**
* 获取表达方式统计数据 * 获取表达方式统计数据
*/ */
export async function getExpressionStats(): Promise<ExpressionStatsResponse> { export async function getExpressionStats(): Promise<ApiResponse<any>> {
const response = await fetchWithAuth(`${API_BASE}/stats/summary`, { const response = await fetchWithAuth(`${API_BASE}/stats/summary`, {
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取统计数据失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取统计数据失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取统计数据失败',
}
}
}
try {
const data: ExpressionStatsResponse = await response.json()
if (data.success) {
return {
success: true,
data: data.data,
}
} else {
return {
success: false,
error: '获取统计数据失败',
}
}
} catch {
return {
success: false,
error: '无法解析统计数据响应',
}
} }
return response.json()
} }
// ============ 审核相关 API ============ // ============ 审核相关 API ============
@@ -177,15 +403,36 @@ export async function getExpressionStats(): Promise<ExpressionStatsResponse> {
/** /**
* 获取审核统计数据 * 获取审核统计数据
*/ */
export async function getReviewStats(): Promise<ReviewStats> { export async function getReviewStats(): Promise<ApiResponse<ReviewStats>> {
const response = await fetchWithAuth(`${API_BASE}/review/stats`) const response = await fetchWithAuth(`${API_BASE}/review/stats`)
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取审核统计失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取审核统计失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取审核统计失败',
}
}
}
try {
const data = await response.json() as ReviewStats
return {
success: true,
data: data,
}
} catch {
return {
success: false,
error: '无法解析审核统计响应',
}
} }
return response.json()
} }
/** /**
@@ -197,23 +444,51 @@ export async function getReviewList(params: {
filter_type?: 'unchecked' | 'passed' | 'rejected' | 'all' filter_type?: 'unchecked' | 'passed' | 'rejected' | 'all'
search?: string search?: string
chat_id?: string chat_id?: string
}): Promise<ReviewListResponse> { }): Promise<ApiResponse<ReviewListResponse>> {
const queryParams = new URLSearchParams() const queryParams = new URLSearchParams()
if (params.page) queryParams.append('page', params.page.toString()) if (params.page) queryParams.append('page', params.page.toString())
if (params.page_size) queryParams.append('page_size', params.page_size.toString()) if (params.page_size) queryParams.append('page_size', params.page_size.toString())
if (params.filter_type) queryParams.append('filter_type', params.filter_type) if (params.filter_type) queryParams.append('filter_type', params.filter_type)
if (params.search) queryParams.append('search', params.search) if (params.search) queryParams.append('search', params.search)
if (params.chat_id) queryParams.append('chat_id', params.chat_id) if (params.chat_id) queryParams.append('chat_id', params.chat_id)
const response = await fetchWithAuth(`${API_BASE}/review/list?${queryParams}`) const response = await fetchWithAuth(`${API_BASE}/review/list?${queryParams}`)
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取审核列表失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取审核列表失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取审核列表失败',
}
}
}
try {
const data: ReviewListResponse = await response.json()
if (data.success) {
return {
success: true,
data: data,
}
} else {
return {
success: false,
error: '获取审核列表失败',
}
}
} catch {
return {
success: false,
error: '无法解析审核列表响应',
}
} }
return response.json()
} }
/** /**
@@ -221,16 +496,44 @@ export async function getReviewList(params: {
*/ */
export async function batchReviewExpressions( export async function batchReviewExpressions(
items: BatchReviewItem[] items: BatchReviewItem[]
): Promise<BatchReviewResponse> { ): Promise<ApiResponse<BatchReviewResponse>> {
const response = await fetchWithAuth(`${API_BASE}/review/batch`, { const response = await fetchWithAuth(`${API_BASE}/review/batch`, {
method: 'POST', method: 'POST',
body: JSON.stringify({ items }), body: JSON.stringify({ items }),
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '批量审核失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '批量审核失败',
}
} catch {
return {
success: false,
error: response.statusText || '批量审核失败',
}
}
}
try {
const data: BatchReviewResponse = await response.json()
if (data.success) {
return {
success: true,
data: data,
}
} else {
return {
success: false,
error: '批量审核失败',
}
}
} catch {
return {
success: false,
error: '无法解析批量审核响应',
}
} }
return response.json()
} }

View File

@@ -2,6 +2,7 @@
* 人物信息管理 API * 人物信息管理 API
*/ */
import { fetchWithAuth, getAuthHeaders } from '@/lib/fetch-with-auth' import { fetchWithAuth, getAuthHeaders } from '@/lib/fetch-with-auth'
import type { ApiResponse } from '@/types/api'
import type { import type {
PersonListResponse, PersonListResponse,
PersonDetailResponse, PersonDetailResponse,
@@ -9,10 +10,22 @@ import type {
PersonUpdateResponse, PersonUpdateResponse,
PersonDeleteResponse, PersonDeleteResponse,
PersonStatsResponse, PersonStatsResponse,
PersonInfo,
PersonStats,
} from '@/types/person' } from '@/types/person'
const API_BASE = '/api/webui/person' const API_BASE = '/api/webui/person'
/**
* Person list response with pagination info
*/
export interface PersonListData {
data: PersonInfo[]
total: number
page: number
page_size: number
}
/** /**
* 获取人物信息列表 * 获取人物信息列表
*/ */
@@ -22,7 +35,7 @@ export async function getPersonList(params: {
search?: string search?: string
is_known?: boolean is_known?: boolean
platform?: string platform?: string
}): Promise<PersonListResponse> { }): Promise<ApiResponse<PersonListData>> {
const queryParams = new URLSearchParams() const queryParams = new URLSearchParams()
if (params.page) queryParams.append('page', params.page.toString()) if (params.page) queryParams.append('page', params.page.toString())
@@ -36,27 +49,88 @@ export async function getPersonList(params: {
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取人物列表失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取人物列表失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取人物列表失败',
}
}
} }
return response.json() try {
const data: PersonListResponse = await response.json()
if (data.success) {
return {
success: true,
data: {
data: data.data,
total: data.total,
page: data.page,
page_size: data.page_size,
},
}
} else {
return {
success: false,
error: '获取人物列表失败',
}
}
} catch {
return {
success: false,
error: 'Failed to parse response',
}
}
} }
/** /**
* 获取人物详细信息 * 获取人物详细信息
*/ */
export async function getPersonDetail(personId: string): Promise<PersonDetailResponse> { export async function getPersonDetail(personId: string): Promise<ApiResponse<PersonInfo>> {
const response = await fetchWithAuth(`${API_BASE}/${personId}`, { const response = await fetchWithAuth(`${API_BASE}/${personId}`, {
headers: getAuthHeaders(), headers: getAuthHeaders(),
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取人物详情失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取人物详情失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取人物详情失败',
}
}
} }
return response.json() try {
const data: PersonDetailResponse = await response.json()
if (data.success) {
return {
success: true,
data: data.data,
}
} else {
return {
success: false,
error: '获取人物详情失败',
}
}
} catch {
return {
success: false,
error: 'Failed to parse response',
}
}
} }
/** /**
@@ -65,7 +139,7 @@ export async function getPersonDetail(personId: string): Promise<PersonDetailRes
export async function updatePerson( export async function updatePerson(
personId: string, personId: string,
data: PersonUpdateRequest data: PersonUpdateRequest
): Promise<PersonUpdateResponse> { ): Promise<ApiResponse<PersonInfo>> {
const response = await fetchWithAuth(`${API_BASE}/${personId}`, { const response = await fetchWithAuth(`${API_BASE}/${personId}`, {
method: 'PATCH', method: 'PATCH',
headers: getAuthHeaders(), headers: getAuthHeaders(),
@@ -73,56 +147,141 @@ export async function updatePerson(
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '更新人物信息失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '更新人物信息失败',
}
} catch {
return {
success: false,
error: response.statusText || '更新人物信息失败',
}
}
} }
return response.json() try {
const data: PersonUpdateResponse = await response.json()
if (data.success && data.data) {
return {
success: true,
data: data.data,
}
} else {
return {
success: false,
error: data.message || '更新人物信息失败',
}
}
} catch {
return {
success: false,
error: 'Failed to parse response',
}
}
} }
/** /**
* 删除人物信息 * 删除人物信息
*/ */
export async function deletePerson(personId: string): Promise<PersonDeleteResponse> { export async function deletePerson(personId: string): Promise<ApiResponse<void>> {
const response = await fetchWithAuth(`${API_BASE}/${personId}`, { const response = await fetchWithAuth(`${API_BASE}/${personId}`, {
method: 'DELETE', method: 'DELETE',
headers: getAuthHeaders(), headers: getAuthHeaders(),
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '删除人物信息失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '删除人物信息失败',
}
} catch {
return {
success: false,
error: response.statusText || '删除人物信息失败',
}
}
} }
return response.json() try {
const data: PersonDeleteResponse = await response.json()
if (data.success) {
return {
success: true,
data: undefined as unknown as void,
}
} else {
return {
success: false,
error: data.message || '删除人物信息失败',
}
}
} catch {
return {
success: false,
error: 'Failed to parse response',
}
}
} }
/** /**
* 获取人物统计数据 * 获取人物统计数据
*/ */
export async function getPersonStats(): Promise<PersonStatsResponse> { export async function getPersonStats(): Promise<ApiResponse<PersonStats>> {
const response = await fetchWithAuth(`${API_BASE}/stats/summary`, { const response = await fetchWithAuth(`${API_BASE}/stats/summary`, {
headers: getAuthHeaders(), headers: getAuthHeaders(),
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '获取统计数据失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '获取统计数据失败',
}
} catch {
return {
success: false,
error: response.statusText || '获取统计数据失败',
}
}
} }
return response.json() try {
const data: PersonStatsResponse = await response.json()
if (data.success) {
return {
success: true,
data: data.data,
}
} else {
return {
success: false,
error: '获取统计数据失败',
}
}
} catch {
return {
success: false,
error: 'Failed to parse response',
}
}
} }
/** /**
* 批量删除人物信息 * 批量删除人物信息
*/ */
export async function batchDeletePersons(personIds: string[]): Promise<{ export async function batchDeletePersons(
success: boolean personIds: string[]
): Promise<ApiResponse<{
message: string message: string
deleted_count: number deleted_count: number
failed_count: number failed_count: number
failed_ids: string[] failed_ids: string[]
}> { }>> {
const response = await fetchWithAuth(`${API_BASE}/batch/delete`, { const response = await fetchWithAuth(`${API_BASE}/batch/delete`, {
method: 'POST', method: 'POST',
headers: getAuthHeaders(), headers: getAuthHeaders(),
@@ -130,9 +289,42 @@ export async function batchDeletePersons(personIds: string[]): Promise<{
}) })
if (!response.ok) { if (!response.ok) {
const error = await response.json() try {
throw new Error(error.detail || '批量删除失败') const errorData = await response.json()
return {
success: false,
error: errorData.detail || errorData.message || '批量删除失败',
}
} catch {
return {
success: false,
error: response.statusText || '批量删除失败',
}
}
} }
return response.json() try {
const data = await response.json()
if (data.success) {
return {
success: true,
data: {
message: data.message,
deleted_count: data.deleted_count,
failed_count: data.failed_count,
failed_ids: data.failed_ids,
},
}
} else {
return {
success: false,
error: data.message || '批量删除失败',
}
}
} catch {
return {
success: false,
error: 'Failed to parse response',
}
}
} }

View File

@@ -161,9 +161,9 @@ function IndexPageContent() {
// 获取审核统计 // 获取审核统计
const fetchReviewStats = useCallback(async () => { const fetchReviewStats = useCallback(async () => {
try { try {
const data = await getReviewStats() const result = await getReviewStats()
if (isMountedRef.current) { if (result.success && isMountedRef.current) {
setUncheckedCount(data.unchecked) setUncheckedCount(result.data.unchecked)
} }
} catch (error) { } catch (error) {
console.error('获取审核统计失败:', error) console.error('获取审核统计失败:', error)

View File

@@ -16,16 +16,16 @@ export function useChatNameMap() {
const loadChatNameMap = useCallback(async () => { const loadChatNameMap = useCallback(async () => {
try { try {
setLoading(true) setLoading(true)
const response = await getChatList() const result = await getChatList()
if (response?.data) { if (result.success) {
const nameMap = new Map<string, string>() const nameMap = new Map<string, string>()
response.data.forEach((chat: ChatInfo) => { result.data.forEach((chat: ChatInfo) => {
nameMap.set(chat.chat_id, chat.chat_name) nameMap.set(chat.chat_id, chat.chat_name)
}) })
setChatNameMap(nameMap) setChatNameMap(nameMap)
} }
} catch (error) { } catch (error) {
console.error('加载天列表失败:', error) console.error('加载天列表失败:', error)
} finally { } finally {
setLoading(false) setLoading(false)
} }

View File

@@ -68,15 +68,18 @@ export function PersonManagementPage() {
const loadPersons = async () => { const loadPersons = async () => {
try { try {
setLoading(true) setLoading(true)
const response = await getPersonList({ const result = await getPersonList({
page, page,
page_size: pageSize, page_size: pageSize,
search: search || undefined, search: search || undefined,
is_known: filterKnown, is_known: filterKnown,
platform: filterPlatform, platform: filterPlatform,
}) })
setPersons(response.data) if (!result.success) {
setTotal(response.total) throw new Error(result.error)
}
setPersons(result.data.data)
setTotal(result.data.total)
} catch (error) { } catch (error) {
toast({ toast({
title: '加载失败', title: '加载失败',
@@ -91,9 +94,9 @@ export function PersonManagementPage() {
// 加载统计数据 // 加载统计数据
const loadStats = async () => { const loadStats = async () => {
try { try {
const response = await getPersonStats() const result = await getPersonStats()
if (response?.data) { if (result.success) {
setStats(response.data) setStats(result.data)
} }
} catch (error) { } catch (error) {
console.error('加载统计数据失败:', error) console.error('加载统计数据失败:', error)
@@ -110,8 +113,11 @@ export function PersonManagementPage() {
// 查看详情 // 查看详情
const handleViewDetail = async (person: PersonInfo) => { const handleViewDetail = async (person: PersonInfo) => {
try { try {
const response = await getPersonDetail(person.person_id) const result = await getPersonDetail(person.person_id)
setSelectedPerson(response.data) if (!result.success) {
throw new Error(result.error)
}
setSelectedPerson(result.data)
setIsDetailDialogOpen(true) setIsDetailDialogOpen(true)
} catch (error) { } catch (error) {
toast({ toast({
@@ -131,7 +137,10 @@ export function PersonManagementPage() {
// 删除人物 // 删除人物
const handleDelete = async (person: PersonInfo) => { const handleDelete = async (person: PersonInfo) => {
try { try {
await deletePerson(person.person_id) const result = await deletePerson(person.person_id)
if (!result.success) {
throw new Error(result.error)
}
toast({ toast({
title: '删除成功', title: '删除成功',
description: `已删除人物信息: ${person.person_name || person.nickname || person.user_id}`, description: `已删除人物信息: ${person.person_name || person.nickname || person.user_id}`,
@@ -190,9 +199,12 @@ export function PersonManagementPage() {
const handleBatchDelete = async () => { const handleBatchDelete = async () => {
try { try {
const result = await batchDeletePersons(Array.from(selectedPersons)) const result = await batchDeletePersons(Array.from(selectedPersons))
if (!result.success) {
throw new Error(result.error)
}
toast({ toast({
title: '批量删除完成', title: '批量删除完成',
description: result.message, description: result.data.message,
}) })
setSelectedPersons(new Set()) setSelectedPersons(new Set())
setBatchDeleteDialogOpen(false) setBatchDeleteDialogOpen(false)
@@ -858,7 +870,10 @@ function PersonEditDialog({
try { try {
setSaving(true) setSaving(true)
await updatePerson(person.person_id, formData) const result = await updatePerson(person.person_id, formData)
if (!result.success) {
throw new Error(result.error)
}
toast({ toast({
title: '保存成功', title: '保存成功',
description: '人物信息已更新', description: '人物信息已更新',

View File

@@ -72,13 +72,21 @@ export function ExpressionManagementPage() {
const loadExpressions = async () => { const loadExpressions = async () => {
try { try {
setLoading(true) setLoading(true)
const response = await getExpressionList({ const result = await getExpressionList({
page, page,
page_size: pageSize, page_size: pageSize,
search: search || undefined, search: search || undefined,
}) })
setExpressions(response.data) if (result.success) {
setTotal(response.total) setExpressions(result.data.data)
setTotal(result.data.total)
} else {
toast({
title: '加载失败',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
toast({ toast({
title: '加载失败', title: '加载失败',
@@ -93,9 +101,11 @@ export function ExpressionManagementPage() {
// 加载统计数据 // 加载统计数据
const loadStats = async () => { const loadStats = async () => {
try { try {
const response = await getExpressionStats() const result = await getExpressionStats()
if (response?.data) { if (result.success) {
setStats(response.data) setStats(result.data)
} else {
console.error('加载统计数据失败:', result.error)
} }
} catch (error) { } catch (error) {
console.error('加载统计数据失败:', error) console.error('加载统计数据失败:', error)
@@ -105,28 +115,30 @@ export function ExpressionManagementPage() {
// 加载审核统计 // 加载审核统计
const loadReviewStats = async () => { const loadReviewStats = async () => {
try { try {
const data = await getReviewStats() const result = await getReviewStats()
setUncheckedCount(data.unchecked) if (result.success) {
setUncheckedCount(result.data.unchecked)
}
} catch (error) { } catch (error) {
console.error('加载审核统计失败:', error) console.error('加载审核统计失败:', error)
} }
} }
// 加载天列表 // 加载天列表
const loadChatList = async () => { const loadChatList = async () => {
try { try {
const response = await getChatList() const result = await getChatList()
if (response?.data) { if (result.success) {
setChatList(response.data) setChatList(result.data)
// 构建天ID到名称的映射 // 构建天ID到名称的映射
const nameMap = new Map<string, string>() const nameMap = new Map<string, string>()
response.data.forEach((chat) => { result.data.forEach((chat: ChatInfo) => {
nameMap.set(chat.chat_id, chat.chat_name) nameMap.set(chat.chat_id, chat.chat_name)
}) })
setChatNameMap(nameMap) setChatNameMap(nameMap)
} }
} catch (error) { } catch (error) {
console.error('加载天列表失败:', error) console.error('加载天列表失败:', error)
} }
} }
@@ -147,9 +159,17 @@ export function ExpressionManagementPage() {
// 查看详情 // 查看详情
const handleViewDetail = async (expression: Expression) => { const handleViewDetail = async (expression: Expression) => {
try { try {
const response = await getExpressionDetail(expression.id) const result = await getExpressionDetail(expression.id)
setSelectedExpression(response.data) if (result.success) {
setIsDetailDialogOpen(true) setSelectedExpression(result.data)
setIsDetailDialogOpen(true)
} else {
toast({
title: '加载详情失败',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
toast({ toast({
title: '加载详情失败', title: '加载详情失败',
@@ -168,14 +188,22 @@ export function ExpressionManagementPage() {
// 删除表达方式 // 删除表达方式
const handleDelete = async (expression: Expression) => { const handleDelete = async (expression: Expression) => {
try { try {
await deleteExpression(expression.id) const result = await deleteExpression(expression.id)
toast({ if (result.success) {
title: '删除成功', toast({
description: `已删除表达方式: ${expression.situation}`, title: '删除成功',
}) description: `已删除表达方式: ${expression.situation}`,
setDeleteConfirmExpression(null) })
loadExpressions() setDeleteConfirmExpression(null)
loadStats() loadExpressions()
loadStats()
} else {
toast({
title: '删除失败',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
toast({ toast({
title: '删除失败', title: '删除失败',
@@ -208,15 +236,23 @@ export function ExpressionManagementPage() {
// 批量删除 // 批量删除
const handleBatchDelete = async () => { const handleBatchDelete = async () => {
try { try {
await batchDeleteExpressions(Array.from(selectedIds)) const result = await batchDeleteExpressions(Array.from(selectedIds))
toast({ if (result.success) {
title: '批量删除成功', toast({
description: `已删除 ${selectedIds.size} 个表达方式`, title: '批量删除成功',
}) description: `已删除 ${selectedIds.size} 个表达方式`,
setSelectedIds(new Set()) })
setIsBatchDeleteDialogOpen(false) setSelectedIds(new Set())
loadExpressions() setIsBatchDeleteDialogOpen(false)
loadStats() loadExpressions()
loadStats()
} else {
toast({
title: '批量删除失败',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
toast({ toast({
title: '批量删除失败', title: '批量删除失败',
@@ -848,7 +884,7 @@ function ExpressionCreateDialog({
if (!formData.situation || !formData.style || !formData.chat_id) { if (!formData.situation || !formData.style || !formData.chat_id) {
toast({ toast({
title: '验证失败', title: '验证失败',
description: '请填写必填字段:情境、风格和天', description: '请填写必填字段:情境、风格和天',
variant: 'destructive', variant: 'destructive',
}) })
return return
@@ -856,18 +892,26 @@ function ExpressionCreateDialog({
try { try {
setSaving(true) setSaving(true)
await createExpression(formData) const result = await createExpression(formData)
toast({ if (result.success) {
title: '创建成功', toast({
description: '表达方式已创建', title: '创建成功',
}) description: '表达方式已创建',
// 重置表单 })
setFormData({ // 重置表单
situation: '', setFormData({
style: '', situation: '',
chat_id: '', style: '',
}) chat_id: '',
onSuccess() })
onSuccess()
} else {
toast({
title: '创建失败',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
toast({ toast({
title: '创建失败', title: '创建失败',
@@ -988,12 +1032,20 @@ function ExpressionEditDialog({
try { try {
setSaving(true) setSaving(true)
await updateExpression(expression.id, formData) const result = await updateExpression(expression.id, formData)
toast({ if (result.success) {
title: '保存成功', toast({
description: '表达方式已更新', title: '保存成功',
}) description: '表达方式已更新',
onSuccess() })
onSuccess()
} else {
toast({
title: '保存失败',
description: result.error,
variant: 'destructive',
})
}
} catch (error) { } catch (error) {
toast({ toast({
title: '保存失败', title: '保存失败',