Files
insar-management-system-v2/docs/GLOBAL_TASK_STATUS_LOCK_REDESIGN_20260612.md

18 KiB
Raw Permalink Blame History

全局任务状态与界面锁重构设计

最后更新:2026-06-12

1. 背景

当前前端存在一个“全局界面锁”机制:只要系统检测到仍有活跃任务,并且该任务不在前端非阻塞白名单内,就会把整个系统切到全局锁定状态,并弹出 ActiveTasksOverlay 全屏遮罩。

这个设计在早期可以避免用户在 ENVI / SARscape / IDL 长任务执行期间重复点击、切换入口或提交冲突任务。但随着系统扩展到 D-InSAR、多引擎生产、SBAS-InSAR、数据接入、资产扫描、洪涝分析、AI 诊断等多个相对独立的任务域,全局锁已经过粗:

  • SBAS Gamma / LandSAR SBAS 已经证明长任务可以不锁全局界面,只在模块内展示状态。
  • 后端 task_service.create_task() 已经按 task_type 做同类型 PENDING/RUNNING 互斥。
  • 很多任务只是扫描、解包、目录重建或产物刷新,不应阻断其他业务操作。
  • 全屏遮罩会遮住当前功能页,用户看不到任务详情,也无法继续浏览结果或管理其他独立任务。

因此,本设计将“全局界面锁”降级为“全局任务状态中心”,并要求每个功能模块设计自己的任务状态面板和局部操作约束。

2. 当前实现审计

2.1 前端锁入口

核心文件:

  • frontend/src/hooks/useGlobalTaskControl.js
  • frontend/src/hooks/useDinsarOperations.js
  • frontend/src/components/ActiveTasksOverlay.jsx
  • frontend/src/components/app/AppOverlays.jsx
  • frontend/src/ProductionWorkspace.jsx

当前逻辑:

  1. useGlobalTaskControl 订阅 /tasks/active 或 SSE /tasks/active/stream
  2. 前端维护 pendingTaskIdsnonBlockingTaskIds
  3. 活跃任务中只要存在不属于 NON_BLOCKING_TASK_TYPES 的任务,就设置 isGlobalLocked=true
  4. AppOverlays 根据 isGlobalLocked 渲染 ActiveTasksOverlay
  5. App.ensureCanOperate() 会因为 isGlobalLocked 拒绝写操作。

2.2 当前非阻塞任务

当前白名单包括:

  • UNPACK_ARCHIVES
  • UNPACK_SENTINEL1
  • GF3_UNPACK
  • GF3_SARSCAPE_PRODUCE
  • GF3_SARSCAPE_SYNC
  • GF3_SARSCAPE_CLEAN
  • SCAN_ASSET_INVENTORY
  • COPY_DATA
  • SBAS_GAMMA_WORKFLOW
  • SBAS_LANDSAR_WORKFLOW
  • SBAS_COREGISTRATION
  • SBAS_RDC_DEM
  • SBAS_INTERFEROGRAMS
  • SBAS_IPTA_TIMESERIES
  • REBUILD_SBAS_INSAR_CATALOG

这些任务已经以“不锁全局界面”的方式运行。

2.3 当前仍会锁全局界面的任务

主要原因通常不是任务本身必须锁,而是提交时没有传 taskTypenonBlocking: true

已审计到的典型入口:

  • D-InSAR 生产任务
    • ProductionWorkspace.handleDinsarRunQueued()
    • DinsarProductionPanel.handleSubmit()
  • D-InSAR 结果扫描
    • ProductionWorkspace.handleDinsarProductQueued()
    • DinsarProductsPanel.handleScan()
  • IDL Import / IDL DInSAR
    • IDLAutomationPanel
  • AI 训练、全量预测、AI 诊断
    • useDinsarOperations.handleTrainAi()
    • useDinsarOperations.handlePredictAll()
    • useDinsarOperations.handleAnalyzeResult()
  • 灾害点同步
    • HazardPointPanel
  • 水体 / 洪涝检测任务
    • WaterMonitorPanel
  • 部分数据监控任务
    • GF3_BATCH_PROCESS
    • SCAN_DATA
    • 手动 LT-1 / 精轨 / GF3 扫描
  • 预览缓存重建
    • App.rebuildRadarPreviewCache() 通过 handleTaskStart(null, ...) 触发临时全局锁

