// ============================================================ // 任务模块 — 任务记录 CRUD API // GET /api/v1/tasks → 任务列表(分页/游标) // POST /api/v1/tasks → 提交任务 // GET /api/v1/tasks/:id → 任务详情 // GET /api/v1/tasks/:id/status → 轻量轮询状态 // GET /api/v1/tasks/export → 导出 CSV // // TaskAPI 的静态方法直接返回 get/post 的 Promise, // 不做额外的 async/await 包装,由调用方决定是否 await。 // ============================================================ import {get, post} from '../request'; import {getDeviceId} from '@/utils/device.ts'; // ============================================================ // 基础枚举 / 字面量类型 // ============================================================ /** 输出类型映射 */ export enum OutputTypeMap { image = '图片', video = '视频', audio = '音频', } export type OutputTypeString = keyof typeof OutputTypeMap; /** 任务状态 */ export enum TaskStatusMap { pending = '待处理', submitted = '已提交', processing = '处理中', success = '成功', failed = '失败', } export type TaskStatusString = keyof typeof TaskStatusMap; // ============================================================ // 请求体 / 参数类型 // ============================================================ /** 任务列表请求参数 */ export interface TaskListRequestParams { status?: TaskStatusString; user_ids?: string[]; model_ids?: string[]; /** 游标分页:上次最后一条的 created_at(ISO 8601) */ cursor?: string; /** 页码(OFFSET 分页模式),default: 1 */ page?: number; /** 每页条数,default: 20 */ page_size?: number; } /** 创建任务请求体 */ export interface CreatTaskRequestBody { model_id: string; params: Record; device_id: string; tags?: string[]; } /** 导出 CSV 参数 */ export interface ExportTaskCSVParams { start_date: Date; end_date: Date; status?: TaskStatusString; user_ids?: string[]; model_ids?: string[]; } // ============================================================ // 内容项 — 带判别标签的联合类型 // ============================================================ interface BaseContentItem { type: string; } interface TextContent extends BaseContentItem { type: 'text'; text: string; } interface ImageUrlContent extends BaseContentItem { type: 'image_url'; role: 'reference_image'; image_url: { url: string }; } interface VideoUrlContent extends BaseContentItem { type: 'video_url'; role: 'reference_video'; video_url: { url: string }; } interface AudioContent extends BaseContentItem { type: 'audio_url'; role: 'reference_audio'; audio_url: { url: string }; } export type ContentItem = | TextContent | ImageUrlContent | VideoUrlContent | AudioContent; // ============================================================ // 各输出类型的 UI 参数 // ============================================================ export interface ImageUiParams { imageUrls?: string[]; prompt: string; aspectRatio: string; resolution?: string; } export interface VideoUiParams { resolution?: string; ratio?: string; model?: string; generate_audio: boolean; duration: number; content: ContentItem[]; } export interface AudioUiParams { format?: string; sampleRate?: number; bitrate?: number; } // ============================================================ // 响应体 — 用判别联合按 output_type 分发 // ============================================================ /** 任务条目基类(不含 output_type 特有字段) */ interface BaseTaskItemResponseBody { id: string; user_id: string; model_id: string; model_name: string; provider_name: string; status: TaskStatusString; external_task_id: string; cost_cent: number; estimated_cost_cent: number; error_code: string; poll_attempts: number; is_favorite: boolean; submitted_at: Date; finished_at: Date; created_at: Date; updated_at: Date; prompt?: string; output_assets?: Array<{ url: string }>; tags: Array; error_message: string; user_display_name: string | null; } /** 判别联合:根据 output_type 收窄 ui_params */ export type TaskInfoItemResponseBody = | (BaseTaskItemResponseBody & { output_type: 'image'; ui_params: ImageUiParams }) | (BaseTaskItemResponseBody & { output_type: 'video'; ui_params: VideoUiParams }) | (BaseTaskItemResponseBody & { output_type: 'audio'; ui_params: AudioUiParams }); /** 任务列表分页响应 */ export interface TaskInfoDataResponseBody { has_more: boolean; next_cursor: string; page: number; page_size: number; total: number; items: TaskInfoItemResponseBody[]; } /** 轻量轮询状态响应 */ export interface TaskStatusResponseBody { task_id: string; status: TaskStatusString; progress: number; output_assets?: string[]; error_code?: string; error_message?: string; cost_cent?: number; } // ============================================================ // 路由常量 // ============================================================ const TaskUrlObj = { task: '/api/v1/tasks', taskInfo: (task_id: string) => `/api/v1/tasks/${task_id}`, taskStatus: (task_id: string) => `/api/v1/tasks/${task_id}/status`, tasksCSV: '/api/v1/tasks/export', } as const; // ============================================================ // TaskAPI — 静态方法直接返回 Promise,不做二次 async 包装 // ============================================================ export class TaskAPI { /** 获取任务列表(分页/游标) */ static getTaskList(params: TaskListRequestParams, signal?: AbortSignal): Promise { return get(TaskUrlObj.task, params as Record, { signal }); } /** 提交新任务(device_id 由 API 层自动注入 getDeviceId(),调用方无需传入) */ static submitTask(body: Omit): Promise { return post(TaskUrlObj.task, { ...body, device_id: getDeviceId(), }); } /** 获取任务详情 */ static getTaskInfo(task_id: string, signal?: AbortSignal): Promise { return get(TaskUrlObj.taskInfo(task_id), undefined, { signal }); } /** 轻量轮询任务状态 */ static getTaskStatus(task_id: string): Promise { return get(TaskUrlObj.taskStatus(task_id)); } /** 导出任务 CSV */ static exportTaskCSV(params: ExportTaskCSVParams): Promise { return get(TaskUrlObj.tasksCSV, params as unknown as Record); } }