Переглянути джерело

feat(s8): close golden runtime, department lookup and order execution | Web 2.4.335 / server 1.0.414

- exception first view: require stage_code for the classified default list (CC-1) and
  unify the unresolved-FK fallback label across list/detail/dashboard (CC-2)
- department lookup: resolve DepartmentMaster by the request trusted factoryId instead of
  the global S8MasterData.DepartmentFactoryRefId, which pointed at another tenant's factory
  and left every Golden row unresolved (B-1)
- auto watch/hit creation: apply the same rule with a tenant-scoped config override, so a
  cross-tenant setting can no longer reject all valid departments and throw (B-2)
- monitoring KPI: compute ORDER_DELIVERY_RATE / PO_DELIVERY_RATE / WO_COMPLETION_RATE from
  the order flow tables; keep IQC_PASS_RATE as DEMO_BASELINE with explicit provenance, and
  keep targets sourced only from ado_s8_monitor_metric
- order execution archive: count exceptions by related_object_code rather than
  source_object_type (F-1), label started-but-open stages as in progress (F-2), and route the
  workbench status label through computeNodeStatus so it matches the stage status row (D-3)
- add WIP golden dataset and exception lineage SQL under placeholder names; they carry no
  version number and no csproj copy entry, so the migration runner never picks them up
YY968XX 12 годин тому
батько
коміт
ac4bb23882

+ 1 - 1
Web/package.json

