diff --git a/frontend-vue/src/shared/api/types/task.ts b/frontend-vue/src/shared/api/types/task.ts new file mode 100644 index 00000000..dee8c253 --- /dev/null +++ b/frontend-vue/src/shared/api/types/task.ts @@ -0,0 +1,37 @@ +/** 任务状态联合类型(与 Java 后端 status 字段对应) */ +export type TaskStatus = + | 'PENDING' + | 'RUNNING' + | 'SUCCESS' + | 'FAILED' + | 'CANCELLED' + +/** 结果文件阶段字段:旧 Java 版本可能缺失,全部可选 */ +export interface ResultFilePhase { + /** 文件是否就绪可下载 */ + fileReady?: boolean + /** 文件级状态 */ + fileStatus?: string + /** 文件处理进度 0-100 */ + fileProgress?: number + /** 进度文案 */ + fileProgressLabel?: string + /** 进度阶段标记 */ + fileProgressStage?: string + /** 新鲜下载链接(优先于 downloadUrl) */ + freshDownloadUrl?: string | null +} + +/** 分页结果 */ +export interface PageResult { + items: T[] + total: number + page: number + pageSize: number +} + +/** 进度批量查询响应 */ +export interface TaskProgressBatchVo { + items: T[] + missingTaskIds: number[] +} diff --git a/frontend-vue/tests/types-task.test.ts b/frontend-vue/tests/types-task.test.ts new file mode 100644 index 00000000..9d9cb30d --- /dev/null +++ b/frontend-vue/tests/types-task.test.ts @@ -0,0 +1,62 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' +import type { + TaskStatus, + ResultFilePhase, + PageResult, + TaskProgressBatchVo, +} from '../src/shared/api/types/task.ts' + +test('test_type_task_status_assignable', () => { + const values: TaskStatus[] = ['PENDING', 'RUNNING', 'SUCCESS', 'FAILED', 'CANCELLED'] + assert.equal(values.length, 5) + // @ts-expect-error 未知状态字符串不应可赋值 + const invalid: TaskStatus = 'SYNCING' + assert.ok(invalid) +}) + +test('test_type_result_file_phase_all_optional', () => { + const empty: ResultFilePhase = {} + const partial: ResultFilePhase = { fileReady: true } + const full: ResultFilePhase = { + fileReady: true, + fileStatus: 'SUCCESS', + fileProgress: 100, + fileProgressLabel: '完成', + fileProgressStage: 'done', + freshDownloadUrl: '/newApi/api/x', + } + assert.deepEqual(empty, {}) + assert.equal(partial.fileReady, true) + assert.equal(full.fileProgress, 100) +}) + +test('test_type_page_result_shape', () => { + const page: PageResult<{ id: number }> = { + items: [{ id: 1 }], + total: 1, + page: 1, + pageSize: 20, + } + assert.equal(page.items.length, 1) + assert.equal(page.total, 1) + // @ts-expect-error items 必须是元素数组,不是裸对象 + const invalid: PageResult<{ id: number }> = { items: { id: 1 }, total: 1 } + assert.ok(invalid) +}) + +test('test_type_progress_batch_shape', () => { + const batch: TaskProgressBatchVo<{ taskId: number }> = { + items: [{ taskId: 1 }], + missingTaskIds: [99], + } + assert.equal(batch.items.length, 1) + assert.deepEqual(batch.missingTaskIds, [99]) +}) + +test('test_type_compat_compile', () => { + // 类型与值在同一包内可被引用;运行时检查命名导出存在性由 vue-tsc 全量检查兜底 + const s: TaskStatus = 'SUCCESS' + const statuses: string[] = [s, 'FAILED', 'CANCELLED'] + assert.deepEqual(statuses, ['SUCCESS', 'FAILED', 'CANCELLED']) +})