/** * 通用 HTTP 客户端:自动注入 Bearer Token,统一处理错误 */ import { useAuthStore } from '@/stores/auth' import { pinia } from '@/stores' import { fetchApi } from '@/config/apiBase' // 后端返回的错误消息中英映射 const MESSAGE_MAP: Record = { 'You can only operate your own resources': '只能操作自己的资源', 'Preference keyword already exists for this user': '该关键词已订阅', 'Keyword cannot be empty': '关键词不能为空', 'This delivery time already exists': '该推送时间已存在', 'This channel type already exists for the user': '该渠道类型已存在', 'Schedule not found': '推送时间不存在', 'Push endpoint not found': '推送渠道不存在', 'Preference not found': '偏好不存在', 'Invalid or expired token': '登录已过期,请重新登录', 'Authentication credentials were not provided': '请先登录', } function localizeMessage(msg: string): string { return MESSAGE_MAP[msg] ?? msg } function getAuthHeaders(): Record { const authStore = useAuthStore(pinia) const headers: Record = { 'Content-Type': 'application/json' } if (authStore.accessToken) { headers['Authorization'] = `Bearer ${authStore.accessToken}` } return headers } async function handleResponse(response: Response): Promise { const raw = await response.text() let data: Record = {} if (raw) { try { data = JSON.parse(raw) as Record } catch { data = {} } } if (!response.ok) { const detail = data.detail if (typeof detail === 'string') { throw new Error(localizeMessage(detail)) } throw new Error(`请求失败 (${response.status})`) } return data as T } /** GET 请求 */ export async function apiGet(path: string, params?: Record): Promise { let requestPath = path if (params) { const searchParams = new URLSearchParams() for (const [key, value] of Object.entries(params)) { searchParams.set(key, String(value)) } const separator = requestPath.includes('?') ? '&' : '?' requestPath += `${separator}${searchParams.toString()}` } const response = await fetchApi(requestPath, { method: 'GET', headers: getAuthHeaders() }) return handleResponse(response) } /** POST 请求 */ export async function apiPost(path: string, body?: unknown): Promise { const response = await fetchApi(path, { method: 'POST', headers: getAuthHeaders(), body: body !== undefined ? JSON.stringify(body) : undefined, }) return handleResponse(response) } /** PATCH 请求 */ export async function apiPatch(path: string, body: unknown): Promise { const response = await fetchApi(path, { method: 'PATCH', headers: getAuthHeaders(), body: JSON.stringify(body), }) return handleResponse(response) } /** DELETE 请求 */ export async function apiDelete(path: string): Promise { const response = await fetchApi(path, { method: 'DELETE', headers: getAuthHeaders(), }) if (!response.ok && response.status !== 204) { const raw = await response.text() let detail = `请求失败 (${response.status})` try { const data = JSON.parse(raw) as Record if (typeof data.detail === 'string') detail = localizeMessage(data.detail) } catch { /* ignore */ } throw new Error(detail) } }