2.4 后端已有保护

后端 backend/app/services/task_service.pycreate_task() 中已做同类型任务互斥:

  • 查询相同 task_type 且状态为 PENDING / RUNNING 的任务。
  • 如果存在,直接返回任务冲突错误。

这说明前端全局锁不是唯一安全机制。真正的任务并发保护应继续下沉到后端,并从 task_type 扩展到更精确的资源锁。

3. 设计目标

  1. 取消“任意阻塞任务遮住整个系统”的交互模式。
  2. 全局层只负责展示所有活跃任务、最近任务、失败任务和快捷入口。
  3. 每个功能模块拥有自己的任务状态面板,只展示和本功能相关的任务。
  4. 默认任务不锁全局界面。
  5. 需要互斥的场景由后端资源锁保证,而不是靠前端遮罩保证。
  6. 前端只做局部禁用:禁用同一个功能里会造成重复提交或破坏状态的按钮。
  7. 保留管理员取消任务能力,但从“强制解锁”改为“取消/中止任务”。

4. 非目标

本设计不要求一次性重写所有任务系统。

不在本阶段处理:

  • 重做后端任务表结构。
  • 重写 job worker。
  • 改变现有任务 API 的基本返回格式。
  • 一次性把所有历史任务类型改名。
  • 删除所有旧 overlay 代码。

本设计优先保证渐进迁移。

5. 新架构概览

后端任务系统
├─ SystemTaskORM / TaskLogORM
├─ SystemJobORM
├─ task_type 同类型互斥
└─ 后续扩展:resource_lock_key / resource_scope

前端任务状态层
├─ 全局任务状态中心
│  ├─ 展示所有活跃任务
│  ├─ 展示最近失败 / 完成任务
│  ├─ 支持跳转到所属功能
│  └─ 不遮住全系统
├─ 功能级任务面板
│  ├─ D-InSAR 生产任务面板
│  ├─ SBAS-InSAR 生产任务面板
│  ├─ 数据接入任务面板
│  ├─ 资产库存任务面板
│  ├─ 洪涝分析任务面板
│  └─ AI / 灾害点任务面板
└─ 局部操作约束
   ├─ 同类任务运行中,禁用同类提交按钮
   ├─ 目录重建中,禁用同一目录重建按钮
   └─ 其他功能仍可浏览和操作

6. 全局任务状态中心

6.1 职责

全局任务状态中心只做观察和导航,不做全局阻断。

职责:

  • 汇总 /tasks/active
  • 汇总最近任务 /tasks/recent
  • 显示任务类型、状态、进度、开始时间、最近消息。
  • 支持按功能域筛选:
    • 数据接入
    • D-InSAR
    • SBAS-InSAR
    • 洪涝
    • AI
    • 运维
  • 支持点击任务跳转到所属功能页。
  • 支持查看任务日志。
  • 支持管理员取消任务。

6.2 交互形态

建议替换现有 ActiveTasksOverlay

  • 不再使用全屏遮罩。
  • 使用顶部状态入口或右下角任务抽屉。
  • 有活跃任务时显示小型状态指示。
  • 点击后打开任务中心抽屉或弹层。
  • 弹层不阻止用户关闭和继续使用系统。

6.3 状态文案

旧文案:

为了保证数据一致性,耗时任务执行期间 UI 已锁定。

应替换为:

后台任务正在执行。你可以继续使用其他功能;同类任务的重复提交会由系统自动限制。

7. 功能级任务面板

每个功能页应自行展示本功能任务状态。这样用户在当前业务上下文里能直接看到“我刚提交的任务跑到哪一步”,而不是被全局遮罩挡住。

7.1 通用组件建议

新增通用组件:

  • TaskStatusPanel
  • TaskLogPanel
  • TaskProgressRow
  • TaskStatusBadge
  • useTaskMonitor

建议参数:

type TaskStatusPanelProps = {
  title: string;
  taskTypes?: string[];
  taskTypePrefixes?: string[];
  taskIds?: string[];
  showRecent?: boolean;
  compact?: boolean;
  onTaskClick?: (task) => void;
};

useTaskMonitor 负责:

  • taskTypes / taskTypePrefixes 查询活跃任务。
  • 轮询或复用全局 SSE 数据。
  • 拉取指定任务日志。
  • 返回 activeTasksrecentTaskslatestTaskisBusy