@@ -1,7 +1,7 @@
 {
 	"name": "admin.net",
 	"type": "module",
-	"version": "2.4.334",
+	"version": "2.4.335",
 	"packageManager": "pnpm@10.32.1",
 	"lastBuildTime": "2026.03.15",
 	"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",

+ 4 - 2
Web/src/views/aidop/s8/dashboard/S8DashboardPage.vue

@@ -288,13 +288,15 @@ async function renderMainChart() {
 		barColor = '#6366f1';
 	} else if (panel === 'occurrence') {
 		const src = distributions.value?.byOccurrenceDept ?? [];
-		categories = src.map(x => x.deptName || String(x.key));
+		// S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-2):无部门名时不再回显裸 key(内部 ID)。
+		categories = src.map(x => x.deptName || '历史数据 / 未配置');
 		values     = src.map(x => x.count);
 		title = '发生部门异常数';
 		barColor = '#f59e0b';
 	} else {
 		const src = deptBacklog.value;
-		categories = src.map(x => x.deptName || String(x.deptId));
+		// S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-2):无部门名时不再回显裸 deptId。
+		categories = src.map(x => x.deptName || '历史数据 / 未配置');
 		values     = src.map(x => x.pending);
 		title = '处理部门待处理积压';
 		barColor = '#ef4444';

+ 4 - 2
Web/src/views/aidop/s8/exceptions/S8ExceptionListPage.vue

@@ -310,10 +310,12 @@ function orderFlowLabel(code: string | null | undefined): string {
 	return ORDER_FLOW_OPTIONS.find((o) => o.value === code)?.label || code;
 }
 
-// S8-DEPT-DISPLAY-CONSISTENCY-1(P0-A-2):部门展示 fallback —— 名→部门ID:{id}→未归属。后端已按 factory_ref_id 水合。
+// S8-DEPT-DISPLAY-CONSISTENCY-1(P0-A-2):部门展示 fallback。后端已按 factory_ref_id 水合。
+// S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-2):不再由前端拼「部门ID:{id}」——
+// 后端解析不到时已返回「历史数据 / 未配置」,此处只在后端未给名时兜同一文案,绝不暴露裸内部 ID。
 function deptDisplay(name?: string | null, id?: number | null) {
 	if (name && name.trim()) return name;
-	if (id && id > 0) return `部门ID:${id}`;
+	if (id && id > 0) return '历史数据 / 未配置';
 	return '未归属';
 }
 

+ 17 - 4
Web/src/views/aidop/s8/exceptions/S8TaskDetailPage.vue

@@ -257,12 +257,24 @@ function ruleTypeLabel(t: string | null | undefined) {
 
 // BUG-3:部门字段兜底。优先 deptName;deptName 为空时若 deptId 为 0/"0"/null/undefined 显示"未分派",
 // 否则显示 deptId(保留有意义的 id 引用)。
+// S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-2):原实现在无名时直接回显 deptId 数字(裸内部 ID)。
+// 改为与列表页/后端一致的业务可读文案。
 function formatDept(deptName: string | null | undefined, deptId: number | string | null | undefined) {
 	if (deptName) return deptName;
 	if (deptId === null || deptId === undefined) return '未分派';
 	const idStr = String(deptId).trim();
 	if (idStr === '' || idStr === '0') return '未分派';
-	return idStr;
+	return '历史数据 / 未配置';
+}
+
+// S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-2):人员展示 fallback。
+// 名可解析→显示名;无 id→「—」;有 id 但名解析不到(EmployeeMaster FK 悬空)→业务可读文案,不暴露裸 RecID。
+function formatPerson(name: string | null | undefined, id: number | string | null | undefined) {
+	if (name && String(name).trim()) return name;
+	if (id === null || id === undefined) return '—';
+	const idStr = String(id).trim();
+	if (idStr === '' || idStr === '0') return '—';
+	return '历史数据 / 未配置';
 }
 
 onMounted(async () => {
@@ -297,10 +309,11 @@ onMounted(async () => {
 						<!-- BUG-3:部门 0/"0"/null/undefined 都视为未分派,不显示裸 0 -->
 						<el-descriptions-item label="发生部门">{{ formatDept(detail.occurrenceDeptName, detail.occurrenceDeptId) }}</el-descriptions-item>
 						<el-descriptions-item label="责任部门">{{ formatDept(detail.responsibleDeptName, detail.responsibleDeptId) }}</el-descriptions-item>
-						<el-descriptions-item label="处理人">{{ detail.assigneeName || detail.assigneeId || '—' }}</el-descriptions-item>
-						<el-descriptions-item label="检验人">{{ detail.verifierName || detail.verifierId || '—' }}</el-descriptions-item>
+						<!-- S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-2):人员名解析不到时不再回显裸 EmployeeMaster.RecID -->
+						<el-descriptions-item label="处理人">{{ formatPerson(detail.assigneeName, detail.assigneeId) }}</el-descriptions-item>
+						<el-descriptions-item label="检验人">{{ formatPerson(detail.verifierName, detail.verifierId) }}</el-descriptions-item>
 						<el-descriptions-item label="检验结果">{{ detail.verificationResult || '—' }}</el-descriptions-item>
-						<el-descriptions-item label="提报人">{{ detail.reporterName || detail.reporterId || '—' }}</el-descriptions-item>
+						<el-descriptions-item label="提报人">{{ formatPerson(detail.reporterName, detail.reporterId) }}</el-descriptions-item>
 						<el-descriptions-item>
 							<template #label>
 								<S8FieldTooltip field-key="priority_level" fallback-label="优先级" />

+ 8 - 2
Web/src/views/aidop/s8/monitoring/components/order-execution/OrderExecutionOrderList.vue

@@ -12,8 +12,9 @@ import {
 	DEMO_TARGET_SO_NO,
 	DEMO_TOP_TITLE,
 } from '/@/views/aidop/s8/monitoring/data/order-execution/demo-step-config';
+// S8-STEP5A-D3-WORKBENCH-STATUS-FIX-1:STAGE_STATUS_LABEL 原为工作台状态标签唯一消费点,
+// 该处已改走 computeNodeStatus,本文件不再直接索引标签表(避免再次绕过状态派生)。
 import {
-	STAGE_STATUS_LABEL,
 	computeNodeStatus,
 	computeOrderStatus,
 	type OrderExecutionStatusTone,
@@ -439,7 +440,12 @@ const rows = computed(() =>
 													:class="`oe-detail-matrix-table__status--${workbenchStage(row.so, row.raw)!.status}`"
 												>
 													<span class="oe-detail-matrix-table__status-dot" />
-													{{ STAGE_STATUS_LABEL[workbenchStage(row.so, row.raw)!.status] }}
+													<!-- S8-STEP5A-D3-WORKBENCH-STATUS-FIX-1:改走 computeNodeStatus 取 label,
+													     与「节点状态」行同源。原写法直接索引 STAGE_STATUS_LABEL[status],
+													     把已开工(actual_start_at 非空、actual_end_at 为空)的 pending 阶段
+													     标成「未到达」,与同页节点状态行的「进行中」自相矛盾。
+													     tone 仍由上面的 :class 用 stage.status 决定,本处只改文案。 -->
+													{{ computeNodeStatus(workbenchStage(row.so, row.raw)).label }}
 												</span>
 													</div>
 													<div class="oe-workbench__row">

+ 7 - 1
Web/src/views/aidop/s8/monitoring/components/order-execution/statusMapping.ts

@@ -37,11 +37,17 @@ function toneByVariance(days: number | null | undefined): OrderExecutionStatusTo
 	return 'red';
 }
 
+// S8-STEP5A-ORDER-EXEC-CLOSURE-1(F-2):DB 的 stage.status='pending' 同时覆盖「未开始」与「已开始未完成」
+// 两种业务态(实测 3 条 pending 中 2 条已有 actual_start_at),统一标「未到达」会把在制阶段说成没到。
+// 仅按 actualStartedAt 细分**标签**,tone 不变(不引入新配色、不改任何 CSS / 汇总口径)。
+const NODE_LABEL_IN_PROGRESS = '进行中';
+
 export function computeNodeStatus(stage: StageSnapshot | null | undefined): StatusVisual {
 	if (!stage) return { tone: 'pending', label: NODE_LABEL.pending };
 	const s = stage.status;
 	if (s === 'green' || s === 'yellow' || s === 'red' || s === 'pending') {
-		return { tone: s, label: NODE_LABEL[s] };
+		const label = s === 'pending' && stage.actualStartedAt ? NODE_LABEL_IN_PROGRESS : NODE_LABEL[s];
+		return { tone: s, label };
 	}
 	const tone = toneByVariance(stage.nodeVarianceDays);
 	return { tone, label: NODE_LABEL[tone] };

+ 2 - 0
Web/src/views/aidop/s8/monitoring/data/order-execution/domainMapper.ts

@@ -104,6 +104,8 @@ export function mapDomainStage(stage: OrderFlowStage): StageSnapshot {
 		name: stage.orderFlowName,
 		targetDate: toMonthDayTime(stage.targetAt),
 		actualReachedAt: stage.actualEndAt ? toMonthDayTime(stage.actualEndAt) : null,
+		// S8-STEP5A-ORDER-EXEC-CLOSURE-1(F-2):透传阶段实际开始时点,供状态标签区分「进行中」/「未到达」。
+		actualStartedAt: stage.actualStartAt ? toMonthDayTime(stage.actualStartAt) : null,
 		actualDays: stage.actualDays,
 		nodeVarianceDays: stage.nodeVarianceDays,
 		cumulativeVarianceDays: stage.cumulativeVarianceDays,

+ 3 - 0
Web/src/views/aidop/s8/monitoring/data/order-execution/types.ts

@@ -55,6 +55,9 @@ export interface StageSnapshot {
 	name: string;
 	targetDate: string;
 	actualReachedAt: string | null;
+	// S8-STEP5A-ORDER-EXEC-CLOSURE-1(F-2):区分「未开始」与「已开始未完成」所需。
+	// actualReachedAt 取的是 actualEndAt(完成时点),无法表达在制中的阶段。
+	actualStartedAt?: string | null;
 	actualDays?: number | null;
 	nodeVarianceDays: number | null;
 	cumulativeVarianceDays: number | null;

+ 3 - 3
server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -11,9 +11,9 @@
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <Copyright>Admin.NET</Copyright>
     <Description>Admin.NET 通用权限开发平台</Description>
-    <AssemblyVersion>1.0.413</AssemblyVersion>
-    <FileVersion>1.0.413</FileVersion>
-    <Version>1.0.413</Version>
+    <AssemblyVersion>1.0.414</AssemblyVersion>
+    <FileVersion>1.0.414</FileVersion>
+    <Version>1.0.414</Version>
   </PropertyGroup>
 
   <ItemGroup>

+ 249 - 0
server/Admin.NET.Web.Entry/UpdateScripts/WIP-S8-DEMO-EXCEPTION-LINEAGE.sql

@@ -0,0 +1,249 @@
+-- =====================================================================================
+-- S8-DEMO-DATA-SEED-1 / STEP 3:UATAdminA Golden Exception / Watch / Detection / Timeline / Decision
+--
+-- 目标 scope :tenant=838257186181189 (UATTEST_CHL) / factory=838257186320453
+-- 依赖       :STEP 2 的 5 条 Golden Order(WIP-S8-DEMO-GOLDEN-DATASET.sql,555 行)已落库
+-- 设计来源   :STEP 3 DESIGN CLOSURE,G1–G24 = 24/24 PASS
+--
+-- 总计 79 行:Watch 4 + Exception 14 + Detection 8 + Timeline 50 + Decision 3|Evidence = 0
+--
+-- 边界铁律:
+--   * 不修改 legacy 438 / 439 / 470,不修改 factory_id=1 的 14 条 junk,不修改 UATA-A01-WATCH-01
+--   * 不做 whole-table copy,不 INSERT...SELECT FROM 源租户(797403760988230)
+--   * 全部为新设计的 deterministic Golden 记录,固定 ID 段,禁用 MAX(id)+1
+--   * 幂等:每条 INSERT 均带 NOT EXISTS(id) 守卫;exception 另有唯一键
+--     uk_ado_s8_exception_tenant_factory_code (tenant_id, factory_id, exception_code) 作第二重保险
+--
+-- ⚠️ 运行时安全(本脚本相对 STEP 3 approved roster 的两处偏离,均为防污染所必需):
+--   D-1  watch_rule.enabled = 0
+--        job_s8_watch_scheduler 当前处于运行态(每 60s)。S8WatchSchedulerService 按 `WHERE r.enabled = 1`
+--        选规则;若置 1,调度器会在 60 秒内评估这 4 条规则并可能自动建单 / 写 EVALUATE_FAILED 检测日志,
+--        直接破坏 Golden 集的 14/8 基数。故落库为 0(规则结构完整、可随时启用)。
+--   D-2  exception.sla_deadline = NULL
+--        job_s8_timeout_auto_escalation 当前处于运行态(每 5min),扫描条件为
+--        `sla_deadline IS NOT NULL AND sla_deadline < now AND status IN ('ASSIGNED','IN_PROGRESS')`。
+--        Golden 的历史日期(2026-08)必然过期,若写入非空 sla_deadline,5 条 OPEN 异常会被自动升级为
+--        ESCALATED,破坏状态分布与 timeline 连续性。NULL 与 legacy 470 的既有取值一致。
+--
+-- 状态机权威:S8StatusRules + S8TaskFlowService(逐动作 IsAllowedTransition 强校验)
+--   合法链 NEW → ASSIGNED → IN_PROGRESS → PENDING_VERIFICATION → CLOSED
+--   对应 CREATE / CLAIM / START_PROGRESS / VERIFY_SUBMITTED / VERIFY_APPROVED
+--   禁用旧 seed 的 NEW→IN_PROGRESS 与 IN_PROGRESS→CLOSED,服务层无 CLOSE 动作
+--
+-- TIMEOUT 语义:S8TimeoutRuleEvaluator 判定 `dueAt <= now - graceMinutes`(即 now >= dueAt + grace)
+--   Golden graceMinutes = 0;CREATED detection 统一取 dueAt + 1 minute(在 > 与 >= 下均成立)
+-- =====================================================================================
+
+-- ---------------------------------------------------------------------------
+-- 1) WATCH RULE × 4    id 1329909460001 ~ 1329909460004
+--    params_json 严格对齐 S8TimeoutParams 的解析键(dueAtField / statusField /
+--    completedStates / objectCodeField / objectIdField / graceMinutes / exceptionTypeCode)。
+--    expression 的列别名对齐 evaluator 的 canonical 列(related_object_code / source_object_id /
+--    due_at / status),status 用 CASE 表达「阶段是否已完成」,使
+--    HIT ⇔ due_at <= now AND status NOT IN ('COMPLETED')
+--        ⇔ now >= actual_start_at + planned_days AND actual_end_at IS NULL
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_watch_rule`
+  (`id`,`tenant_id`,`factory_id`,`rule_code`,`scene_code`,`data_source_id`,`watch_object_type`,`expression`,
+   `severity`,`poll_interval_seconds`,`enabled`,`created_at`,`updated_at`,`rule_type`,`source_object_type`,
+   `params_json`,`consecutive_failure_count`,`trigger_count_required`,`recover_count_required`,
+   `stage_code`,`order_flow_code`,`rule_mechanism`)
+SELECT 1329909460001, 838257186181189, 838257186320453, 'GWR-S1-ORDER-DUE', 'S1', 5, 'ORDER',
+       'SELECT o.order_code AS related_object_code, o.order_code AS source_object_id, DATE_ADD(o.release_at, INTERVAL o.target_cycle_days DAY) AS due_at, CASE WHEN o.actual_cycle_days IS NULL THEN ''RUNNING'' ELSE ''COMPLETED'' END AS status FROM ado_s8_order_flow_order o WHERE o.tenant_id=838257186181189 AND o.factory_id=838257186320453 AND o.is_deleted=0 AND o.current_order_flow_code=''FINAL_ASSEMBLY_DELIVERY''',
+       'FOLLOW', 300, 0, '2026-08-28 00:00:00', NULL, 'TIMEOUT', 'ORDER',
+       '{"dueAtField":"due_at","statusField":"status","completedStates":["COMPLETED"],"objectCodeField":"related_object_code","objectIdField":"source_object_id","graceMinutes":0,"exceptionTypeCode":"ORDER_DELIVERY_DELAY_WARNING"}',
+       0, 1, 1, 'S1', NULL, 'RULE'
+FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_watch_rule` x WHERE x.`id` = 1329909460001);
+
+INSERT INTO `ado_s8_watch_rule`
+  (`id`,`tenant_id`,`factory_id`,`rule_code`,`scene_code`,`data_source_id`,`watch_object_type`,`expression`,
+   `severity`,`poll_interval_seconds`,`enabled`,`created_at`,`updated_at`,`rule_type`,`source_object_type`,
+   `params_json`,`consecutive_failure_count`,`trigger_count_required`,`recover_count_required`,
+   `stage_code`,`order_flow_code`,`rule_mechanism`)
+SELECT 1329909460002, 838257186181189, 838257186320453, 'GWR-S4-PO-DELAY', 'S4', 5, 'PURCHASE_ORDER',
+       'SELECT s.order_code AS related_object_code, s.order_code AS source_object_id, DATE_ADD(s.actual_start_at, INTERVAL s.planned_days DAY) AS due_at, CASE WHEN s.actual_end_at IS NULL THEN ''RUNNING'' ELSE ''COMPLETED'' END AS status FROM ado_s8_order_flow_stage s WHERE s.tenant_id=838257186181189 AND s.factory_id=838257186320453 AND s.is_deleted=0 AND s.order_flow_code=''MATERIAL_PURCHASE'' AND s.actual_start_at IS NOT NULL',
+       'FOLLOW', 300, 0, '2026-08-28 00:00:00', NULL, 'TIMEOUT', 'PURCHASE_ORDER',
+       '{"dueAtField":"due_at","statusField":"status","completedStates":["COMPLETED"],"objectCodeField":"related_object_code","objectIdField":"source_object_id","graceMinutes":0,"exceptionTypeCode":"PURCHASE_EXECUTION_DELAY"}',
+       0, 1, 1, 'S4', 'MATERIAL_PURCHASE', 'RULE'
+FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_watch_rule` x WHERE x.`id` = 1329909460002);
+
+INSERT INTO `ado_s8_watch_rule`
+  (`id`,`tenant_id`,`factory_id`,`rule_code`,`scene_code`,`data_source_id`,`watch_object_type`,`expression`,
+   `severity`,`poll_interval_seconds`,`enabled`,`created_at`,`updated_at`,`rule_type`,`source_object_type`,
+   `params_json`,`consecutive_failure_count`,`trigger_count_required`,`recover_count_required`,
+   `stage_code`,`order_flow_code`,`rule_mechanism`)
+SELECT 1329909460003, 838257186181189, 838257186320453, 'GWR-S6-WO-DELAY', 'S6', 5, 'WORK_ORDER',
+       'SELECT s.order_code AS related_object_code, s.order_code AS source_object_id, DATE_ADD(s.actual_start_at, INTERVAL s.planned_days DAY) AS due_at, CASE WHEN s.actual_end_at IS NULL THEN ''RUNNING'' ELSE ''COMPLETED'' END AS status FROM ado_s8_order_flow_stage s WHERE s.tenant_id=838257186181189 AND s.factory_id=838257186320453 AND s.is_deleted=0 AND s.order_flow_code=''BODY_PRODUCTION'' AND s.actual_start_at IS NOT NULL',
+       'SERIOUS', 300, 0, '2026-08-28 00:00:00', NULL, 'TIMEOUT', 'WORK_ORDER',
+       '{"dueAtField":"due_at","statusField":"status","completedStates":["COMPLETED"],"objectCodeField":"related_object_code","objectIdField":"source_object_id","graceMinutes":0,"exceptionTypeCode":"PRODUCTION_WORK_ORDER_COMPLETION_DELAY"}',
+       0, 1, 1, 'S6', 'BODY_PRODUCTION', 'RULE'
+FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_watch_rule` x WHERE x.`id` = 1329909460003);
+
+INSERT INTO `ado_s8_watch_rule`
+  (`id`,`tenant_id`,`factory_id`,`rule_code`,`scene_code`,`data_source_id`,`watch_object_type`,`expression`,
+   `severity`,`poll_interval_seconds`,`enabled`,`created_at`,`updated_at`,`rule_type`,`source_object_type`,
+   `params_json`,`consecutive_failure_count`,`trigger_count_required`,`recover_count_required`,
+   `stage_code`,`order_flow_code`,`rule_mechanism`)
+SELECT 1329909460004, 838257186181189, 838257186320453, 'GWR-S7-SHIP-DELAY', 'S7', 5, 'SHIPMENT',
+       'SELECT s.order_code AS related_object_code, s.order_code AS source_object_id, DATE_ADD(s.actual_start_at, INTERVAL s.planned_days DAY) AS due_at, CASE WHEN s.actual_end_at IS NULL THEN ''RUNNING'' ELSE ''COMPLETED'' END AS status FROM ado_s8_order_flow_stage s WHERE s.tenant_id=838257186181189 AND s.factory_id=838257186320453 AND s.is_deleted=0 AND s.order_flow_code=''FINAL_ASSEMBLY_DELIVERY'' AND s.actual_start_at IS NOT NULL',
+       'SERIOUS', 300, 0, '2026-08-28 00:00:00', NULL, 'TIMEOUT', 'SHIPMENT',
+       '{"dueAtField":"due_at","statusField":"status","completedStates":["COMPLETED"],"objectCodeField":"related_object_code","objectIdField":"source_object_id","graceMinutes":0,"exceptionTypeCode":"SHIPMENT_ABNORMAL"}',
+       0, 1, 1, 'S7', 'FINAL_ASSEMBLY_DELIVERY', 'RULE'
+FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_watch_rule` x WHERE x.`id` = 1329909460004);
+
+-- ---------------------------------------------------------------------------
+-- 2) GOLDEN EXCEPTION × 14    id 1329909430001 ~ 1329909430014
+--    priority:SERIOUS → (80.00,'P1');FOLLOW → (0.00,'P3')(沿用库内既有取值先例)
+--    sla_deadline 一律 NULL(见文件头 D-2)
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_exception`
+ (`id`,`tenant_id`,`factory_id`,`exception_code`,`title`,`description`,`scene_code`,`source_type`,`status`,`severity`,
+  `priority_score`,`priority_level`,`occurrence_dept_id`,`responsible_dept_id`,`assignee_id`,`sla_deadline`,`timeout_flag`,
+  `created_at`,`updated_at`,`closed_at`,`is_deleted`,`related_object_code`,`module_code`,`exception_type_code`,
+  `source_rule_code`,`source_object_type`,`source_object_id`,`dedup_key`,`last_detected_at`,
+  `consecutive_hit_count`,`consecutive_miss_count`,`stage_code`,`order_flow_code`,`rule_mechanism`)
+SELECT * FROM (
+  SELECT 1329909430001 AS a,838257186181189 AS b,838257186320453 AS c,'GEX-001-PD' AS d,'订单 SO-2026-001 产品设计延期 2 天' AS e,'高压接线盒图纸评审反复,产品设计阶段计划 3 天、实际 5 天,节点偏差 +2 天。' AS f,'S1' AS g,'MANUAL' AS h,'CLOSED' AS i,'SERIOUS' AS j,80.00 AS k,'P1' AS l,250 AS m,250 AS n,3210 AS o,NULL AS p,0 AS q,'2026-07-28 09:00:00' AS r,'2026-07-30 09:00:00' AS s,'2026-07-30 09:00:00' AS t,0 AS u,'SO-2026-001' AS v,'S1' AS w,'PRODUCT_DESIGN_DELAY' AS x,NULL AS y,NULL AS z,NULL AS aa,'GEX-001-PD' AS ab,NULL AS ac,0 AS ad,0 AS ae,'S1' AS af,'PRODUCT_DESIGN' AS ag,'MANUAL' AS ah
+  UNION ALL SELECT 1329909430002,838257186181189,838257186320453,'GEX-002-MP','订单 SO-2026-002 材料采购超期 2 天','材料采购阶段计划 14 天、实际 16 天,到期未完成触发监控规则;到货后超期解除。','S4','AUTO_WATCH','CLOSED','FOLLOW',0.00,'P3',245,245,3183,NULL,0,'2026-08-11 09:01:00','2026-08-13 10:00:00','2026-08-13 10:00:00',0,'SO-2026-002','S4','PURCHASE_EXECUTION_DELAY','GWR-S4-PO-DELAY','PURCHASE_ORDER','SO-2026-002','T838257186181189:F838257186320453:RGWR-S4-PO-DELAY:PURCHASE_ORDER:SO-2026-002','2026-08-13 09:00:00',1,1,'S4','MATERIAL_PURCHASE','RULE'
+  UNION ALL SELECT 1329909430003,838257186181189,838257186320453,'GEX-002-ETA','订单 SO-2026-002 供应商未回复交期','供应商迟迟未回复交期确认,是材料采购阶段超期的上游成因。','S3','MANUAL','CLOSED','FOLLOW',0.00,'P3',245,245,3183,NULL,0,'2026-08-05 09:00:00','2026-08-10 09:00:00','2026-08-10 09:00:00',0,'SO-2026-002','S3','SUPPLIER_ETA_ISSUE',NULL,NULL,NULL,'GEX-002-ETA',NULL,0,0,'S3','MATERIAL_PURCHASE','MANUAL'
+  UNION ALL SELECT 1329909430004,838257186181189,838257186320453,'GEX-012-MP','订单 SO-2026-012 采购到货交期异常','材料采购到期未到货,收货环节暴露;经切换备选供应商后到货,超期解除。','S4','AUTO_WATCH','CLOSED','SERIOUS',80.00,'P1',251,245,3183,NULL,0,'2026-08-11 09:01:00','2026-08-13 10:30:00','2026-08-13 10:30:00',0,'SO-2026-012','S4','PURCHASE_DELIVERY_ABNORMAL','GWR-S4-PO-DELAY','PURCHASE_ORDER','SO-2026-012','T838257186181189:F838257186320453:RGWR-S4-PO-DELAY:PURCHASE_ORDER:SO-2026-012','2026-08-13 09:00:00',1,1,'S4','MATERIAL_PURCHASE','RULE'
+  UNION ALL SELECT 1329909430005,838257186181189,838257186320453,'GEX-012-BP','订单 SO-2026-012 本体生产延期预警','上游累计偏差 +2 天,本体生产开工即预警;经压缩排产实际 4 天完成,累计偏差回正。','S6','MANUAL','CLOSED','FOLLOW',0.00,'P3',249,249,3185,NULL,0,'2026-08-13 11:00:00','2026-08-17 09:00:00','2026-08-17 09:00:00',0,'SO-2026-012','S6','BODY_PRODUCTION_DELAY_WARNING',NULL,NULL,NULL,'GEX-012-BP',NULL,0,0,'S6','BODY_PRODUCTION','MANUAL'
+  UNION ALL SELECT 1329909430006,838257186181189,838257186320453,'GEX-019-WO','订单 SO-2026-019 生产工单完工延期','本体生产 08-11 开工、计划 6 天应于 08-17 完工,至今未完工,监控规则持续命中。','S6','AUTO_WATCH','IN_PROGRESS','SERIOUS',80.00,'P1',249,249,3185,NULL,0,'2026-08-17 09:01:00','2026-08-24 09:01:00',NULL,0,'SO-2026-019','S6','PRODUCTION_WORK_ORDER_COMPLETION_DELAY','GWR-S6-WO-DELAY','WORK_ORDER','SO-2026-019','T838257186181189:F838257186320453:RGWR-S6-WO-DELAY:WORK_ORDER:SO-2026-019','2026-08-24 09:01:00',2,0,'S6','BODY_PRODUCTION','RULE'
+  UNION ALL SELECT 1329909430007,838257186181189,838257186320453,'GEX-019-BPW','订单 SO-2026-019 本体生产延期预警','完工前 2 天发出延期预警;08-17 到期未完工后升级为正式工单完工延期,本预警闭环。','S6','MANUAL','CLOSED','FOLLOW',0.00,'P3',249,249,3185,NULL,0,'2026-08-15 14:00:00','2026-08-17 08:00:00','2026-08-17 08:00:00',0,'SO-2026-019','S6','BODY_PRODUCTION_DELAY_WARNING',NULL,NULL,NULL,'GEX-019-BPW',NULL,0,0,'S6','BODY_PRODUCTION','MANUAL'
+  UNION ALL SELECT 1329909430008,838257186181189,838257186320453,'GEX-019-KIT','订单 SO-2026-019 物料齐套延期预警','齐套缺料导致本体生产线停滞,问题在产线暴露,备料责任在仓储,已认领待处理。','S5','MANUAL','ASSIGNED','FOLLOW',0.00,'P3',249,251,3179,NULL,0,'2026-08-19 10:00:00','2026-08-19 11:00:00',NULL,0,'SO-2026-019','S5','MATERIAL_KITTING_DELAY_WARNING',NULL,NULL,NULL,'GEX-019-KIT',NULL,0,0,'S5','BODY_PRODUCTION','MANUAL'
+  UNION ALL SELECT 1329909430009,838257186181189,838257186320453,'GEX-019-EQ','订单 SO-2026-019 关键设备停机故障','本体生产线关键设备故障停机,问题在产线暴露,维修责任在工程,处理中。','S2','MANUAL','IN_PROGRESS','SERIOUS',80.00,'P1',249,247,3172,NULL,0,'2026-08-20 08:00:00','2026-08-20 10:00:00',NULL,0,'SO-2026-019','S2','EQUIP_FAULT',NULL,NULL,NULL,'GEX-019-EQ',NULL,0,0,'S2','BODY_PRODUCTION','MANUAL'
+  UNION ALL SELECT 1329909430010,838257186181189,838257186320453,'GEX-019-QC','订单 SO-2026-019 制造协同质量异常','停机复产后首件出现批量质量异常,在产线发现,判定与处置责任在质量,尚未认领。','S2','MANUAL','NEW','SERIOUS',80.00,'P1',249,248,NULL,NULL,0,'2026-08-26 11:00:00',NULL,NULL,0,'SO-2026-019','S2','MFG_QUALITY_ABNORMAL',NULL,NULL,NULL,'GEX-019-QC',NULL,0,0,'S2','BODY_PRODUCTION','MANUAL'
+  UNION ALL SELECT 1329909430011,838257186181189,838257186320453,'GEX-020-FAW','订单 SO-2026-020 总装发货延期预警','总装发货阶段开工即发出发货风险预警;08-20 到期未完成后升级为出货异常,本预警闭环。','S7','MANUAL','CLOSED','FOLLOW',0.00,'P3',251,251,3179,NULL,0,'2026-08-18 09:00:00','2026-08-20 08:00:00','2026-08-20 08:00:00',0,'SO-2026-020','S7','DELIVERY_DELAY_WARNING',NULL,NULL,NULL,'GEX-020-FAW',NULL,0,0,'S7','FINAL_ASSEMBLY_DELIVERY','MANUAL'
+  UNION ALL SELECT 1329909430012,838257186181189,838257186320453,'GEX-020-SHIP','订单 SO-2026-020 出货异常','总装发货 08-17 开工、计划 3 天应于 08-20 完成,至今未完成,出货受阻。','S7','AUTO_WATCH','IN_PROGRESS','SERIOUS',80.00,'P1',251,251,3179,NULL,0,'2026-08-20 09:01:00','2026-08-20 14:00:00',NULL,0,'SO-2026-020','S7','SHIPMENT_ABNORMAL','GWR-S7-SHIP-DELAY','SHIPMENT','SO-2026-020','T838257186181189:F838257186320453:RGWR-S7-SHIP-DELAY:SHIPMENT:SO-2026-020','2026-08-20 09:01:00',1,0,'S7','FINAL_ASSEMBLY_DELIVERY','RULE'
+  UNION ALL SELECT 1329909430013,838257186181189,838257186320453,'GEX-020-PEND','订单 SO-2026-020 成品待发异常','成品已下线但迟迟未发运,仓储已认领待处理。','S7','MANUAL','ASSIGNED','FOLLOW',0.00,'P3',251,251,3179,NULL,0,'2026-08-24 14:00:00','2026-08-24 15:00:00',NULL,0,'SO-2026-020','S7','FINISHED_GOODS_PENDING_SHIPMENT',NULL,NULL,NULL,'GEX-020-PEND',NULL,0,0,'S7','FINAL_ASSEMBLY_DELIVERY','MANUAL'
+  UNION ALL SELECT 1329909430014,838257186181189,838257186320453,'GEX-020-DUE','订单 SO-2026-020 交付逾期预警','订单 07-20 下达、目标周期 31 天应于 08-20 交付,现已逾期且仍在总装发货阶段。','S1','AUTO_WATCH','NEW','FOLLOW',0.00,'P3',251,246,NULL,NULL,0,'2026-08-20 09:01:00',NULL,NULL,0,'SO-2026-020','S1','ORDER_DELIVERY_DELAY_WARNING','GWR-S1-ORDER-DUE','ORDER','SO-2026-020','T838257186181189:F838257186320453:RGWR-S1-ORDER-DUE:ORDER:SO-2026-020','2026-08-20 09:01:00',1,0,'S1',NULL,'RULE'
+) g
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_exception` x WHERE x.`id` = g.a)
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_exception` y
+                  WHERE y.`tenant_id` = g.b AND y.`factory_id` = g.c AND y.`exception_code` = g.d);
+
+-- ---------------------------------------------------------------------------
+-- 3) DETECTION LOG × 8    id 1329909450001 ~ 1329909450008
+--    lineage:detection_log.exception_id → exception.id;rule_id/rule_code → watch_rule
+--    dedup_key 沿用源租户实测格式 T{tenant}:F{factory}:R{ruleCode}:{sourceObjectType}:{relatedObjectCode}
+--    CREATED 时间 = due_time + 1 minute;RECOVERED 时间 = 阶段 actual_end_at
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_detection_log`
+ (`id`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`,`rule_id`,`rule_code`,`rule_type`,`scene_code`,
+  `source_object_type`,`source_object_id`,`related_object_code`,`dedup_key`,`detect_result`,`exception_id`,`detected_at`,
+  `trigger_source`,`remark`)
+SELECT * FROM (
+  SELECT 1329909450001 AS a,838257186181189 AS b,838257186320453 AS c,'2026-08-11 09:01:00' AS d,NULL AS e,0 AS f,1329909460002 AS g,'GWR-S4-PO-DELAY' AS h,'TIMEOUT' AS i,'S4' AS j,'PURCHASE_ORDER' AS k,'SO-2026-002' AS l,'SO-2026-002' AS m,'T838257186181189:F838257186320453:RGWR-S4-PO-DELAY:PURCHASE_ORDER:SO-2026-002' AS n,'CREATED' AS o,1329909430002 AS p,'2026-08-11 09:01:00' AS q,'SCHEDULER' AS r,'材料采购到期未完成(due 2026-08-11 09:00)' AS s
+  UNION ALL SELECT 1329909450002,838257186181189,838257186320453,'2026-08-13 09:00:00',NULL,0,1329909460002,'GWR-S4-PO-DELAY','TIMEOUT','S4','PURCHASE_ORDER','SO-2026-002','SO-2026-002','T838257186181189:F838257186320453:RGWR-S4-PO-DELAY:PURCHASE_ORDER:SO-2026-002','RECOVERED',1329909430002,'2026-08-13 09:00:00','SCHEDULER','采购阶段完成落库,超期条件解除'
+  UNION ALL SELECT 1329909450003,838257186181189,838257186320453,'2026-08-11 09:01:00',NULL,0,1329909460002,'GWR-S4-PO-DELAY','TIMEOUT','S4','PURCHASE_ORDER','SO-2026-012','SO-2026-012','T838257186181189:F838257186320453:RGWR-S4-PO-DELAY:PURCHASE_ORDER:SO-2026-012','CREATED',1329909430004,'2026-08-11 09:01:00','SCHEDULER','材料采购到期未完成(due 2026-08-11 09:00)'
+  UNION ALL SELECT 1329909450004,838257186181189,838257186320453,'2026-08-13 09:00:00',NULL,0,1329909460002,'GWR-S4-PO-DELAY','TIMEOUT','S4','PURCHASE_ORDER','SO-2026-012','SO-2026-012','T838257186181189:F838257186320453:RGWR-S4-PO-DELAY:PURCHASE_ORDER:SO-2026-012','RECOVERED',1329909430004,'2026-08-13 09:00:00','SCHEDULER','切换供应商后到货,超期条件解除'
+  UNION ALL SELECT 1329909450005,838257186181189,838257186320453,'2026-08-17 09:01:00',NULL,0,1329909460003,'GWR-S6-WO-DELAY','TIMEOUT','S6','WORK_ORDER','SO-2026-019','SO-2026-019','T838257186181189:F838257186320453:RGWR-S6-WO-DELAY:WORK_ORDER:SO-2026-019','CREATED',1329909430006,'2026-08-17 09:01:00','SCHEDULER','本体生产到期未完工(due 2026-08-17 09:00)'
+  UNION ALL SELECT 1329909450006,838257186181189,838257186320453,'2026-08-24 09:01:00',NULL,0,1329909460003,'GWR-S6-WO-DELAY','TIMEOUT','S6','WORK_ORDER','SO-2026-019','SO-2026-019','T838257186181189:F838257186320453:RGWR-S6-WO-DELAY:WORK_ORDER:SO-2026-019','REFRESHED',1329909430006,'2026-08-24 09:01:00','SCHEDULER','仍未完工,命中刷新'
+  UNION ALL SELECT 1329909450007,838257186181189,838257186320453,'2026-08-20 09:01:00',NULL,0,1329909460004,'GWR-S7-SHIP-DELAY','TIMEOUT','S7','SHIPMENT','SO-2026-020','SO-2026-020','T838257186181189:F838257186320453:RGWR-S7-SHIP-DELAY:SHIPMENT:SO-2026-020','CREATED',1329909430012,'2026-08-20 09:01:00','SCHEDULER','总装发货到期未完成(due 2026-08-20 09:00)'
+  UNION ALL SELECT 1329909450008,838257186181189,838257186320453,'2026-08-20 09:01:00',NULL,0,1329909460001,'GWR-S1-ORDER-DUE','TIMEOUT','S1','ORDER','SO-2026-020','SO-2026-020','T838257186181189:F838257186320453:RGWR-S1-ORDER-DUE:ORDER:SO-2026-020','CREATED',1329909430014,'2026-08-20 09:01:00','SCHEDULER','订单目标交付到期未交付(due 2026-08-20 09:00)'
+) d
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_detection_log` x WHERE x.`id` = d.a);
+
+-- ---------------------------------------------------------------------------
+-- 4) TIMELINE × 50    id 1329909440001 ~ 1329909440050
+--    唯一合法范式(S8StatusRules):
+--      NEW         CREATE
+--      ASSIGNED    CREATE → CLAIM
+--      IN_PROGRESS CREATE → CLAIM → START_PROGRESS
+--      CLOSED      CREATE → CLAIM → START_PROGRESS → VERIFY_SUBMITTED → VERIFY_APPROVED
+--    严格 event[i].to_status == event[i+1].from_status;不使用旧 CLOSE action
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_exception_timeline`
+ (`id`,`exception_id`,`action_code`,`action_label`,`from_status`,`to_status`,`operator_id`,`operator_name`,`action_remark`,`created_at`)
+SELECT * FROM (
+  -- GEX-001-PD (CLOSED)
+  SELECT 1329909440001 AS a,1329909430001 AS b,'CREATE' AS c,'创建' AS d,NULL AS e,'NEW' AS f,NULL AS g,'系统' AS h,'产品设计阶段偏差 +2 天,创建异常' AS i,'2026-07-28 09:00:00' AS j
+  UNION ALL SELECT 1329909440002,1329909430001,'CLAIM','认领','NEW','ASSIGNED',3210,'研发工程师01','研发认领',                    '2026-07-28 10:00:00'
+  UNION ALL SELECT 1329909440003,1329909430001,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3210,'研发工程师01','加派评审资源',        '2026-07-28 14:00:00'
+  UNION ALL SELECT 1329909440004,1329909430001,'VERIFY_SUBMITTED','提交复检','IN_PROGRESS','PENDING_VERIFICATION',3210,'研发工程师01','图纸定版,提交复检','2026-07-30 08:00:00'
+  UNION ALL SELECT 1329909440005,1329909430001,'VERIFY_APPROVED','检验通过','PENDING_VERIFICATION','CLOSED',3210,'研发工程师01','复检通过,闭环',      '2026-07-30 09:00:00'
+  -- GEX-002-MP (CLOSED, AUTO_WATCH)
+  UNION ALL SELECT 1329909440006,1329909430002,'CREATE','派生',NULL,'NEW',NULL,'系统(监控规则)','GWR-S4-PO-DELAY 命中,自动建单','2026-08-11 09:01:00'
+  UNION ALL SELECT 1329909440007,1329909430002,'CLAIM','认领','NEW','ASSIGNED',3183,'梁军','采购认领',                          '2026-08-11 10:00:00'
+  UNION ALL SELECT 1329909440008,1329909430002,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3183,'梁军','催交并跟踪到货',            '2026-08-11 14:00:00'
+  UNION ALL SELECT 1329909440009,1329909430002,'VERIFY_SUBMITTED','提交复检','IN_PROGRESS','PENDING_VERIFICATION',3183,'梁军','到货完成,提交复检','2026-08-13 09:10:00'
+  UNION ALL SELECT 1329909440010,1329909430002,'VERIFY_APPROVED','检验通过','PENDING_VERIFICATION','CLOSED',3183,'梁军','复检通过,闭环',      '2026-08-13 10:00:00'
+  -- GEX-002-ETA (CLOSED)
+  UNION ALL SELECT 1329909440011,1329909430003,'CREATE','创建',NULL,'NEW',3183,'梁军','供应商未回复交期,人工提报','2026-08-05 09:00:00'
+  UNION ALL SELECT 1329909440012,1329909430003,'CLAIM','认领','NEW','ASSIGNED',3183,'梁军','采购认领',              '2026-08-05 10:00:00'
+  UNION ALL SELECT 1329909440013,1329909430003,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3183,'梁军','升级联系供应商商务', '2026-08-05 14:00:00'
+  UNION ALL SELECT 1329909440014,1329909430003,'VERIFY_SUBMITTED','提交复检','IN_PROGRESS','PENDING_VERIFICATION',3183,'梁军','供应商已确认交期','2026-08-10 08:00:00'
+  UNION ALL SELECT 1329909440015,1329909430003,'VERIFY_APPROVED','检验通过','PENDING_VERIFICATION','CLOSED',3183,'梁军','复检通过,闭环','2026-08-10 09:00:00'
+  -- GEX-012-MP (CLOSED, AUTO_WATCH)
+  UNION ALL SELECT 1329909440016,1329909430004,'CREATE','派生',NULL,'NEW',NULL,'系统(监控规则)','GWR-S4-PO-DELAY 命中,自动建单','2026-08-11 09:01:00'
+  UNION ALL SELECT 1329909440017,1329909430004,'CLAIM','认领','NEW','ASSIGNED',3183,'梁军','采购认领',                          '2026-08-11 10:30:00'
+  UNION ALL SELECT 1329909440018,1329909430004,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3183,'梁军','评估备选供应商',            '2026-08-11 14:30:00'
+  UNION ALL SELECT 1329909440019,1329909430004,'VERIFY_SUBMITTED','提交复检','IN_PROGRESS','PENDING_VERIFICATION',3183,'梁军','备选供应商到货,提交复检','2026-08-13 09:20:00'
+  UNION ALL SELECT 1329909440020,1329909430004,'VERIFY_APPROVED','检验通过','PENDING_VERIFICATION','CLOSED',3183,'梁军','复检通过,闭环',      '2026-08-13 10:30:00'
+  -- GEX-012-BP (CLOSED)
+  UNION ALL SELECT 1329909440021,1329909430005,'CREATE','创建',NULL,'NEW',NULL,'系统','累计偏差 +2 天,本体生产开工预警','2026-08-13 11:00:00'
+  UNION ALL SELECT 1329909440022,1329909430005,'CLAIM','认领','NEW','ASSIGNED',3185,'路群','生产认领',                    '2026-08-13 12:00:00'
+  UNION ALL SELECT 1329909440023,1329909430005,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3185,'路群','压缩排产追赶工期',      '2026-08-13 14:00:00'
+  UNION ALL SELECT 1329909440024,1329909430005,'VERIFY_SUBMITTED','提交复检','IN_PROGRESS','PENDING_VERIFICATION',3185,'路群','本体生产提前完成','2026-08-17 08:00:00'
+  UNION ALL SELECT 1329909440025,1329909430005,'VERIFY_APPROVED','检验通过','PENDING_VERIFICATION','CLOSED',3185,'路群','复检通过,闭环',  '2026-08-17 09:00:00'
+  -- GEX-019-WO (IN_PROGRESS, AUTO_WATCH)
+  UNION ALL SELECT 1329909440026,1329909430006,'CREATE','派生',NULL,'NEW',NULL,'系统(监控规则)','GWR-S6-WO-DELAY 命中,自动建单','2026-08-17 09:01:00'
+  UNION ALL SELECT 1329909440027,1329909430006,'CLAIM','认领','NEW','ASSIGNED',3185,'路群','生产认领',                          '2026-08-17 10:00:00'
+  UNION ALL SELECT 1329909440028,1329909430006,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3185,'路群','排查停滞原因并调整排产',      '2026-08-17 14:00:00'
+  -- GEX-019-BPW (CLOSED)
+  UNION ALL SELECT 1329909440029,1329909430007,'CREATE','创建',NULL,'NEW',NULL,'系统','本体生产完工前延期预警','2026-08-15 14:00:00'
+  UNION ALL SELECT 1329909440030,1329909430007,'CLAIM','认领','NEW','ASSIGNED',3185,'路群','生产认领',            '2026-08-15 15:00:00'
+  UNION ALL SELECT 1329909440031,1329909430007,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3185,'路群','评估工期风险',    '2026-08-15 16:00:00'
+  UNION ALL SELECT 1329909440032,1329909430007,'VERIFY_SUBMITTED','提交复检','IN_PROGRESS','PENDING_VERIFICATION',3185,'路群','已升级为正式工单延期','2026-08-17 07:00:00'
+  UNION ALL SELECT 1329909440033,1329909430007,'VERIFY_APPROVED','检验通过','PENDING_VERIFICATION','CLOSED',3185,'路群','预警闭环','2026-08-17 08:00:00'
+  -- GEX-019-KIT (ASSIGNED)
+  UNION ALL SELECT 1329909440034,1329909430008,'CREATE','创建',NULL,'NEW',NULL,'系统','齐套缺料导致产线停滞','2026-08-19 10:00:00'
+  UNION ALL SELECT 1329909440035,1329909430008,'CLAIM','认领','NEW','ASSIGNED',3179,'章立坤','仓储认领',        '2026-08-19 11:00:00'
+  -- GEX-019-EQ (IN_PROGRESS)
+  UNION ALL SELECT 1329909440036,1329909430009,'CREATE','创建',NULL,'NEW',NULL,'系统','关键设备停机','2026-08-20 08:00:00'
+  UNION ALL SELECT 1329909440037,1329909430009,'CLAIM','认领','NEW','ASSIGNED',3172,'WB8异常A','工程认领',  '2026-08-20 09:00:00'
+  UNION ALL SELECT 1329909440038,1329909430009,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3172,'WB8异常A','现场抢修','2026-08-20 10:00:00'
+  -- GEX-019-QC (NEW)
+  UNION ALL SELECT 1329909440039,1329909430010,'CREATE','创建',NULL,'NEW',NULL,'系统','复产首件批量质量异常','2026-08-26 11:00:00'
+  -- GEX-020-FAW (CLOSED)
+  UNION ALL SELECT 1329909440040,1329909430011,'CREATE','创建',NULL,'NEW',NULL,'系统','总装开工发货风险预警','2026-08-18 09:00:00'
+  UNION ALL SELECT 1329909440041,1329909430011,'CLAIM','认领','NEW','ASSIGNED',3179,'章立坤','仓储认领',      '2026-08-18 10:00:00'
+  UNION ALL SELECT 1329909440042,1329909430011,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3179,'章立坤','协调总装与发运资源','2026-08-18 14:00:00'
+  UNION ALL SELECT 1329909440043,1329909430011,'VERIFY_SUBMITTED','提交复检','IN_PROGRESS','PENDING_VERIFICATION',3179,'章立坤','已升级为出货异常','2026-08-20 07:00:00'
+  UNION ALL SELECT 1329909440044,1329909430011,'VERIFY_APPROVED','检验通过','PENDING_VERIFICATION','CLOSED',3179,'章立坤','预警闭环','2026-08-20 08:00:00'
+  -- GEX-020-SHIP (IN_PROGRESS, AUTO_WATCH)
+  UNION ALL SELECT 1329909440045,1329909430012,'CREATE','派生',NULL,'NEW',NULL,'系统(监控规则)','GWR-S7-SHIP-DELAY 命中,自动建单','2026-08-20 09:01:00'
+  UNION ALL SELECT 1329909440046,1329909430012,'CLAIM','认领','NEW','ASSIGNED',3179,'章立坤','仓储认领',                        '2026-08-20 10:00:00'
+  UNION ALL SELECT 1329909440047,1329909430012,'START_PROGRESS','开始处理','ASSIGNED','IN_PROGRESS',3179,'章立坤','协调分批发运',              '2026-08-20 14:00:00'
+  -- GEX-020-PEND (ASSIGNED)
+  UNION ALL SELECT 1329909440048,1329909430013,'CREATE','创建',NULL,'NEW',NULL,'系统','成品下线后待发运','2026-08-24 14:00:00'
+  UNION ALL SELECT 1329909440049,1329909430013,'CLAIM','认领','NEW','ASSIGNED',3179,'章立坤','仓储认领',    '2026-08-24 15:00:00'
+  -- GEX-020-DUE (NEW, AUTO_WATCH)
+  UNION ALL SELECT 1329909440050,1329909430014,'CREATE','派生',NULL,'NEW',NULL,'系统(监控规则)','GWR-S1-ORDER-DUE 命中,自动建单','2026-08-20 09:01:00'
+) t
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_exception_timeline` x WHERE x.`id` = t.a);
+
+-- ---------------------------------------------------------------------------
+-- 5) DECISION RECORD × 3    id 1329909470001 ~ 1329909470003
+--
+--    ⚠️ decision_type 的三个取值 SUPPLIER_SWITCH / SCHEDULE_ADJUST / SPLIT_SHIPMENT
+--       定性为 GOLDEN_SEED_LOCAL_VOCABULARY:
+--         * 不是平台 enum
+--         * 不是 SysDictType / SysDictData 字典项
+--         * 不是 Config Dictionary 配置项
+--         * 业务代码不得依赖这些值做逻辑分支
+--       依据:ado_s8_decision_record.decision_type 为无受控字典的自由 varchar(64),
+--             S8DecisionService 无取值校验,全库此前 0 行、无既有枚举可复用。
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_decision_record`
+ (`id`,`exception_id`,`decision_type`,`decision_maker_id`,`decision_maker_name`,`decision_basis`,`decision_result`,`decision_remark`,`decision_time`,`is_deleted`)
+SELECT * FROM (
+  SELECT 1329909470001 AS a,1329909430004 AS b,'SUPPLIER_SWITCH' AS c,3183 AS d,'梁军' AS e,'材料采购 2026-08-11 09:00 到期未到货,检测记录 GDL-03(GWR-S4-PO-DELAY)' AS f,'切换至备选供应商 B,剩余批次加急空运' AS g,'GOLDEN_SEED_LOCAL_VOCABULARY:decision_type 非平台 enum / 非字典项,代码不得据此分支' AS h,'2026-08-11 15:00:00' AS i,0 AS j
+  UNION ALL SELECT 1329909470002,1329909430006,'SCHEDULE_ADJUST',3185,'路群','本体生产 2026-08-17 09:00 到期未完工,检测记录 GDL-05(GWR-S6-WO-DELAY)','19# 工位任务转移至 22#,本体生产顺延 1 天,优先保交付','GOLDEN_SEED_LOCAL_VOCABULARY:decision_type 非平台 enum / 非字典项,代码不得据此分支','2026-08-19 09:00:00',0
+  UNION ALL SELECT 1329909470003,1329909430012,'SPLIT_SHIPMENT',3179,'章立坤','总装发货 2026-08-20 09:00 到期未完成,检测记录 GDL-07(GWR-S7-SHIP-DELAY)','急单优先发运,与客户确认分批交付','GOLDEN_SEED_LOCAL_VOCABULARY:decision_type 非平台 enum / 非字典项,代码不得据此分支','2026-08-22 10:00:00',0
+) d
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_decision_record` x WHERE x.`id` = d.a);
+
+-- =====================================================================================
+-- Evidence:本设计 = 0 行。无真实、可访问、安全的文件引用,拒绝 seed 假 URL / PDF / 图片 / 路径。
+-- =====================================================================================

+ 525 - 0
server/Admin.NET.Web.Entry/UpdateScripts/WIP-S8-DEMO-GOLDEN-DATASET.sql

@@ -0,0 +1,525 @@
+-- =====================================================================================
+-- S8-DEMO-DATA-SEED-1 / STEP 2:UATAdminA Golden Demo Dataset(Order Flow 12 表)
+--
+-- 目标 scope :tenant=838257186181189 (UATTEST_CHL) / factory=838257186320453
+-- 源  模板   :tenant=797403760988230 (AIDOP-Demo) / factory=797403760988231 —— 只读,绝不修改
+--
+-- 设计要点(S8-DEMO-DATA-SEED-1 STEP 1 拍板):
+--   D1-A  SO-2026-012 定义为「采购延期但后续追回工期」对照单,actual_cycle_days=30
+--   D2-A  snapshot 按 Golden Roster 重算(total_orders=5 / total_customers=5),不复制 PPT 的 105
+--   D3    KPI 口径:PO=green 占比、WO=AVG(achievement_rate)
+--   D4-A  日期整体平移 +96 天(源业务窗 2026-04-15~05-18 → 2026-07-20~08-22,落入 Q3 且不含未来日期)
+--   客户身份统一取 UAT S0 CustMaster 主数据,不沿用源 Demo 的「电气设备厂A/B/D/E」
+--
+-- ID 段:UAT 专用固定段 13299093xxxxx ~ 13299094xxxxx(全库未占用),无 MAX(id)+1
+-- 幂等:每条 INSERT 均带 NOT EXISTS(id) 守卫;012 修正为对固定值的 UPDATE(天然幂等)
+-- 系统审计列 created_at 固定写 '2026-08-27 00:00:00'(确定性,非 NOW());
+--   这 6 张表无业务日期列,且经代码核查不参与任何页面过滤,故不做业务平移。
+-- =====================================================================================
+
+-- ---------------------------------------------------------------------------
+-- 1) 订单主表:5 条 Golden Order(客户身份改绑 UAT CustMaster)
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_order`
+  (`id`,`tenant_id`,`factory_id`,`order_code`,`product_name`,`product_line`,`customer_code`,`customer_name`,
+   `customer_type`,`region`,`priority`,`workflow_status`,`current_order_flow_code`,`release_at`,
+   `target_cycle_days`,`actual_cycle_days`,`response_minutes`,`processing_minutes`,`total_loss_minutes`,
+   `scenario_code`,`data_source`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909300000 + m.n, 838257186181189, 838257186320453,
+       s.`order_code`, s.`product_name`, s.`product_line`,
+       m.cust_code, m.cust_name,
+       s.`customer_type`, s.`region`, s.`priority`, s.`workflow_status`, s.`current_order_flow_code`,
+       DATE_ADD(s.`release_at`, INTERVAL 96 DAY),
+       s.`target_cycle_days`,
+       CASE WHEN s.`order_code` = 'SO-2026-012' THEN 30.0 ELSE s.`actual_cycle_days` END,
+       s.`response_minutes`, s.`processing_minutes`, s.`total_loss_minutes`,
+       'DEMO', 'SEED', '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_order` s
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n, '10000000'     AS cust_code, '瑞贝德'         AS cust_name UNION ALL
+  SELECT 'SO-2026-002',               2,      '10000002',                  '通广'                        UNION ALL
+  SELECT 'SO-2026-012',               3,      '10000004',                  '岛包'                        UNION ALL
+  SELECT 'SO-2026-019',               4,      'UATA-A01-CUS',              'A套UAT客户'                  UNION ALL
+  SELECT 'SO-2026-020',               5,      'CUST0005',                  'TJRQWKQXGFYXGS'
+) m ON m.order_code = s.`order_code`
+WHERE s.`tenant_id` = 797403760988230 AND s.`factory_id` = 797403760988231 AND s.`is_deleted` = 0
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_order` x WHERE x.`id` = 1329909300000 + m.n);
+
+-- ---------------------------------------------------------------------------
+-- 2) 阶段表:5 单 × 5 阶段 = 25 行
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_stage`
+  (`id`,`order_id`,`order_code`,`order_flow_code`,`order_flow_name`,`owner_dept`,`sort_no`,
+   `planned_days`,`actual_days`,`target_at`,`actual_start_at`,`actual_end_at`,`status`,
+   `node_variance_days`,`cumulative_variance_days`,`scenario_code`,`data_source`,
+   `tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909310000 + (m.n - 1) * 5 + s.`sort_no`,
+       1329909300000 + m.n, s.`order_code`, s.`order_flow_code`, s.`order_flow_name`, s.`owner_dept`, s.`sort_no`,
+       s.`planned_days`, s.`actual_days`,
+       DATE_ADD(s.`target_at`,        INTERVAL 96 DAY),
+       DATE_ADD(s.`actual_start_at`,  INTERVAL 96 DAY),
+       DATE_ADD(s.`actual_end_at`,    INTERVAL 96 DAY),
+       s.`status`, s.`node_variance_days`, s.`cumulative_variance_days`,
+       'DEMO', 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_stage` s
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = s.`order_code`
+WHERE s.`tenant_id` = 797403760988230 AND s.`is_deleted` = 0
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_stage` x
+                  WHERE x.`id` = 1329909310000 + (m.n - 1) * 5 + s.`sort_no`);
+
+-- ---------------------------------------------------------------------------
+-- 3) 子步骤:5 单 × 5 = 25 行(仅 ORDER_REVIEW_PLAN_CALC 建模)
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_substep`
+  (`id`,`order_id`,`order_code`,`order_flow_code`,`substep_code`,`substep_name`,`pi_hours`,`actual_hours`,
+   `status`,`sort_no`,`scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909320000 + (m.n - 1) * 5 + s.`sort_no`,
+       1329909300000 + m.n, s.`order_code`, s.`order_flow_code`, s.`substep_code`, s.`substep_name`,
+       s.`pi_hours`, s.`actual_hours`, s.`status`, s.`sort_no`,
+       'DEMO', 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_substep` s
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = s.`order_code`
+WHERE s.`tenant_id` = 797403760988230 AND s.`is_deleted` = 0
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_substep` x
+                  WHERE x.`id` = 1329909320000 + (m.n - 1) * 5 + s.`sort_no`);
+
+-- ---------------------------------------------------------------------------
+-- 4) 子步骤单元:5 单 × 15 = 75 行
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_substep_unit`
+  (`id`,`substep_id`,`order_id`,`order_code`,`order_flow_code`,`substep_code`,`unit_code`,`unit_name`,
+   `pi_hours`,`actual_hours`,`status`,`sort_no`,`scenario_code`,`data_source`,
+   `tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909330000 + (m.n - 1) * 15 + u.rn,
+       1329909320000 + (m.n - 1) * 5 + sub.`sort_no`,
+       1329909300000 + m.n, u.`order_code`, u.`order_flow_code`, u.`substep_code`, u.`unit_code`, u.`unit_name`,
+       u.`pi_hours`, u.`actual_hours`, u.`status`, u.`sort_no`,
+       'DEMO', 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.`order_code` ORDER BY t.`id`) AS rn
+  FROM `ado_s8_order_flow_substep_unit` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0
+) u
+JOIN `ado_s8_order_flow_substep` sub ON sub.`id` = u.`substep_id`
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = u.`order_code`
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_substep_unit` x
+                  WHERE x.`id` = 1329909330000 + (m.n - 1) * 15 + u.rn);
+
+-- ---------------------------------------------------------------------------
+-- 5) 产品设计图纸:5 单 × 7 = 35 行(4 个业务日期列平移)
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_product_design_drawing`
+  (`id`,`tenant_id`,`factory_id`,`order_code`,`drawing_no`,`product_type`,`product_quantity`,`responsible_person`,
+   `planned_start_date`,`planned_end_date`,`actual_start_date`,`actual_end_date`,
+   `kpi_days`,`actual_days`,`is_achieved`,`status`,`sort_no`,`is_deleted`,`create_time`,`update_time`)
+SELECT 1329909340000 + (m.n - 1) * 7 + d.rn,
+       838257186181189, 838257186320453,
+       d.`order_code`, d.`drawing_no`, d.`product_type`, d.`product_quantity`, d.`responsible_person`,
+       DATE_ADD(d.`planned_start_date`, INTERVAL 96 DAY),
+       DATE_ADD(d.`planned_end_date`,   INTERVAL 96 DAY),
+       DATE_ADD(d.`actual_start_date`,  INTERVAL 96 DAY),
+       DATE_ADD(d.`actual_end_date`,    INTERVAL 96 DAY),
+       d.`kpi_days`, d.`actual_days`, d.`is_achieved`, d.`status`, d.`sort_no`,
+       0, '2026-08-27 00:00:00', NULL
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.`order_code` ORDER BY t.`id`) AS rn
+  FROM `ado_s8_order_flow_product_design_drawing` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0
+) d
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = d.`order_code`
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_product_design_drawing` x
+                  WHERE x.`id` = 1329909340000 + (m.n - 1) * 7 + d.rn);
+
+-- ---------------------------------------------------------------------------
+-- 6a) 材料采购透视 —— baseline 行 48 条(PPT 第 4 页真值参考基线,order_id IS NULL)
+--     语义 = BUSINESS/PPT REFERENCE BASELINE,非 20 单聚合 → 原样复制
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_procurement_pivot`
+  (`id`,`order_id`,`order_code`,`material_code`,`supplier_code`,`spec_code`,`cycle_days`,`status`,
+   `scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`,
+   `impact_count`,`kit_rate`)
+SELECT 1329909350000 + p.rn, NULL, NULL,
+       p.`material_code`, p.`supplier_code`, p.`spec_code`, p.`cycle_days`, p.`status`,
+       p.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0,
+       p.`impact_count`, p.`kit_rate`
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.`id`) AS rn
+  FROM `ado_s8_order_flow_procurement_pivot` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0 AND t.`order_id` IS NULL
+) p
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_procurement_pivot` x
+                  WHERE x.`id` = 1329909350000 + p.rn);
+
+-- 6b) 材料采购透视 —— 订单级行 5 单 × 48 = 240 条
+INSERT INTO `ado_s8_order_flow_procurement_pivot`
+  (`id`,`order_id`,`order_code`,`material_code`,`supplier_code`,`spec_code`,`cycle_days`,`status`,
+   `scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`,
+   `impact_count`,`kit_rate`)
+SELECT 1329909360000 + (m.n - 1) * 48 + p.rn,
+       1329909300000 + m.n, p.`order_code`,
+       p.`material_code`, p.`supplier_code`, p.`spec_code`, p.`cycle_days`, p.`status`,
+       p.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0,
+       p.`impact_count`, p.`kit_rate`
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.`order_code` ORDER BY t.`id`) AS rn
+  FROM `ado_s8_order_flow_procurement_pivot` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0 AND t.`order_id` IS NOT NULL
+) p
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = p.`order_code`
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_procurement_pivot` x
+                  WHERE x.`id` = 1329909360000 + (m.n - 1) * 48 + p.rn);
+
+-- ---------------------------------------------------------------------------
+-- 7) 本体生产 · 工序明细:baseline 5 + 订单级 20 = 25 行
+--    baseline 语义 = MANUFACTURING_DETAIL_FIXTURE 参考基线 → 原样复制
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_manufacturing_process`
+  (`id`,`order_id`,`order_code`,`process_code`,`process_name`,`pi_days`,`actual_days`,`cycle_status`,
+   `plan_qty`,`achievement_rate`,`achievement_status`,`sort_no`,`scenario_code`,`data_source`,
+   `tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909380000 + p.rn, NULL, NULL,
+       p.`process_code`, p.`process_name`, p.`pi_days`, p.`actual_days`, p.`cycle_status`,
+       p.`plan_qty`, p.`achievement_rate`, p.`achievement_status`, p.`sort_no`,
+       p.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.`sort_no`) AS rn
+  FROM `ado_s8_order_flow_manufacturing_process` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0 AND t.`order_id` IS NULL
+) p
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_manufacturing_process` x
+                  WHERE x.`id` = 1329909380000 + p.rn);
+
+INSERT INTO `ado_s8_order_flow_manufacturing_process`
+  (`id`,`order_id`,`order_code`,`process_code`,`process_name`,`pi_days`,`actual_days`,`cycle_status`,
+   `plan_qty`,`achievement_rate`,`achievement_status`,`sort_no`,`scenario_code`,`data_source`,
+   `tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909380100 + (m.n - 1) * 5 + p.`sort_no`,
+       1329909300000 + m.n, p.`order_code`,
+       p.`process_code`, p.`process_name`, p.`pi_days`, p.`actual_days`, p.`cycle_status`,
+       p.`plan_qty`, p.`achievement_rate`, p.`achievement_status`, p.`sort_no`,
+       p.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_manufacturing_process` p
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = p.`order_code`
+WHERE p.`tenant_id` = 797403760988230 AND p.`is_deleted` = 0 AND p.`order_id` IS NOT NULL
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_manufacturing_process` x
+                  WHERE x.`id` = 1329909380100 + (m.n - 1) * 5 + p.`sort_no`);
+
+-- ---------------------------------------------------------------------------
+-- 8) 本体生产 · 损失因素:baseline 5 + 订单级 20 = 25 行
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_manufacturing_loss_factor`
+  (`id`,`order_id`,`order_code`,`factor_code`,`factor_name`,`count_value`,`ratio_pct`,`loss_hours`,
+   `sort_no`,`scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909390000 + f.rn, NULL, NULL,
+       f.`factor_code`, f.`factor_name`, f.`count_value`, f.`ratio_pct`, f.`loss_hours`, f.`sort_no`,
+       f.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.`sort_no`) AS rn
+  FROM `ado_s8_order_flow_manufacturing_loss_factor` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0 AND t.`order_id` IS NULL
+) f
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_manufacturing_loss_factor` x
+                  WHERE x.`id` = 1329909390000 + f.rn);
+
+INSERT INTO `ado_s8_order_flow_manufacturing_loss_factor`
+  (`id`,`order_id`,`order_code`,`factor_code`,`factor_name`,`count_value`,`ratio_pct`,`loss_hours`,
+   `sort_no`,`scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909390100 + (m.n - 1) * 5 + f.`sort_no`,
+       1329909300000 + m.n, f.`order_code`,
+       f.`factor_code`, f.`factor_name`, f.`count_value`, f.`ratio_pct`, f.`loss_hours`, f.`sort_no`,
+       f.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_manufacturing_loss_factor` f
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = f.`order_code`
+WHERE f.`tenant_id` = 797403760988230 AND f.`is_deleted` = 0 AND f.`order_id` IS NOT NULL
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_manufacturing_loss_factor` x
+                  WHERE x.`id` = 1329909390100 + (m.n - 1) * 5 + f.`sort_no`);
+
+-- ---------------------------------------------------------------------------
+-- 9) 本体生产 · 操作员表现:baseline 3 + 订单级 12 = 15 行
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_manufacturing_operator`
+  (`id`,`order_id`,`order_code`,`operator_code`,`operator_name`,`avg_hours`,`status`,`sort_no`,
+   `scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909400000 + o.rn, NULL, NULL,
+       o.`operator_code`, o.`operator_name`, o.`avg_hours`, o.`status`, o.`sort_no`,
+       o.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.`sort_no`) AS rn
+  FROM `ado_s8_order_flow_manufacturing_operator` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0 AND t.`order_id` IS NULL
+) o
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_manufacturing_operator` x
+                  WHERE x.`id` = 1329909400000 + o.rn);
+
+INSERT INTO `ado_s8_order_flow_manufacturing_operator`
+  (`id`,`order_id`,`order_code`,`operator_code`,`operator_name`,`avg_hours`,`status`,`sort_no`,
+   `scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909400100 + (m.n - 1) * 3 + o.`sort_no`,
+       1329909300000 + m.n, o.`order_code`,
+       o.`operator_code`, o.`operator_name`, o.`avg_hours`, o.`status`, o.`sort_no`,
+       o.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_manufacturing_operator` o
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = o.`order_code`
+WHERE o.`tenant_id` = 797403760988230 AND o.`is_deleted` = 0 AND o.`order_id` IS NOT NULL
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_manufacturing_operator` x
+                  WHERE x.`id` = 1329909400100 + (m.n - 1) * 3 + o.`sort_no`);
+
+-- ---------------------------------------------------------------------------
+-- 10) 总装发货 · 主门禁:baseline 5 + 订单级 15 = 20 行
+--     baseline 语义 = 清洁 PPT 基线(planned=actual、variance=0、全 green)→ 原样复制
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_final_assembly_gate`
+  (`id`,`order_id`,`order_code`,`gate_code`,`gate_name`,`planned_offset_days`,`actual_offset_days`,
+   `variance_days`,`status`,`impacted_order_count`,`risk_order_count`,`top_risk_type`,`top_risk_label`,
+   `owner_side_text`,`sort_no`,`scenario_code`,`data_source`,`tenant_id`,`factory_id`,
+   `created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909410000 + g.rn, NULL, NULL,
+       g.`gate_code`, g.`gate_name`, g.`planned_offset_days`, g.`actual_offset_days`,
+       g.`variance_days`, g.`status`, g.`impacted_order_count`, g.`risk_order_count`,
+       g.`top_risk_type`, g.`top_risk_label`, g.`owner_side_text`, g.`sort_no`,
+       g.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.`sort_no`) AS rn
+  FROM `ado_s8_order_flow_final_assembly_gate` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0 AND t.`order_id` IS NULL
+) g
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_final_assembly_gate` x
+                  WHERE x.`id` = 1329909410000 + g.rn);
+
+INSERT INTO `ado_s8_order_flow_final_assembly_gate`
+  (`id`,`order_id`,`order_code`,`gate_code`,`gate_name`,`planned_offset_days`,`actual_offset_days`,
+   `variance_days`,`status`,`impacted_order_count`,`risk_order_count`,`top_risk_type`,`top_risk_label`,
+   `owner_side_text`,`sort_no`,`scenario_code`,`data_source`,`tenant_id`,`factory_id`,
+   `created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909410100 + (m.n - 1) * 5 + (g.`sort_no` / 10),
+       1329909300000 + m.n, g.`order_code`,
+       g.`gate_code`, g.`gate_name`, g.`planned_offset_days`, g.`actual_offset_days`,
+       g.`variance_days`, g.`status`, g.`impacted_order_count`, g.`risk_order_count`,
+       g.`top_risk_type`, g.`top_risk_label`, g.`owner_side_text`, g.`sort_no`,
+       g.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_final_assembly_gate` g
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = g.`order_code`
+WHERE g.`tenant_id` = 797403760988230 AND g.`is_deleted` = 0 AND g.`order_id` IS NOT NULL
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_final_assembly_gate` x
+                  WHERE x.`id` = 1329909410100 + (m.n - 1) * 5 + (g.`sort_no` / 10));
+
+-- ---------------------------------------------------------------------------
+-- 11) 总装发货 · 并行准备:baseline 4 + 订单级 12 = 16 行
+-- ---------------------------------------------------------------------------
+INSERT INTO `ado_s8_order_flow_final_assembly_parallel`
+  (`id`,`order_id`,`order_code`,`prep_code`,`prep_name`,`kitting_rate`,`status`,
+   `impacted_order_count`,`risk_order_count`,`top_risk_type`,`top_risk_label`,`owner_side_text`,`sort_no`,
+   `scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909420000 + p.rn, NULL, NULL,
+       p.`prep_code`, p.`prep_name`, p.`kitting_rate`, p.`status`,
+       p.`impacted_order_count`, p.`risk_order_count`, p.`top_risk_type`, p.`top_risk_label`,
+       p.`owner_side_text`, p.`sort_no`,
+       p.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM (
+  SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.`sort_no`) AS rn
+  FROM `ado_s8_order_flow_final_assembly_parallel` t
+  WHERE t.`tenant_id` = 797403760988230 AND t.`is_deleted` = 0 AND t.`order_id` IS NULL
+) p
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_final_assembly_parallel` x
+                  WHERE x.`id` = 1329909420000 + p.rn);
+
+INSERT INTO `ado_s8_order_flow_final_assembly_parallel`
+  (`id`,`order_id`,`order_code`,`prep_code`,`prep_name`,`kitting_rate`,`status`,
+   `impacted_order_count`,`risk_order_count`,`top_risk_type`,`top_risk_label`,`owner_side_text`,`sort_no`,
+   `scenario_code`,`data_source`,`tenant_id`,`factory_id`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909420100 + (m.n - 1) * 4 + (p.`sort_no` / 10),
+       1329909300000 + m.n, p.`order_code`,
+       p.`prep_code`, p.`prep_name`, p.`kitting_rate`, p.`status`,
+       p.`impacted_order_count`, p.`risk_order_count`, p.`top_risk_type`, p.`top_risk_label`,
+       p.`owner_side_text`, p.`sort_no`,
+       p.`scenario_code`, 'SEED', 838257186181189, 838257186320453, '2026-08-27 00:00:00', NULL, 0
+FROM `ado_s8_order_flow_final_assembly_parallel` p
+JOIN (
+  SELECT 'SO-2026-001' AS order_code, 1 AS n UNION ALL SELECT 'SO-2026-002', 2 UNION ALL
+  SELECT 'SO-2026-012', 3 UNION ALL SELECT 'SO-2026-019', 4 UNION ALL SELECT 'SO-2026-020', 5
+) m ON m.order_code = p.`order_code`
+WHERE p.`tenant_id` = 797403760988230 AND p.`is_deleted` = 0 AND p.`order_id` IS NOT NULL
+  AND NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_final_assembly_parallel` x
+                  WHERE x.`id` = 1329909420100 + (m.n - 1) * 4 + (p.`sort_no` / 10));
+
+-- =====================================================================================
+-- 12) SO-2026-012 对照单重算:采购延期 +2 天 → 本体生产追回 2 天 → 总装追回 1 天
+--     stage 实际 5/3/16/4/2 = 30(planned 5/3/14/6/3 = 31 = target_cycle_days,不变)
+--     全部为对固定值的 UPDATE,天然幂等;只作用于 UAT scope,源租户不受影响。
+-- =====================================================================================
+
+-- 12.1 阶段:BODY_PRODUCTION 6→4,FINAL_ASSEMBLY 3→2,同步 variance 链与实际日期
+UPDATE `ado_s8_order_flow_stage`
+SET `actual_days` = 4.0, `node_variance_days` = -2.0, `cumulative_variance_days` = 0.0,
+    `actual_start_at` = '2026-08-13 09:00:00', `actual_end_at` = '2026-08-17 09:00:00'
+WHERE `tenant_id` = 838257186181189 AND `factory_id` = 838257186320453
+  AND `order_id` = 1329909300003 AND `order_flow_code` = 'BODY_PRODUCTION';
+
+UPDATE `ado_s8_order_flow_stage`
+SET `actual_days` = 2.0, `node_variance_days` = -1.0, `cumulative_variance_days` = -1.0,
+    `actual_start_at` = '2026-08-17 09:00:00', `actual_end_at` = '2026-08-19 09:00:00'
+WHERE `tenant_id` = 838257186181189 AND `factory_id` = 838257186320453
+  AND `order_id` = 1329909300003 AND `order_flow_code` = 'FINAL_ASSEMBLY_DELIVERY';
+
+-- 12.2 工序明细:按既有算法 actual[i] = round(baseline_actual[i] × A / 6.8, 3),A=4.0;
+--      末位守恒尾差修正;plan_qty = round(baseline_plan × A/6.8);
+--      cycle_status = ≤pi green / ≤pi×1.2 yellow / 否则 red(achievement_* 为工序固定属性,不随 A 变)
+UPDATE `ado_s8_order_flow_manufacturing_process`
+SET `actual_days` = 1.412, `plan_qty` = 26, `cycle_status` = 'red'
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `process_code` = 'P10';
+UPDATE `ado_s8_order_flow_manufacturing_process`
+SET `actual_days` = 0.471, `plan_qty` = 2, `cycle_status` = 'green'
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `process_code` = 'P20';
+UPDATE `ado_s8_order_flow_manufacturing_process`
+SET `actual_days` = 1.765, `plan_qty` = 9, `cycle_status` = 'green'
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `process_code` = 'P30';
+UPDATE `ado_s8_order_flow_manufacturing_process`
+SET `actual_days` = 0.352, `plan_qty` = 1, `cycle_status` = 'green'
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `process_code` = 'P40';
+UPDATE `ado_s8_order_flow_manufacturing_process`
+SET `actual_days` = 4.000, `cycle_status` = 'green'
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `process_code` = 'TOTAL';
+
+-- 12.3 损失因素:count = round(baseline_count × A/6.8),loss_hours = round(baseline_hours × A/6.8, 2)
+UPDATE `ado_s8_order_flow_manufacturing_loss_factor`
+SET `count_value` = 5, `loss_hours` = 5.29
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `factor_code` = 'MATERIAL';
+UPDATE `ado_s8_order_flow_manufacturing_loss_factor`
+SET `count_value` = 4, `loss_hours` = 2.65
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `factor_code` = 'EQUIPMENT';
+UPDATE `ado_s8_order_flow_manufacturing_loss_factor`
+SET `count_value` = 2, `loss_hours` = 2.00
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `factor_code` = 'QUALITY';
+UPDATE `ado_s8_order_flow_manufacturing_loss_factor`
+SET `count_value` = 7, `loss_hours` = 5.44
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `factor_code` = 'EFFICIENCY';
+UPDATE `ado_s8_order_flow_manufacturing_loss_factor`
+SET `count_value` = 18, `loss_hours` = 4.41
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `factor_code` = 'SUBTOTAL';
+
+-- 12.4 操作员表现:avg_hours = round(baseline_hours × A/6.8, 2)(status 为操作员固定属性)
+UPDATE `ado_s8_order_flow_manufacturing_operator`
+SET `avg_hours` = 11.76
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `operator_code` = 'OP_ZHANG';
+UPDATE `ado_s8_order_flow_manufacturing_operator`
+SET `avg_hours` = 5.88
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `operator_code` = 'OP_LI';
+UPDATE `ado_s8_order_flow_manufacturing_operator`
+SET `avg_hours` = 4.71
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `operator_code` = 'OP_WANG';
+
+-- 12.5 总装门禁:scale = stage.actual_days / 3.0 = 2/3;末位守恒修正 = stage.actual_days
+--      variance = actual - planned(全为负 = 提前,status green)
+UPDATE `ado_s8_order_flow_final_assembly_gate`
+SET `actual_offset_days` = 0.400, `variance_days` = -0.200, `status` = 'green',
+    `impacted_order_count` = 0, `risk_order_count` = 0, `top_risk_type` = NULL, `top_risk_label` = NULL
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `gate_code` = 'ASSEMBLY_COMPLETION';
+UPDATE `ado_s8_order_flow_final_assembly_gate`
+SET `actual_offset_days` = 0.800, `variance_days` = -0.400, `status` = 'green',
+    `impacted_order_count` = 0, `risk_order_count` = 0, `top_risk_type` = NULL, `top_risk_label` = NULL
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `gate_code` = 'QUALITY_RELEASE';
+UPDATE `ado_s8_order_flow_final_assembly_gate`
+SET `actual_offset_days` = 1.200, `variance_days` = -0.600, `status` = 'green',
+    `impacted_order_count` = 0, `risk_order_count` = 0, `top_risk_type` = NULL, `top_risk_label` = NULL
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `gate_code` = 'PACKAGING_COMPLETION';
+UPDATE `ado_s8_order_flow_final_assembly_gate`
+SET `actual_offset_days` = 1.600, `variance_days` = -0.800, `status` = 'green',
+    `impacted_order_count` = 0, `risk_order_count` = 0, `top_risk_type` = NULL, `top_risk_label` = NULL
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `gate_code` = 'GOODS_HANDOVER';
+UPDATE `ado_s8_order_flow_final_assembly_gate`
+SET `actual_offset_days` = 2.000, `variance_days` = -1.000, `status` = 'green',
+    `impacted_order_count` = 0, `risk_order_count` = 0, `top_risk_type` = NULL, `top_risk_label` = NULL
+WHERE `tenant_id` = 838257186181189 AND `order_id` = 1329909300003 AND `gate_code` = 'SHIPMENT_CONFIRMATION';
+
+-- 12.6 SO-2026-001 源缺陷修正:MATERIAL_PURCHASE 标注 actual_days=13,
+--      但其自身日期跨度(+96 后 2026-07-30 → 2026-08-11)实为 12 天,
+--      且该单其余 4 阶段的天数与日期跨度均自洽、5 阶段日期跨度合计 30 = actual_cycle_days=30。
+--      故 13 为源模板笔误;在 UAT 副本内校正为 12,使 stage 合计 = 订单周期(源租户不改)。
+--      连带修正 variance 链:nv = 12-14 = -2.0;cv = 2.0 + (-2.0) = 0.0(由 red 阶段回正)。
+UPDATE `ado_s8_order_flow_stage`
+SET `actual_days` = 12.0, `node_variance_days` = -2.0, `cumulative_variance_days` = 0.0, `status` = 'green'
+WHERE `tenant_id` = 838257186181189 AND `factory_id` = 838257186320453
+  AND `order_id` = 1329909300001 AND `order_flow_code` = 'MATERIAL_PURCHASE';
+
+UPDATE `ado_s8_order_flow_stage`
+SET `cumulative_variance_days` = -1.0
+WHERE `tenant_id` = 838257186181189 AND `factory_id` = 838257186320453
+  AND `order_id` = 1329909300001 AND `order_flow_code` = 'BODY_PRODUCTION';
+
+UPDATE `ado_s8_order_flow_stage`
+SET `cumulative_variance_days` = -1.0
+WHERE `tenant_id` = 838257186181189 AND `factory_id` = 838257186320453
+  AND `order_id` = 1329909300001 AND `order_flow_code` = 'FINAL_ASSEMBLY_DELIVERY';
+
+-- =====================================================================================
+-- 13) 链路聚合快照:按 Golden Roster 实算(D2-A),不复制 PPT 的 total_orders=105
+--     total_orders / total_customers / 三项 avg / 5 阶段 JSON 全部由 UAT 数据聚合得出
+-- =====================================================================================
+INSERT INTO `ado_s8_order_flow_snapshot`
+  (`id`,`tenant_id`,`factory_id`,`snapshot_code`,`scope_code`,`total_orders`,`total_customers`,
+   `avg_response_minutes`,`avg_processing_minutes`,`avg_loss_minutes`,`stage_snapshots_json`,
+   `scenario_code`,`data_source`,`created_at`,`updated_at`,`is_deleted`)
+SELECT 1329909370001, 838257186181189, 838257186320453,
+       'CHAIN_AGGREGATE_BASELINE', 'BASELINE_PPT',
+       agg.total_orders, agg.total_customers,
+       agg.avg_r, agg.avg_p, agg.avg_l,
+       js.stage_json,
+       'DEMO', 'SEED', '2026-08-27 00:00:00', NULL, 0
+FROM (
+  SELECT COUNT(*) AS total_orders,
+         COUNT(DISTINCT `customer_code`) AS total_customers,
+         ROUND(AVG(`response_minutes`), 2)   AS avg_r,
+         ROUND(AVG(`processing_minutes`), 2) AS avg_p,
+         ROUND(AVG(`total_loss_minutes`), 2) AS avg_l
+  FROM `ado_s8_order_flow_order`
+  WHERE `tenant_id` = 838257186181189 AND `factory_id` = 838257186320453 AND `is_deleted` = 0
+) agg
+CROSS JOIN (
+  SELECT CAST(CONCAT('[', GROUP_CONCAT(j ORDER BY sn SEPARATOR ','), ']') AS CHAR) AS stage_json
+  FROM (
+    SELECT MIN(st.`sort_no`) AS sn,
+           JSON_OBJECT(
+             'orderFlowCode',  st.`order_flow_code`,
+             'orderFlowName',  MIN(st.`order_flow_name`),
+             'kpiAvgDays',     CAST(ROUND(AVG(st.`planned_days`), 1) AS DECIMAL(10,1)),
+             'actualAvgDays',  CAST(ROUND(AVG(st.`actual_days`), 1) AS DECIMAL(10,1)),
+             'onTimeRate',     CAST(ROUND(100 * SUM(st.`actual_days` IS NOT NULL AND st.`actual_days` <= st.`planned_days`)
+                                          / NULLIF(SUM(st.`actual_days` IS NOT NULL), 0), 0) AS SIGNED),
+             'green',          CAST(SUM(st.`status` = 'green')   AS SIGNED),
+             'yellow',         CAST(SUM(st.`status` = 'yellow')  AS SIGNED),
+             'red',            CAST(SUM(st.`status` = 'red')     AS SIGNED),
+             'pending',        CAST(SUM(st.`status` = 'pending') AS SIGNED)
+           ) AS j
+    FROM `ado_s8_order_flow_stage` st
+    WHERE st.`tenant_id` = 838257186181189 AND st.`factory_id` = 838257186320453 AND st.`is_deleted` = 0
+    GROUP BY st.`order_flow_code`
+  ) t
+) js
+WHERE NOT EXISTS (SELECT 1 FROM `ado_s8_order_flow_snapshot` x WHERE x.`id` = 1329909370001);

