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

697 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 全局任务状态与界面锁重构设计
最后更新: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. 前端维护 `pendingTaskIds``nonBlockingTaskIds`
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 当前仍会锁全局界面的任务
主要原因通常不是任务本身必须锁,而是提交时没有传 `taskType``nonBlocking: 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.py``create_task()` 中已做同类型任务互斥:
- 查询相同 `task_type` 且状态为 `PENDING` / `RUNNING` 的任务。
- 如果存在,直接返回任务冲突错误。
这说明前端全局锁不是唯一安全机制。真正的任务并发保护应继续下沉到后端,并从 `task_type` 扩展到更精确的资源锁。
## 3. 设计目标
1. 取消“任意阻塞任务遮住整个系统”的交互模式。
2. 全局层只负责展示所有活跃任务、最近任务、失败任务和快捷入口。
3. 每个功能模块拥有自己的任务状态面板,只展示和本功能相关的任务。
4. 默认任务不锁全局界面。
5. 需要互斥的场景由后端资源锁保证,而不是靠前端遮罩保证。
6. 前端只做局部禁用:禁用同一个功能里会造成重复提交或破坏状态的按钮。
7. 保留管理员取消任务能力,但从“强制解锁”改为“取消/中止任务”。
## 4. 非目标
本设计不要求一次性重写所有任务系统。
不在本阶段处理:
- 重做后端任务表结构。
- 重写 job worker。
- 改变现有任务 API 的基本返回格式。
- 一次性把所有历史任务类型改名。
- 删除所有旧 overlay 代码。
本设计优先保证渐进迁移。
## 5. 新架构概览
```text
后端任务系统
├─ 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 状态文案
旧文案:
```text
为了保证数据一致性,耗时任务执行期间 UI 已锁定。
```
应替换为:
```text
后台任务正在执行。你可以继续使用其他功能;同类任务的重复提交会由系统自动限制。
```
## 7. 功能级任务面板
每个功能页应自行展示本功能任务状态。这样用户在当前业务上下文里能直接看到“我刚提交的任务跑到哪一步”,而不是被全局遮罩挡住。
### 7.1 通用组件建议
新增通用组件:
- `TaskStatusPanel`
- `TaskLogPanel`
- `TaskProgressRow`
- `TaskStatusBadge`
- `useTaskMonitor`
建议参数:
```ts
type TaskStatusPanelProps = {
title: string;
taskTypes?: string[];
taskTypePrefixes?: string[];
taskIds?: string[];
showRecent?: boolean;
compact?: boolean;
onTaskClick?: (task) => void;
};
```
`useTaskMonitor` 负责:
-`taskTypes` / `taskTypePrefixes` 查询活跃任务。
- 轮询或复用全局 SSE 数据。
- 拉取指定任务日志。
- 返回 `activeTasks``recentTasks``latestTask``isBusy`
### 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. 任务分类模型
建议引入统一任务元数据配置,前端和后端可逐步共享。
```ts
type TaskUiPolicy = {
taskType: string;
featureScope: string;
label: string;
globalVisible: boolean;
globalBlocking: boolean;
localBlocking: boolean;
resourceScope?: string;
routeTarget?: string;
};
```
默认策略:
- `globalVisible=true`
- `globalBlocking=false`
- `localBlocking=true`
也就是说,任务默认显示在全局任务中心,但不锁整个系统。
只有极少数任务可设置:
```ts
globalBlocking=true
```
但这应作为过渡兼容,不作为长期设计。
## 9. 后端资源锁设计
后端当前只有 `task_type` 级互斥。这不足以表达以下场景:
- 同一个 run 不能同时执行两个会修改同一状态文件的步骤。
- 同一个 catalog 不能并发重建。
- 同一个输出目录不能被两个生产任务同时写入。
- 同一个 ENVI / SARscape 单实例资源不能并行调用。
建议新增资源锁概念。
### 9.1 资源锁 key
示例:
```text
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`
```json
{
"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
建议新增:
```text
frontend/src/hooks/useTaskCenter.js
frontend/src/hooks/useTaskMonitor.js
frontend/src/config/taskUiPolicies.js
```
`useTaskCenter`
- 订阅 active tasks。
- 维护全局任务状态。
- 提供任务完成事件分发。
`useTaskMonitor`
- 从全局任务状态中过滤当前功能相关任务。
- 提供 `isBusy``latestTask``activeTasks``recentTasks`
## 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_*` | 洪涝工作台当前场景/事件 |
实现上可维护:
```ts
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_scope``resource_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. 结论
全局界面锁应退出核心设计。任务系统的正确边界应是:
- 全局:看见所有任务。
- 功能页:管理本功能任务。
- 后端:保证资源互斥和并发安全。
前端全屏锁只应作为临时兼容手段,不应继续扩展。