7.2 D-InSAR 生产

任务类型:

  • IDL_RUN_DINSAR
  • ISCE2_RUN
  • PYINT_RUN
  • LANDSAR_RUN

面板位置:

  • DinsarProductionPanel 右侧或提交区下方。

局部约束:

  • 如果同一 engine 的任务正在运行,禁用同 engine 再提交。
  • 其他 engine 是否允许并行应由后端资源锁决定。
  • 用户仍可查看历史 run、日志、结果列表。

7.3 D-InSAR 产物

任务类型:

  • SCAN_DINSAR
  • DINSAR_RESULT_SCAN
  • DINSAR_RESULT_PACKAGE
  • 以当前后端实际 task_type 为准。

面板位置:

  • DinsarProductsPanel 扫描按钮旁或结果列表顶部。

局部约束:

  • 同一 catalog 重建任务运行中,禁用重复重建按钮。
  • 不影响 SBAS 生产、D-InSAR 生产、结果浏览。

7.4 SBAS-InSAR 生产

任务类型:

  • SBAS_GAMMA_WORKFLOW
  • SBAS_LANDSAR_WORKFLOW
  • SBAS_COREGISTRATION
  • SBAS_RDC_DEM
  • SBAS_INTERFEROGRAMS
  • SBAS_IPTA_TIMESERIES

当前状态:

  • 已基本符合新设计。
  • 已在模块内展示 Runtime Status。
  • 已显式设置 nonBlocking: true

后续调整:

  • 将 Runtime Status 抽成 TaskStatusPanel 风格组件。
  • 支持按 run_id 过滤关联任务。
  • 全局任务中心只显示摘要和跳转入口。

7.5 SBAS-InSAR 结果

任务类型:

  • REBUILD_SBAS_INSAR_CATALOG

局部约束:

  • 重建中只禁用“重建目录”按钮。
  • 结果列表仍可浏览。

当前已接近目标。

7.6 数据接入与资产扫描

任务类型:

  • UNPACK_ARCHIVES
  • UNPACK_SENTINEL1
  • GF3_UNPACK
  • GF3_BATCH_PROCESS
  • GF3_SARSCAPE_PRODUCE
  • GF3_SARSCAPE_SYNC
  • GF3_SARSCAPE_CLEAN
  • SCAN_DATA
  • SCAN_ASSET_INVENTORY

建议:

  • 数据接入页按卫星/流程展示独立状态卡。
  • SCAN_DATA 不应锁全局界面。
  • GF3_BATCH_PROCESS 是否需要局部锁取决于是否写共享目录;默认只锁 GF3 预处理按钮。

7.7 洪涝 / 水体分析

任务类型:

  • WATER_GEOCODE_*
  • WATER_DETECT_*
  • WATER_FLOOD_*
  • FLOOD_SCENE_PREPROCESS_*
  • FLOOD_WATER_EXTRACTION_*
  • FLOOD_DETECTION_*
  • GF3_PROCESS_*

建议:

  • 洪涝工作台显示场景级任务状态。
  • 使用 task type prefix 匹配。
  • 不再触发全局锁。
  • 单个场景处理时,只禁用该场景相关按钮。

7.8 AI 与灾害点

任务类型:

  • AI_TRAIN
  • AI_PREDICT
  • AI_ANALYZE
  • AI_WARMUP
  • SCAN_HAZARD

建议:

  • AI 质量页显示 AI 训练/预测任务状态。
  • AI 诊断页显示诊断任务状态。
  • AI_TRAIN 运行中禁用再次训练;不阻止浏览结果。
  • AI_ANALYZE 运行中只禁用同一结果的重复诊断。
  • SCAN_HAZARD 只影响灾害点同步按钮。

8. 任务分类模型

建议引入统一任务元数据配置,前端和后端可逐步共享。

type TaskUiPolicy = {
  taskType: string;
  featureScope: string;
  label: string;
  globalVisible: boolean;
  globalBlocking: boolean;
  localBlocking: boolean;
  resourceScope?: string;
  routeTarget?: string;
};

默认策略:

  • globalVisible=true
  • globalBlocking=false
  • localBlocking=true

也就是说,任务默认显示在全局任务中心,但不锁整个系统。

只有极少数任务可设置:

globalBlocking=true