+ 6 - 1
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/OrderFlow/S8OrderFlowService.cs

@@ -252,9 +252,14 @@ public class S8OrderFlowService : ITransient
         var result = orderCodes.ToDictionary(c => c, _ => (Count: 0, Status: string.Empty));
         if (orderCodes.Count == 0) return result;
 
+        // S8-STEP5A-ORDER-EXEC-CLOSURE-1(F-1):**移除 source_object_type == "SALES_ORDER" 过滤**。
+        // related_object_code 才是「异常挂在哪张销售订单上」的关联键;source_object_type 描述的是
+        // **触发对象类型**(PURCHASE_ORDER / WORK_ORDER / SHIPMENT / ORDER / NULL),与订单归属正交。
+        // 原实现只认 SALES_ORDER,导致由采购单 / 工单 / 发运触发但同属该销售订单的异常被整体漏计——
+        // Golden 14 条实测 0/14 命中,订单卡「异常」列恒为 0,与 Dashboard byObject 口径自相矛盾。
+        // 作用域安全性不变:tenant/factory 过滤保留,且 orderCodes 本身已限定为本作用域内的订单。
         var rows = await _exceptionRep.AsQueryable()
             .Where(e => e.TenantId == tenantId && e.FactoryId == factoryId && !e.IsDeleted)
