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

+5
View File
@@ -108,3 +108,8 @@
- Excel 第 47 行:截图显示 NSE 将 `-sT` 当作脚本名,原因是 scan_type 根据“最后一个非标志参数”找插入位置,遇到 additional_args 的 --script 值时插入到了标志与值之间。nmap 的 scan_type 改为置于参数列表开头,保留各 flag/value 相邻关系;其他工具的既有插入规则保留。
- `-sC` 与 --script 不是互斥错误;官方说明明确有 --script 时忽略 -sC,故不新增错误拒绝规则。来源:https://nmap.org/book/man-nse.html 。
- 读取真实 tools/nmap.yaml 验证截图参数、nse_scripts 字段和尾部 --max-retries 值的完整顺序;既有空可选参数测试通过。测试不向目标发送扫描;本机没有 nmap,因此不声称完成实际扫描验证。
## 追加:批量队列结果标签
- Excel 第 58 行:截图的子任务为 failed,队列 completed 表示所有子任务已经结束;绿色“已完成”和 100% 被误认为全部成功。列表和详情共用展示函数,现在全部失败显示失败色及“执行失败”,部分失败显示“已结束(N 项失败)”,详情解释进度统计结束任务,包含失败/取消。后端生命周期和重跑规则不变。
- 3 项实际展示函数回归覆盖全失败、混合结果、成功、运行中及已有周期队列的后续运行信息;不扩展调度能力。
+1 -1
View File
@@ -60,7 +60,7 @@
| 55 | — | - 选择其专用测试会话,打开「终端」。 | 已修复,Unix Shell 回归通过 |
| 56 | — | AI看代码给提的问题 | 已修复并有针对性验证 |
| 57 | — | 审计日志搜索无结果后,点击“清空”不会清除关键词或恢复列表,无反应 | 当前重置会清空关键词并请求首个无筛选页面,回归未复现 |
| 58 | — | 任务失败,也显示已完成 | 待核对 |
| 58 | — | 任务失败,也显示已完成 | 已修复队列结果标签:全部失败显示失败,混合结果显示失败数;3 项回归通过 |
| 59 | — | 批量任务在“用户并发配额已满”时进入等待调度后,即使后来停止容器、腾出配额,队列仍停在“执行中 | 当前仓库未实现,不扩展能力 |
| 60 | — | 资产扫描任务在后续工具调用的 HITL 审批阶段已被系统中断,审批卡片明确显示“任务已中断、审批 | 待核对 |
| 61 | — | 问题:事实正文里的 discovered_on: target/xxx 只会当普通文本保存,不会 | 已按建议明确关系字段说明 |
+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'),