但这应作为过渡兼容,不作为长期设计。

9. 后端资源锁设计

后端当前只有 task_type 级互斥。这不足以表达以下场景:

  • 同一个 run 不能同时执行两个会修改同一状态文件的步骤。
  • 同一个 catalog 不能并发重建。
  • 同一个输出目录不能被两个生产任务同时写入。
  • 同一个 ENVI / SARscape 单实例资源不能并行调用。

建议新增资源锁概念。

9.1 资源锁 key

示例:

sbas-run:{run_id}
sbas-catalog:{catalog_root}
dinsar-engine:{engine_code}
dinsar-root:{root_dir}
envi-runtime:{host_or_profile}
gf3-sarscape-root:{root_dir}
water-scene:{scene_id}
ai-model:{model_id}

9.2 后端行为

创建任务时检查:

  • task_type 是否已有活跃任务。
  • resource_lock_key 是否已有活跃任务。

如果冲突:

  • 返回 409 Conflict
  • 返回冲突任务 ID、任务类型、状态、消息。

前端收到后:

  • 不弹全局锁。
  • 在当前功能面板提示“已有同资源任务运行中”。
  • 提供跳转到任务日志。

9.3 迁移方式

第一阶段不必改数据库结构,可把资源锁写入 SystemTaskORM.params

{
  "resource_lock_key": "sbas-run:sbas_e21648e52bd4",
  "feature_scope": "sbas_insar"
}

后续再考虑独立列或资源锁表。

10. 前端状态管理重构

10.1 保留内容

保留:

  • /tasks/active SSE / fallback polling。
  • activeTasks 全局缓存。
  • 任务完成后刷新相关数据的能力。

10.2 移除或降级内容

降级:

  • isGlobalLocked
  • pendingTaskIds
  • nonBlockingTaskIds
  • NON_BLOCKING_TASK_TYPES
  • ActiveTasksOverlay

迁移后:

  • isGlobalLocked 不再驱动全屏遮罩。
  • pendingTaskIds 不再用于判断系统锁定。
  • nonBlockingTaskIds 不再需要。
  • NON_BLOCKING_TASK_TYPES 变成 TASK_UI_POLICIES
  • ActiveTasksOverlay 替换为 GlobalTaskCenter

10.3 新 hook

建议新增:

frontend/src/hooks/useTaskCenter.js
frontend/src/hooks/useTaskMonitor.js
frontend/src/config/taskUiPolicies.js

useTaskCenter

  • 订阅 active tasks。
  • 维护全局任务状态。
  • 提供任务完成事件分发。

useTaskMonitor

  • 从全局任务状态中过滤当前功能相关任务。
  • 提供 isBusylatestTaskactiveTasksrecentTasks

11. 任务完成后的刷新策略

当前全局锁解除时会调用 initializeAppData({ refreshRadarSearch: true }),这也过粗。

应改成按任务类型刷新:

任务类型 刷新目标
UNPACK_ARCHIVES LT-1 数据检索选项、当前检索页
UNPACK_SENTINEL1 Sentinel-1 资产状态
SCAN_ASSET_INVENTORY 资产库存状态
SBAS_* 对应 SBAS run detail 或产品 catalog
REBUILD_SBAS_INSAR_CATALOG SBAS 产品列表
SCAN_DINSAR D-InSAR 产品列表
AI_TRAIN / AI_PREDICT AI 状态与结果质量
SCAN_HAZARD 灾害点列表
WATER_* / FLOOD_* 洪涝工作台当前场景/事件

实现上可维护:

TASK_COMPLETION_REFRESH_POLICIES

每个功能页也可以订阅自己的任务完成事件。

12. 迁移计划

阶段 1:文档与审计

状态:本设计文档。

输出:

  • 当前锁机制审计。
  • 新交互原则。
  • 迁移边界。

阶段 2:抽象任务 UI policy

新增:

  • frontend/src/config/taskUiPolicies.js

内容:

  • task type -> label
  • task type -> feature scope
  • task type -> route target
  • task type -> local/global blocking policy

替换:

  • useGlobalTaskControl.NON_BLOCKING_TASK_TYPES
  • useDinsarOperations.NON_BLOCKING_TASK_TYPES
  • ActiveTasksOverlay.getTaskTypeLabel

验收:

  • 所有任务类型 label 来自同一配置。
  • 新任务默认不锁全局。