-            .Where(e => e.SourceObjectType == ExceptionSourceObjectType)
             .Where(e => orderCodes.Contains(e.RelatedObjectCode!))
             .Select(e => new { e.RelatedObjectCode, e.Severity })
             .ToListAsync();

+ 30 - 24
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8ExceptionService.cs

@@ -56,7 +56,12 @@ public class S8ExceptionService : ITransient
             .LeftJoin<AdoS8WatchRule>((e, sc, wr) =>
                 e.TenantId == wr.TenantId && e.FactoryId == wr.FactoryId && e.SourceRuleCode == wr.RuleCode)
             .Where((e, sc, wr) => e.TenantId == q.TenantId && e.FactoryId == q.FactoryId && !e.IsDeleted)
-            .WhereIF(!includeUnclassified, (e, sc, wr) => !SqlFunc.IsNullOrEmpty(e.ExceptionTypeCode))
+            // S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-1):默认列表的「已分类」口径同时要求 stage_code 非空。
+            // stage_code 是 Monitoring 聚合键(GetSummaryAsync 按 S8ModuleCode.All 过滤),为空即「未归入任何业务域」,
+            // 与 exception_type_code 为空同属未分类。所有建单入口(S8ManualReportService 的手工/watch/hit 三分支)
+            // 都会写 stage_code,故本条只影响历史遗留行;下钻仍可用 includeUnclassified=true 查看全量。
+            .WhereIF(!includeUnclassified, (e, sc, wr) =>
+                !SqlFunc.IsNullOrEmpty(e.ExceptionTypeCode) && !SqlFunc.IsNullOrEmpty(e.StageCode))
             .WhereIF(!string.IsNullOrWhiteSpace(q.Status), (e, sc, wr) => e.Status == q.Status)
             .WhereIF(string.IsNullOrWhiteSpace(q.Status) && q.StatusBucket == "pending",
                 (e, sc, wr) => pendingStatuses.Contains(e.Status))
