|
|
@@ -0,0 +1,1520 @@
|
|
|
+# S2–S7 及 S9 看板刷新与重算统一架构执行任务书
|
|
|
+
|
|
|
+> 日期:2026-08-15
|
|
|
+> 状态:WP0–WP9 已落地,API/SQL/浏览器 UAT 已留证
|
|
|
+> 执行对象:供其他大模型或协作者逐工作包直接实施
|
|
|
+> 优先级:P0(S2~S7 当前“刷新”仍同步执行长耗时全量任务,且多数完整管线尚未实现严格租户/工厂隔离)
|
|
|
+> 目标策略:统一目标架构,分波迁移;禁止一次性“大爆炸”替换
|
|
|
+> 行为基线:`doc/plan/S1看板刷新机制修复执行任务书_20260815.md`、`doc/plan/S1看板重算进度体验优化执行任务书_20260815.md`
|
|
|
+> 当前参考版本:后端 `1.0.347`、前端 `2.4.298`;留证见 `doc/plan/UAT留证/2026-08-15-S2-S7看板刷新重算统一架构/`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 0. 执行须知
|
|
|
+
|
|
|
+### 0.1 开工前必须做
|
|
|
+
|
|
|
+1. 阅读 `AGENTS.md`、`.cursor/rules/`、本任务书及两份 S1 前置任务书;
|
|
|
+2. 检查 `git status --short`、`git diff`、`git log -10 --oneline`;
|
|
|
+3. 识别并保护工作区已有改动,禁止覆盖、回退或夹带其他任务;
|
|
|
+4. 记录前后端实际版本、当前运行进程版本及 `sys_db_migration_log`;
|
|
|
+5. 全仓检索 S1~S7 所有 `RunFullAsync`、手工刷新、AUTO、BOOTSTRAP 和业务触发调用点;
|
|
|
+6. 动手前列出本工作包的目标、文件、可见行为、跨模块影响、风险和明确不做项,等待负责人确认;
|
|
|
+7. 一次只实施一个 WP;每个 WP 构建、测试和留证通过后再进入下一 WP;
|
|
|
+8. 未经用户明确说“提交”,不得 commit/push;用户说“提交”时按仓库规则执行 commit + push。
|
|
|
+
|
|
|
+### 0.2 完成定义
|
|
|
+
|
|
|
+代码存在不等于完成。每个模块必须同时具备:
|
|
|
+
|
|
|
+- 真实租户/工厂隔离;
|
|
|
+- 轻量刷新与全量重算语义分离;
|
|
|
+- 持久异步任务、作用域锁、真实阶段进度和可靠收尾;
|
|
|
+- 后端单测、前端测试/构建、迁移验证;
|
|
|
+- A/B 租户运行 UAT 与 SQL/日志/截图证据;
|
|
|
+- AUTO、BOOTSTRAP、旧接口和业务触发入口收口。
|
|
|
+
|
|
|
+任一 SQL、锁、任务查询或完整重算入口仍可能跨租户时,该模块不得标记完成,不得仅交付进度 UI。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. 任务目标
|
|
|
+
|
|
|
+在不改变 S1~S7 既有业务指标公式、不修改 165 外部数据库对象的前提下:
|
|
|
+
|
|
|
+1. 建立 S1~S7 共用的模块级异步重算框架;
|
|
|
+2. 统一作用域为 `moduleCode + tenantId + factoryId`;
|
|
|
+3. 同一模块、租户、工厂最多一个活动任务;
|
|
|
+4. 不同租户/工厂可受控并行,A 不得读取、改写或锁住 B;
|
|
|
+5. S2~S7 将现有同步全量“刷新”迁为独立的“重算数据”;
|
|
|
+6. S1~S7 的“刷新”只做秒级目标快照更新与看板重载;
|
|
|
+7. S1~S7 重算均显示可折叠真实阶段进度卡;
|
|
|
+8. 发起浏览器可恢复本地关注任务;
|
|
|
+9. 同租户其他浏览器进入页面时不自动显示任务,仅在主动点击重算并收到 409 后接管;
|
|
|
+10. AUTO、BOOTSTRAP 和业务触发统一进入持久任务框架;
|
|
|
+11. S1 保持现有用户体验,最后一波平滑迁入通用框架;
|
|
|
+12. S9 仅提供轻量刷新,不显示“重算数据”或进度卡;
|
|
|
+13. S8 完全不在本任务范围内。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. 已核实的当前基线
|
|
|
+
|
|
|
+### 2.1 前端
|
|
|
+
|
|
|
+共享入口:
|
|
|
+
|
|
|
+```text
|
|
|
+Web/src/views/aidop/kanban/components/DynamicModuleDashboard.vue
|
|
|
+Web/src/views/aidop/kanban/config/moduleDashboardRegistry.ts
|
|
|
+Web/src/views/aidop/api/kanbanData.ts
|
|
|
+```
|
|
|
+
|
|
|
+现状:
|
|
|
+
|
|
|
+| 模块 | “刷新”当前行为 | “重算数据” | 进度卡 |
|
|
|
+|---|---|---|---|
|
|
|
+| S1 | 最新日目标快照轻量刷新 | 异步任务 | 已有 |
|
|
|
+| S2~S4 | 同步 `RunFullAsync`,前端超时 180 秒 | 无 | 无 |
|
|
|
+| S5~S7 | 同步 T8 入站 + 模块 KPI,前端超时 180 秒 | 无 | 无 |
|
|
|
+| S9 | 仅重新加载看板 | 无 | 无 |
|
|
|
+
|
|
|
+S1 已实现:
|
|
|
+
|
|
|
+- `POST /api/AidopKanban/s1/rebuild-jobs`;
|
|
|
+- 202/409;
|
|
|
+- 5 秒/15 秒轮询;
|
|
|
+- localStorage 按租户/工厂保存 watched jobId;
|
|
|
+- 无 watched jobId 时不自动查 latest;
|
|
|
+- 可折叠 `S1RebuildProgressCard.vue`;
|
|
|
+- 真实阶段权重、心跳、失败阶段和成功自动 reload。
|
|
|
+
|
|
|
+### 2.2 S2~S4 后端
|
|
|
+
|
|
|
+核心服务:
|
|
|
+
|
|
|
+```text
|
|
|
+Production/S2MdpSyncTransformService.cs
|
|
|
+Supply/S3MdpSyncTransformService.cs
|
|
|
+ProcurementExecution/S4MdpSyncTransformService.cs
|
|
|
+```
|
|
|
+
|
|
|
+当前 `RunFullAsync` 不接收强制 scope。S2、S3 的典型流程:
|
|
|
+
|
|
|
+```text
|
|
|
+STAGING → STANDARD → DWD → KPI → ATOMIC
|
|
|
+```
|
|
|
+
|
|
|
+S4:
|
|
|
+
|
|
|
+```text
|
|
|
+STAGING → STANDARD → DWD → KPI
|
|
|
+```
|
|
|
+
|
|
|
+已核实风险:
|
|
|
+
|
|
|
+- `RunFullAsync` 一次处理全部可见租户;
|
|
|
+- Pull 路径仍存在 `tenantId=0`;
|
|
|
+- Standard/DWD/KPI 通过分组写多租户,不等于当前任务租户隔离;
|
|
|
+- S2/S3 Atomic 仍调用无 scope 的 `Build*DomainForAllDatesAsync`;
|
|
|
+- 手工 HTTP 同步等待;
|
|
|
+- 定时任务和 BOOTSTRAP 直接执行完整 RunFull;
|
|
|
+- 无模块级持久任务、真实进度和作用域锁。
|
|
|
+
|
|
|
+S3 的 `RefreshMaterialReadinessAsync` 是独立业务动作。除非 WP0 审计证明它是完整 S3 重算不可分割的一步,否则首轮不得擅自并入重算阶段。
|
|
|
+
|
|
|
+### 2.3 S5~S7 后端
|
|
|
+
|
|
|
+核心服务:
|
|
|
+
|
|
|
+```text
|
|
|
+MaterialWarehouse/S5MdpSyncTransformService.cs
|
|
|
+Manufacturing/S6MdpSyncTransformService.cs
|
|
|
+FinishedWarehouse/S7MdpSyncTransformService.cs
|
|
|
+DataPlatform/T8BaseInboundMdpSyncService.cs
|
|
|
+DataPlatform/AidopT8KpiManualRefreshService.cs
|
|
|
+Job/S5MdpRefreshJob.cs
|
|
|
+```
|
|
|
+
|
|
|
+特点:
|
|
|
+
|
|
|
+- S5~S7 共用 T8 基础入站;
|
|
|
+- 使用 `AidopSourceTenantMap` 将源账套映射到目标租户;
|
|
|
+- 当前手工刷新由 `AidopT8KpiManualRefreshService` 编排;
|
|
|
+- 存在全局 Cache 锁 `aidop:t8kpi:refresh:lock`;
|
|
|
+- S5/S6/S7 的 `RefreshOption` 含 `SourceZtid / TargetTenantId / TargetFactoryId`;
|
|
|
+- 各模块按 KPI 子项执行,可取得 `PerKpiDwdRows / PerKpiKpiRows`;
|
|
|
+- S5 的 legacy TVF 查询有独立超时护栏;
|
|
|
+- S5~S7 不能假装成与 S2~S4 完全相同的五段 ETL。
|
|
|
+
|
|
|
+### 2.4 S9 后端
|
|
|
+
|
|
|
+S9 当前没有:
|
|
|
+
|
|
|
+- `S9MdpSyncTransformService`;
|
|
|
+- 独立源数据拉取;
|
|
|
+- 独立 DWD/KPI 重算管线;
|
|
|
+- 可定义真实阶段的 S9 full rebuild。
|
|
|
+
|
|
|
+S9 看板主要读取 S1~S7 写入的 `ado_s9_kpi_value_l1_day`~`l4_day` 及共享原子层。因此本任务固定:
|
|
|
+
|
|
|
+```text
|
|
|
+S9 刷新 = 更新可用的最新日目标快照(无可更新行时返回 0)+ reload
|
|
|
+S9 不提供重算数据
|
|
|
+S9 不展示进度卡
|
|
|
+S9 不编排 S1~S7 全量重算
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. 产品交互规格
|
|
|
+
|
|
|
+### 3.1 S1~S7 两个动作
|
|
|
+
|
|
|
+#### 刷新
|
|
|
+
|
|
|
+- 语义:更新当前租户/工厂/模块最新业务日的目标快照,再重新加载看板;
|
|
|
+- 不拉取源数据;
|
|
|
+- 不重建 Standard、DWD、KPI 实际值或 Atomic;
|
|
|
+- 不增加 `mdp_transform_run_log` 完整重算记录;
|
|
|
+- 正常 P95 ≤ 2 秒,硬上限 5 秒;
|
|
|
+- 无日值时返回成功、更新 0 行,不伪造日值。
|
|
|
+
|
|
|
+#### 重算数据
|
|
|
+
|
|
|
+- 语义:执行该模块完整数据生产管线;
|
|
|
+- 点击后显示模块化确认弹窗;
|
|
|
+- 创建 API 应在 2 秒内返回 202 或 409;
|
|
|
+- 后台执行,不绑定浏览器请求 CancellationToken;
|
|
|
+- 展示真实阶段进度、已耗时、行数和失败阶段;
|
|
|
+- 成功后自动 reload 一次;
|
|
|
+- 禁止通过延长 HTTP timeout 代替异步任务。
|
|
|
+
|
|
|
+### 3.2 S9
|
|
|
+
|
|
|
+S9 工具栏只显示“刷新”。不得显示:
|
|
|
+
|
|
|
+- “重算数据”;
|
|
|
+- 重算确认弹窗;
|
|
|
+- 重算进度卡;
|
|
|
+- “去重算 S1~S7”的自动级联操作。
|
|
|
+
|
|
|
+### 3.3 任务卡可见性
|
|
|
+
|
|
|
+1. 发起浏览器收到 202 后保存并显示 jobId;
|
|
|
+2. 本浏览器刷新/关闭重开后按本地 watched jobId 恢复;
|
|
|
+3. 无 watched jobId 时不得自动调用 latest 展示同租户他人任务;
|
|
|
+4. 同租户其他浏览器主动点击并收到 409 后,才保存并接管该 jobId;
|
|
|
+5. A 不能查询 B 的 jobId;
|
|
|
+6. localStorage key:
|
|
|
+
|
|
|
+```text
|
|
|
+aidop.{moduleCodeLower}.rebuild.watch.{tenantId}.{factoryId}
|
|
|
+```
|
|
|
+
|
|
|
+7. 切换租户、工厂或模块不得沿用旧 key;
|
|
|
+8. 403/404/作用域不符时清理本地 key。
|
|
|
+
|
|
|
+### 3.4 可折叠进度卡
|
|
|
+
|
|
|
+泛化现有 S1 卡片为模块通用组件。折叠态至少显示:
|
|
|
+
|
|
|
+```text
|
|
|
+模块数据重算 · 当前阶段 · 预计进度% · 已耗时 · 状态
|
|
|
+```
|
|
|
+
|
|
|
+展开态显示:
|
|
|
+
|
|
|
+- 模块名称;
|
|
|
+- QUEUED/RUNNING/SUCCESS/FAILED/CANCELLED;
|
|
|
+- 阶段 N/M;
|
|
|
+- 标注“按处理阶段估算”的进度条;
|
|
|
+- 已耗时和非精确剩余提示;
|
|
|
+- jobId、batchId;
|
|
|
+- 通用阶段行数及模块扩展摘要;
|
|
|
+- 失败阶段和可读错误;
|
|
|
+- 成功后“看板已自动刷新”;
|
|
|
+- 重试/刷新看板操作。
|
|
|
+
|
|
|
+点击“重算数据”后必须自动展开。进度百分比只能来自后端,不得前端定时增长。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. 统一目标架构
|
|
|
+
|
|
|
+### 4.1 架构图
|
|
|
+
|
|
|
+```mermaid
|
|
|
+flowchart LR
|
|
|
+ Ui["DynamicModuleDashboard"] --> Api["Module Rebuild API"]
|
|
|
+ Api --> Service["ModuleRebuildService"]
|
|
|
+ Service --> JobTable["ado_module_dashboard_rebuild_job"]
|
|
|
+ Service --> Signal["In-memory wake signal"]
|
|
|
+ Signal --> Worker["ModuleRebuildWorker"]
|
|
|
+ Worker --> Handler["IModuleRebuildHandler"]
|
|
|
+ Handler --> Lock["Scoped DB lease"]
|
|
|
+ Handler --> Pipeline["S1-S7 module pipeline"]
|
|
|
+ Pipeline --> Reporter["Progress reporter"]
|
|
|
+ Reporter --> JobTable
|
|
|
+ AutoJob["AUTO / BOOTSTRAP"] --> Service
|
|
|
+```
|
|
|
+
|
|
|
+数据库任务表是事实源;内存 signal 只负责唤醒 worker。
|
|
|
+
|
|
|
+### 4.2 通用运行作用域
|
|
|
+
|
|
|
+建议新增:
|
|
|
+
|
|
|
+```csharp
|
|
|
+public sealed record MdpRebuildScope(
|
|
|
+ string ModuleCode,
|
|
|
+ long TenantId,
|
|
|
+ long FactoryId);
|
|
|
+```
|
|
|
+
|
|
|
+硬要求:
|
|
|
+
|
|
|
+- `ModuleCode` 只允许 `S1`~`S7`;
|
|
|
+- `TenantId > 0`;
|
|
|
+- `FactoryId > 0`;
|
|
|
+- 手工 API 的 tenantId 只能来自 `AidopTenantScope.ResolveOrThrow`;
|
|
|
+- factoryId 必须验证属于当前租户;
|
|
|
+- 禁止 `tenantId=0` 作为业务 SQL 的“全部租户”;
|
|
|
+- AUTO/BOOTSTRAP 先枚举 scope,再逐 scope 入队;
|
|
|
+- batch、日志、锁和每个进度事件均带完整 scope。
|
|
|
+
|
|
|
+### 4.3 通用任务表
|
|
|
+
|
|
|
+新增统一表:
|
|
|
+
|
|
|
+```text
|
|
|
+ado_module_dashboard_rebuild_job
|
|
|
+```
|
|
|
+
|
|
|
+最低字段:
|
|
|
+
|
|
|
+```sql
|
|
|
+id BIGINT PRIMARY KEY,
|
|
|
+module_code VARCHAR(8) NOT NULL,
|
|
|
+tenant_id BIGINT NOT NULL,
|
|
|
+factory_id BIGINT NOT NULL,
|
|
|
+status VARCHAR(20) NOT NULL,
|
|
|
+trigger_type VARCHAR(20) NOT NULL,
|
|
|
+requested_by BIGINT NULL,
|
|
|
+batch_id VARCHAR(100) NULL,
|
|
|
+transform_run_log_id BIGINT NULL,
|
|
|
+current_stage VARCHAR(32) NOT NULL DEFAULT 'QUEUED',
|
|
|
+stage_index TINYINT NOT NULL DEFAULT 0,
|
|
|
+stage_total TINYINT NOT NULL DEFAULT 0,
|
|
|
+progress_percent TINYINT NOT NULL DEFAULT 0,
|
|
|
+progress_message VARCHAR(255) NULL,
|
|
|
+failed_stage VARCHAR(32) NULL,
|
|
|
+stage_rows INT NOT NULL DEFAULT 0,
|
|
|
+standard_rows INT NOT NULL DEFAULT 0,
|
|
|
+dwd_rows INT NOT NULL DEFAULT 0,
|
|
|
+kpi_rows INT NOT NULL DEFAULT 0,
|
|
|
+atomic_rows INT NOT NULL DEFAULT 0,
|
|
|
+detail_json TEXT NULL,
|
|
|
+error_message VARCHAR(2000) NULL,
|
|
|
+submitted_at DATETIME(3) NOT NULL,
|
|
|
+started_at DATETIME(3) NULL,
|
|
|
+finished_at DATETIME(3) NULL,
|
|
|
+heartbeat_at DATETIME(3) NULL,
|
|
|
+last_progress_at DATETIME(3) NULL,
|
|
|
+duration_ms BIGINT NULL,
|
|
|
+create_time DATETIME(3) NOT NULL,
|
|
|
+update_time DATETIME(3) NOT NULL
|
|
|
+```
|
|
|
+
|
|
|
+必须有索引:
|
|
|
+
|
|
|
+```text
|
|
|
+(module_code, tenant_id, factory_id, status, submitted_at)
|
|
|
+(status, submitted_at)
|
|
|
+(heartbeat_at, status)
|
|
|
+```
|
|
|
+
|
|
|
+`detail_json` 只存模块特有摘要,例如 S5~S7 的 per-KPI 行数和分母状态;不得存异常堆栈或逐秒进度历史。
|
|
|
+
|
|
|
+### 4.4 通用锁
|
|
|
+
|
|
|
+建议统一 DB 租约锁表:
|
|
|
+
|
|
|
+```text
|
|
|
+ado_mdp_rebuild_lock
|
|
|
+```
|
|
|
+
|
|
|
+最低字段:
|
|
|
+
|
|
|
+```sql
|
|
|
+lock_name VARCHAR(160) PRIMARY KEY,
|
|
|
+module_code VARCHAR(8) NOT NULL,
|
|
|
+tenant_id BIGINT NOT NULL,
|
|
|
+factory_id BIGINT NOT NULL,
|
|
|
+holder_id VARCHAR(100) NOT NULL,
|
|
|
+job_id BIGINT NULL,
|
|
|
+status VARCHAR(20) NOT NULL,
|
|
|
+acquired_at DATETIME(3) NULL,
|
|
|
+heartbeat_at DATETIME(3) NULL,
|
|
|
+create_time DATETIME(3) NOT NULL,
|
|
|
+update_time DATETIME(3) NOT NULL,
|
|
|
+KEY ix_mdp_rebuild_lock_scope (module_code, tenant_id, factory_id),
|
|
|
+KEY ix_mdp_rebuild_lock_heartbeat (status, heartbeat_at)
|
|
|
+```
|
|
|
+
|
|
|
+锁键:
|
|
|
+
|
|
|
+```text
|
|
|
+{moduleCode}_MDP_FULL:{tenantId}:{factoryId}
|
|
|
+```
|
|
|
+
|
|
|
+要求:
|
|
|
+
|
|
|
+- 同 scope 同模块最大并行数 1;
|
|
|
+- 不同模块是否可并行由依赖审计决定,不能仅凭锁键不同就放行;
|
|
|
+- 不同租户/工厂允许受控并行;
|
|
|
+- holder、jobId、acquiredAt、heartbeatAt、释放操作必须匹配锁键;
|
|
|
+- 长阶段持续锁心跳;
|
|
|
+- stale 判定不得误杀仍有新鲜心跳的任务;
|
|
|
+- S5~S7 的共享 T8 入站锁继续存在,并与模块作用域锁分层处理。
|
|
|
+
|
|
|
+### 4.5 Handler 与阶段协议
|
|
|
+
|
|
|
+建议:
|
|
|
+
|
|
|
+```csharp
|
|
|
+public interface IModuleRebuildHandler
|
|
|
+{
|
|
|
+ string ModuleCode { get; }
|
|
|
+ Task<ModuleRebuildResult> RunAsync(
|
|
|
+ MdpRebuildScope scope,
|
|
|
+ string triggerType,
|
|
|
+ long jobId,
|
|
|
+ Func<ModuleProgressUpdate, Task> report,
|
|
|
+ CancellationToken cancellationToken);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+通用阶段码:
|
|
|
+
|
|
|
+```text
|
|
|
+QUEUED
|
|
|
+ACQUIRING_LOCK
|
|
|
+PREPARING
|
|
|
+STAGING
|
|
|
+STANDARD
|
|
|
+DWD
|
|
|
+KPI
|
|
|
+ATOMIC
|
|
|
+FINALIZING
|
|
|
+SUCCESS
|
|
|
+FAILED
|
|
|
+CANCELLED
|
|
|
+```
|
|
|
+
|
|
|
+模块可省略不存在的业务阶段,但不得上报未实际执行的阶段。阶段定义:
|
|
|
+
|
|
|
+| 模块 | 用户可见阶段 |
|
|
|
+|---|---|
|
|
|
+| S1 | STAGING、STANDARD、DWD、KPI、ATOMIC |
|
|
|
+| S2 | STAGING、STANDARD、DWD、KPI、ATOMIC |
|
|
|
+| S3 | STAGING、STANDARD、DWD、KPI、ATOMIC |
|
|
|
+| S4 | STAGING、STANDARD、DWD、KPI |
|
|
|
+| S5 | T8_INBOUND、KPI_PREPARING、KPI_CALCULATING、FINALIZING |
|
|
|
+| S6 | T8_INBOUND、KPI_PREPARING、KPI_CALCULATING、FINALIZING |
|
|
|
+| S7 | T8_INBOUND、KPI_PREPARING、KPI_CALCULATING、FINALIZING |
|
|
|
+
|
|
|
+S5~S7 新增阶段码应放入统一注册表,例如:
|
|
|
+
|
|
|
+```text
|
|
|
+T8_INBOUND
|
|
|
+KPI_PREPARING
|
|
|
+KPI_CALCULATING
|
|
|
+```
|
|
|
+
|
|
|
+前后端共享同一阶段契约来源或由 API 返回中文 message;不得各模块散落硬编码分支。
|
|
|
+
|
|
|
+### 4.6 Worker
|
|
|
+
|
|
|
+- 一个通用 Worker 从 DB claim;
|
|
|
+- Handler 按 `module_code` 解析;
|
|
|
+- 每个任务使用独立 DI scope 和数据库连接;
|
|
|
+- 配置 `AiDOP:MdpRebuild:MaxParallelScopes`,默认 2,限制 1~8;
|
|
|
+- 只有租户隔离集成测试通过的模块才允许并行;
|
|
|
+- claim 必须避免同 scope 重复领取;
|
|
|
+- job 心跳建议 30 秒,正常年龄不超过 90 秒;
|
|
|
+- 服务停止时可靠标记 CANCELLED,或在重启时由 stale 收口;
|
|
|
+- 进度写失败仅告警,不掩盖业务异常;
|
|
|
+- 状态更新使用列级 UPDATE,避免并发整行覆盖。
|
|
|
+
|
|
|
+### 4.7 模块能力开关
|
|
|
+
|
|
|
+在 `server/Admin.NET.Application/Configuration/App.json` 增加通用配置,建议:
|
|
|
+
|
|
|
+```text
|
|
|
+AiDOP:MdpRebuild:MaxParallelScopes = 2
|
|
|
+AiDOP:MdpRebuild:Modules:S1:Enabled = false
|
|
|
+...
|
|
|
+AiDOP:MdpRebuild:Modules:S7:Enabled = false
|
|
|
+```
|
|
|
+
|
|
|
+要求:
|
|
|
+
|
|
|
+- 新框架上线时所有生产模块默认关闭;
|
|
|
+- 每个 WP 仅开启当波已验收模块;
|
|
|
+- S1 到 WP6 才开启;
|
|
|
+- S9 不得配置为可重算模块;
|
|
|
+- WP6 后将旧 `AiDOP:S1Rebuild:MaxParallelTenants` 迁到通用并发配置,兼容期不得有两个 Worker 同时领取 S1。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. API 契约
|
|
|
+
|
|
|
+### 5.0 鉴权硬边界
|
|
|
+
|
|
|
+所有新 `target-snapshot/refresh`、`rebuild-jobs`、jobId 和 latest API 必须:
|
|
|
+
|
|
|
+- 使用独立 `[Authorize]` 控制器,参考现有 `AidopKanbanS1RefreshController`;
|
|
|
+- 禁止把新端点挂到带 `[AllowAnonymous]` 的 `AidopKanbanController` 或其 partial 类;
|
|
|
+- tenantId 仅从认证上下文解析;
|
|
|
+- factoryId 验证归属;
|
|
|
+- 不接受客户端 tenantId;
|
|
|
+- 未认证请求返回 401/403,不得进入 Job Store 或轻量刷新服务。
|
|
|
+
|
|
|
+### 5.1 轻量刷新
|
|
|
+
|
|
|
+```http
|
|
|
+POST /api/AidopKanban/{moduleCode}/target-snapshot/refresh?factoryId=1
|
|
|
+Authorization: Bearer ...
|
|
|
+```
|
|
|
+
|
|
|
+适用:S1~S7、S9。
|
|
|
+
|
|
|
+成功示例:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "ok": true,
|
|
|
+ "moduleCode": "S2",
|
|
|
+ "factoryId": 1,
|
|
|
+ "updatedRows": 24,
|
|
|
+ "missingRows": 0,
|
|
|
+ "elapsedMs": 280,
|
|
|
+ "message": "S2 看板目标快照已刷新"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+实现要求:
|
|
|
+
|
|
|
+- 泛化 `S1KpiTargetSnapshotRefreshService`,参数化 moduleCode;
|
|
|
+- 仅更新当前租户、工厂、模块 L1~L4 各自最新业务日;
|
|
|
+- 使用日值行自己的 `biz_date` 解析目标;
|
|
|
+- 只更新 target 相关字段和 update_time;
|
|
|
+- 不修改 metric、状态色、趋势、calc_time 或历史日期;
|
|
|
+- `MISSING` 不覆盖原值;
|
|
|
+- S9 无日值时返回 200、updatedRows=0;
|
|
|
+- 不访问外部源数据库。
|
|
|
+
|
|
|
+### 5.2 创建重算任务
|
|
|
+
|
|
|
+```http
|
|
|
+POST /api/AidopKanban/{moduleCode}/rebuild-jobs?factoryId=1
|
|
|
+Authorization: Bearer ...
|
|
|
+```
|
|
|
+
|
|
|
+仅允许 S1~S7。S9 返回 404/405 或不注册路由,禁止创建空任务。
|
|
|
+
|
|
|
+202:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "ok": true,
|
|
|
+ "moduleCode": "S2",
|
|
|
+ "jobId": 123,
|
|
|
+ "status": "QUEUED",
|
|
|
+ "message": "S2 数据重算已排队"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+409:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "ok": false,
|
|
|
+ "moduleCode": "S2",
|
|
|
+ "jobId": 122,
|
|
|
+ "status": "RUNNING",
|
|
|
+ "message": "S2 数据重算正在执行,请勿重复提交"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+活动判定键:
|
|
|
+
|
|
|
+```text
|
|
|
+module_code + tenant_id + factory_id + status IN ('QUEUED','RUNNING')
|
|
|
+```
|
|
|
+
|
|
|
+### 5.3 查询
|
|
|
+
|
|
|
+```http
|
|
|
+GET /api/AidopKanban/{moduleCode}/rebuild-jobs/{jobId}?factoryId=1
|
|
|
+GET /api/AidopKanban/{moduleCode}/rebuild-jobs/latest?factoryId=1
|
|
|
+```
|
|
|
+
|
|
|
+每次查询必须同时过滤 moduleCode、认证 tenantId、factoryId。不得仅凭 jobId 返回。
|
|
|
+
|
|
|
+`latest` 保留给显式恢复/运维,不允许前端在没有本地 watched jobId 时自动调用并展示。
|
|
|
+
|
|
|
+### 5.4 旧接口
|
|
|
+
|
|
|
+现有:
|
|
|
+
|
|
|
+```text
|
|
|
+POST /api/AidopKanban/mdp/refresh/{moduleCode}
|
|
|
+POST /api/AidopKanban/s2-mdp/refresh ~ s7-mdp/refresh
|
|
|
+```
|
|
|
+
|
|
|
+迁移对应模块后:
|
|
|
+
|
|
|
+- 不再同步等待完整 RunFull;
|
|
|
+- 有调用方时内部转为 Enqueue,返回兼容的 202 数据;
|
|
|
+- 无调用方且负责人同意时可标记 Deprecated;
|
|
|
+- 不得悄悄保持同步长请求;
|
|
|
+- `[AllowAnonymous]` 入口必须收口到 `[Authorize]`;
|
|
|
+- inbound 专用接口不得接受客户端 tenantId 冒充认证租户。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 租户/工厂隔离硬要求
|
|
|
+
|
|
|
+对每个模块、每个阶段逐条审计:
|
|
|
+
|
|
|
+### 6.1 入站
|
|
|
+
|
|
|
+- 源查询真实限定当前租户/账套映射;
|
|
|
+- `fullRefresh` 只清理当前 scope;
|
|
|
+- staging DELETE/UPSERT、游标、同步日志带 scope;
|
|
|
+- 源表无可信 tenant/factory 时停止并报告;
|
|
|
+- 不得把所有源行强行写成当前 JWT tenant;
|
|
|
+- 同业务键跨租户不得 JOIN。
|
|
|
+
|
|
|
+### 6.2 Standard/DWD
|
|
|
+
|
|
|
+每条 SQL:
|
|
|
+
|
|
|
+- 参数含 `@TenantId/@FactoryId`;
|
|
|
+- 源 WHERE 限定 scope;
|
|
|
+- 多表 JOIN 包含 tenant,适用时包含 factory;
|
|
|
+- DELETE/UPDATE/UPSERT 不作用其它租户;
|
|
|
+- 唯一键能够区分租户;
|
|
|
+- 行数只统计当前 scope。
|
|
|
+
|
|
|
+### 6.3 KPI
|
|
|
+
|
|
|
+- 所有 UNION 分支都带 scope;
|
|
|
+- 目标解析只解析当前 tenant/factory;
|
|
|
+- Upsert WHERE 和唯一键包含 tenant/factory/module/metric/date;
|
|
|
+- 不遍历其它租户;
|
|
|
+- S5~S7 的 `TargetTenantId` 必须与认证 tenant 和 `SourceZtid` 映射一致。
|
|
|
+
|
|
|
+### 6.4 Atomic
|
|
|
+
|
|
|
+S1~S3 所有 `Build*DomainForAllDatesAsync` 必须提供 scope 版本:
|
|
|
+
|
|
|
+- 日期只从当前 scope 事实表枚举;
|
|
|
+- DELETE 含 tenant + factory + domain + date;
|
|
|
+- INSERT SELECT 限定当前 scope;
|
|
|
+- A 执行前后 B 的行数、checksum、batchId、calc_time 不变。
|
|
|
+
|
|
|
+### 6.5 S5~S7 特殊边界
|
|
|
+
|
|
|
+T8 入站可能是共享物理动作,不得虚构“各租户完全独立拉源”:
|
|
|
+
|
|
|
+1. WP0 必须证明 `SourceZtid → TargetTenantId` 是一对一可信映射;
|
|
|
+2. 共享入站使用独立全局/账套锁;
|
|
|
+3. 入站完成后,模块 KPI 写入使用 module + tenant + factory 作用域锁;
|
|
|
+4. 若同一入站批次可供 S5~S7 复用,应记录批次依赖,不重复并发拉取;
|
|
|
+5. A 的源账套映射不得写入 B;
|
|
|
+6. 无法证明映射安全时停止 S5~S7 迁移,不得以全局串行冒充隔离。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. 分波迁移总览
|
|
|
+
|
|
|
+```text
|
|
|
+WP0 基线、依赖和安全审计
|
|
|
+ → WP1 通用任务/锁/Worker/进度框架(先不切 S1)
|
|
|
+ → WP2 S2 试点
|
|
|
+ → WP3 S3、S4
|
|
|
+ → WP4 S5~S7
|
|
|
+ → WP5 前端通用化并按模块开关启用
|
|
|
+ → WP6 S1 平滑迁入通用框架
|
|
|
+ → WP7 S9 轻量刷新
|
|
|
+ → WP8 AUTO/BOOTSTRAP/旧接口/业务触发收口
|
|
|
+ → WP9 迁移、测试、UAT 与交付
|
|
|
+```
|
|
|
+
|
|
|
+每波必须可独立回滚。不得在 WP2 未通过时同时切 S3~S7。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP0:基线、依赖与停止条件审计
|
|
|
+
|
|
|
+### 步骤
|
|
|
+
|
|
|
+1. 保存各模块最近 20 条 `mdp_transform_run_log`;
|
|
|
+2. 记录 S2~S7 当前同步刷新响应和耗时;
|
|
|
+3. 列出各模块实体清单、源表、stg/std/dwd/KPI/Atomic 表;
|
|
|
+4. 列出所有完整 RunFull 调用点;
|
|
|
+5. 审计所有 SQL 的 tenant/factory WHERE、JOIN、DELETE;
|
|
|
+6. 审计唯一键是否包含租户维度;
|
|
|
+7. 审计 S2/S3 Atomic 全局删除;
|
|
|
+8. 审计 S5~S7 SourceZtid 映射、共享入站锁及可复用批次;
|
|
|
+9. 明确 S2~S7 各模块真实阶段和当前耗时;
|
|
|
+10. 确认 S9 无独立重算管线;
|
|
|
+11. 检查数据库版本占用、多实例部署和连接池上限;
|
|
|
+12. 输出逐模块“可迁移/阻塞”结论。
|
|
|
+
|
|
|
+### 完成标准
|
|
|
+
|
|
|
+- 有完整调用点和 SQL 审计清单;
|
|
|
+- 任一不能可信分租户的数据源被标记为阻塞;
|
|
|
+- 明确 S3 material readiness 是否属于本轮 full rebuild;
|
|
|
+- 明确 S5~S7 T8 入站互斥粒度;
|
|
|
+- 未通过 WP0 不得开始通用框架代码。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP1:通用任务、锁、Worker 与进度框架
|
|
|
+
|
|
|
+### 建议新增
|
|
|
+
|
|
|
+```text
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/DataPlatform/MdpRebuild/
|
|
|
+ MdpRebuildScope.cs
|
|
|
+ ModuleRebuildStages.cs
|
|
|
+ ModuleRebuildModels.cs
|
|
|
+ IModuleRebuildHandler.cs
|
|
|
+ ModuleRebuildJobStore.cs
|
|
|
+ ModuleRebuildService.cs
|
|
|
+ ModuleRebuildQueue.cs
|
|
|
+ ModuleRebuildWorker.cs
|
|
|
+ MdpRebuildLock.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/Entity/SmartOps/AdoModuleDashboardRebuildJob.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP.Tests/DataPlatform/ModuleRebuildServiceTests.cs
|
|
|
+```
|
|
|
+
|
|
|
+### 步骤
|
|
|
+
|
|
|
+1. 建立 scope、DTO、状态机、阶段注册和 Handler 接口;
|
|
|
+2. 建统一 Job/Lock 实体和幂等迁移;
|
|
|
+3. 实现 scoped active/get/latest/claim;
|
|
|
+4. 实现 202/409 Enqueue;
|
|
|
+5. 实现 DB 事实源 + 内存唤醒;
|
|
|
+6. 实现通用 Worker、受控并行和 Handler 路由;
|
|
|
+7. 实现 30 秒 job heartbeat、锁 heartbeat 和 stale 收口;
|
|
|
+8. 实现列级进度更新及状态不变量;
|
|
|
+9. 新增独立 `[Authorize]` 通用或薄控制器,禁止继承/复用带 `[AllowAnonymous]` 的 `AidopKanbanController`;
|
|
|
+10. 注册 hosted service 和配置;
|
|
|
+11. 默认只注册测试 Handler,不切换生产模块。
|
|
|
+
|
|
|
+### 状态不变量
|
|
|
+
|
|
|
+- RUNNING progress < 100;
|
|
|
+- SUCCESS progress = 100;
|
|
|
+- FAILED 有 failedStage 或明确“未知阶段”;
|
|
|
+- stageIndex ≤ stageTotal;
|
|
|
+- 百分比单调不减;
|
|
|
+- 终态不再更新心跳;
|
|
|
+- 任务查询始终带 module + tenant + factory。
|
|
|
+
|
|
|
+### 完成标准
|
|
|
+
|
|
|
+- 20 个同 scope 并发提交只有一个活动任务;
|
|
|
+- 不同 scope 可各有一个任务;
|
|
|
+- A 不能查询 B;
|
|
|
+- stale、重启、锁冲突、取消均有单测;
|
|
|
+- 未改变现有 S1 生产路径。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP2:S2 试点迁移
|
|
|
+
|
|
|
+### 修改重点
|
|
|
+
|
|
|
+```text
|
|
|
+Production/S2MdpSyncTransformService.cs
|
|
|
+Job/S2MdpSyncTransformJob.cs
|
|
|
+SmartOps/SmartOpsKpiAtomicBuildService.cs
|
|
|
+Controllers/独立的 [Authorize] 模块刷新控制器(参考 AidopKanbanS1RefreshController)
|
|
|
+Job/SmartOpsKpiMdpBootstrapJob.cs
|
|
|
+```
|
|
|
+
|
|
|
+### 步骤
|
|
|
+
|
|
|
+1. `RunFullAsync` 强制接收 `MdpRebuildScope`;
|
|
|
+2. STAGING 实际按 tenant/factory 拉取和清理;
|
|
|
+3. Standard/DWD 每条 SQL 增加 scope 和 tenant JOIN;
|
|
|
+4. KPI 每个分支及 Upsert 限定 scope;
|
|
|
+5. 新增 scope 版 `BuildWorkScheduleDomainForAllDatesAsync`;
|
|
|
+6. 建 `S2ModuleRebuildHandler`;
|
|
|
+7. 在真实边界上报五阶段进度;
|
|
|
+8. S2 手工重算切换到通用 API;
|
|
|
+9. 旧 S2 refresh API 转 Enqueue,不再同步等待;
|
|
|
+10. S2 AUTO/BOOTSTRAP 暂仅在能力开关开启时 Enqueue;`SmartOpsKpiMdpBootstrapJob` 只切 S2 分支,S3/S4 继续旧路径并留到 WP3;
|
|
|
+11. 执行 A/B 租户隔离与受控并行 UAT。
|
|
|
+
|
|
|
+### 阶段权重
|
|
|
+
|
|
|
+沿用 S1 五段权重:0、2、5、10、35、50、65、80、82、98、99、100。
|
|
|
+
|
|
|
+### 完成标准
|
|
|
+
|
|
|
+- A 重算不改变 B 的 stg/std/dwd/KPI/Atomic;
|
|
|
+- A/B 可分别 RUNNING;
|
|
|
+- 同 scope 409;
|
|
|
+- API 2 秒内返回;
|
|
|
+- S2 完整阶段达到 SUCCESS 100%;
|
|
|
+- S2 通过后才能复制模式到 S3/S4。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP3:S3、S4 迁移
|
|
|
+
|
|
|
+### S3
|
|
|
+
|
|
|
+修改:
|
|
|
+
|
|
|
+```text
|
|
|
+Supply/S3MdpSyncTransformService.cs
|
|
|
+Job/S3MdpSyncTransformJob.cs
|
|
|
+SmartOps/SmartOpsKpiAtomicBuildService.cs
|
|
|
+```
|
|
|
+
|
|
|
+要求:
|
|
|
+
|
|
|
+- 复制经过 S2 验证的 scope 模式,不复制业务 SQL;
|
|
|
+- Supply/Purchase 与 Inventory Atomic 均限定 scope;
|
|
|
+- `RefreshMaterialReadinessAsync` 按 WP0 结论独立或作为真实阶段,禁止猜测;
|
|
|
+- `S3MdpSyncTransformJob` 和 `SmartOpsKpiMdpBootstrapJob` 的 S3 分支改为按启用 scope Enqueue,能力开关默认关闭并在 S3 验收后开启;
|
|
|
+- 保持现有 KPI 公式和日期口径。
|
|
|
+
|
|
|
+### S4
|
|
|
+
|
|
|
+修改:
|
|
|
+
|
|
|
+```text
|
|
|
+ProcurementExecution/S4MdpSyncTransformService.cs
|
|
|
+Job/S4MdpSyncTransformJob.cs
|
|
|
+Supply/ProcurementPipelineService.cs
|
|
|
+```
|
|
|
+
|
|
|
+要求:
|
|
|
+
|
|
|
+- 四阶段:STAGING、STANDARD、DWD、KPI;
|
|
|
+- `stageTotal=4`,权重按真实边界配置;
|
|
|
+- 采购管线业务触发改为 Enqueue;
|
|
|
+- `S4MdpSyncTransformJob` 和 `SmartOpsKpiMdpBootstrapJob` 的 S4 分支改为按启用 scope Enqueue,能力开关默认关闭并在 S4 验收后开启;
|
|
|
+- 不新增虚假 Atomic 阶段;
|
|
|
+- S4 对共享 S3 数据的依赖必须带 tenant/factory JOIN。
|
|
|
+
|
|
|
+### 完成标准
|
|
|
+
|
|
|
+- S3/S4 各自通过 A/B 隔离;
|
|
|
+- S4 pipeline 触发可查到持久 Job;
|
|
|
+- S3/S4 某一失败不阻塞另一模块或另一租户;
|
|
|
+- 旧同步 HTTP 不再长时间占用。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP4:S5~S7 迁移
|
|
|
+
|
|
|
+### 原则
|
|
|
+
|
|
|
+复用通用任务壳,不重写 T8 业务算法。T8 入站和模块 KPI 分层上报。
|
|
|
+
|
|
|
+### 步骤
|
|
|
+
|
|
|
+1. 将 `AidopT8KpiManualRefreshService` 拆出可由 Handler 调用的编排能力;
|
|
|
+2. 为 S5/S6/S7 建独立 Handler;
|
|
|
+3. scope 生成 `RefreshOption`,客户端不得直接指定 TargetTenantId;
|
|
|
+4. 验证 `SourceZtid` 映射与认证 tenant 一致;
|
|
|
+5. T8 入站持有共享账套锁,写入按 tenant 分区;
|
|
|
+6. KPI 阶段持有模块 scope 锁;
|
|
|
+7. 每个 KPI 子项完成时更新 `detail_json` 和预计进度;
|
|
|
+8. S5 legacy TVF 超时保留,不扩大;
|
|
|
+9. `S5MdpRefreshJob` 改为逐 scope/module Enqueue;
|
|
|
+10. 共享 inbound 失败时依赖模块明确 FAILED,不继续使用陈旧批次冒充成功;
|
|
|
+11. 验证 S5、S6、S7 之间的锁等待文案和状态。
|
|
|
+
|
|
|
+### 建议阶段权重
|
|
|
+
|
|
|
+| 事件 | 百分比 |
|
|
|
+|---|---:|
|
|
|
+| QUEUED | 0 |
|
|
|
+| ACQUIRING_LOCK | 2 |
|
|
|
+| PREPARING | 5 |
|
|
|
+| T8_INBOUND 开始 | 10 |
|
|
|
+| T8_INBOUND 完成 | 35 |
|
|
|
+| KPI_PREPARING 完成 | 45 |
|
|
|
+| KPI_CALCULATING 按子项真实完成数 | 45~95 |
|
|
|
+| FINALIZING | 99 |
|
|
|
+| SUCCESS | 100 |
|
|
|
+
|
|
|
+只有 KPI 子项总数固定且每项真实完成后,才允许按 `completed/total` 在 45~95 之间计算;不得按时间增长。
|
|
|
+
|
|
|
+### 完成标准
|
|
|
+
|
|
|
+- 手工请求不再等待 T8 长任务;
|
|
|
+- 同 scope/module 409;
|
|
|
+- 共享 inbound 锁冲突表现为等待,不创建重复拉源;
|
|
|
+- A 的映射数据不写入 B;
|
|
|
+- S5 TVF 失败能定位到 KPI 子项;
|
|
|
+- S5~S7 各自任务结果和 per-KPI 摘要可查询。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP5:前端通用化和逐模块启用
|
|
|
+
|
|
|
+### 建议改动
|
|
|
+
|
|
|
+```text
|
|
|
+Web/src/views/aidop/api/kanbanData.ts
|
|
|
+Web/src/views/aidop/api/moduleRebuildWatch.ts
|
|
|
+Web/src/views/aidop/kanban/components/ModuleRebuildProgressCard.vue
|
|
|
+Web/src/views/aidop/kanban/composables/useModuleAsyncRebuild.ts
|
|
|
+Web/src/views/aidop/kanban/config/moduleDashboardRegistry.ts
|
|
|
+Web/src/views/aidop/kanban/components/DynamicModuleDashboard.vue
|
|
|
+```
|
|
|
+
|
|
|
+### 步骤
|
|
|
+
|
|
|
+1. 参数化 create/get/latest/target snapshot API;
|
|
|
+2. 将 `S1RebuildJob` 泛化为 `ModuleRebuildJob`;
|
|
|
+3. 将 watch key 泛化并包含 module;
|
|
|
+4. 将 S1 卡片泛化,保留折叠功能;
|
|
|
+5. 将轮询、恢复、409 接管、toast 去重、reload 去重抽 composable;
|
|
|
+6. 确认弹窗动作列表、阶段名、预计耗时由 registry 配置;
|
|
|
+7. S2~S7 在各自后端验收后才开启:
|
|
|
+
|
|
|
+```text
|
|
|
+quickRefresh
|
|
|
+rebuild
|
|
|
+supportsAsyncRebuild
|
|
|
+supportsAsyncRebuildProgress
|
|
|
+```
|
|
|
+
|
|
|
+8. 移除 S2~S7 将全量 `refreshMdp` 绑定到“刷新”的配置;
|
|
|
+9. S9 暂时保持纯 reload;后端轻量 API 和 registry `quickRefresh` 统一在 WP7 接入;
|
|
|
+10. S8 不接入 registry;
|
|
|
+11. 点击重算自动展开卡片;
|
|
|
+12. 无 watched jobId 不自动展示。
|
|
|
+
|
|
|
+### 完成标准
|
|
|
+
|
|
|
+- S1 用户行为不回归;
|
|
|
+- S2~S7 两按钮语义一致;
|
|
|
+- S9 只有刷新;
|
|
|
+- 202、409、恢复、网络中断、终态停止轮询均有测试;
|
|
|
+- 共享组件没有散落 `if (moduleCode === 'Sx')`;模块差异通过配置/阶段元数据表达。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP6:S1 平滑迁入通用框架
|
|
|
+
|
|
|
+这是最后迁移,不得在通用框架未经 S2~S7 验证前执行。
|
|
|
+
|
|
|
+### 切换前
|
|
|
+
|
|
|
+1. 禁止新建旧 S1 任务;
|
|
|
+2. 等待旧 `ado_s1_dashboard_rebuild_job` 的 QUEUED/RUNNING 全部终态;
|
|
|
+3. 保存任务与锁基线;
|
|
|
+4. 确认旧 `S1_MDP_FULL:*` 无活动 holder;
|
|
|
+5. 备份或保留旧表只读。
|
|
|
+
|
|
|
+### 迁移策略
|
|
|
+
|
|
|
+- 不强制复制全部 S1 历史;
|
|
|
+- 在同一次受控切换中先停止旧 `S1DashboardRebuildWorker` 领取新任务,再将 S1 Enqueue 路由切到 `ModuleRebuildService`,最后开启 `AiDOP:MdpRebuild:Modules:S1:Enabled`;
|
|
|
+- `Startup.cs` 不得同时注册可领取 S1 的旧、新 Worker;若为回滚暂保留旧类型,必须有互斥配置且默认仅一套启用;
|
|
|
+- 新任务写统一表;
|
|
|
+- S1 API 路径保持不变;
|
|
|
+- 查询新 Job 使用统一表;
|
|
|
+- 对本地保存的旧 jobId,S1 查询接口提供只读 fallback;
|
|
|
+- 新 ID 必须避免与旧 S1 ID 冲突;
|
|
|
+- 旧表不得在切换后继续产生新任务;
|
|
|
+- 旧 `ado_s1_mdp_run_lock` 与新 `ado_mdp_rebuild_lock` 在兼容期并存;切换后 S1 只能获取新锁,确认无旧 holder 后旧锁表转只读,禁止两个锁体系同时放行 S1;
|
|
|
+- 前端兼容读取现有 `aidop.s1.rebuild.watch.{tenantId}.{factoryId}`;恢复成功后写入通用 key,兼容期结束前不得直接丢弃旧 watched job;
|
|
|
+- 兼容期结束需另行确认,不在本任务直接 DROP 旧表。
|
|
|
+
|
|
|
+切换验收:
|
|
|
+
|
|
|
+- 旧 Worker 不再 claim;
|
|
|
+- 新 S1 任务只写统一表;
|
|
|
+- 同一 S1 scope 不会被两套 Worker 同时执行;
|
|
|
+- 关闭通用 S1 能力开关并恢复旧注册可作为代码回滚路径,但回滚前必须先收口统一表中的活动 S1 任务。
|
|
|
+
|
|
|
+### 回归
|
|
|
+
|
|
|
+- 现有 `S1DashboardRebuildServiceTests` 全部迁移/兼容;
|
|
|
+- S1 五阶段、折叠卡、409、localStorage 恢复不变;
|
|
|
+- AUTO/BOOTSTRAP/订单评审/LinkagePlan 全调用点复核;
|
|
|
+- S1 A/B 隔离重新执行;
|
|
|
+- 不改变 S1 指标、目标快照或 Atomic 口径。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP7:S9 轻量刷新
|
|
|
+
|
|
|
+1. registry 给 S9 注册通用 target snapshot refresh;
|
|
|
+2. 后端允许 `moduleCode=S9` 的轻量刷新;
|
|
|
+3. 有 S9 日值时只更新最新业务日目标字段;
|
|
|
+4. 无 S9 日值时返回 updatedRows=0;
|
|
|
+5. 前端成功后 reload;
|
|
|
+6. 不显示重算按钮;
|
|
|
+7. 后端拒绝 S9 rebuild job;
|
|
|
+8. 验证点击刷新没有新增 S1~S7 任务或 transform log。
|
|
|
+
|
|
|
+完成标准:
|
|
|
+
|
|
|
+- 5 秒内完成;
|
|
|
+- 不触发任何完整 MDP;
|
|
|
+- 不级联 S1~S7;
|
|
|
+- S9 页面无进度卡。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP8:AUTO、BOOTSTRAP、旧入口与业务触发收口
|
|
|
+
|
|
|
+### 必查
|
|
|
+
|
|
|
+```text
|
|
|
+Job/S1MdpSyncTransformJob.cs
|
|
|
+Job/S2MdpSyncTransformJob.cs
|
|
|
+Job/S3MdpSyncTransformJob.cs
|
|
|
+Job/S4MdpSyncTransformJob.cs
|
|
|
+Job/S5MdpRefreshJob.cs
|
|
|
+Job/SmartOpsKpiMdpBootstrapJob.cs
|
|
|
+DataPlatform/AidopT8KpiManualRefreshService.cs
|
|
|
+Order/OrderReviewOrchestrationService.cs
|
|
|
+Order/LinkagePlanService.cs
|
|
|
+Supply/ProcurementPipelineService.cs
|
|
|
+Production/ProductionSchedulingActionService.cs
|
|
|
+Infrastructure/MdpRefreshDispatcher.cs
|
|
|
+Controllers/AidopKanbanController.cs
|
|
|
+```
|
|
|
+
|
|
|
+### 要求
|
|
|
+
|
|
|
+- AUTO/BOOTSTRAP 查询启用 scope 后逐一 Enqueue;
|
|
|
+- 不能用 tenantId=0 跑全租户 SQL;
|
|
|
+- 某 scope 失败不阻止其它 scope 入队;
|
|
|
+- 业务触发不得绕过统一锁和任务事实源;
|
|
|
+- triggerType 保留 MANUAL/AUTO/BOOTSTRAP/业务来源;
|
|
|
+- `MdpRefreshDispatcher` 不再用于完整 RunFull;`ProductionSchedulingActionService` 使用它调度 S2 排程动作而非完整 MDP,不得因此误删或误改该轻量调度;
|
|
|
+- `AidopT8KpiManualRefreshService` 不再从看板 HTTP 路径同步执行完整 S5~S7 RunFull;
|
|
|
+- legacy API 全部鉴权;
|
|
|
+- 多实例只能由 DB claim 保证唯一执行。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## WP9:迁移、测试、UAT 与交付
|
|
|
+
|
|
|
+### 9.1 数据库迁移
|
|
|
+
|
|
|
+- 使用执行时实际下一个后端 patch;
|
|
|
+- `.sql` + `.verify.sql`;
|
|
|
+- 在 `Admin.NET.Web.Entry.csproj` 登记 CopyToOutput;
|
|
|
+- 幂等创建统一 Job/Lock 表和索引;
|
|
|
+- 不修改已成功执行脚本;
|
|
|
+- 迁移只在确认旧 S1 在途任务策略后处理兼容标记;
|
|
|
+- `sys_db_migration_log` 必须为 Success 且 verify Success;
|
|
|
+- 不修改 165。
|
|
|
+
|
|
|
+### 9.2 后端测试
|
|
|
+
|
|
|
+通用框架至少覆盖:
|
|
|
+
|
|
|
+1. 初始 QUEUED/0;
|
|
|
+2. 202/409;
|
|
|
+3. 同 scope 20 并发只有一个活动任务;
|
|
|
+4. 不同 tenant/factory 可分别活动;
|
|
|
+5. module 维度隔离;
|
|
|
+6. jobId/latest 租户隔离;
|
|
|
+7. 阶段单调;
|
|
|
+8. SUCCESS=100;
|
|
|
+9. FAILED 保留阶段和进度;
|
|
|
+10. 心跳启动/停止;
|
|
|
+11. stale 收口;
|
|
|
+12. 锁冲突重新排队;
|
|
|
+13. 服务停止;
|
|
|
+14. 未注册/S9 Handler 被拒绝;
|
|
|
+15. AUTO/BOOTSTRAP 逐 scope 入队。
|
|
|
+
|
|
|
+每个 S2~S7 至少覆盖:
|
|
|
+
|
|
|
+- A 重算不改变 B;
|
|
|
+- fullRefresh 不清 B;
|
|
|
+- 相同业务键不跨租户 JOIN;
|
|
|
+- KPI Upsert 只写当前 scope;
|
|
|
+- Atomic 模块只删除当前 scope;
|
|
|
+- 真实阶段和行数;
|
|
|
+- 阶段异常定位;
|
|
|
+- 旧入口不再同步长跑。
|
|
|
+
|
|
|
+### 9.3 前端测试
|
|
|
+
|
|
|
+至少覆盖:
|
|
|
+
|
|
|
+1. S1~S7 刷新只调用轻量 API;
|
|
|
+2. S1~S7 重算调用异步 API;
|
|
|
+3. S9 无重算按钮;
|
|
|
+4. 取消确认不请求;
|
|
|
+5. 202 展示并自动展开;
|
|
|
+6. 409 接管;
|
|
|
+7. 后端百分比原样展示;
|
|
|
+8. 前端不自行增长;
|
|
|
+9. 折叠/展开;
|
|
|
+10. watched job 恢复;
|
|
|
+11. 无 watched job 不自动展示;
|
|
|
+12. tenant/factory/module 切换隔离;
|
|
|
+13. 终态停止轮询;
|
|
|
+14. 成功只 reload 一次;
|
|
|
+15. S8 无回归。
|
|
|
+
|
|
|
+### 9.4 构建
|
|
|
+
|
|
|
+```text
|
|
|
+dotnet test server/Plugins/Admin.NET.Plugin.AiDOP.Tests/Admin.NET.Plugin.AiDOP.Tests.csproj -f net10.0
|
|
|
+dotnet test server/Plugins/Admin.NET.Plugin.AiDOP.Tests/Admin.NET.Plugin.AiDOP.Tests.csproj -f net8.0
|
|
|
+dotnet build server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj -c Debug -f net10.0
|
|
|
+pnpm --dir Web build
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. UAT 场景
|
|
|
+
|
|
|
+每个模块 S2~S7 都必须执行 UAT-1~UAT-8;S1 在 WP6 回归;S9 执行 UAT-9。
|
|
|
+
|
|
|
+### UAT-1 轻量刷新
|
|
|
+
|
|
|
+- 记录最新业务日 metric/target;
|
|
|
+- 修改有效目标;
|
|
|
+- 点击刷新;
|
|
|
+- 5 秒内目标变化;
|
|
|
+- metric 不变;
|
|
|
+- 历史日期不变;
|
|
|
+- 无新增完整 transform run。
|
|
|
+
|
|
|
+### UAT-2 确认与入队
|
|
|
+
|
|
|
+- 取消不创建任务;
|
|
|
+- 确认后 2 秒内返回 202;
|
|
|
+- 卡片自动展开;
|
|
|
+- 显示 QUEUED 和后台执行提示。
|
|
|
+
|
|
|
+### UAT-3 真实进度
|
|
|
+
|
|
|
+- 阶段顺序与实际调用一致;
|
|
|
+- 不出现不存在的阶段;
|
|
|
+- 百分比不倒退;
|
|
|
+- RUNNING 不为 100;
|
|
|
+- SUCCESS 为 100;
|
|
|
+- 行数只属于当前 scope。
|
|
|
+
|
|
|
+### UAT-4 页面恢复
|
|
|
+
|
|
|
+- 发起浏览器关闭页面后任务继续;
|
|
|
+- 重进恢复同 jobId;
|
|
|
+- 不创建新任务;
|
|
|
+- 成功只自动 reload 一次。
|
|
|
+
|
|
|
+### UAT-5 同租户主动接管
|
|
|
+
|
|
|
+- 浏览器 1 发起;
|
|
|
+- 浏览器 2 只进入页面时不显示卡片;
|
|
|
+- 浏览器 2 主动点击后收到 409;
|
|
|
+- 两端显示同一 jobId;
|
|
|
+- 数据库只有一个活动任务。
|
|
|
+
|
|
|
+### UAT-6 跨租户独立
|
|
|
+
|
|
|
+- A 运行时 B 发起;
|
|
|
+- B 返回 202,不返回 A 的 409/jobId;
|
|
|
+- A/B 可分别 RUNNING;
|
|
|
+- 双方不能查询对方 jobId;
|
|
|
+- 锁键、batchId 不同。
|
|
|
+
|
|
|
+### UAT-7 A 不改变 B
|
|
|
+
|
|
|
+执行前后比较 B 的:
|
|
|
+
|
|
|
+- stg/std/dwd;
|
|
|
+- L1~L4 KPI;
|
|
|
+- Atomic(适用模块);
|
|
|
+- 行数、最大 update_time、batchId、checksum。
|
|
|
+
|
|
|
+所有值必须不变。
|
|
|
+
|
|
|
+### UAT-8 失败、心跳与重启
|
|
|
+
|
|
|
+- 模拟阶段失败,failedStage 正确;
|
|
|
+- 长阶段 heartbeat_age 通常 <90 秒;
|
|
|
+- 停止后端后任务不永久 RUNNING;
|
|
|
+- 重启后 stale 收口;
|
|
|
+- 可重新创建任务。
|
|
|
+
|
|
|
+### UAT-9 S9
|
|
|
+
|
|
|
+- 点击刷新后 5 秒内完成;
|
|
|
+- 页面 reload;
|
|
|
+- 无 rebuild 请求;
|
|
|
+- 无新 Job;
|
|
|
+- 无 S1~S7 transform run;
|
|
|
+- 页面无进度卡。
|
|
|
+
|
|
|
+留证放入:
|
|
|
+
|
|
|
+```text
|
|
|
+doc/plan/UAT留证/<日期>-S2-S7看板刷新重算统一架构/
|
|
|
+```
|
|
|
+
|
|
|
+至少包含 README、API JSON、SQL 输出、日志和截图。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. 验收 SQL
|
|
|
+
|
|
|
+### 9.1 活动任务唯一性
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT module_code, tenant_id, factory_id, COUNT(*) AS active_count
|
|
|
+FROM ado_module_dashboard_rebuild_job
|
|
|
+WHERE status IN ('QUEUED','RUNNING')
|
|
|
+GROUP BY module_code, tenant_id, factory_id
|
|
|
+HAVING COUNT(*) > 1;
|
|
|
+```
|
|
|
+
|
|
|
+期望 0 行。
|
|
|
+
|
|
|
+### 9.2 非法状态
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT id, module_code, status, current_stage,
|
|
|
+ stage_index, stage_total, progress_percent
|
|
|
+FROM ado_module_dashboard_rebuild_job
|
|
|
+WHERE progress_percent < 0
|
|
|
+ OR progress_percent > 100
|
|
|
+ OR stage_index < 0
|
|
|
+ OR stage_index > stage_total
|
|
|
+ OR (status='SUCCESS' AND progress_percent<>100)
|
|
|
+ OR (status='RUNNING' AND progress_percent=100);
|
|
|
+```
|
|
|
+
|
|
|
+期望 0 行。
|
|
|
+
|
|
|
+### 9.3 心跳
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT id, module_code, tenant_id, factory_id, current_stage,
|
|
|
+ TIMESTAMPDIFF(SECOND, heartbeat_at, NOW(3)) AS heartbeat_age_seconds
|
|
|
+FROM ado_module_dashboard_rebuild_job
|
|
|
+WHERE status='RUNNING';
|
|
|
+```
|
|
|
+
|
|
|
+正常任务通常小于 90 秒。
|
|
|
+
|
|
|
+### 9.4 锁与任务
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT lock_name, module_code, tenant_id, factory_id,
|
|
|
+ holder_id, job_id, status, acquired_at, heartbeat_at
|
|
|
+FROM ado_mdp_rebuild_lock
|
|
|
+ORDER BY module_code, tenant_id, factory_id;
|
|
|
+```
|
|
|
+
|
|
|
+锁键和 Job scope 必须一致。
|
|
|
+
|
|
|
+### 9.5 跨租户 KPI 校验
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT module_code, tenant_id, factory_id,
|
|
|
+ COUNT(*) AS row_count,
|
|
|
+ MAX(update_time) AS max_update_time,
|
|
|
+ BIT_XOR(CAST(CRC32(CONCAT_WS('|',
|
|
|
+ module_code, tenant_id, factory_id, metric_code, biz_date,
|
|
|
+ IFNULL(metric_value,''), IFNULL(target_value,''),
|
|
|
+ IFNULL(calc_batch_id,''))) AS UNSIGNED)) AS checksum_value
|
|
|
+FROM ado_s9_kpi_value_l1_day
|
|
|
+WHERE module_code=@ModuleCode
|
|
|
+ AND tenant_id IN (@TenantA,@TenantB)
|
|
|
+GROUP BY module_code, tenant_id, factory_id;
|
|
|
+```
|
|
|
+
|
|
|
+执行 A 重算后,B 的结果必须完全不变。执行者还需对本模块 stg/std/dwd/Atomic 选择稳定列做同类校验。
|
|
|
+
|
|
|
+### 9.6 S9 不触发重算
|
|
|
+
|
|
|
+点击 S9 刷新前后:
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT COUNT(*) AS c
|
|
|
+FROM ado_module_dashboard_rebuild_job
|
|
|
+WHERE submitted_at >= @BeforeRefresh;
|
|
|
+```
|
|
|
+
|
|
|
+不得因 S9 增加记录。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 10. 性能与可靠性门槛
|
|
|
+
|
|
|
+- 轻量刷新 P95 ≤ 2 秒,硬上限 5 秒;
|
|
|
+- 创建任务 P95 ≤ 1 秒,硬上限 2 秒;
|
|
|
+- 查询任务 P95 ≤ 500ms;
|
|
|
+- 进度单次写入目标 ≤ 200ms;
|
|
|
+- 前台轮询 5 秒,隐藏页面 15 秒;
|
|
|
+- Job 心跳 30 秒,正常年龄不超过 90 秒;
|
|
|
+- 不逐秒写 DB;
|
|
|
+- 不因进度写失败中断核心 MDP;
|
|
|
+- 不通过跳过业务阶段满足耗时;
|
|
|
+- Worker 并发必须受连接池和源端容量约束;
|
|
|
+- S5~S7 T8 慢查询不得无限持锁。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 11. 预计文件范围
|
|
|
+
|
|
|
+### 通用后端
|
|
|
+
|
|
|
+```text
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/DataPlatform/MdpRebuild/*
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/Entity/SmartOps/AdoModuleDashboardRebuildJob.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/Entity/SmartOps/AdoMdpRebuildLock.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/AidopTenantScope.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/AidopKanbanController*.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/SmartOps/S1KpiTargetSnapshotRefreshService.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/SmartOps/SmartOpsKpiAtomicBuildService.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP/Startup.cs
|
|
|
+server/Plugins/Admin.NET.Plugin.AiDOP.Tests/DataPlatform/*
|
|
|
+server/Admin.NET.Web.Entry/UpdateScripts/<actual-next-version>.sql
|
|
|
+server/Admin.NET.Web.Entry/UpdateScripts/<actual-next-version>.verify.sql
|
|
|
+server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj
|
|
|
+server/Admin.NET.Application/Configuration/App.json
|
|
|
+```
|
|
|
+
|
|
|
+### 模块后端
|
|
|
+
|
|
|
+```text
|
|
|
+Production/S2MdpSyncTransformService.cs
|
|
|
+Supply/S3MdpSyncTransformService.cs
|
|
|
+ProcurementExecution/S4MdpSyncTransformService.cs
|
|
|
+MaterialWarehouse/S5MdpSyncTransformService.cs
|
|
|
+Manufacturing/S6MdpSyncTransformService.cs
|
|
|
+FinishedWarehouse/S7MdpSyncTransformService.cs
|
|
|
+DataPlatform/T8BaseInboundMdpSyncService.cs
|
|
|
+DataPlatform/AidopT8KpiManualRefreshService.cs
|
|
|
+Job/S1MdpSyncTransformJob.cs
|
|
|
+Job/S2MdpSyncTransformJob.cs
|
|
|
+Job/S3MdpSyncTransformJob.cs
|
|
|
+Job/S4MdpSyncTransformJob.cs
|
|
|
+Job/S5MdpRefreshJob.cs
|
|
|
+Job/SmartOpsKpiMdpBootstrapJob.cs
|
|
|
+```
|
|
|
+
|
|
|
+### 前端
|
|
|
+
|
|
|
+```text
|
|
|
+Web/src/views/aidop/api/kanbanData.ts
|
|
|
+Web/src/views/aidop/api/s1RebuildWatch.ts
|
|
|
+Web/src/views/aidop/api/moduleRebuildWatch.ts
|
|
|
+Web/src/views/aidop/kanban/components/S1RebuildProgressCard.vue
|
|
|
+Web/src/views/aidop/kanban/components/ModuleRebuildProgressCard.vue
|
|
|
+Web/src/views/aidop/kanban/components/DynamicModuleDashboard.vue
|
|
|
+Web/src/views/aidop/kanban/composables/useModuleAsyncRebuild.ts
|
|
|
+Web/src/views/aidop/kanban/config/moduleDashboardRegistry.ts
|
|
|
+Web/package.json
|
|
|
+```
|
|
|
+
|
|
|
+执行模型必须以全仓检索补齐调用点;本清单不是跳过审计的理由。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 12. 跨模块影响
|
|
|
+
|
|
|
+- S1:最终迁入通用框架,必须完整回归;
|
|
|
+- S2~S4:完整 MDP 从同步全局处理改为 scope 异步任务;
|
|
|
+- S5~S7:共享 T8 入站、账套映射和全局锁会受影响;
|
|
|
+- S9:仅轻量刷新;
|
|
|
+- S8:禁止修改;
|
|
|
+- 数据中台:Pull、Writer、游标、日志和 fullRefresh 语义需回归;
|
|
|
+- SmartOps:Atomic 和 KPI target snapshot 泛化;
|
|
|
+- 调度:AUTO/BOOTSTRAP 改为逐 scope 入队;
|
|
|
+- 业务链:订单评审、联动计划、采购管线触发需收口;
|
|
|
+- 部署:新增统一任务/锁表和 hosted worker;
|
|
|
+- 多实例:必须依赖 DB claim/lease,不依赖单进程内存锁。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 13. 明确不做
|
|
|
+
|
|
|
+- 不新增或重排菜单/FUNC;
|
|
|
+- 不改 `sys_menu.Title`;
|
|
|
+- 不修改 S8;
|
|
|
+- 不给 S9 增加重算或级联重算;
|
|
|
+- 不优化各模块业务算法总耗时;
|
|
|
+- 不修改 KPI 公式和日期口径;
|
|
|
+- 不修改 165 数据库对象或存储过程;
|
|
|
+- 不回写历史目标;
|
|
|
+- 不新增 SSE/WebSocket;
|
|
|
+- 不实现用户主动取消运行中任务;
|
|
|
+- 不删除旧 S1 表;
|
|
|
+- 不强制改造 S5 outsource、IQC、FQC、生产领退料等独立卫星 refresh;本任务只覆盖看板工具栏对应的 S5~S7 主重算链路;
|
|
|
+- 不顺手重构无关页面、警告或模块;
|
|
|
+- 不用虚假时间进度;
|
|
|
+- 不提交 secret、日志或构建产物。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 14. 停止条件
|
|
|
+
|
|
|
+遇到以下任一项必须停止并报告:
|
|
|
+
|
|
|
+1. 源表无法可信识别 tenant/factory/SourceZtid;
|
|
|
+2. fullRefresh 无法限定 scope;
|
|
|
+3. 唯一键无法隔离租户;
|
|
|
+4. 任一 DELETE/Atomic 只能全局执行;
|
|
|
+5. 相同业务键会跨租户 JOIN;
|
|
|
+6. S5~S7 账套映射不是可信一对一且无明确产品规则;
|
|
|
+7. S9 被要求编排 S1~S7 重算;
|
|
|
+8. 通用 Handler 迫使不同模块伪造阶段;
|
|
|
+9. 多实例 claim 无法保证唯一;
|
|
|
+10. Worker 并发会耗尽连接池或压垮源端;
|
|
|
+11. 当前迁移版本已占用;
|
|
|
+12. 旧 S1 仍有无法安全收口的活动任务;
|
|
|
+13. 需要修改 165;
|
|
|
+14. 工作区存在未识别的同文件并行改动;
|
|
|
+15. 必须改变指标公式才能实现;
|
|
|
+16. 只能靠前端计时伪造进度。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 15. 回滚策略
|
|
|
+
|
|
|
+### 分波回滚
|
|
|
+
|
|
|
+- 每个模块由能力开关控制,失败时关闭该模块通用 rebuild;
|
|
|
+- 前端恢复该模块旧配置前,确认不会重新暴露同步长请求;必要时只保留轻量刷新;
|
|
|
+- 通用 Worker 可停止领取特定 module,不影响其它模块;
|
|
|
+- 已运行任务先收口,禁止直接删除活动 Job。
|
|
|
+
|
|
|
+### 数据库
|
|
|
+
|
|
|
+- 新表保留,不在紧急回滚中 DROP;
|
|
|
+- 已 Success 的迁移不修改、不伪造降版;
|
|
|
+- 代码回滚使用 Git revert;
|
|
|
+- 旧 S1 表在兼容期只读保留;
|
|
|
+- 数据批次回滚必须按 tenant + factory + module + batch 条件,执行前备份;
|
|
|
+- 已写入 165 的外部业务动作不适用自动回滚,本任务原则上不触及。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 16. 版本、迁移和 Git
|
|
|
+
|
|
|
+执行代码工作包时:
|
|
|
+
|
|
|
+- 改前端运行代码:`Web/package.json` patch +1,并同步实际锁文件;
|
|
|
+- 改后端运行代码:`.csproj` 的 Version/AssemblyVersion/FileVersion 三处 patch +1;
|
|
|
+- 同一提交同时改前后端:两端分别 +1;
|
|
|
+- 迁移号与该次后端版本一致;
|
|
|
+- 每波若独立提交/部署,应独立取号,禁止所有 WP 共用一个预估版本;
|
|
|
+- 本任务书自身是纯文档,不递增版本;
|
|
|
+- 用户说“提交”才执行 commit + push。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 17. 交付报告模板
|
|
|
+
|
|
|
+```text
|
|
|
+执行波次:
|
|
|
+- WP:WP0–WP9 一次落地
|
|
|
+- 模块:S1–S7 异步重算 + S9 轻量刷新;S8 未改
|
|
|
+
|
|
|
+完成:
|
|
|
+- 通用 Job/Lock/Worker/Handler
|
|
|
+- S2–S4 强制 scope,无 scope RunFull 抛错
|
|
|
+- S5–S7 经 T8 模块刷新入队
|
|
|
+- 前端 registry 双按钮 + 通用进度卡
|
|
|
+- S1 能力开关打开时走统一表
|
|
|
+- 旧 HTTP / AUTO / BOOTSTRAP 收口为 Enqueue
|
|
|
+- 1.0.347 表已在 aidopdev 创建
|
|
|
+
|
|
|
+租户隔离:
|
|
|
+- Tenant A / Factory:1300000000001 / 1(L1 checksum);运行账号 JWT 租户 797403760988229
|
|
|
+- Tenant B / Factory:1300000000888 / 1;隔离账号 797403760988230、824585161322565
|
|
|
+- A 对 B 的 L1 checksum:UAT-7 后 B 的 S2/S3/S4 仍为 1934982272 / 212701925 / 2010673860
|
|
|
+- 同业务键串租验证:MdpSqlScope.InjectTenantFactory 单测;无 scope 入口已禁用
|
|
|
+
|
|
|
+任务:
|
|
|
+- jobId:181/182 SUCCESS(跨租户 S2);183/184/201 FAILED STAGING(A 租户源数据)
|
|
|
+- triggerType:MANUAL/AUTO/BOOTSTRAP 均已入队
|
|
|
+- 阶段流转:SUCCESS=100;FAILED 保留 failedStage
|
|
|
+- 总耗时:181=2310ms,182=2209ms,183=30247ms,201=90248ms
|
|
|
+- 202/409:同 scope 第二请求 409 同一 jobId;跨租户互不阻塞
|
|
|
+- 浏览器接管:第二浏览器静默进入无卡,点击后 409 挂 job 201
|
|
|
+
|
|
|
+刷新:
|
|
|
+- S1 updatedRows=43 / 4785ms;S2=13 / 1396ms;S3=7 / 915ms;S4=9 / 1017ms;S5–S9 200
|
|
|
+- S9 rebuild HTTP 404,页面无重算按钮、无进度卡
|
|
|
+
|
|
|
+测试:
|
|
|
+- net8:506 passed / 2 skipped
|
|
|
+- net10:506 passed / 2 skipped
|
|
|
+- Web build:成功
|
|
|
+- 前端本次 vitest:14 passed
|
|
|
+- UAT:API + SQL + 浏览器截图已写入留证目录
|
|
|
+
|
|
|
+迁移:
|
|
|
+- 版本:1.0.347
|
|
|
+- sys_db_migration_log:Success / verify Success
|
|
|
+- verify:COUNT(DISTINCT INDEX_NAME)
|
|
|
+
|
|
|
+回滚/能力开关:
|
|
|
+- AiDOP:MdpRebuild.Modules.S1–S7.Enabled=true;MaxParallelScopes=1
|
|
|
+
|
|
|
+未完成/风险:
|
|
|
+- A 租户 797403760988229 的 S2 STAGING 失败来自源/165 数据,未改 165
|
|
|
+- 既有 S8 vitest 6 条失败与本任务无关
|
|
|
+
|
|
|
+Git:
|
|
|
+- 未提交(用户未说「提交」)
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 18. 可直接复制给执行模型的总提示词
|
|
|
+
|
|
|
+```text
|
|
|
+请严格执行:
|
|
|
+doc/plan/S2-S7及S9看板刷新与重算统一架构执行任务书_20260815.md
|
|
|
+
|
|
|
+要求:
|
|
|
+1. 先阅读 AGENTS.md、.cursor/rules、两份 S1 前置任务书和本任务书。
|
|
|
+2. 先做 WP0,只读审计全部 RunFull、SQL、锁、AUTO/BOOTSTRAP 和业务触发调用点。
|
|
|
+3. 每个 WP 动手前列目标、文件、可见行为、跨模块影响、风险和明确不做项,等我确认。
|
|
|
+4. 采用“统一目标架构 + 分波迁移”,禁止一次性替换 S1~S7。
|
|
|
+5. 顺序必须是 WP0→WP1→WP2(S2)→WP3(S3/S4)→WP4(S5~S7)→WP5(前端)→WP6(S1)→WP7(S9)→WP8→WP9。
|
|
|
+6. WP2 S2 隔离和 UAT 未通过,不得复制到 S3~S7;S1 最后迁移。
|
|
|
+7. 所有完整重算必须有 moduleCode+tenantId+factoryId,禁止 tenantId=0。
|
|
|
+8. 每条 STG/STD/DWD/KPI/Atomic SQL、JOIN、DELETE、UPSERT 都要证明不会跨租户。
|
|
|
+9. S5~S7 必须正视共享 T8 入站和 SourceZtid 映射,不得用全局串行冒充隔离。
|
|
|
+10. 任务 DB 是事实源;同 scope 409,不同租户可受控并行;查询 jobId 必须鉴权隔离。
|
|
|
+11. 进度来自真实阶段;禁止前端计时增长。
|
|
|
+12. 无本地 watched jobId 不自动展示;其他浏览器仅主动点击收到 409 后接管。
|
|
|
+13. S1~S7:刷新=轻量目标快照,重算=异步 full rebuild。
|
|
|
+14. S9 仅轻量刷新,不提供重算或进度卡;S8 不修改。
|
|
|
+15. 不改 KPI 公式、不改 165、不加 SSE/取消、不新增菜单/FUNC。
|
|
|
+16. 每波完成单测、构建、迁移 Success、A/B UAT 和留证后再继续。
|
|
|
+17. 使用执行时实际版本号;未经我明确说“提交”,不得 commit/push。
|
|
|
+```
|