import { clearAdminToken, getAdminToken, setAdminToken, setAdminUser } from './admin-auth' const API_BASE = import.meta.env.VITE_API_BASE || (import.meta.env.DEV ? 'http://127.0.0.1:8000/api/v1' : '/api/v1') type RequestError = Error & { status?: number } async function request(path: string, init: RequestInit = {}) { const token = getAdminToken() const headers: Record = { 'Content-Type': 'application/json', ...(init.headers as Record || {}), } if (token) headers.Authorization = `Bearer ${token}` let resp: Response try { resp = await fetch(`${API_BASE}${path}`, { ...init, headers, }) } catch { throw new Error(`接口连接失败:${API_BASE}${path}`) } if (resp.status === 401) { clearAdminToken() throw new Error('登录已失效,请重新登录') } const data = await resp.json().catch(() => ({})) if (!resp.ok) { const message = data?.detail || data?.message || `请求失败(${resp.status})` const error = new Error(message) as RequestError error.status = resp.status throw error } return data as T } export function isRequestNotFound(error: unknown) { return (error as RequestError)?.status === 404 } export async function adminLogin(account: string, password: string) { const data = await request<{ access_token: string refresh_token: string user: Record }>('/admin/auth/login', { method: 'POST', body: JSON.stringify({ account, password }), }) setAdminToken(data.access_token) setAdminUser(data.user) return data } export function adminMe() { return request('/admin/auth/me') } export function adminDashboard() { return request('/admin/dashboard') } export function adminModuleDesign() { return request('/admin/module-design') } export function adminListUsers(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/users?${q.toString()}`) } export function adminCreateUser(payload: Record) { return request('/admin/users', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateUser(userId: number, payload: Record) { return request(`/admin/users/${userId}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteUser(userId: number) { return request(`/admin/users/${userId}`, { method: 'DELETE', }) } export function adminUserOptions(keyword = '') { const q = new URLSearchParams() if (keyword.trim()) q.set('keyword', keyword.trim()) return request(`/admin/user-options?${q.toString()}`) } export function adminListSpots(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/spots?${q.toString()}`) } export function adminGetSpot(spotId: number) { return request(`/spots/${spotId}`).then((detail: any) => ({ id: detail.id, title: detail.title || '', city: detail.city || '', creator_id: detail.creator?.id || detail.creator_id || 0, longitude: detail.longitude ?? 0, latitude: detail.latitude ?? 0, description: detail.description || '', transport: detail.transport || '', best_time: detail.best_time || '', difficulty: detail.difficulty || '', is_free: detail.is_free ?? true, price_min: detail.price_min ?? null, price_max: detail.price_max ?? null, audit_status: detail.audit_status || 'pending', reject_reason: detail.reject_reason || '', tag_ids: (detail.tags || []).map((t: any) => t.id), image_urls: (detail.images || []).map((img: any) => img.image_url), images: detail.images || [], })) } export function adminCreateSpot(payload: Record) { return request('/admin/spots', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateSpot(spotId: number, payload: Record) { return request(`/admin/spots/${spotId}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteSpot(spotId: number) { return request(`/admin/spots/${spotId}`, { method: 'DELETE', }) } export function adminSpotTagOptions(keyword = '') { return request('/tags?sort=hot').then((items: any[]) => { const key = keyword.trim().toLowerCase() const normalized = (items || []).map((item: any) => ({ id: item.id, title: item.name || item.title || '', })) if (!key) return normalized return normalized.filter(item => String(item.title).toLowerCase().includes(key)) }) } export function adminAuditSpot(spotId: number, payload: { audit_status: string, reject_reason?: string }) { return request(`/admin/spots/${spotId}/audit`, { method: 'PATCH', body: JSON.stringify(payload), }) } export function adminBatchAuditSpots(payload: { ids: number[], audit_status: string, reject_reason?: string }) { return request('/admin/spots/batch-audit', { method: 'POST', body: JSON.stringify(payload), }) } export function adminListEvents(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/events?${q.toString()}`) } export function adminGetEvent(eventId: number) { return request(`/admin/events/${eventId}`) } export function adminCreateEvent(payload: Record) { return request('/admin/events', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateEvent(eventId: number, payload: Record) { return request(`/admin/events/${eventId}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteEvent(eventId: number) { return request(`/admin/events/${eventId}`, { method: 'DELETE', }) } export function adminListEventRegistrations(eventId: number) { return request(`/admin/events/${eventId}/registrations`) } export function adminCreateEventRegistration(eventId: number, payload: Record) { return request(`/admin/events/${eventId}/registrations`, { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateEventRegistration(eventId: number, registrationId: number, payload: Record) { return request(`/admin/events/${eventId}/registrations/${registrationId}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteEventRegistration(eventId: number, registrationId: number) { return request(`/admin/events/${eventId}/registrations/${registrationId}`, { method: 'DELETE', }) } export function adminListEventPhotos(eventId: number) { return request(`/admin/events/${eventId}/photos`) } export function adminCreateEventPhoto(eventId: number, payload: Record) { return request(`/admin/events/${eventId}/photos`, { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateEventPhoto(eventId: number, photoId: number, payload: Record) { return request(`/admin/events/${eventId}/photos/${photoId}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteEventPhoto(eventId: number, photoId: number) { return request(`/admin/events/${eventId}/photos/${photoId}`, { method: 'DELETE', }) } export function adminAuditEvent(eventId: number, payload: { audit_status: string, reject_reason?: string }) { return request(`/admin/events/${eventId}/audit`, { method: 'PATCH', body: JSON.stringify(payload), }) } export function adminBatchAuditEvents(payload: { ids: number[], audit_status: string, reject_reason?: string }) { return request('/admin/events/batch-audit', { method: 'POST', body: JSON.stringify(payload), }) } export function adminListShooting(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/shooting?${q.toString()}`) } export function adminGetShooting(requestId: number) { return request(`/admin/shooting/${requestId}`) } export function adminCreateShooting(payload: Record) { return request('/admin/shooting', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateShooting(requestId: number, payload: Record) { return request(`/admin/shooting/${requestId}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteShooting(requestId: number) { return request(`/admin/shooting/${requestId}`, { method: 'DELETE', }) } export function adminListShootingApplications(requestId: number) { return request(`/admin/shooting/${requestId}/applications`) } export function adminCreateShootingApplication(requestId: number, payload: Record) { return request(`/admin/shooting/${requestId}/applications`, { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateShootingApplication(requestId: number, applicationId: number, payload: Record) { return request(`/admin/shooting/${requestId}/applications/${applicationId}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteShootingApplication(requestId: number, applicationId: number) { return request(`/admin/shooting/${requestId}/applications/${applicationId}`, { method: 'DELETE', }) } export function adminAuditShooting(requestId: number, payload: { audit_status: string, reject_reason?: string }) { return request(`/admin/shooting/${requestId}/audit`, { method: 'PATCH', body: JSON.stringify(payload), }) } export function adminBatchAuditShooting(payload: { ids: number[], audit_status: string, reject_reason?: string }) { return request('/admin/shooting/batch-audit', { method: 'POST', body: JSON.stringify(payload), }) } export function adminListAppNavConfigs(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/app-nav-configs?${q.toString()}`) } export function adminCreateAppNavConfig(payload: Record) { return request('/admin/app-nav-configs', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateAppNavConfig(id: number, payload: Record) { return request(`/admin/app-nav-configs/${id}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteAppNavConfig(id: number) { return request(`/admin/app-nav-configs/${id}`, { method: 'DELETE', }) } export function adminListPromotions(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/promotions?${q.toString()}`) } export function adminCreatePromotion(payload: Record) { return request('/admin/promotions', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdatePromotion(id: number, payload: Record) { return request(`/admin/promotions/${id}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeletePromotion(id: number) { return request(`/admin/promotions/${id}`, { method: 'DELETE', }) } export function adminPromotionLinkOptions(linkType: 'spot' | 'event' | 'shooting', keyword = '') { const q = new URLSearchParams() q.set('link_type', linkType) if (keyword.trim()) q.set('keyword', keyword.trim()) return request(`/admin/promotion-link-options?${q.toString()}`) } export function adminListMembershipPlans(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/membership/plans?${q.toString()}`) } export function adminCreateMembershipPlan(payload: Record) { return request('/admin/membership/plans', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateMembershipPlan(id: number, payload: Record) { return request(`/admin/membership/plans/${id}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteMembershipPlan(id: number) { return request(`/admin/membership/plans/${id}`, { method: 'DELETE', }) } export function adminListUserMemberships(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/membership/user-memberships?${q.toString()}`) } export function adminCreateUserMembership(payload: Record) { return request('/admin/membership/user-memberships', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateUserMembership(id: number, payload: Record) { return request(`/admin/membership/user-memberships/${id}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteUserMembership(id: number) { return request(`/admin/membership/user-memberships/${id}`, { method: 'DELETE', }) } export function adminListPointLedger(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/points/ledger?${q.toString()}`) } export function adminAdjustPoints(payload: Record) { return request('/admin/points/adjust', { method: 'POST', body: JSON.stringify(payload), }) } export function adminRollbackPointLedger(ledgerId: number) { return request(`/admin/points/ledger/${ledgerId}/rollback`, { method: 'POST', }) } export function adminListNotifications(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/notifications?${q.toString()}`) } export function adminCreateNotification(payload: Record) { return request('/admin/notifications', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateNotification(id: number, payload: Record) { return request(`/admin/notifications/${id}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteNotification(id: number) { return request(`/admin/notifications/${id}`, { method: 'DELETE', }) } export function adminListAuditLogs(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/audit-logs?${q.toString()}`) } export function adminCreateAuditLog(payload: Record) { return request('/admin/audit-logs', { method: 'POST', body: JSON.stringify(payload), }) } export function adminListReports(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/reports?${q.toString()}`) } export function adminUpdateReport(id: number, payload: Record) { return request(`/admin/reports/${id}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteReport(id: number) { return request(`/admin/reports/${id}`, { method: 'DELETE', }) } export function adminListSystemConfigs(params: Record) { const q = new URLSearchParams() Object.entries(params || {}).forEach(([k, v]) => { if (v !== undefined && v !== null && String(v).trim() !== '') q.set(k, String(v)) }) return request(`/admin/system-configs?${q.toString()}`) } export function adminCreateSystemConfig(payload: Record) { return request('/admin/system-configs', { method: 'POST', body: JSON.stringify(payload), }) } export function adminUpdateSystemConfig(id: number, payload: Record) { return request(`/admin/system-configs/${id}`, { method: 'PUT', body: JSON.stringify(payload), }) } export function adminDeleteSystemConfig(id: number) { return request(`/admin/system-configs/${id}`, { method: 'DELETE', }) }