阶段 3:全局遮罩改为任务中心

新增:

  • GlobalTaskCenter
  • TaskCenterButton 或顶部状态入口

替换:

  • ActiveTasksOverlay

验收:

  • 有活跃任务时不再遮住全系统。
  • 用户可继续切换页面和浏览结果。
  • 管理员仍可取消任务。

阶段 4:功能级任务面板

优先级:

  1. D-InSAR 生产
  2. D-InSAR 产物
  3. 数据接入
  4. 洪涝分析
  5. AI / 灾害点
  6. SBAS 组件收敛到通用面板

验收:

  • 每个功能页能看到本功能任务状态。
  • 同类任务运行中,只禁用同类提交按钮。

阶段 5:后端资源锁

新增:

  • 任务 params 中写入 feature_scoperesource_lock_key
  • task_service.create_task() 支持资源锁冲突检查。

验收:

  • 同 run、同目录、同 catalog 的冲突任务由后端返回 409
  • 前端不靠全局遮罩防冲突。

阶段 6:删除旧全局锁状态

删除或废弃:

  • isGlobalLocked
  • pendingTaskIds
  • nonBlockingTaskIds
  • handleTaskStart(null, ...) 触发全局锁的路径

验收:

  • 代码中不存在“无 taskId 触发全局锁”的逻辑。
  • 全局任务中心只展示状态,不控制系统可用性。

13. 风险与处理

13.1 后端资源冲突未覆盖

风险:

  • 去掉前端全局锁后,某些共享目录或单实例程序可能被并发调用。

处理:

  • 迁移初期保留少量 globalBlocking=true 兼容策略。
  • 优先给 ENVI / SARscape / 同目录生产补资源锁。
  • 对不确定任务先做局部锁,不做全局遮罩。

13.2 用户忽略后台任务

风险:

  • 没有全屏遮罩后,用户可能不知道任务仍在跑。

处理:

  • 顶部或右下角常驻任务指示。
  • 功能页内明确显示任务状态。
  • 失败任务有醒目提示。

13.3 任务完成刷新过少

风险:

  • 以前全局刷新掩盖了局部刷新缺失。

处理:

  • 建立 TASK_COMPLETION_REFRESH_POLICIES
  • 逐功能补刷新策略。
  • 保留手动刷新入口。

14. 验收标准

完成重构后应满足:

  1. 任意 SBAS 任务运行时,系统其他功能可正常浏览和操作。
  2. D-InSAR 任务运行时,不再弹全屏锁;D-InSAR 面板显示任务进度。
  3. 同一个 D-InSAR engine 或同一输出目录的冲突提交由后端拒绝。
  4. 数据接入扫描/解包任务运行时,不影响生产管理和结果浏览。
  5. 洪涝场景级任务运行时,只影响对应场景按钮。
  6. AI 训练/预测运行时,不阻断地图、结果浏览和 SBAS/DInSAR 生产。
  7. 所有活跃任务都能在全局任务中心找到。
  8. 每个功能页能看到与本功能相关的任务。
  9. 管理员取消任务能力仍可用,但语义是“取消任务”,不是“强制解锁界面”。
  10. 代码中不再通过 handleTaskStart(null, ...) 触发全局锁。

15. 推荐首批改动清单

建议第一批代码改动只做前端交互,不动后端任务模型:

  1. 新建 taskUiPolicies.js,统一任务 label、scope、blocking 策略。
  2. 将 D-InSAR 生产、D-InSAR 产物、IDL、AI、灾害点、水体任务全部标为 globalBlocking=false
  3. ActiveTasksOverlay 改为可关闭的 GlobalTaskCenter
  4. ensureCanOperate() 不再读取 isGlobalLocked,只检查用户权限。
  5. rebuildRadarPreviewCache() 删除 handleTaskStart(null, ...),改用局部 loading。
  6. D-InSAR 生产面板增加任务状态卡。
  7. D-InSAR 产物面板增加目录扫描任务状态卡。

第二批再做后端资源锁。

16. 结论

全局界面锁应退出核心设计。任务系统的正确边界应是:

  • 全局:看见所有任务。
  • 功能页:管理本功能任务。
  • 后端:保证资源互斥和并发安全。

前端全屏锁只应作为临时兼容手段,不应继续扩展。