Integrate SBAS workflows and redesign task center

This commit is contained in:
2026-06-13 13:10:12 +08:00
parent b931e8db53
commit 58a87706ef
275 changed files with 18750 additions and 42524 deletions
@@ -0,0 +1,696 @@
# 全局任务状态与界面锁重构设计
最后更新: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. 结论
全局界面锁应退出核心设计。任务系统的正确边界应是:
- 全局:看见所有任务。
- 功能页:管理本功能任务。
- 后端:保证资源互斥和并发安全。
前端全屏锁只应作为临时兼容手段,不应继续扩展。