fix: show failed outcomes in finished batch queues

This commit is contained in:
Ed1s0nZ committed 2026-10-06 03:05:25 +08:00
1 parent 91c53ab451
commit 07066f9219
8 files changed
+48 -1

No files matched your search

+6
View File
@@ -46688,3 +46688,9 @@ html[data-theme="dark"] { --tool-blocked-color: #f4bc62; }
font-size: .75rem;
line-height: 1.6;
}
.batch-queue-status-failed {
color: var(--error-color, #dc2626);
background: rgba(220, 38, 38, 0.08);
border-color: rgba(220, 38, 38, 0.25);
}
+2
View File
@@ -1099,6 +1099,8 @@
"toolFailed": "Failed"
},
"tasks": {
"statusEndedWithFailures": "Finished ({{count}} failed)",
"finishedProgressHint": "Progress counts finished tasks, including failures and cancellations; it is not the success rate.",
"title": "Task Management",
"stopTask": "Stop task",
"interruptModalTitle": "Interrupt current step",
+2
View File
@@ -1111,6 +1111,8 @@
"toolFailed": "Ошибка"
},
"tasks": {
"statusEndedWithFailures": "Завершено (ошибок: {{count}})",
"finishedProgressHint": "Прогресс учитывает завершённые задачи, включая ошибки и отмены, а не долю успешных задач.",
"title": "Управление задачами",
"stopTask": "Остановить задачу",
"interruptModalTitle": "Прервать текущий шаг",
+2
View File
@@ -1087,6 +1087,8 @@
"toolFailed": "失败"
},
"tasks": {
"statusEndedWithFailures": "已结束({{count}} 项失败)",
"finishedProgressHint": "进度表示已结束的任务比例,包含失败和取消,不表示成功率。",
"title": "任务管理",
"stopTask": "停止任务",
"interruptModalTitle": "中断当前步骤",
@@ -0,0 +1,18 @@
const fs=require('node:fs'),vm=require('node:vm'),test=require('node:test'),assert=require('node:assert/strict');
const source=fs.readFileSync('web/static/js/tasks.js','utf8');
const start=source.indexOf('function getBatchQueueStatusPresentation('),end=source.indexOf('\nfunction ',start+10);
const c=vm.createContext({_t:key=>key,_tPlain:(key,args)=>key+':'+JSON.stringify(args)});vm.runInContext(source.slice(start,end),c);
test('all failed batch tasks show failure even when the queue lifecycle is completed',()=>{
const q={status:'completed',tasks:[{status:'failed'},{status:'failed'}]};const p=c.getBatchQueueStatusPresentation(q);
assert.equal(p.text,'tasks.statusFailed');assert.equal(p.class,'batch-queue-status-failed');assert.equal(q.status,'completed');
});
test('mixed outcome names the failure count while successful queues remain completed',()=>{
const mixed=c.getBatchQueueStatusPresentation({status:'completed',tasks:[{status:'completed'},{status:'failed'}]});
assert.match(mixed.text,/statusEndedWithFailures.*"count":1/);assert.equal(mixed.progressNote,'tasks.finishedProgressHint');
assert.equal(c.getBatchQueueStatusPresentation({status:'completed',tasks:[{status:'completed'}]}).text,'tasks.statusCompleted');
});
test('running queues retain running status and existing recurring queues retain their next-run information',()=>{
assert.equal(c.getBatchQueueStatusPresentation({status:'running',tasks:[{status:'failed'}]}).text,'tasks.statusRunning');
const p=c.getBatchQueueStatusPresentation({status:'completed',scheduleMode:'cron',scheduleEnabled:true,nextRunAt:'2026-10-07T00:00:00Z',tasks:[{status:'failed'}]});
assert.equal(p.text,'tasks.statusFailed');assert.match(p.sublabel,/cronNextRunLine/);assert.equal(p.callout,'tasks.cronRecurringCallout');
});
+12
View File
@@ -45,6 +45,18 @@ function getBatchQueueStatusPresentation(queue) {
const nextStr = queue.nextRunAt ? new Date(queue.nextRunAt).toLocaleString() : '';
const empty = { sublabel: null, progressNote: null, callout: null };
const failedCount = (queue.tasks || []).filter(task => task.status === 'failed').length;
if (queue.status === 'completed' && failedCount > 0) {
const allFailed = failedCount === (queue.tasks || []).length;
return {
text: allFailed ? _t('tasks.statusFailed') : _tPlain('tasks.statusEndedWithFailures', { count: failedCount }),
class: 'batch-queue-status-failed',
sublabel: cronOn && nextStr ? _tPlain('tasks.cronNextRunLine', { time: nextStr }) : null,
progressNote: _t('tasks.finishedProgressHint'),
callout: cronOn ? _t('tasks.cronRecurringCallout') : null
};
}
if (cronOn && queue.status === 'completed') {
return {
text: _t('tasks.statusCronCycleIdle'),