@@ -413,15 +418,23 @@ public class S8ExceptionService : ITransient
             .Distinct()
             .ToList();
 
-        // S8-EXCEPTION-DEPT-NAME-HYDRATION-MASTERDATA-FACTORY-FIX-1:
-        // 部门主数据位于 S0 域 factory(与 S8 运营 factoryId 不同源),且 DepartmentMaster.tenant_id 属 S0 域租户。
-        // 用主数据 factory + ClearFilter(factory_ref_id + 具体 dept_id 集合做硬边界,等价安全,无跨租户泄漏),
-        // 取代旧的“运营 factoryId + 全局租户过滤”——后者必然查空 → 全部回落“部门ID:N”。
-        var masterDataFactoryId = await ResolveMasterDataDepartmentFactoryRefIdAsync(factoryId);
+        // S8-DEPARTMENT-LOOKUP-CLOSURE-1(B-1):部门水合改用**当前请求的 trusted factoryId**,
+        // 与 S8DashboardService 既有可工作范式(d.FactoryRefId == factoryId)对齐。
+        //
+        // 原实现经 S8MasterData.DepartmentFactoryRefId 解析主数据 factory;该配置是**全局单值**,
+        // 在多租户下必然只能服务一个租户:实测它指向 factory 1329900200002,其下 DepartmentMaster
+        // 仅 1 行且 tenant_id=797403760988229,导致
+        //   * 其余租户(含 UAT 838257186181189)部门 0/N 解析,全部回落未解析文案;
+        //   * 唯一"解析成功"的 7 条实为**跨租户读取**(异常属 797403760988230,部门属 797403760988229)。
+        // 各 S8 运营租户的 DepartmentMaster.factory_ref_id 均等于其运营 factoryId(实测每租户 factory 唯一),
+        // 故 trusted factoryId 即正确的主数据边界,且天然不跨租户。
+        //
+        // 配置项本身保留(S8ManualReportService 的自动建单写路径仍在用),但对本显示路径而言标记为
+        // LEGACY / NON-MULTITENANT-SAFE FOR S8 EXCEPTION LOOKUP,不再让它无条件覆盖 trusted runtime factory。
         var deptMap = deptIds.Count == 0
             ? new Dictionary<long, string>()
             : (await _deptRep.AsQueryable().ClearFilter()
-                .Where(x => deptIds.Contains(x.Id) && x.FactoryRefId == masterDataFactoryId)
+                .Where(x => deptIds.Contains(x.Id) && x.FactoryRefId == factoryId)
                 .Select(x => new { x.Id, Name = x.Descr ?? x.Department })
                 .ToListAsync())
                 .ToDictionary(x => x.Id, x => x.Name);
