/** * Admin API layer - connects to the FastAPI backend. */ import { api, setToken, clearToken } from './client'; import type { User, CreditRecord, Project, GenerationRecord, GenerationParams, Industry, AdminUser, AdminStats, ModelConfig, SystemConfig, AdminNotification, GenerationAiEnginesResponse, GenerationAITaskListOut, GenerationAITaskQueryParams, AdminHotOpeningTaskQueryParams, HotOpeningTaskListOut, ReplicationProjectDetailOut, AdminShotTaskSetQueryParams, ShotTaskSetListOut, ShotTaskSetDetailOut, AdminShotSegmentQueryParams, ShotSegmentListOut, ShotSegmentDetailOut, VideoPromptSchemaConfigOut, VideoPromptSchemaConfigSavePayload, VideoPromptSchemaPreviewPayload, VideoPromptSchemaPreviewOut, VideoPromptSchemaExportOut, AdminCreditRecordListResponse, AdminCreditRecordQueryParams, ResourceCapacityConfigOut, ResourceCapacityConfigPayload, AdminUserResourceCapacityOut, AdminTeam, AdminTeamListResponse, AdminTeamOption, AdminTeamPayload, AdminTeamQueryParams, PrivatePortraitConfig, PrivatePortraitProjectListOut, PrivatePortraitAssetListOut, } from '../types'; import type { HomeMaterialAsset, HomeMaterialAssetQueryParams, HomeMaterialAssetStatusOut, HomeMaterialAssetUpdatePayload, HomeMaterialCategory, HomeMaterialCategoryPayload, HomeMaterialCategoryQueryParams, HomeMaterialConfig, HomeMaterialListResponse, HomeMaterialRegeneratePayload, HomeMaterialTextWatermarkConfig, HomeMaterialTextWatermarkPayload, HomeMaterialTextWatermarkPreviewRequest, HomeMaterialTextWatermarkPreviewResponse, HomeMaterialUploadAssetParams, HomeMaterialUploadResult, HomeMaterialWatermark, HomeMaterialWatermarkPayload, HomeMaterialWatermarkQueryParams, } from '../types'; // ── Auth ────────────────────────────────────────────────── export async function login(username: string, password: string, captchaToken?: string, rememberMe?: boolean): Promise { const res = await api.post<{ accessToken: string; user: User }>('/auth/admin-login', { username, password, captcha_token: captchaToken, remember_me: rememberMe || false }, false); setToken(res.accessToken); return res.user; } export async function logout(): Promise { await api.post('/auth/logout'); clearToken(); } export async function getUser(): Promise { try { return await api.get('/auth/me'); } catch { return null; } } export async function changePassword(oldPwd: string, newPwd: string): Promise { await api.post('/auth/change-password', { old_password: oldPwd, new_password: newPwd }); } // ── Projects ────────────────────────────────────────────── export async function getProjects(): Promise { return api.get('/projects'); } export async function createProject(name: string, industry: Industry): Promise { return api.post('/projects', { name, industry }); } export async function deleteProject(id: string): Promise { await api.delete(`/projects/${id}`); } // ── Generation ──────────────────────────────────────────── export async function getRecords(projectId?: string): Promise { const q = projectId ? `?project_id=${projectId}` : ''; return api.get(`/generation-records${q}`); } export async function optimizePrompt(projectId: string, params: GenerationParams): Promise { return api.post('/generation-records/optimize', { project_id: projectId, ...params }); } export async function generateVideo(recordId: string): Promise { return api.post(`/generation-records/${recordId}/generate`); } // ── Credits ─────────────────────────────────────────────── export async function getCredits(): Promise<{ credits: number; records: CreditRecord[] }> { return api.get('/credits'); } // ── Notifications ───────────────────────────────────────── export async function getNotifications(): Promise { return api.get('/notifications'); } export async function getUnreadCount(): Promise { const res = await api.get<{ count: number }>('/notifications/unread-count'); return res.count; } export async function markNotificationRead(id: string): Promise { await api.put(`/notifications/${id}/read`); } // ── Admin ───────────────────────────────────────────────── export async function getAdminStats(startDate?: string, endDate?: string): Promise { const params: Record = {}; if (startDate) params.start_date = startDate; if (endDate) params.end_date = endDate; const query = new URLSearchParams(params).toString(); return api.get(`/admin/stats${query ? `?${query}` : ''}`); } export async function getAdminUsers( page = 1, pageSize = 20, search?: string, userType?: string, frontendUserKind?: string, teamId?: string, ): Promise<{ items: AdminUser[]; total: number }> { const params = new URLSearchParams(); params.set('page', String(page)); params.set('page_size', String(pageSize)); if (search) params.set('search', search); if (userType) params.set('user_type', userType); if (frontendUserKind) params.set('frontend_user_kind', frontendUserKind); if (teamId) params.set('team_id', teamId); return api.get(`/admin/users?${params.toString()}`); } export async function updateFrontendUserKind(userId: string, frontendUserKind: 'internal' | 'external'): Promise { return api.put(`/admin/users/${userId}/frontend-kind`, { frontend_user_kind: frontendUserKind }); } export async function updateUserTeam(userId: string, teamId: string | null): Promise { return api.put(`/admin/users/${userId}/team`, { team_id: teamId }); } export async function getAdminTeams(filters?: AdminTeamQueryParams): Promise { const params = new URLSearchParams(); setMaybe(params, 'page', filters?.page); setMaybe(params, 'page_size', filters?.pageSize); setMaybe(params, 'keyword', filters?.keyword); setMaybe(params, 'status', filters?.status); const q = params.toString() ? `?${params}` : ''; return api.get(`/admin/teams${q}`); } export async function getTeamOptions(includeDisabled = true): Promise { return api.get(`/admin/teams/options?include_disabled=${includeDisabled ? 'true' : 'false'}`); } export async function saveAdminTeam(team: Partial & { id?: string }): Promise { const payload = { name: team.name, code: team.code || null, description: team.description || null, status: team.status || 'active', sort_order: team.sort_order ?? 0, }; if (team.id) return api.put(`/admin/teams/${team.id}`, payload); return api.post('/admin/teams', payload); } export async function deleteAdminTeam(id: string): Promise { await api.delete(`/admin/teams/${id}`); } export async function setTeamManager(teamId: string, userId: string | null): Promise { return api.put(`/admin/teams/${teamId}/manager`, { user_id: userId }); } export async function getTeamMembersForAdmin(teamId: string, page = 1, pageSize = 50): Promise<{ items: AdminUser[]; total: number }> { const params = new URLSearchParams(); params.set('page', String(page)); params.set('page_size', String(pageSize)); params.set('user_type', 'frontend'); params.set('team_id', teamId); return api.get(`/admin/users?${params.toString()}`); } export async function adjustCredits(userId: string, amount: number, description: string): Promise { await api.post(`/admin/users/${userId}/credits`, { amount, description }); } export async function toggleUserStatus(userId: string, isActive: boolean): Promise { await api.put(`/admin/users/${userId}/status`, { is_active: isActive }); } export async function getModelConfigs(): Promise { return api.get('/admin/model-configs'); } export async function saveModelConfig(config: Partial & { id?: string }): Promise { if (config.id) return api.put(`/admin/model-configs/${config.id}`, config); return api.post('/admin/model-configs', config); } export async function deleteModelConfig(id: string): Promise { await api.delete(`/admin/model-configs/${id}`); } export async function getSystemConfigs(): Promise { return api.get('/admin/system-configs'); } export async function updateSystemConfig(id: string, value: string): Promise { await api.put(`/admin/system-configs/${id}`, { value }); } export async function getGlobalResourceCapacity(): Promise { return api.get('/admin/resource-capacity/global'); } export async function saveGlobalResourceCapacity(payload: ResourceCapacityConfigPayload): Promise { return api.put('/admin/resource-capacity/global', { enabled: payload.enabled, limit_value: payload.limitValue ?? null, limit_unit: payload.limitUnit ?? null, }); } export async function getUserResourceCapacity(userId: string): Promise { return api.get(`/admin/users/${userId}/resource-capacity`); } export async function saveUserResourceCapacity(userId: string, payload: ResourceCapacityConfigPayload): Promise { return api.put(`/admin/users/${userId}/resource-capacity`, { enabled: payload.enabled, limit_value: payload.limitValue ?? null, limit_unit: payload.limitUnit ?? null, }); } export async function deleteUserResourceCapacity(userId: string): Promise { return api.delete(`/admin/users/${userId}/resource-capacity`); } export async function uploadPdf(file: File, configKey: string): Promise<{ url: string }> { const formData = new FormData(); formData.append('file', file); formData.append('config_key', configKey); const token = localStorage.getItem('auth_token'); const baseUrl = import.meta.env.VITE_API_BASE || 'http://localhost:8000'; const res = await fetch(`${baseUrl}/api/admin/upload-pdf`, { method: 'POST', headers: token ? { 'Authorization': `Bearer ${token}` } : {}, body: formData, }); if (!res.ok) { const text = await res.text(); throw new Error(text || '上传失败'); } return res.json(); } export async function uploadLogo(file: File): Promise<{ url: string }> { const formData = new FormData(); formData.append('file', file); const token = localStorage.getItem('auth_token'); const baseUrl = import.meta.env.VITE_API_BASE || 'http://localhost:8000'; const res = await fetch(`${baseUrl}/api/admin/upload-logo`, { method: 'POST', headers: token ? { 'Authorization': `Bearer ${token}` } : {}, body: formData, }); if (!res.ok) { const text = await res.text(); throw new Error(text || '上传失败'); } return res.json(); } function setMaybe(params: URLSearchParams, key: string, value: unknown): void { if (value !== undefined && value !== null && String(value) !== '') params.set(key, String(value)); } export async function getCreditRecords(filters?: AdminCreditRecordQueryParams): Promise { const params = new URLSearchParams(); setMaybe(params, 'page', filters?.page); setMaybe(params, 'page_size', filters?.pageSize); setMaybe(params, 'user_id', filters?.userId); setMaybe(params, 'user_name', filters?.userName); setMaybe(params, 'user_type', filters?.userType); setMaybe(params, 'frontend_user_kind', filters?.frontendUserKind); setMaybe(params, 'team_id', filters?.teamId); setMaybe(params, 'record_type', filters?.recordType || filters?.type); setMaybe(params, 'credit_subject', filters?.creditSubject); setMaybe(params, 'media_type', filters?.mediaType); setMaybe(params, 'charge_kind', filters?.chargeKind); setMaybe(params, 'source_module', filters?.sourceModule); setMaybe(params, 'source_step_code', filters?.sourceStepCode); setMaybe(params, 'billing_scene', filters?.billingScene); setMaybe(params, 'start_date', filters?.startDate); setMaybe(params, 'end_date', filters?.endDate); const q = params.toString() ? `?${params}` : ''; return api.get(`/admin/credit-records${q}`); } export async function getIndustryConfigs(): Promise { return api.get('/admin/industry-configs'); } export async function saveIndustryConfig(config: any): Promise { if (config.id) return api.put(`/admin/industry-configs/${config.id}`, config); return api.post('/admin/industry-configs', config); } export async function deleteIndustryConfig(id: string): Promise { await api.delete(`/admin/industry-configs/${id}`); } export async function getVideoEngines(): Promise { return api.get('/admin/video-engines'); } export async function saveVideoEngine(engine: any): Promise { if (engine.id) return api.put(`/admin/video-engines/${engine.id}`, engine); return api.post('/admin/video-engines', engine); } export async function deleteVideoEngine(id: string): Promise { await api.delete(`/admin/video-engines/${id}`); } export async function getImageEngines(): Promise { return api.get('/admin/image-engines'); } export async function saveImageEngine(engine: any): Promise { if (engine.id) return api.put(`/admin/image-engines/${engine.id}`, engine); return api.post('/admin/image-engines', engine); } export async function deleteImageEngine(id: string): Promise { await api.delete(`/admin/image-engines/${id}`); } export async function getCreditRatios(): Promise { return api.get('/admin/credit-ratios'); } export async function saveCreditRatio(ratio: any): Promise { if (ratio.id) return api.put(`/admin/credit-ratios/${ratio.id}`, ratio); return api.post('/admin/credit-ratios', ratio); } export async function deleteCreditRatio(id: string): Promise { await api.delete(`/admin/credit-ratios/${id}`); } export async function getPaymentConfigs(): Promise { return api.get('/admin/payment-configs'); } export async function updatePaymentConfig(id: string, value: string): Promise { await api.put(`/admin/payment-configs/${id}`, { value }); } export async function batchUpdatePaymentConfigs(configs: Record): Promise { await api.put('/admin/payment-configs/batch', configs); } export async function getPaymentStats(params?: { paymentMethod?: string; status?: string; startDate?: string; endDate?: string; }): Promise<{ byStatus: Record; today: { paidCount: number; paidAmount: number }; month: { paidCount: number; paidAmount: number }; recent: any[]; }> { const searchParams = new URLSearchParams(); if (params?.paymentMethod) searchParams.set('payment_method', params.paymentMethod); if (params?.status) searchParams.set('status', params.status); if (params?.startDate) searchParams.set('start_date', params.startDate); if (params?.endDate) searchParams.set('end_date', params.endDate); const queryString = searchParams.toString(); const url = queryString ? `/admin/payment-stats?${queryString}` : '/admin/payment-stats'; return api.get(url); } export async function getAdminPaymentOrders(params?: { method?: string; status?: string; startDate?: string; endDate?: string; page?: number; pageSize?: number }): Promise<{ items: any[]; total: number }> { const qs = new URLSearchParams(); if (params?.method) qs.set('payment_method', params.method); if (params?.status) qs.set('status', params.status); if (params?.startDate) qs.set('start_date', params.startDate); if (params?.endDate) qs.set('end_date', params.endDate); if (params?.page) qs.set('page', String(params.page)); if (params?.pageSize) qs.set('page_size', String(params.pageSize)); return api.get(`/admin/payment-orders?${qs.toString()}`); } export async function refundPaymentOrder(orderNo: string): Promise { await api.post(`/admin/payment-orders/${orderNo}/refund`); } export async function getAdminNotifications(page = 1, pageSize = 20): Promise<{ total: number; items: any[] }> { const params = new URLSearchParams(); params.set('page', String(page)); params.set('page_size', String(pageSize)); return api.get(`/admin/notifications?${params.toString()}`); } export async function createAdminNotification(data: { title: string; content: string; type: string; target_user_id?: string }): Promise { await api.post('/admin/notifications', data); } export async function deleteAdminNotification(id: string): Promise { await api.delete(`/admin/notifications/${id}`); } export async function getNotificationReadUsers(id: string): Promise<{ total: number; items: { userId: string; username: string; readAt: string }[] }> { return api.get(`/admin/notifications/${id}/read-users`); } // ── Menu Config ───────────────────────────────────────── export async function getMenuConfigs(): Promise { return api.get('/admin/menu-configs'); } export async function saveMenuConfig(config: any): Promise { if (config.id) return api.put(`/admin/menu-configs/${config.id}`, config); return api.post('/admin/menu-configs', config); } export async function deleteMenuConfig(id: string): Promise { await api.delete(`/admin/menu-configs/${id}`); } // ── User Creation ─────────────────────────────────────── export async function createUser(data: { username?: string; password: string; email?: string; phone?: string; credits: number; user_type: string; frontend_user_kind?: string; allowed_menus?: string[] | null; private_portrait_image_limit?: number }): Promise { return api.post('/admin/users', data); } export async function updateUserMenus(userId: string, allowedMenus: string[] | null): Promise { await api.put(`/admin/users/${userId}/menus`, { allowed_menus: allowedMenus }); } export async function resetUserPassword(userId: string, newPassword: string): Promise { await api.put(`/admin/users/${userId}/reset-password`, { new_password: newPassword }); } export async function adminChangePassword(oldPwd: string, newPwd: string): Promise { await api.post('/admin/change-password', { old_password: oldPwd, new_password: newPwd }); } export async function getSiteInfo(): Promise<{ siteName: string; siteLogo: string; userAgreementUrl: string; privacyPolicyUrl: string }> { return api.get('/auth/site-info', false); } // ── Recharge Packages ─────────────────────────────────── export async function getRechargePackages(): Promise { return api.get('/admin/recharge-packages'); } export async function saveRechargePackage(pkg: any): Promise { if (pkg.id) return api.put(`/admin/recharge-packages/${pkg.id}`, pkg); return api.post('/admin/recharge-packages', pkg); } export async function deleteRechargePackage(id: string): Promise { await api.delete(`/admin/recharge-packages/${id}`); } // ── Operation Logs ────────────────────────────────────── export async function getOperationLogs(page?: number): Promise<{ total: number; items: any[] }> { const q = page ? `?page=${page}` : ''; return api.get(`/admin/operation-logs${q}`); } // ── oauthapp List ────────────────────────────────────── export async function getOauthAppList(page?: number): Promise<{ total: number; items: any[] }> { const q = page ? `?page=${page}` : ''; return api.get(`/admin/user-oauth-apps/list${q}`); } export async function createOauthApp(data: { app_id: string; secret: string; open_type: number; max_count?: number; auth_url?: string; company?: string; }): Promise { return api.post('/admin/user-oauth-apps/create', data); } export async function getOauthApp(id: string): Promise { return api.get(`/admin/user-oauth-apps/read/${id}`); } export async function updateOauthApp(id: string, data: { app_id?: string; secret?: string; open_type?: number; count?: number; auth_url?: string; company?: string; }): Promise { return api.post(`/admin/user-oauth-apps/update/${id}`, data); } export async function deleteOauthApp(id: string): Promise { await api.get(`/admin/user-oauth-apps/delete/${id}`); } // ── Open Type ─────────────────────────────────────────────── export async function getOpenTypeList(params?: { page?: number; page_size?: number; type_name?: string; open_type?: number; }): Promise<{ pagination: any; data: never[]; }> { const q = new URLSearchParams(); if (params?.page) q.set('page', String(params.page)); if (params?.page_size) q.set('page_size', String(params.page_size)); if (params?.type_name) q.set('type_name', params.type_name); if (params?.open_type) q.set('open_type', String(params.open_type)); const qs = q.toString(); return api.get(`/open-type/list${qs ? `?${qs}` : ''}`); } export async function getOpenType(id: string): Promise { return api.get(`/open-type/select/${id}`); } export async function createOpenType(data: { open_type: number; description: string; type_name: string; thumb?: string; }): Promise { return api.post('/open-type/create', data); } export async function updateOpenType(id: string, data: { open_type?: number; description?: string; type_name?: string; thumb?: string; }): Promise { return api.put(`/open-type/update/${id}`, data); } export async function deleteOpenType(id: string): Promise { await api.delete(`/open-type/delete/${id}`); } export interface OpenTypeItem { id: string; openType: number; typeName: string; description: string; thumb?: string; } export async function getOpenTypeAll(): Promise<{ data: OpenTypeItem[] }> { return api.get('/open-type/open_type_all'); } // ── Generation Records (Admin) ───────────────────────────── export async function getAdminGenerationRecords(params?: { userId?: string; status?: string; page?: number; pageSize?: number; }): Promise<{ total: number; items: any[] }> { const q = new URLSearchParams(); if (params?.userId) q.set('user_id', params.userId); if (params?.status) q.set('status', params.status); if (params?.page) q.set('page', String(params.page)); if (params?.pageSize) q.set('page_size', String(params.pageSize)); const qs = q.toString(); return api.get(`/admin/generation-records${qs ? `?${qs}` : ''}`); } export async function adminUpdateGenerationStatus( recordId: string, status: string, videoUrl?: string ): Promise { await api.put(`/admin/generation-records/${recordId}/status`, { status, video_url: videoUrl }); } export async function adminGenerateVideo( recordId: string, aspectRatio: string, resolution: string, image_size: string ): Promise { await api.post(`/admin/generation-records/${recordId}/generate`, { aspect_ratio: aspectRatio, resolution, image_size }); } // ── Generation AI Engines (Admin) ───────────────────────────── export async function getGenerationAiEngines(): Promise { return api.get(`/generation-ai/engines`); } export async function getAdminGenerationAiTasks(params?: GenerationAITaskQueryParams): Promise { const q = new URLSearchParams(); if (params?.genType) q.set('gen_type', params.genType); if (params?.status) q.set('status', params.status); if (params?.page) q.set('page', String(params.page)); if (params?.pageSize) q.set('page_size', String(params.pageSize)); if (params?.userId) q.set('user_id', params.userId); if (params?.userName) q.set('user_name', params.userName); if (params?.engineId) q.set('engine_id', params.engineId); if (params?.createdStart) q.set('created_start', params.createdStart); if (params?.createdEnd) q.set('created_end', params.createdEnd); const qs = q.toString(); return api.get(`/generation-ai/tasks${qs ? `?${qs}` : ''}`); } // ── Replication Modules (Admin readonly pages reuse original APIs) ─────────── function appendParam(params: URLSearchParams, key: string, value?: string | number | null): void { if (value === undefined || value === null || String(value).trim() === '') return; params.set(key, String(value)); } export async function getAdminHotOpeningTasks(params?: AdminHotOpeningTaskQueryParams): Promise { const q = new URLSearchParams(); appendParam(q, 'status', params?.status); appendParam(q, 'keyword', params?.keyword); appendParam(q, 'user_id', params?.userId); appendParam(q, 'user_name', params?.userName); appendParam(q, 'created_start', params?.createdStart); appendParam(q, 'created_end', params?.createdEnd); appendParam(q, 'page', params?.page); appendParam(q, 'page_size', params?.pageSize); const qs = q.toString(); return api.get(`/hot-opening-replications/tasks${qs ? `?${qs}` : ''}`); } export async function getAdminHotOpeningTaskDetail(projectId: string): Promise { return api.get(`/hot-opening-replications/tasks/${projectId}`); } export async function getAdminShotTaskSets(params?: AdminShotTaskSetQueryParams): Promise { const q = new URLSearchParams(); appendParam(q, 'status', params?.status); appendParam(q, 'analysis_status', params?.analysisStatus); appendParam(q, 'split_status', params?.splitStatus); appendParam(q, 'keyword', params?.keyword); appendParam(q, 'user_id', params?.userId); appendParam(q, 'user_name', params?.userName); appendParam(q, 'created_start', params?.createdStart); appendParam(q, 'created_end', params?.createdEnd); appendParam(q, 'page', params?.page); appendParam(q, 'page_size', params?.pageSize); const qs = q.toString(); return api.get(`/shot-replications/task-sets${qs ? `?${qs}` : ''}`); } export async function getAdminShotTaskSetDetail(taskSetId: string): Promise { return api.get(`/shot-replications/task-sets/${taskSetId}`); } export async function getAdminShotSegments(taskSetId: string, params?: AdminShotSegmentQueryParams): Promise { const q = new URLSearchParams(); appendParam(q, 'source_mode', params?.sourceMode); appendParam(q, 'split_status', params?.splitStatus); appendParam(q, 'analysis_status', params?.analysisStatus); appendParam(q, 'replicate_status', params?.replicateStatus); appendParam(q, 'page', params?.page); appendParam(q, 'page_size', params?.pageSize); const qs = q.toString(); return api.get(`/shot-replications/task-sets/${taskSetId}/segments${qs ? `?${qs}` : ''}`); } export async function getAdminShotSegmentDetail(segmentId: string): Promise { return api.get(`/shot-replications/segments/${segmentId}`); } export async function getAdminShotProjectDetail(projectId: string): Promise { return api.get(`/shot-replications/projects/${projectId}`); } // ── Video Prompt Schema Config (Admin) ───────────────────── export async function getVideoPromptSchemaConfig(): Promise { return api.get('/admin/video-prompt-schema-config'); } export async function saveVideoPromptSchemaConfig(payload: VideoPromptSchemaConfigSavePayload): Promise { return api.put('/admin/video-prompt-schema-config', payload); } export async function resetVideoPromptSchemaConfig(): Promise { return api.post('/admin/video-prompt-schema-config/reset-default'); } export async function exportVideoPromptSchemaConfig(): Promise { return api.get('/admin/video-prompt-schema-config/export'); } export async function importVideoPromptSchemaConfig(payload: VideoPromptSchemaConfigSavePayload): Promise { return api.post('/admin/video-prompt-schema-config/import', payload); } export async function previewVideoPromptSchemaConfig(payload: VideoPromptSchemaPreviewPayload): Promise { return api.post('/admin/video-prompt-schema-config/preview', payload); } // 获取授权链接 export interface RequestOAuthParams { open_type: number; } export async function requestOAuth(params: RequestOAuthParams): Promise { return api.post(`/user-oauth/request_oauth`, params); } // 授权列表 export interface OAuthListParams { account_userid?: string; open_type?: number; account_id?: string; page?: number; page_size?: number; } export async function getOAuthList(params: OAuthListParams): Promise { const query = new URLSearchParams(); if (params.account_userid) query.set('account_userid', params.account_userid); if (params.open_type !== undefined) query.set('open_type', String(params.open_type)); if (params.account_id) query.set('account_id', params.account_id); if (params.page !== undefined) query.set('page', String(params.page)); if (params.page_size !== undefined) query.set('page_size', String(params.page_size)); return api.get(`/user-oauth/oauth_list?${query.toString()}`); } export async function uploadImage(file: File): Promise<{ url: string; filename: string }> { const form = new FormData(); form.append('file', file); const token = localStorage.getItem('auth_token'); const res = await fetch(`${import.meta.env.VITE_API_BASE || 'http://localhost:8000'}/api/generation-records/upload-image`, { method: 'POST', headers: token ? { Authorization: `Bearer ${token}` } : {}, body: form, }); if (!res.ok) throw new Error('图片上传失败'); const data = await res.json(); return { url: data.url, filename: data.filename }; } // 自定义表头字段 export async function getFields(): Promise { return api.get('/material-consumption/fields'); } // 查询素材消耗列表 export interface MaterialConsumpParams { advertiser_id?: string; user_id?: string; consume_date?: [string, string]; page?: number; page_size?: number; } export async function getMaterialConsumpList(params: MaterialConsumpParams): Promise { const query = new URLSearchParams(); if (params.advertiser_id) query.set('advertiser_id', params.advertiser_id); if (params.user_id) query.set('user_id', params.user_id); if (params.consume_date !== undefined) query.set('consume_date', params.consume_date[0] + ',' + params.consume_date[1]); if (params.page !== undefined) query.set('page', String(params.page)); if (params.page_size !== undefined) query.set('page_size', String(params.page_size)); return api.get(`/material-consumption/admin/list?${query.toString()}`); } // ── 首页素材行业装修 ────────────────────────────────────── export async function getHomeMaterialConfig(): Promise { return api.get('/admin/home-material/config'); } export async function saveHomeMaterialConfig(payload: HomeMaterialConfig): Promise { return api.put('/admin/home-material/config', { enabled: payload.enabled, title: payload.title, subtitle: payload.subtitle, show_original_in_admin: payload.showOriginalInAdmin, }); } export async function getHomeMaterialCategories(filters?: HomeMaterialCategoryQueryParams): Promise> { const params = new URLSearchParams(); setMaybe(params, 'page', filters?.page); setMaybe(params, 'page_size', filters?.pageSize); setMaybe(params, 'keyword', filters?.keyword); setMaybe(params, 'is_active', filters?.isActive); const q = params.toString() ? `?${params}` : ''; return api.get(`/admin/home-material/categories${q}`); } export async function saveHomeMaterialCategory(payload: HomeMaterialCategoryPayload & { id?: string }): Promise { const body = { name: payload.name, key: payload.key, description: payload.description || null, icon: payload.icon || null, is_active: payload.is_active, sort_order: payload.sort_order, }; if (payload.id) return api.put(`/admin/home-material/categories/${payload.id}`, body); return api.post('/admin/home-material/categories', body); } export async function deleteHomeMaterialCategory(id: string): Promise { await api.delete(`/admin/home-material/categories/${id}`); } export async function getHomeMaterialWatermarks(filters?: HomeMaterialWatermarkQueryParams): Promise> { const params = new URLSearchParams(); setMaybe(params, 'page', filters?.page); setMaybe(params, 'page_size', filters?.pageSize); setMaybe(params, 'is_active', filters?.isActive); const q = params.toString() ? `?${params}` : ''; return api.get(`/admin/home-material/watermarks${q}`); } export async function uploadHomeMaterialWatermark(file: File, name?: string, isDefault = false): Promise { const form = new FormData(); form.append('file', file); if (name) form.append('name', name); form.append('is_default', String(isDefault)); return api.post('/admin/home-material/watermarks', form); } export async function updateHomeMaterialWatermark(id: string, payload: HomeMaterialWatermarkPayload): Promise { return api.put(`/admin/home-material/watermarks/${id}`, payload); } export async function deleteHomeMaterialWatermark(id: string): Promise { await api.delete(`/admin/home-material/watermarks/${id}`); } export async function getHomeMaterialAssets(filters?: HomeMaterialAssetQueryParams): Promise> { const params = new URLSearchParams(); setMaybe(params, 'page', filters?.page); setMaybe(params, 'page_size', filters?.pageSize); setMaybe(params, 'category_id', filters?.categoryId); setMaybe(params, 'media_type', filters?.mediaType); setMaybe(params, 'status', filters?.status); setMaybe(params, 'is_active', filters?.isActive); setMaybe(params, 'keyword', filters?.keyword); const q = params.toString() ? `?${params}` : ''; return api.get(`/admin/home-material/assets${q}`); } export async function getHomeMaterialAsset(id: string): Promise { return api.get(`/admin/home-material/assets/${id}`); } export async function getHomeMaterialAssetStatus(id: string): Promise { return api.get(`/admin/home-material/assets/${id}/status`); } function toHomeMaterialTextWatermarkPayload(config?: HomeMaterialTextWatermarkConfig | null): HomeMaterialTextWatermarkPayload | null { if (!config) return null; return { text: config.text, opacity_level: config.opacityLevel, font_size_px: config.fontSizePx, color: config.color, rotate_deg: config.rotateDeg, gap_x: config.gapX, gap_y: config.gapY, staggered: config.staggered, }; } export async function uploadHomeMaterialAsset(payload: HomeMaterialUploadAssetParams): Promise { const form = new FormData(); form.append('category_id', payload.categoryId); form.append('file', payload.file); form.append('media_type', payload.mediaType); if (payload.title && payload.title.trim()) form.append('title', payload.title.trim()); form.append('watermark_type', payload.watermarkType); if (payload.watermarkType === 'image') { if (payload.watermarkId) form.append('watermark_id', payload.watermarkId); if (payload.watermarkFile) form.append('watermark_file', payload.watermarkFile); form.append('opacity_level', String(payload.opacityLevel)); form.append('position', payload.position); if (payload.customXRatio !== undefined && payload.customXRatio !== null) form.append('custom_x_ratio', String(payload.customXRatio)); if (payload.customYRatio !== undefined && payload.customYRatio !== null) form.append('custom_y_ratio', String(payload.customYRatio)); form.append('size_mode', payload.sizeMode); if (payload.widthRatio !== undefined && payload.widthRatio !== null) form.append('width_ratio', String(payload.widthRatio)); if (payload.widthPx !== undefined && payload.widthPx !== null) form.append('width_px', String(payload.widthPx)); form.append('margin_x', String(payload.marginX)); form.append('margin_y', String(payload.marginY)); } else { const text = toHomeMaterialTextWatermarkPayload(payload.textWatermark); if (text) { form.append('text_watermark_text', text.text); form.append('text_watermark_opacity_level', String(text.opacity_level)); form.append('text_watermark_font_size_px', String(text.font_size_px)); form.append('text_watermark_color', text.color); form.append('text_watermark_rotate_deg', String(text.rotate_deg)); form.append('text_watermark_gap_x', String(text.gap_x)); form.append('text_watermark_gap_y', String(text.gap_y)); form.append('text_watermark_staggered', String(text.staggered)); } } form.append('is_active', String(payload.isActive)); form.append('sort_order', String(payload.sortOrder)); form.append('wait', String(!!payload.wait)); form.append('wait_timeout_seconds', String(payload.waitTimeoutSeconds ?? 30)); return api.post('/admin/home-material/assets', form); } export async function updateHomeMaterialAsset(id: string, payload: HomeMaterialAssetUpdatePayload): Promise { return api.put(`/admin/home-material/assets/${id}`, payload); } export async function previewHomeMaterialTextWatermark(payload: HomeMaterialTextWatermarkPreviewRequest): Promise { return api.post('/admin/home-material/text-watermark-preview', payload); } export async function regenerateHomeMaterialWatermark(id: string, payload: HomeMaterialRegeneratePayload): Promise { return api.post(`/admin/home-material/assets/${id}/regenerate-watermark`, payload); } export async function deleteHomeMaterialAsset(id: string): Promise { await api.delete(`/admin/home-material/assets/${id}`); } export interface OAuthAccountParams { id?: string; phone?: string; account_id?: string; account_userid?: string; created_at?: [string, string]; appid?: string; open_type?: number; page?: number; page_size?: number; } export async function getOAuthAccountList(params: OAuthAccountParams): Promise { const query = new URLSearchParams(); if (params.id) query.set('id', params.id); if (params.phone) query.set('phone', params.phone); if (params.account_id) query.set('account_id', params.account_id); if (params.account_userid) query.set('account_userid', params.account_userid); if (params.created_at !== undefined) query.set('created_at', params.created_at[0] + ',' + params.created_at[1]); if (params.appid) query.set('appid', params.appid); if (params.open_type !== undefined) query.set('open_type', String(params.open_type)); if (params.page !== undefined) query.set('page', String(params.page)); if (params.page_size !== undefined) query.set('page_size', String(params.page_size)); return api.get(`/material-admin/oauth-list?${query.toString()}`); } export interface MaterialListParams { id?: string; phone?: string; resource_type?: string; advertiser_id?: string; material_id?: string; upload_id?: string; created_at?: [string, string]; status?: string; page?: number; page_size?: number; } export async function getMaterialList(params: MaterialListParams): Promise { const query = new URLSearchParams(); if (params.id) query.set('id', params.id); if (params.phone) query.set('phone', params.phone); if (params.resource_type) query.set('resource_type', params.resource_type); if (params.advertiser_id) query.set('advertiser_id', params.advertiser_id); if (params.material_id) query.set('material_id', params.material_id); if (params.upload_id) query.set('upload_id', params.upload_id); if (params.created_at !== undefined) query.set('created_at', params.created_at[0] + ',' + params.created_at[1]); if (params.status) query.set('status', params.status); if (params.page !== undefined) query.set('page', String(params.page)); if (params.page_size !== undefined) query.set('page_size', String(params.page_size)); return api.get(`/material-admin/material-list?${query.toString()}`); } export interface PreTestTemplateListParams { id?: string; phone?: string; created_at?: [string, string]; page?: number; page_size?: number; } export async function getPreTestTemplateList(params: PreTestTemplateListParams): Promise { const query = new URLSearchParams(); if (params.id) query.set('id', params.id); if (params.phone) query.set('phone', params.phone); if (params.created_at !== undefined) query.set('created_at', params.created_at[0] + ',' + params.created_at[1]); if (params.page !== undefined) query.set('page', String(params.page)); if (params.page_size !== undefined) query.set('page_size', String(params.page_size)); return api.get(`/material-admin/pre-test-template-list?${query.toString()}`); } // ── Private Portrait Admin ──────────────────────────────── export async function adminGetPrivatePortraitProjects(params: { userId?: string; keyword?: string; status?: string; page?: number; pageSize?: number } = {}): Promise { const query = new URLSearchParams(); query.set('page', String(params.page || 1)); query.set('page_size', String(params.pageSize || 20)); if (params.userId) query.set('user_id', params.userId); if (params.keyword) query.set('keyword', params.keyword); if (params.status) query.set('status', params.status); return api.get(`/admin/private-portrait/projects?${query.toString()}`); } export async function adminGetPrivatePortraitAssets(params: { userId?: string; projectId?: string; keyword?: string; status?: string; page?: number; pageSize?: number } = {}): Promise { const query = new URLSearchParams(); query.set('page', String(params.page || 1)); query.set('page_size', String(params.pageSize || 20)); if (params.userId) query.set('user_id', params.userId); if (params.projectId) query.set('project_id', params.projectId); if (params.keyword) query.set('keyword', params.keyword); if (params.status) query.set('status', params.status); return api.get(`/admin/private-portrait/assets?${query.toString()}`); } export async function adminGetPrivatePortraitConfig(userId: string): Promise { return api.get(`/admin/private-portrait/users/${userId}/config`); } export async function adminUpdatePrivatePortraitConfig(userId: string, limit: number): Promise { return api.put(`/admin/private-portrait/users/${userId}/config`, { private_portrait_image_limit: limit }); }