/** 契约端点封装:页面只调用这些函数,不直接拼 URL。 */ import { apiDelete, apiGet, apiPatch, apiPost, apiUpload } from './client'; import type { ContractVersion, Health, Model, ModelPage, ModelUpdate, Motion, MotionPage, MotionUpdate, PendingPage, PublishStatus, ReviewActionBody, ReviewPage, Song, SongCreate, SongPage, SongUpdate, TargetType, } from './types'; /* ------------------------------------------------------------------ 列表 */ export interface ListParams { page: number; pageSize: number; } export function listSongs( params: ListParams & { q?: string; status?: PublishStatus | ''; artist?: string }, ): Promise { return apiGet('/api/songs', { page: params.page, pageSize: params.pageSize, q: params.q, status: params.status, artist: params.artist, }); } export function listMotions( params: ListParams & { q?: string; songId?: number | ''; format?: string; status?: PublishStatus | ''; }, ): Promise { return apiGet('/api/motions', { page: params.page, pageSize: params.pageSize, q: params.q, songId: params.songId, format: params.format, status: params.status, }); } export function listModels( params: ListParams & { q?: string; format?: string; status?: PublishStatus | '' }, ): Promise { return apiGet('/api/models', { page: params.page, pageSize: params.pageSize, q: params.q, format: params.format, status: params.status, }); } /* ------------------------------------------------------------------ 曲目 */ export function createSong(payload: SongCreate): Promise { return apiPost('/api/songs', payload); } export function updateSong(id: number, payload: SongUpdate): Promise { return apiPatch(`/api/songs/${id}`, payload); } export function deleteSong(id: number): Promise { return apiDelete(`/api/songs/${id}`); } /** multipart 字段名固定为 file。 */ export function uploadSongAudio(id: number, file: File): Promise { const form = new FormData(); form.append('file', file); return apiUpload(`/api/songs/${id}/audio`, form); } /* ------------------------------------------------------------------ 动作 */ export interface MotionUploadInput { file: File; name: string; songId?: number; fps?: number; autoPreprocess: boolean; status: PublishStatus; sourceType: string; sourceName: string; sourceUrl: string; license: string; author: string; } export function uploadMotion(input: MotionUploadInput): Promise { const form = new FormData(); form.append('file', input.file); form.append('name', input.name); if (input.songId !== undefined) form.append('songId', String(input.songId)); if (input.fps !== undefined) form.append('fps', String(input.fps)); form.append('autoPreprocess', input.autoPreprocess ? 'true' : 'false'); form.append('status', input.status); form.append('sourceType', input.sourceType); form.append('sourceName', input.sourceName); form.append('sourceUrl', input.sourceUrl); form.append('license', input.license); form.append('author', input.author); return apiUpload('/api/motions', form); } export function updateMotion(id: number, payload: MotionUpdate): Promise { return apiPatch(`/api/motions/${id}`, payload); } export function deleteMotion(id: number): Promise { return apiDelete(`/api/motions/${id}`); } export function preprocessMotion(id: number): Promise { return apiPost(`/api/motions/${id}/preprocess`, {}); } /* ------------------------------------------------------------------ 模型 */ export interface ModelUploadInput { file: File; name: string; isPrimary: boolean; status: PublishStatus; sourceType: string; sourceName: string; sourceUrl: string; license: string; author: string; } export function uploadModel(input: ModelUploadInput): Promise { const form = new FormData(); form.append('file', input.file); form.append('name', input.name); form.append('isPrimary', input.isPrimary ? 'true' : 'false'); form.append('status', input.status); form.append('sourceType', input.sourceType); form.append('sourceName', input.sourceName); form.append('sourceUrl', input.sourceUrl); form.append('license', input.license); form.append('author', input.author); return apiUpload('/api/models', form); } export function updateModel(id: number, payload: ModelUpdate): Promise { return apiPatch(`/api/models/${id}`, payload); } /** GET /api/models/:id —— 审核面板用它拉详情(stats 可能为 null,旧数据未分析)。 */ export function getModel(id: number): Promise { return apiGet(`/api/models/${id}`); } export function deleteModel(id: number): Promise { return apiDelete(`/api/models/${id}`); } export function setPrimaryModel(id: number): Promise { return apiPost(`/api/models/${id}/primary`, {}); } /* ------------------------------------------------------------------ 审核 */ export function listPending(params: ListParams & { status: PublishStatus }): Promise { return apiGet('/api/reviews', { status: params.status, page: params.page, pageSize: params.pageSize, }); } export function listReviewRecords( params: ListParams & { targetType?: TargetType | '' }, ): Promise { return apiGet('/api/reviews', { targetType: params.targetType, page: params.page, pageSize: params.pageSize, }); } export function reviewApprove( targetType: TargetType, id: number, body: ReviewActionBody, ): Promise { return apiPost(`/api/reviews/${targetType}/${id}/approve`, body); } export function reviewReject( targetType: TargetType, id: number, body: ReviewActionBody, ): Promise { return apiPost(`/api/reviews/${targetType}/${id}/reject`, body); } export function reviewSubmit( targetType: TargetType, id: number, body: ReviewActionBody, ): Promise { return apiPost(`/api/reviews/${targetType}/${id}/submit`, body); } /* -------------------------------------------------------------- 目录/健康 */ export function getHealth(): Promise { return apiGet('/health'); } export function getCatalogVersion(): Promise { return apiGet('/api/catalog/version'); }