@@ -473,30 +486,23 @@ public class S8ExceptionService : ITransient
         }
     }
 
-    // S8-DEPT-DISPLAY-CONSISTENCY-1(P0-A-3):dept fallback —— 0/缺失=未归属,查不到名=部门ID:{id}。
+    // S8-DEPT-DISPLAY-CONSISTENCY-1(P0-A-3):dept fallback —— 0/缺失=未归属。
+    // S8-EXCEPTION-FIRST-VIEW-CLOSURE-1(CC-2):解析不到主数据时不再回落「部门ID:{id}」——
+    // 裸内部 ID 对业务用户无意义,且只可能出现在 FK 悬空的历史行上,统一显示为业务可读文案。
+    internal const string UnresolvedLookupLabel = "历史数据 / 未配置";
+
     private static string? ResolveDeptName(long deptId, Dictionary<long, string> deptMap)
     {
         if (deptId <= 0) return "未归属";
         return deptMap.TryGetValue(deptId, out var name) && !string.IsNullOrWhiteSpace(name)
             ? name
-            : $"部门ID:{deptId}";
+            : UnresolvedLookupLabel;
     }
 
-    // S8-EXCEPTION-DEPT-NAME-HYDRATION-MASTERDATA-FACTORY-FIX-1:解析 DepartmentMaster 主数据所在 factory_ref_id。
-    // 优先读配置 S8MasterData.DepartmentFactoryRefId;缺失(<=0)则推导“唯一 active D-UNASSIGNED 的 FactoryRefId”。
-    // 显示路径不抛错:推导 0 条 / 不唯一时退回运营 factoryId(保持旧“部门ID:N”兜底,绝不让列表/详情查询失败)。
-    private async Task<long> ResolveMasterDataDepartmentFactoryRefIdAsync(long fallbackFactoryId)
-    {
-        var configured = _masterDataOptions.DepartmentFactoryRefId;
-        if (configured > 0) return configured;
-
-        var factories = await _deptRep.AsQueryable().ClearFilter()
-            .Where(x => x.Department == UnassignedDepartmentCode && x.IsActive)
-            .Select(x => x.FactoryRefId)
-            .Distinct()
-            .ToListAsync();
-        return factories.Count == 1 ? factories[0] : fallbackFactoryId;
-    }
+    // S8-DEPARTMENT-LOOKUP-CLOSURE-1(B-1):原 ResolveMasterDataDepartmentFactoryRefIdAsync 已移除。
+    // 它把全局单值配置 S8MasterData.DepartmentFactoryRefId 作为部门主数据 factory,在多租户下不安全
+    // (实测导致跨租户读取 + 其余租户全部解析失败)。显示路径改为直接使用 trusted factoryId,
+    // 与 S8DashboardService 同口径。配置项与 S8ManualReportService 的写路径用法保持不变,未触碰。
 
     /// <summary>
     /// S2-EW:debug-only 软删 RelatedObjectCode 以 "TEST_G09_" 开头的异常。

