From 09458824582e16a97c880a0f7ca45761cc91ab9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Wed, 2 Sep 2026 05:49:01 +0800 Subject: [PATCH] =?UTF-8?q?task-145:=20=E5=89=8D=E7=AB=AF=E5=8F=AF?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=88=A4=E5=AE=9A=E5=A5=91=E7=BA=A6=EF=BC=88?= =?UTF-8?q?fileReady/downloadUrl/=E7=BB=88=E6=80=81=20SUCCESS+FAILED=20?= =?UTF-8?q?=E5=8F=AF=E4=B8=8B=E8=BD=BD=EF=BC=8CPENDING/RUNNING/=E6=97=A0?= =?UTF-8?q?=20resultId=20=E4=B8=8D=E5=8F=AF=E4=B8=8B=E8=BD=BD=EF=BC=89+=20?= =?UTF-8?q?8=20=E6=9D=A1=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/shared/merge-live-progress.ts | 11 +++- frontend-vue/tests/downloadability.test.ts | 60 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 frontend-vue/tests/downloadability.test.ts diff --git a/frontend-vue/src/shared/merge-live-progress.ts b/frontend-vue/src/shared/merge-live-progress.ts index de26559e..530dcd07 100644 --- a/frontend-vue/src/shared/merge-live-progress.ts +++ b/frontend-vue/src/shared/merge-live-progress.ts @@ -17,13 +17,20 @@ export const FILE_PROGRESS_KEYS = [ export interface DownloadableItem { resultId?: number | null fileReady?: boolean | null + fileStatus?: string | null downloadUrl?: string | null } -/** 可下载判定:有结果记录且文件已就绪(fileReady 或 downloadUrl 非空)。 */ +/** + * 可下载判定(Task 145):有结果记录,且满足其一—— + * fileReady=true、downloadUrl 非空、fileStatus 为终态(SUCCESS 可下载结果文件, + * FAILED 可下载错误文件或用于展示错误)。 + */ export function canDownloadItem(item: DownloadableItem | null | undefined): boolean { if (!item) return false - return Boolean(item.resultId && (item.fileReady || item.downloadUrl)) + const ready = item.fileReady || Boolean(item.downloadUrl) + const terminalStatus = item.fileStatus === 'SUCCESS' || item.fileStatus === 'FAILED' + return Boolean(item.resultId && (ready || terminalStatus)) } /** 有效进度值:仅 fileProgressPercent 需要严格 >0,其余字段非空即可(0 是合法计数)。 */ diff --git a/frontend-vue/tests/downloadability.test.ts b/frontend-vue/tests/downloadability.test.ts new file mode 100644 index 00000000..e09c055b --- /dev/null +++ b/frontend-vue/tests/downloadability.test.ts @@ -0,0 +1,60 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' + +import { canDownloadItem } from '../src/shared/merge-live-progress.ts' + +interface Item { + taskId?: number + resultId?: number + fileReady?: boolean + fileStatus?: string + downloadUrl?: string +} + +test('downloadable when fileReady is true', () => { + assert.equal(canDownloadItem({ resultId: 1, fileReady: true }), true) +}) + +test('downloadable when fileStatus is SUCCESS', () => { + assert.equal(canDownloadItem({ resultId: 1, fileStatus: 'SUCCESS' }), true) +}) + +test('downloadable when fileStatus is FAILED (error file or display)', () => { + assert.equal(canDownloadItem({ resultId: 1, fileStatus: 'FAILED' }), true) +}) + +test('not downloadable when not ready', () => { + assert.equal(canDownloadItem({ resultId: 1, fileReady: false, fileStatus: 'RUNNING' }), false) + assert.equal(canDownloadItem({ resultId: 1 }), false) +}) + +test('downloadable when downloadUrl present', () => { + assert.equal(canDownloadItem({ resultId: 1, downloadUrl: 'https://dl.example/a.xlsx' }), true) +}) + +test('not downloadable without resultId', () => { + assert.equal(canDownloadItem({ fileReady: true }), false) + assert.equal(canDownloadItem({ fileStatus: 'SUCCESS' }), false) +}) + +test('not downloadable while pending or running', () => { + assert.equal(canDownloadItem({ resultId: 1, fileStatus: 'PENDING' }), false) + assert.equal(canDownloadItem({ resultId: 1, fileStatus: 'RUNNING' }), false) + assert.equal(canDownloadItem({ resultId: 1, fileStatus: null }), false) +}) + +test('downloadability contract frozen', () => { + assert.equal(canDownloadItem(undefined), false) + assert.equal(canDownloadItem(null), false) + assert.equal(canDownloadItem({}), false) + // 终态与就绪任一满足即可下载;RUNNING/PENDING/空态均不可 + const terminalCases = ['SUCCESS', 'FAILED'] + const runningCases = ['PENDING', 'RUNNING', undefined, null] + for (const status of terminalCases) { + assert.equal(canDownloadItem({ resultId: 1, fileStatus: status as string }), true, `终态 ${status} 可下载`) + } + for (const status of runningCases) { + assert.equal(canDownloadItem({ resultId: 1, fileStatus: status as string | undefined }), false, + `非终态 ${String(status)} 不可下载`) + } +})