+ 43 - 16
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8ManualReportService.cs

@@ -167,6 +167,7 @@ public class S8ManualReportService : ITransient
 
     private async Task<AutoWatchDeptResolution> ResolveAutoWatchDepartmentsAsync(
         string path,
+        long tenantId,
         long factoryId,
         long? hitOccurrenceDeptId,
         long? hitResponsibleDeptId,
@@ -182,11 +183,11 @@ public class S8ManualReportService : ITransient
     {
         var result = new AutoWatchDeptResolution();
 
-        // S8-AUTO-WATCH-DEPT-RESOLVE-MASTERDATA-FACTORY-FIX-1:
-        // S8 运营 factoryId(异常/规则/调度归属)≠ DepartmentMaster 主数据 factory
-        // 部门解析(hit/params/D-UNASSIGNED)一律按主数据 factory,不再用运营 factoryId 错杀。
-        // 配置缺失/不唯一时此处抛出明确错误(区分缺配置 vs D-UNASSIGNED 不存在/不唯一)
-        var masterDataFactoryId = await ResolveMasterDataDepartmentFactoryRefIdAsync();
+        // S8-STEP3.5-INTEGRITY-CLOSURE-1(B-2):部门主数据 factory 改为**优先当前请求 trusted factoryId**,
+        // 与 S8DashboardService / S8ExceptionService(B-1 已修)统一
+        // 原实现无条件使用全局单值 S8MasterData.DepartmentFactoryRefId,在多租户下会把本租户的合法部门全部判为非法,
+        // 再回落到全库不存在的 D-UNASSIGNED,最终必然抛错——自动建单(watch/hit)对所有 S8 租户都不可用
+        var masterDataFactoryId = await ResolveDepartmentFactoryRefIdAsync(tenantId, factoryId);
 
         // 1) hit dept 优先(按主数据 factory 校验)
         var occOk = await ValidateDeptForMasterDataFactoryAsync(hitOccurrenceDeptId, masterDataFactoryId);
@@ -262,25 +263,49 @@ public class S8ManualReportService : ITransient
     }
 
     /// <summary>
-    /// S8-AUTO-WATCH-DEPT-RESOLVE-MASTERDATA-FACTORY-FIX-1:解析 DepartmentMaster 主数据所在 factory_ref_id。
-    /// 优先读配置 S8MasterData.DepartmentFactoryRefId;配置缺失(&lt;=0)时 fallback 推导“唯一 active D-UNASSIGNED 的 FactoryRefId”。
-    /// 推导结果 0 条 → 抛“缺配置且无 D-UNASSIGNED”;&gt;1 条 → 抛“D-UNASSIGNED 跨 factory 不唯一”。不跨 factory 猜测。
+    /// S8-STEP3.5-INTEGRITY-CLOSURE-1(B-2):解析 DepartmentMaster 主数据所在 factory_ref_id。
+    /// 顺序:① 当前请求 trusted factoryId(其下存在 active 部门即采用,与 S8DashboardService / S8ExceptionService 一致)
+    /// → ② 配置 S8MasterData.DepartmentFactoryRefId,**且必须校验其部门主数据属于当前 trusted tenant**(跨租户一律拒绝)
+    /// → ③ 当前租户下唯一 active D-UNASSIGNED 的 FactoryRefId。三者皆不成立才抛错,错误信息带上各步实际取值。
+    /// 取代旧的 S8-AUTO-WATCH-DEPT-RESOLVE-MASTERDATA-FACTORY-FIX-1 实现:它把全局单值配置无条件当作主数据 factory,
+    /// 与 B-1 同源缺陷(跨租户读 / 本租户合法部门被判非法)。
     /// </summary>
-    private async Task<long> ResolveMasterDataDepartmentFactoryRefIdAsync()
+    private async Task<long> ResolveDepartmentFactoryRefIdAsync(long tenantId, long factoryId)
     {
+        // ① 当前 trusted factory 自带部门主数据 → 直接使用,不再跨 factory 绕行。
+        if (factoryId > 0)
+        {
+            var hasOwnDept = await _deptRep.AsQueryable().ClearFilter()
+                .Where(x => x.FactoryRefId == factoryId && x.IsActive)
+                .AnyAsync();
+            if (hasOwnDept) return factoryId;
+        }
+
+        // ② 配置 override 仅在「属于当前租户」时才被接受,避免复制 B-1 的跨租户错误范式。
         var configured = _masterDataOptions.DepartmentFactoryRefId;
-        if (configured > 0) return configured;
+        if (configured > 0)
+        {
+            var inScope = await _deptRep.AsQueryable().ClearFilter()
+                .Where(x => x.FactoryRefId == configured && x.TenantId == tenantId && x.IsActive)
+                .AnyAsync();
+            if (inScope) return configured;
+            _logger.LogWarning(
+                "s8_dept_factory_config_out_of_scope configured={Configured} tenantId={TenantId} factoryId={FactoryId} reason=configured_factory_not_in_current_tenant",
+                configured, tenantId, factoryId);
+        }
 
+        // ③ 当前租户下唯一 active D-UNASSIGNED 推导;跨租户不猜测。
         var factories = await _deptRep.AsQueryable().ClearFilter()
-            .Where(x => x.Department == UnassignedDepartmentCode && x.IsActive)
+            .Where(x => x.Department == UnassignedDepartmentCode && x.TenantId == tenantId && x.IsActive)
             .Select(x => x.FactoryRefId)
             .Distinct()
             .ToListAsync();
-        if (factories.Count == 0)
-            throw new S8BizException($"自动建单部门解析失败:未配置 S8MasterData.DepartmentFactoryRefId,且主数据中不存在 active {UnassignedDepartmentCode} 可推导主数据 factory");
-        if (factories.Count > 1)
-            throw new S8BizException($"自动建单部门解析失败:未配置 S8MasterData.DepartmentFactoryRefId,且 active {UnassignedDepartmentCode} 跨 {factories.Count} 个 factory 不唯一,无法推导主数据 factory");
-        return factories[0];
+        if (factories.Count == 1) return factories[0];
+
+        throw new S8BizException(
+            $"自动建单部门解析失败:当前 factory_id={factoryId} 下无 active 部门主数据;" +
+            $"配置 S8MasterData.DepartmentFactoryRefId={configured} 不属当前租户 {tenantId};" +
+            $"且本租户 active {UnassignedDepartmentCode} 数量={factories.Count}(需恰为 1)无法推导主数据 factory");
     }
 
     // S8-AUTO-WATCH-DEPT-RESOLVE-MASTERDATA-FACTORY-FIX-1:按主数据 factory 校验部门(取代旧的按 S8 运营 factory 校验)。
@@ -677,6 +702,7 @@ public class S8ManualReportService : ITransient
         // S8-AUTO-WATCH-DEPT-RESOLVE-1(P2):部门解析顺序 hit → params → unassigned;resolver 失败 → throw。
         var deptResolution = await ResolveAutoWatchDepartmentsAsync(
             path: nameof(CreateFromWatchAsync),
+            tenantId: tenantId,
             factoryId: factoryId,
             hitOccurrenceDeptId: hit.OccurrenceDeptId,
             hitResponsibleDeptId: hit.ResponsibleDeptId,
@@ -801,6 +827,7 @@ public class S8ManualReportService : ITransient
         // S8-AUTO-WATCH-DEPT-RESOLVE-1(P2):部门解析顺序 hit → params → unassigned;resolver 失败 → throw。
         var deptResolution = await ResolveAutoWatchDepartmentsAsync(
             path: nameof(CreateFromHitAsync),
+            tenantId: tenantId,
             factoryId: factoryId,
             hitOccurrenceDeptId: hit.OccurrenceDeptId,
             hitResponsibleDeptId: hit.ResponsibleDeptId,

+ 108 - 20
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8MonitoringService.cs

@@ -18,19 +18,30 @@ public class S8MonitoringService : ITransient
     private readonly SqlSugarRepository<AdoS8MonitorMetric> _metricRep;
     // S9-RESULT-KPI-DEMO-BASELINE-AND-ORDER-FLOW-CALC-1:ORDER_FLOW_CYCLE_RATIO 半真实计算依赖订单链路阶段表
     private readonly SqlSugarRepository<AdoS8OrderFlowStage> _orderFlowStageRep;
+    // S8-STEP4-KPI-CURRENT-VALUE-CLOSURE-1:ORDER_DELIVERY_RATE / PO_DELIVERY_RATE / WO_COMPLETION_RATE
+    // 由 DEMO_BASELINE 常量改为订单链路真实聚合,各自依赖以下三张表。
+    private readonly SqlSugarRepository<AdoS8OrderFlowOrder> _orderFlowOrderRep;
+    private readonly SqlSugarRepository<AdoS8OrderFlowProcurementPivot> _procurementPivotRep;
+    private readonly SqlSugarRepository<AdoS8OrderFlowManufacturingProcess> _manufacturingProcessRep;
 
     public S8MonitoringService(
         SqlSugarRepository<AdoS8Exception> rep,
         SqlSugarRepository<AdoS8ExceptionType> typeRep,
         SqlSugarRepository<AdoS0DepartmentMaster> deptRep,
         SqlSugarRepository<AdoS8MonitorMetric> metricRep,
-        SqlSugarRepository<AdoS8OrderFlowStage> orderFlowStageRep)
+        SqlSugarRepository<AdoS8OrderFlowStage> orderFlowStageRep,
+        SqlSugarRepository<AdoS8OrderFlowOrder> orderFlowOrderRep,
+        SqlSugarRepository<AdoS8OrderFlowProcurementPivot> procurementPivotRep,
+        SqlSugarRepository<AdoS8OrderFlowManufacturingProcess> manufacturingProcessRep)
     {
         _rep = rep;
         _typeRep = typeRep;
         _deptRep = deptRep;
         _metricRep = metricRep;
         _orderFlowStageRep = orderFlowStageRep;
+        _orderFlowOrderRep = orderFlowOrderRep;
+        _procurementPivotRep = procurementPivotRep;
+        _manufacturingProcessRep = manufacturingProcessRep;
     }
 
     /// <summary>
@@ -806,7 +817,11 @@ public class S8MonitoringService : ITransient
         foreach (var m in resolved)
         {
             var unit = string.IsNullOrWhiteSpace(m.Unit) ? "%" : m.Unit!;
-            if (_demoBaselineMap.TryGetValue(m.MetricCode, out var baseline))
+
+            // S8-STEP4-KPI-CURRENT-VALUE-CLOSURE-1:CurrentValue 优先走订单链路真实聚合;
+            // 仅 IQC_PASS_RATE 因 S8 无权威 IQC 源,保留 DEMO_BASELINE(provenance 由 Source 字段如实标注)。
+            var computed = await ComputeResultKpiCurrentAsync(m.MetricCode, tenantId, factoryId);
+            if (computed != null)
             {
                 items.Add(new AdoS8ResultKpiItemDto
                 {
@@ -814,31 +829,29 @@ public class S8MonitoringService : ITransient
                     MetricName = m.MetricName,
                     ObjectCode = m.ObjectCode,
                     Unit = unit,
-                    CurrentValue = baseline.CurrentValue,
-                    // S8-CONFIG-GLOBAL-ROW-SEMANTICS-AND-KPI-TARGET-1:目标值改为 DB 唯一事实源
-                    // (工厂覆盖 > 平台默认),不再由 _demoBaselineMap 覆盖;未配置时返回 null,
-                    // 前端显示「目标 待配置」,不做 C# 常量兜底。CurrentValue 维持原样,不属本批范围。
+                    CurrentValue = computed.Value.CurrentValue,
+                    // S8-CONFIG-GLOBAL-ROW-SEMANTICS-AND-KPI-TARGET-1:目标值 DB 唯一事实源
+                    // (工厂覆盖 > 平台默认),与 CurrentValue 完全解耦;未配置时返回 null,
+                    // 前端显示「目标 待配置」,不做 C# 常量兜底。
                     TargetRatio = ResolveEffectiveTarget(m),
                     DictionaryEnabled = true,
-                    Remark = baseline.Remark,
-                    Source = "DEMO_BASELINE",
+                    Remark = computed.Value.Remark,
+                    Source = computed.Value.Source,
                 });
             }
-            else if (m.MetricCode == "ORDER_FLOW_CYCLE_RATIO")
+            else if (_demoBaselineMap.TryGetValue(m.MetricCode, out var baseline))
             {
-                var (cv, source) = await ComputeOrderFlowCycleRatioAsync(tenantId, factoryId);
                 items.Add(new AdoS8ResultKpiItemDto
                 {
                     MetricCode = m.MetricCode,
                     MetricName = m.MetricName,
                     ObjectCode = m.ObjectCode,
                     Unit = unit,
-                    CurrentValue = cv,
-                    // S8-CONFIG-GLOBAL-ROW-SEMANTICS-AND-KPI-TARGET-1:原 90.0m 字面量改为 DB 生效目标值。
+                    CurrentValue = baseline.CurrentValue,
                     TargetRatio = ResolveEffectiveTarget(m),
                     DictionaryEnabled = true,
-                    Remark = "基于已完成订单链路阶段 actual_days/planned_days 计算,pending 阶段暂不纳入",
-                    Source = source,
+                    Remark = baseline.Remark,
+                    Source = "DEMO_BASELINE",
                 });
             }
             else
@@ -867,17 +880,92 @@ public class S8MonitoringService : ITransient
 
     /// <summary>
     /// KPI 当前值演示基线。S8-CONFIG-GLOBAL-ROW-SEMANTICS-AND-KPI-TARGET-1 已把 TargetRatio 从本 map 解耦
-    /// (目标值改由 ado_s8_monitor_metric.default_target_ratio 承载);本 map 只保留 CurrentValue 与 Remark,
-    /// 其归属仍待业务拍板,本批不改数值、不删除、不做真实计算。
+    /// (目标值由 ado_s8_monitor_metric.default_target_ratio 承载),本 map 只保留 CurrentValue 与 Remark。
+    /// S8-STEP4-KPI-CURRENT-VALUE-CLOSURE-1:ORDER_DELIVERY_RATE / PO_DELIVERY_RATE / WO_COMPLETION_RATE
+    /// 三项已迁到订单链路真实聚合(见 ComputeResultKpiCurrentAsync),从本 map 移除。
+    /// 仅 IQC_PASS_RATE 保留——S8 域内无任何权威 IQC 检验源表(IQC 明细归属 S6 qms_gcjydzb,未纳入 S8 订单链路),
+    /// 因此它是**演示基线而非实算**,由 Source="DEMO_BASELINE" 如实对外标注,前端文案为「当前基线」。
     /// </summary>
     private static readonly Dictionary<string, (decimal CurrentValue, string Remark)> _demoBaselineMap = new()
     {
-        ["ORDER_DELIVERY_RATE"] = (92.0m, "演示基线,待真实订单交付口径接入"),
-        ["PO_DELIVERY_RATE"]    = (88.0m, "演示基线,待采购到货真实表接入"),
-        ["IQC_PASS_RATE"]       = (96.0m, "演示基线,待 IQC 检验真实表接入"),
-        ["WO_COMPLETION_RATE"]  = (90.0m, "演示基线,待工单完工真实表接入"),
+        ["IQC_PASS_RATE"] = (96.0m, "演示基线(非实算):S8 域内无权威 IQC 检验源表,待接入后改为真实计算"),
     };
 
+    /// <summary>
+    /// S8-STEP4-KPI-CURRENT-VALUE-CLOSURE-1:结果 KPI 当前值的真实聚合分发。
+    /// 返回 null 表示该指标不由订单链路计算(调用方回落 DEMO_BASELINE / PENDING_REAL)。
+    /// 统一口径:只统计 tenant/factory 内未删除、且 <c>order_code</c> 非空的**订单关联业务记录**;
+    /// order_code 为空的 BASELINE_PPT / DEMO 演示装置行不是业务记录,一律排除。
+    /// </summary>
+    private async Task<(decimal? CurrentValue, string Source, string Remark)?> ComputeResultKpiCurrentAsync(
+        string metricCode, long tenantId, long factoryId) => metricCode switch
+    {
+        "ORDER_DELIVERY_RATE"    => await ComputeOrderDeliveryRateAsync(tenantId, factoryId),
+        "PO_DELIVERY_RATE"       => await ComputePoDeliveryRateAsync(tenantId, factoryId),
+        "WO_COMPLETION_RATE"     => await ComputeWoCompletionRateAsync(tenantId, factoryId),
+        "ORDER_FLOW_CYCLE_RATIO" => await ComputeOrderFlowCycleRatioItemAsync(tenantId, factoryId),
+        _ => null,
+    };
+
+    /// <summary>
+    /// 订单交付满足率 = 已完工订单中 actual_cycle_days &lt;= target_cycle_days 的占比。
+    /// 分母只含 actual_cycle_days 已回填的订单(在制订单尚无实际周期,纳入会系统性低估)。
+    /// </summary>
+    private async Task<(decimal? CurrentValue, string Source, string Remark)> ComputeOrderDeliveryRateAsync(
+        long tenantId, long factoryId)
+    {
+        var rows = await _orderFlowOrderRep.AsQueryable()
+            .Where(x => !x.IsDeleted && x.TenantId == tenantId && x.FactoryId == factoryId
+                && x.ActualCycleDays != null)
+            .Select(x => new { x.ActualCycleDays, x.TargetCycleDays })
+            .ToListAsync();
+        const string remark = "基于订单链路已完工订单 actual_cycle_days/target_cycle_days 计算,在制订单不纳入分母";
+        if (rows.Count == 0) return (null, "ORDER_FLOW_CALC_EMPTY", remark);
+        var numerator = rows.Count(x => x.ActualCycleDays!.Value <= x.TargetCycleDays);
+        return (Math.Round(numerator * 100m / rows.Count, 1), "ORDER_FLOW_CALC", remark);
+    }
+
+    /// <summary>
+    /// 到货达成率 = 采购透视中 status='green'(按期到货)的占比;yellow/red 均计入分母。
+    /// </summary>
+    private async Task<(decimal? CurrentValue, string Source, string Remark)> ComputePoDeliveryRateAsync(
+        long tenantId, long factoryId)
+    {
+        var rows = await _procurementPivotRep.AsQueryable()
+            .Where(x => !x.IsDeleted && x.TenantId == tenantId && x.FactoryId == factoryId
+                && x.OrderCode != null && x.OrderCode != "")
+            .Select(x => x.Status)
+            .ToListAsync();
+        const string remark = "基于订单关联采购透视 status 计算:green 为按期到货,yellow/red 计入分母";
+        if (rows.Count == 0) return (null, "ORDER_FLOW_CALC_EMPTY", remark);
+        var numerator = rows.Count(s => s == "green");
+        return (Math.Round(numerator * 100m / rows.Count, 1), "ORDER_FLOW_CALC", remark);
+    }
+
+    /// <summary>
+    /// 工单完工率 = 订单关联制造工序 achievement_rate 的算术平均(存储为 0~1 比率,×100 转百分比)。
+    /// </summary>
+    private async Task<(decimal? CurrentValue, string Source, string Remark)> ComputeWoCompletionRateAsync(
+        long tenantId, long factoryId)
+    {
+        var rows = await _manufacturingProcessRep.AsQueryable()
+            .Where(x => !x.IsDeleted && x.TenantId == tenantId && x.FactoryId == factoryId
+                && x.OrderCode != null && x.OrderCode != "" && x.AchievementRate != null)
+            .Select(x => x.AchievementRate)
+            .ToListAsync();
+        const string remark = "基于订单关联制造工序 achievement_rate 平均值计算(0~1 比率换算百分比)";
+        if (rows.Count == 0) return (null, "ORDER_FLOW_CALC_EMPTY", remark);
+        return (Math.Round(rows.Sum(x => x!.Value) * 100m / rows.Count, 1), "ORDER_FLOW_CALC", remark);
+    }
+
+    /// <summary>ORDER_FLOW_CYCLE_RATIO 适配到统一分发签名;计算逻辑保持不变。</summary>
+    private async Task<(decimal? CurrentValue, string Source, string Remark)> ComputeOrderFlowCycleRatioItemAsync(
+        long tenantId, long factoryId)
+    {
+        var (cv, source) = await ComputeOrderFlowCycleRatioAsync(tenantId, factoryId);
+        return (cv, source, "基于已完成订单链路阶段 actual_days/planned_days 计算,pending 阶段暂不纳入");
+    }
+
     /// <summary>
     /// 生效目标值:rows 已按 (0,0) 全局 ‖ (tenant,factory) 覆盖取回并按 FactoryId 降序去重,
     /// 因此 <paramref name="m"/> 本身即生效行,直接取其 default_target_ratio。