|
|
@@ -0,0 +1,1222 @@
|
|
|
+# 九宫格最后真实值、数据新鲜度与源数据缺口根治执行任务书
|
|
|
+
|
|
|
+> 适用页面:九宫格智慧运营看板(FUNC-S9-005,`/aidop/smart-ops/grid`)
|
|
|
+> 目标读者:可独立执行代码、迁移、测试和部署验证的开发人员或大语言模型代理
|
|
|
+> 编写日期:2026-09-29
|
|
|
+> 基线提交:`f613da8bc508d10e146ebc51bc30be580d27b11a`
|
|
|
+> 基线版本:server `1.0.591` / Web `2.4.413`
|
|
|
+> UAT 作用域:tenant `838257186181189`、factory `1`
|
|
|
+> 任务类型:跨模块公共取数契约修复 / 数据新鲜度治理 / 源数据链路根治 / 安全治理
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 0. 执行者须知
|
|
|
+
|
|
|
+### 0.1 目标
|
|
|
+
|
|
|
+本任务的最终目标不是“把空格填上数字”,而是在**不编造、不跨租户、不越过用户筛选范围**的前提下,让九宫格同时表达:
|
|
|
+
|
|
|
+1. 最近一次真实可用的指标值;
|
|
|
+2. 该值对应的业务日期;
|
|
|
+3. 最新一次计算是否仍有业务数据;
|
|
|
+4. 该值是否已经超过指标自身的新鲜度 SLA;
|
|
|
+5. 从未产生过真实值时,明确显示「无业务数据」;
|
|
|
+6. 当前确实没有异常时,S8 明确显示「暂无异常告警」,而不是空白。
|
|
|
+
|
|
|
+同时修复导致当前值长期无法更新的三个根因:
|
|
|
+
|
|
|
+- 本地员工状态 `AC/DC` 未映射到中立状态;
|
|
|
+- T8 口令仍以错误的仓库明文/数据库复制思路处理;
|
|
|
+- 指定租户的 S5/S7 重算入口遗漏库存物化互斥锁,和半小时同步并发时产生死锁。
|
|
|
+
|
|
|
+### 0.2 四条禁止
|
|
|
+
|
|
|
+1. **禁止造数**:不得直接 UPDATE `ado_s9_kpi_value_l1_day.metric_value`,不得复制其它租户指标值,不得以前端常量补空。
|
|
|
+2. **禁止隐藏最新失败/无数据状态**:显示最后真实值时,必须同时暴露最新计算状态,不能把旧值冒充当前值。
|
|
|
+3. **禁止越过查询范围回退**:用户显式传 `dateStart/dateEnd` 时,只能在该区间内寻找可用值。
|
|
|
+4. **禁止提交凭据**:数据库口令、AccessSecret、连接串中的密码不得写入代码、迁移、任务书、测试快照、日志或 Git。
|
|
|
+
|
|
|
+### 0.3 本任务推翻的旧结论
|
|
|
+
|
|
|
+| 旧结论 | 正确结论 |
|
|
|
+|---|---|
|
|
|
+| “每指标取 `MAX(biz_date)` 已经完全正确” | 只解决了模块间日期不一致;当最新一行是 `NO_DATA + NULL` 时,它仍会遮住更早的真实值 |
|
|
|
+| “按卡返回 `bizDate` + 前端固定 7 天变灰,就是完整的截至日期方案” | 不完整。`HomeModuleKpiCard.vue` 会先因 `resultStatus='NO_DATA'` 隐藏数值;固定 7 天还会把月度指标长期误判为陈旧 |
|
|
|
+| “三个人效指标都只差 `employment_status`” | S5/S7 的仓储岗位有 5 人但状态全是 `UNKNOWN`;S6 还缺 `PRODUCTION` 岗位人员,不能把空岗位或仓储人员硬映射成生产人员 |
|
|
|
+| “S6 L1 无数据是因为 `mdp_std_s6_report` 本租户为 0” | S6 L1 当前实现主要读 `mdp_std_work_order_line`、`mdp_std_inv_trans` 和 DWD;必须按真实 SQL 查依赖,不能按指标名称猜 |
|
|
|
+| “从 `Database.json` 抄 T8 密码进 `mdp_source` 是最佳方案” | 功能上可临时接通,但不是安全终态;该文件受 Git 跟踪且存在明文凭据,终态应使用环境密钥/密钥服务并轮换已暴露口令 |
|
|
|
+
|
|
|
+### 0.4 必须遵守的仓库约束
|
|
|
+
|
|
|
+- 编码前先列实际改动文件并等待确认。
|
|
|
+- 任何扩大范围的修改必须补充「反向影响推演」。
|
|
|
+- “能力有/无”必须分代码层、配置层、数据层取证。
|
|
|
+- 新迁移不得修改已经成功执行的旧脚本;`AutoVersionUpdate` 会校验 SHA256。
|
|
|
+- 新增 `UpdateScripts/*.sql` 与 `.verify.sql` 必须在 `Admin.NET.Web.Entry.csproj` 登记。
|
|
|
+- 若最终提交同时包含前后端代码,server 与 Web 版本都 patch +1。
|
|
|
+- “提交”默认包含 commit + push。
|
|
|
+
|
|
|
+### 0.5 直接执行规则(本版已冻结设计选择)
|
|
|
+
|
|
|
+本版是**逐项直接执行版**。执行者不得在下列已经冻结的技术选择上另起方案:
|
|
|
+
|
|
|
+1. 九宫格值选择使用 MySQL 8 `ROW_NUMBER()`,不再保留“相关子查询或窗口函数二选一”。
|
|
|
+2. `HomeGridValueRow` 作为 `AidopKanbanController.Generic.cs` 内的私有嵌套 DTO,不新建共享 DTO,不修改 `S4ValRow`。
|
|
|
+3. 新鲜度字段固定为 `AdoSmartOpsKpiMaster.FreshnessSlaDays`;server 负责判定,前端只展示。
|
|
|
+4. 未来业务日的 `valueAgeDays` 固定钳制为 0,并记录不含业务数据内容的 warning。
|
|
|
+5. 员工状态不抽新框架:同步修改 Native SQL CASE 与 Inbound C# normalizer,并用契约测试锁住一致性。
|
|
|
+6. T8 密钥固定走环境变量 resolver;删除 `t8_v5` 注册连接与口令复制服务,不再保留两套来源。
|
|
|
+7. 库存物化锁固定等待 10 分钟、每 2 秒轮询一次;物化 SQL 遇 1213/1205 最多执行 3 次。
|
|
|
+8. 按 §10.1 固定拆包后,新鲜度迁移使用 `1.0.593.sql` / `1.0.593.verify.sql`。若执行时 server 已不再是 1.0.591,**停止执行并按四提交相对顺序整体顺延版本**,不得复用已存在版本号。
|
|
|
+9. WP-F 只允许按现有 `mdp_tenant_std_source` 权威绑定补通;绑定缺失或来源不提供对象时,执行者必须停下请求负责人决策,禁止自行切源。
|
|
|
+10. 实际密钥值注入与外部账号口令轮换是人工门禁;模型负责代码与无秘密配置,走到门禁必须明确停下,不得索要用户在对话中发送口令。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. 现状基线与真实根因
|
|
|
+
|
|
|
+### 1.1 真实取数链路
|
|
|
+
|
|
|
+```text
|
|
|
+Web/src/views/dashboard/home.vue
|
|
|
+ └─ HomeModuleKpiCard.vue
|
|
|
+ └─ fetchHomeGrid()
|
|
|
+ └─ GET /api/AidopKanban/home-grid/{moduleCode}
|
|
|
+ └─ AidopKanbanController.Generic.cs::GetHomeGridGeneric()
|
|
|
+ ├─ 卡片:ado_smart_ops_layout_item
|
|
|
+ ├─ 元数据:ado_smart_ops_kpi_master
|
|
|
+ └─ 数值:ado_s9_kpi_value_l1_day
|
|
|
+```
|
|
|
+
|
|
|
+`GetHomeGridGeneric` 当前值 SQL 位于
|
|
|
+`server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/AidopKanbanController.Generic.cs:95-109`:
|
|
|
+
|
|
|
+```sql
|
|
|
+AND v.biz_date=(
|
|
|
+ SELECT MAX(v2.biz_date)
|
|
|
+ ...
|
|
|
+ AND v2.metric_code=v.metric_code
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+该 SQL 会选中“最新尝试行”,不区分它是 `OK` 还是 `NO_DATA`。
|
|
|
+
|
|
|
+前端 `HomeModuleKpiCard.vue:41-47` 又明确规定:
|
|
|
+
|
|
|
+```js
|
|
|
+if (status === 'NO_DATA') return '无业务数据'
|
|
|
+```
|
|
|
+
|
|
|
+所以,即使历史上有真实数值,只要最新一期落了 `NO_DATA`,旧值就完全不可见。
|
|
|
+
|
|
|
+### 1.2 截图对应的数据基线
|
|
|
+
|
|
|
+2026-09-29 实测,红框中 7 张 `NO_DATA` 卡背后全部存在历史真实值:
|
|
|
+
|
|
|
+| 指标 | 最新尝试 | 最后真实值 |
|
|
|
+|---|---|---|
|
|
|
+| S5_L1_003 物料仓储人效 | `NO_DATA`,2026-08-31 | 693.3333,2026-07-31 |
|
|
|
+| S6_L1_001 订单制造周期 | `NO_DATA`,2026-09-28 | 3.0000,2026-09-23 |
|
|
|
+| S6_L1_002 订单制造满足率 | `NO_DATA`,2026-09-28 | 100.0000,2026-09-23 |
|
|
|
+| S6_L1_003 订单制造人效 | `NO_DATA`,2026-08-31 | 1.1250,2026-07-31 |
|
|
|
+| S7_L1_001 订单发货周期 | `NO_DATA`,2026-09-28 | 0.9667,2026-09-23 |
|
|
|
+| S7_L1_003 成品仓储人效 | `NO_DATA`,2026-08-31 | 433.3333,2026-07-31 |
|
|
|
+| S9_L1_001 质量退货率 | `NO_DATA`,2026-08-31 | 3000.0000,2026-07-31 |
|
|
|
+
|
|
|
+可重跑 SQL:
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT module_code, metric_code,
|
|
|
+ MAX(CASE WHEN result_status='OK' AND metric_value IS NOT NULL
|
|
|
+ THEN biz_date END) AS last_ok_date,
|
|
|
+ SUBSTRING_INDEX(
|
|
|
+ GROUP_CONCAT(
|
|
|
+ CASE WHEN result_status='OK' AND metric_value IS NOT NULL
|
|
|
+ THEN CONCAT(DATE_FORMAT(biz_date,'%Y-%m-%d'),'=',metric_value)
|
|
|
+ END
|
|
|
+ ORDER BY biz_date DESC
|
|
|
+ ), ',', 1
|
|
|
+ ) AS last_ok
|
|
|
+FROM ado_s9_kpi_value_l1_day
|
|
|
+WHERE tenant_id=838257186181189
|
|
|
+ AND factory_id=1
|
|
|
+ AND module_code IN ('S5','S6','S7','S9')
|
|
|
+ AND metric_code LIKE '%\_L1\_%'
|
|
|
+ AND is_deleted=0
|
|
|
+GROUP BY module_code, metric_code
|
|
|
+ORDER BY module_code, metric_code;
|
|
|
+```
|
|
|
+
|
|
|
+> 此 SQL 只用于基线取证。实现不得使用无上界的 `GROUP_CONCAT`;项目已发生过
|
|
|
+> `group_concat_max_len=1024` 导致的致命截断。
|
|
|
+
|
|
|
+### 1.3 S8 不是 KPI 缺失
|
|
|
+
|
|
|
+S8 首页读取 `ado_s8_alert_record`:
|
|
|
+
|
|
|
+- 列表端点:`GET /api/AidopKanban/s8-alerts`
|
|
|
+- 趋势端点:`GET /api/AidopKanban/s8-home-trend`
|
|
|
+- 代码:`AidopKanbanController.cs:128-300`
|
|
|
+
|
|
|
+数据层实测:
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT COUNT(*) AS all_alerts, MAX(alert_time) AS latest
|
|
|
+FROM ado_s8_alert_record
|
|
|
+WHERE tenant_id=838257186181189 AND factory_id=1 AND is_deleted=0;
|
|
|
+```
|
|
|
+
|
|
|
+返回 `all_alerts=0`。这是“当前没有告警”的真实状态,不允许生成模拟告警。当前缺陷只是前端没有显式空态。
|
|
|
+
|
|
|
+### 1.4 员工状态与岗位基线
|
|
|
+
|
|
|
+代码层:
|
|
|
+
|
|
|
+- `NativeNeutralProjectionService.cs:203-208` 只识别中文“在职/离职”和
|
|
|
+ `ACTIVE/ONJOB/...`,不识别本系统自身真实值 `AC/DC`。
|
|
|
+- `MdmMirrorUpsertService.cs:523-528` 的 `NormalizeEmployment` 同样不识别 `AC/DC`。
|
|
|
+- S5/S7 人效分母要求 `employment_status='ACTIVE' AND position_code='WAREHOUSE'`。
|
|
|
+- S6 人效分母要求 `employment_status='ACTIVE' AND position_code='PRODUCTION'`
|
|
|
+ (`S6MdpSyncTransformService.cs:688-691`)。
|
|
|
+
|
|
|
+配置/业务定义层:
|
|
|
+
|
|
|
+- `Web/src/views/aidop/s0/warehouse/EmployeeList.vue:167-170` 明确定义
|
|
|
+ `AC=在职`、`DC=离职`。
|
|
|
+- `UpdateScripts/1.0.402.sql:93` 也把 UAT 在职员工播种为 `AC`。
|
|
|
+
|
|
|
+数据层:
|
|
|
+
|
|
|
+```text
|
|
|
+EmployeeMaster: AC 23 人、空状态 3 人
|
|
|
+mdp_std_employee: ACTIVE 0 人
|
|
|
+岗位:WAREHOUSE 5、PLANNER 2、OTHER 8、UNKNOWN 11、PRODUCTION 0
|
|
|
+```
|
|
|
+
|
|
|
+因此:
|
|
|
+
|
|
|
+- `AC → ACTIVE`、`DC → LEFT` 是确定的映射修复;
|
|
|
+- 空状态必须继续是 `UNKNOWN`,不得因为 `DateTerminated IS NULL` 就推断在职;
|
|
|
+- 不能把 `UNKNOWN` 或 `WAREHOUSE` 人员映射成 `PRODUCTION` 来填 S6。
|
|
|
+
|
|
|
+### 1.5 T8 配置基线
|
|
|
+
|
|
|
+`mdp_source.source_code='T8_V5_SQLSERVER'` 当前:
|
|
|
+
|
|
|
+- `status=1`
|
|
|
+- `health_status=0`
|
|
|
+- `db_password_enc` 为空
|
|
|
+- 健康信息为数据库用户登录失败
|
|
|
+
|
|
|
+`MdpSourceConnectionImportService` 当前只在 `db_host` 为空时整行导入:
|
|
|
+
|
|
|
+```sql
|
|
|
+WHERE source_code='T8_V5_SQLSERVER' AND IFNULL(db_host,'')=''
|
|
|
+```
|
|
|
+
|
|
|
+该行 host 已存在、仅密码为空,所以服务永远不会补上密码。
|
|
|
+
|
|
|
+更重要的是:现有 `Database.json` 受 Git 跟踪且包含明文数据库口令。继续把其中口令复制到数据库只是扩大秘密副本,不是安全终态。
|
|
|
+
|
|
|
+### 1.6 S6/S7/S9 当前业务输入
|
|
|
+
|
|
|
+UAT 租户数据层实测:
|
|
|
+
|
|
|
+| 输入 | 行数/最新日期 |
|
|
|
+|---|---|
|
|
|
+| S6 `PROD_TASK` 标准工单行 | 14 行,release_time 最新 2026-09-03 |
|
|
|
+| `PROD_RECEIPT` 库存流水 | 3790 行,approved_time 最新 2025-12-31 |
|
|
|
+| `dwd_work_order_mfg_fulfillment` | 最新业务日 2026-09-24 |
|
|
|
+| S7 `SALES_ORDER` 标准行 | 14 行 |
|
|
|
+| `SALES_SHIP` 库存流水 | 0 行 |
|
|
|
+| FQC 已报检任务 | 0 行 |
|
|
|
+| `SALES_RETURN` 库存流水 | 0 行 |
|
|
|
+
|
|
|
+这些是当前值算不出来的真实业务输入缺口。完成“最后真实值展示”后,页面可以展示历史事实,但当前值是否更新仍取决于权威来源是否真的提供这些业务数据。
|
|
|
+
|
|
|
+### 1.7 部署版本不一致
|
|
|
+
|
|
|
+源码基线是 Web `2.4.413`,截图页头仍显示 `V2.4.411`。这意味着前端部署物不是当前源码构建物;即使代码修好,若不发布新前端仍看不到任何新鲜度标识。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. 目标数据语义与 API 契约
|
|
|
+
|
|
|
+### 2.1 两条时间线必须分开
|
|
|
+
|
|
|
+每个指标同时维护两种事实:
|
|
|
+
|
|
|
+1. **最后可用值(value row)**:最近一条真实非空数值;
|
|
|
+2. **最新尝试(latest attempt)**:最近一次计算结果,可能是 `OK`、`NO_DATA`、
|
|
|
+ `SOURCE_UNSUPPORTED` 或其它状态。
|
|
|
+
|
|
|
+禁止把两者压成一个 `resultStatus`。
|
|
|
+
|
|
|
+### 2.2 “可用值”的精确定义
|
|
|
+
|
|
|
+兼容历史数据,可用值满足:
|
|
|
+
|
|
|
+```sql
|
|
|
+metric_value IS NOT NULL
|
|
|
+AND (result_status='OK' OR result_status IS NULL OR result_status='')
|
|
|
+```
|
|
|
+
|
|
|
+不得把 `NO_DATA`、`SOURCE_UNSUPPORTED` 或失败状态的行当作可用值。
|
|
|
+
|
|
|
+### 2.3 `home-grid` 每个 item 的目标字段
|
|
|
+
|
|
|
+保留既有字段,并新增:
|
|
|
+
|
|
|
+```text
|
|
|
+currentValue 最后可用值;从未有过则 null
|
|
|
+targetValue 与最后可用值同一行的目标值
|
|
|
+resultStatus 最后可用值行状态;兼容旧前端
|
|
|
+resultReason 最后可用值行原因
|
|
|
+bizDate 最后可用值业务日
|
|
|
+
|
|
|
+latestResultStatus 最新尝试状态
|
|
|
+latestResultReason 最新尝试原因
|
|
|
+latestBizDate 最新尝试业务日
|
|
|
+
|
|
|
+displayState CURRENT_VALUE / LAST_KNOWN_VALUE /
|
|
|
+ NO_DATA / SOURCE_UNSUPPORTED
|
|
|
+freshnessState FRESH / STALE / NO_VALUE / POLICY_MISSING
|
|
|
+valueAgeDays 服务器自然日 - bizDate
|
|
|
+freshnessSlaDays 指标配置的新鲜度天数
|
|
|
+```
|
|
|
+
|
|
|
+判定规则:
|
|
|
+
|
|
|
+- 有可用值,且它就是最新尝试的可用值:`CURRENT_VALUE`。
|
|
|
+- 有可用值,但最新尝试日期更晚且最新状态不可用:`LAST_KNOWN_VALUE`。
|
|
|
+- 从未有可用值,最新状态是 `SOURCE_UNSUPPORTED`:`SOURCE_UNSUPPORTED`。
|
|
|
+- 从未有可用值,其余情况:`NO_DATA`。
|
|
|
+
|
|
|
+`LAST_KNOWN_VALUE` 必须显示数值,同时显示:
|
|
|
+
|
|
|
+```text
|
|
|
+数据截至 YYYY-MM-DD
|
|
|
+最新一期:无业务数据(YYYY-MM-DD)
|
|
|
+```
|
|
|
+
|
|
|
+### 2.4 日期筛选边界
|
|
|
+
|
|
|
+- 未传日期:从全部历史中找最后可用值。
|
|
|
+- 传 `dateStart`:可用值与最新尝试都必须 `biz_date >= dateStart`。
|
|
|
+- 传 `dateEnd`:可用值与最新尝试都必须 `biz_date <= dateEnd`。
|
|
|
+- 传完整区间:严禁从区间外回退。
|
|
|
+- 业务维度筛选走 `filteredBundle` 时,若旁路无法提供历史值及业务日,必须返回当前筛选结果或 `NO_DATA`,不得回退未筛选值。
|
|
|
+
|
|
|
+### 2.5 目标值一致性
|
|
|
+
|
|
|
+`targetValue` 必须取自最后可用值同一行,不能拿最新 `NO_DATA` 行的目标与历史值计算差距。`achievementLevel`、`gapValue`、`gapLabel` 都必须基于同一业务日的 value/target 对。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. WP-A:重构九宫格值选择(P0)
|
|
|
+
|
|
|
+### A.1 改动文件
|
|
|
+
|
|
|
+- `server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/AidopKanbanController.Generic.cs`
|
|
|
+- `server/Plugins/Admin.NET.Plugin.AiDOP.Tests/DataPlatform/HomeGridLastKnownValueContractTests.cs`(新增)
|
|
|
+
|
|
|
+在 `AidopKanbanController.Generic.cs` 的 partial controller 内新增私有嵌套 DTO
|
|
|
+`HomeGridValueRow`。不要继续扩展共用的 `S4ValRow`,避免 S4 专用端点被公共契约牵连。
|
|
|
+
|
|
|
+DTO 字段固定为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+private sealed class HomeGridValueRow
|
|
|
+{
|
|
|
+ public long Id { get; set; }
|
|
|
+ public string? MetricCode { get; set; }
|
|
|
+ public decimal? MetricValue { get; set; }
|
|
|
+ public decimal? TargetValue { get; set; }
|
|
|
+ public string? ResultStatus { get; set; }
|
|
|
+ public string? ResultReason { get; set; }
|
|
|
+ public DateTime BizDate { get; set; }
|
|
|
+ public DateTime? CalcTime { get; set; }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### A.2 实现步骤
|
|
|
+
|
|
|
+1. 把当前单份 `valSql` 拆为两份,均使用 `ROW_NUMBER()`:
|
|
|
+ - `latestAttemptSql`:每 metric 取最新尝试;
|
|
|
+ - `latestUsableSql`:先按 §2.2 过滤可用值,再每 metric 取最新。
|
|
|
+2. 两份 SQL 必须使用完全相同的 tenant/factory/module/date 谓词。
|
|
|
+3. 同日存在多行时固定按 `biz_date DESC, calc_time DESC, id DESC` 稳定取一行。实表已确认三列均存在:
|
|
|
+ `biz_date date`、`calc_time datetime(3)`、`id bigint`。
|
|
|
+4. 分别构造 `latestByMetric` 与 `usableByMetric`,不得用 `ToDictionary` 吞掉重复键;SQL 必须先保证每 metric 唯一。
|
|
|
+5. 按 §2.3 构造返回字段。
|
|
|
+6. `targetValue`、gap、achievement 均读取 usable row。
|
|
|
+7. `filteredBundle != null` 时保持 fail-closed:不能拿无筛选历史值覆盖筛选结果。
|
|
|
+8. 模块级 `bizDate` 保留,只表示模块最新尝试日;注释明确不能给卡片使用。
|
|
|
+
|
|
|
+状态判断固定使用行主键,不只比较日期:
|
|
|
+
|
|
|
+```text
|
|
|
+usable == null:
|
|
|
+ latest.status == SOURCE_UNSUPPORTED ? SOURCE_UNSUPPORTED : NO_DATA
|
|
|
+usable != null && latest != null && usable.Id == latest.Id:
|
|
|
+ CURRENT_VALUE
|
|
|
+usable != null:
|
|
|
+ LAST_KNOWN_VALUE
|
|
|
+```
|
|
|
+
|
|
|
+历史空 `result_status` 的 usable row 在返回兼容字段 `resultStatus` 时规范成 `OK`;原始最新状态不改写。
|
|
|
+
|
|
|
+SQL 固定采用以下骨架,两份查询只在 usable 过滤条件上不同:
|
|
|
+
|
|
|
+```sql
|
|
|
+WITH ranked AS (
|
|
|
+ SELECT v.*,
|
|
|
+ ROW_NUMBER() OVER (
|
|
|
+ PARTITION BY v.metric_code
|
|
|
+ ORDER BY v.biz_date DESC, v.calc_time DESC, v.id DESC
|
|
|
+ ) AS rn
|
|
|
+ FROM ado_s9_kpi_value_l1_day v
|
|
|
+ WHERE v.tenant_id=@t AND v.factory_id=@f AND v.module_code=@m
|
|
|
+ AND v.is_deleted=0
|
|
|
+ AND (@ds IS NULL OR v.biz_date>=@ds)
|
|
|
+ AND (@de IS NULL OR v.biz_date<=@de)
|
|
|
+ /* latestUsableSql 在这里追加:
|
|
|
++ AND v.metric_value IS NOT NULL
|
|
|
+ AND (v.result_status='OK' OR v.result_status IS NULL OR v.result_status='')
|
|
|
+ */
|
|
|
+)
|
|
|
+SELECT id AS Id, metric_code AS MetricCode, metric_value AS MetricValue,
|
|
|
+ target_value AS TargetValue, result_status AS ResultStatus,
|
|
|
+ result_reason AS ResultReason, biz_date AS BizDate, calc_time AS CalcTime
|
|
|
+FROM ranked
|
|
|
+WHERE rn=1;
|
|
|
+```
|
|
|
+
|
|
|
+### A.3 不允许的实现
|
|
|
+
|
|
|
+- 在前端缓存旧值并与新接口合并;
|
|
|
+- `COALESCE(latest.metric_value, old.metric_value)` 但不返回 old 的日期;
|
|
|
+- 把 `NO_DATA` 行直接改成 `OK`;
|
|
|
+- 删除 `NO_DATA` 行;
|
|
|
+- 日期筛选时查询区间外历史值;
|
|
|
+- 用模块级日期填每张卡。
|
|
|
+
|
|
|
+### A.4 测试
|
|
|
+
|
|
|
+至少覆盖:
|
|
|
+
|
|
|
+1. 最新 `NO_DATA`、前一期 `OK`:显示前一期值,状态 `LAST_KNOWN_VALUE`。
|
|
|
+2. 最新就是 `OK`:状态 `CURRENT_VALUE`。
|
|
|
+3. 历史从未有非空值:`NO_DATA`。
|
|
|
+4. 最新 `SOURCE_UNSUPPORTED` 且无历史值:`SOURCE_UNSUPPORTED`。
|
|
|
+5. 显式日期区间内无可用值、区间外有值:不得回退。
|
|
|
+6. value 与 target 必须来自同一行。
|
|
|
+7. 同一天多次计算按 `calc_time/id` 稳定选最后一次。
|
|
|
+8. tenant/factory/module/metric 隔离。
|
|
|
+9. `filteredBundle` 不回落未筛选历史值。
|
|
|
+
|
|
|
+### A.5 数据验收
|
|
|
+
|
|
|
+修复后默认请求应返回:
|
|
|
+
|
|
|
+- S6_L1_001:3.0000,`bizDate=2026-09-23`,`LAST_KNOWN_VALUE`
|
|
|
+- S6_L1_002:100.0000,`bizDate=2026-09-23`,`LAST_KNOWN_VALUE`
|
|
|
+- S7_L1_001:0.9667,`bizDate=2026-09-23`,`LAST_KNOWN_VALUE`
|
|
|
+- 其余基线值以 §1.2 为准
|
|
|
+
|
|
|
+不得把这些值写回成新日期;它们仍然是原业务日的历史事实。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. WP-B:每指标新鲜度 SLA(P0)
|
|
|
+
|
|
|
+### B.1 为什么不能固定 7 天
|
|
|
+
|
|
|
+当前 `HomeModuleKpiCard.vue:80` 写死 `STALE_AFTER_DAYS=7`。月度人效指标以月末作为 `biz_date`,月中看到上月末值是正常行为;固定 7 天会把正常月度值长期标红。
|
|
|
+
|
|
|
+### B.2 数据模型
|
|
|
+
|
|
|
+在 `AdoSmartOpsKpiMaster` 增加可空整数字段:
|
|
|
+
|
|
|
+```csharp
|
|
|
+public int? FreshnessSlaDays { get; set; }
|
|
|
+```
|
|
|
+
|
|
|
+语义:从 value row 的 `biz_date` 起,允许保留为“新鲜”的自然日数。
|
|
|
+
|
|
|
+固定初始策略:
|
|
|
+
|
|
|
+| 指标范围 | SLA |
|
|
|
+|---|---:|
|
|
|
+| `S5_L1_003`、`S5_L1_004`、`S6_L1_003`、`S7_L1_003`、`S9_L1_001`(代码已证实按月末落值或现行月度口径) | 45 |
|
|
|
+| `StatFrequency='实时'` 的其它首页指标 | 1 |
|
|
|
+| 其余首页指标 | 7 |
|
|
|
+
|
|
|
+`StatFrequency` 现存首页值只有“天/实时”,且三个人效与库存周转虽按月末落值,元数据仍写“天”,所以迁移不能只按 `StatFrequency` 推断月度指标。上述 5 个指标必须按 code 显式置 45。以后每个指标允许通过主数据覆盖。
|
|
|
+
|
|
|
+### B.3 迁移
|
|
|
+
|
|
|
+基线固定新建:
|
|
|
+
|
|
|
+- `server/Admin.NET.Web.Entry/UpdateScripts/1.0.593.sql`
|
|
|
+- `server/Admin.NET.Web.Entry/UpdateScripts/1.0.593.verify.sql`
|
|
|
+- 在 `.csproj` 登记两者
|
|
|
+
|
|
|
+要求:
|
|
|
+
|
|
|
+1. 幂等增加列(先查 `information_schema.COLUMNS`)。
|
|
|
+2. 先对上述 5 个 code 写 45,再对 `StatFrequency='实时'` 且仍为空的首页指标写 1,最后对其它仍为空的首页指标写 7。
|
|
|
+3. 所有 `IsHomePage=1 AND IsEnabled=1` 的指标必须有正数 SLA。
|
|
|
+4. verify 返回单个布尔,保证首页启用指标无空/非正数 SLA。
|
|
|
+5. 不修改任何已成功执行的旧迁移。
|
|
|
+
|
|
|
+迁移 SQL 按以下固定顺序编写:
|
|
|
+
|
|
|
+```sql
|
|
|
+SET @has_freshness_sla := (
|
|
|
+ SELECT COUNT(*) FROM information_schema.COLUMNS
|
|
|
+ WHERE TABLE_SCHEMA=DATABASE()
|
|
|
+ AND TABLE_NAME='ado_smart_ops_kpi_master'
|
|
|
+ AND COLUMN_NAME='FreshnessSlaDays'
|
|
|
+);
|
|
|
+SET @ddl := IF(
|
|
|
+ @has_freshness_sla=0,
|
|
|
+ 'ALTER TABLE ado_smart_ops_kpi_master ADD COLUMN FreshnessSlaDays int NULL COMMENT ''首页数据新鲜度SLA(自然日)'' AFTER StatFrequency',
|
|
|
+ 'SELECT 1'
|
|
|
+);
|
|
|
+PREPARE stmt FROM @ddl;
|
|
|
+EXECUTE stmt;
|
|
|
+DEALLOCATE PREPARE stmt;
|
|
|
+
|
|
|
+UPDATE ado_smart_ops_kpi_master
|
|
|
+SET FreshnessSlaDays=45, UpdatedAt=NOW()
|
|
|
+WHERE MetricCode IN
|
|
|
+ ('S5_L1_003','S5_L1_004','S6_L1_003','S7_L1_003','S9_L1_001');
|
|
|
+
|
|
|
+-- ③ 实时
|
|
|
+UPDATE ado_smart_ops_kpi_master
|
|
|
+SET FreshnessSlaDays=1, UpdatedAt=NOW()
|
|
|
+WHERE IsHomePage=1 AND IsEnabled=1
|
|
|
+ AND StatFrequency='实时'
|
|
|
+ AND FreshnessSlaDays IS NULL;
|
|
|
+
|
|
|
+-- ④ 其余首页指标
|
|
|
+UPDATE ado_smart_ops_kpi_master
|
|
|
+SET FreshnessSlaDays=7, UpdatedAt=NOW()
|
|
|
+WHERE IsHomePage=1 AND IsEnabled=1
|
|
|
+ AND FreshnessSlaDays IS NULL;
|
|
|
+```
|
|
|
+
|
|
|
+verify 固定检查:
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT
|
|
|
+ (SELECT COUNT(*) FROM information_schema.TABLES
|
|
|
+ WHERE TABLE_SCHEMA=DATABASE()
|
|
|
+ AND TABLE_NAME='ado_smart_ops_kpi_master')=0
|
|
|
+ OR (
|
|
|
+ (SELECT COUNT(*) FROM information_schema.COLUMNS
|
|
|
+ WHERE TABLE_SCHEMA=DATABASE()
|
|
|
+ AND TABLE_NAME='ado_smart_ops_kpi_master'
|
|
|
+ AND COLUMN_NAME='FreshnessSlaDays')=1
|
|
|
+ AND
|
|
|
+ (SELECT COUNT(*) FROM ado_smart_ops_kpi_master
|
|
|
+ WHERE IsHomePage=1 AND IsEnabled=1
|
|
|
+ AND (FreshnessSlaDays IS NULL OR FreshnessSlaDays<=0))=0
|
|
|
+ );
|
|
|
+```
|
|
|
+
|
|
|
+### B.4 种子
|
|
|
+
|
|
|
+`AidopKpiMasterSeed` 新建租户时必须同步写 `FreshnessSlaDays`。将“频率 → SLA”的映射集中在一个 helper,避免每条 `Ins(...)` 手填并逐渐漂移。
|
|
|
+
|
|
|
+helper 名称固定为
|
|
|
+`ResolveFreshnessSlaDays(string metricCode, string? statFrequency)`,返回 `int`。
|
|
|
+固定按同一优先级:5 个明确月度 code → 45,实时 → 1,其余 → 7。`Ins(...)`
|
|
|
+创建实体时调用该 helper。API 仍保留
|
|
|
+`POLICY_MISSING`,用于拦截手工新增却漏配置的指标。
|
|
|
+
|
|
|
+### B.5 服务端计算
|
|
|
+
|
|
|
+服务端按 `DateTime.Today - bizDate.Date` 计算 `valueAgeDays`,再与 KPI 的 SLA 比较。前端不得自行读取本机日期重新判定,以免客户端时区不同导致结果不一致。
|
|
|
+
|
|
|
+规则:
|
|
|
+
|
|
|
+- 无 value:`NO_VALUE`
|
|
|
+- 无 SLA:`POLICY_MISSING`
|
|
|
+- `valueAgeDays > freshnessSlaDays`:`STALE`
|
|
|
+- 其余:`FRESH`
|
|
|
+
|
|
|
+### B.6 测试
|
|
|
+
|
|
|
+- 日指标 8 天 → STALE;7 天 → FRESH。
|
|
|
+- 月指标 30 天、SLA 45 → FRESH。
|
|
|
+- 月指标 46 天 → STALE。
|
|
|
+- 无策略 → POLICY_MISSING,不得默认 FRESH。
|
|
|
+- 未来业务日(时钟异常)固定记录 warning,并把 `valueAgeDays` 钳制为 0;warning 只带 tenant/factory/metric/date,不带业务报文。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. WP-C:前端真实展示与 S8 空态(P0)
|
|
|
+
|
|
|
+### C.1 改动文件
|
|
|
+
|
|
|
+- `Web/src/views/aidop/api/kanbanData.ts`
|
|
|
+- `Web/src/views/dashboard/components/HomeModuleKpiCard.vue`
|
|
|
+- `Web/src/views/dashboard/components/homeKpiPresentation.ts`(新增)
|
|
|
+- `Web/src/views/dashboard/components/HomeCardGridView.vue`
|
|
|
+- `Web/src/views/dashboard/components/HomeCardTilesView.vue`
|
|
|
+- `Web/src/views/dashboard/components/HomeCardTableView.vue`
|
|
|
+- `Web/src/views/dashboard/home.vue`
|
|
|
+- `Web/src/views/dashboard/components/homeKpiPresentation.spec.ts`(新增)
|
|
|
+- `Web/src/views/dashboard/home.s8-empty.spec.ts`(新增)
|
|
|
+
|
|
|
+### C.2 卡片展示规则
|
|
|
+
|
|
|
+把 `fmtResult`、日期/状态提示和 cell 映射移入
|
|
|
+`homeKpiPresentation.ts`,导出纯函数 `mapHomeKpiItem(item)`,供组件与 Vitest 共用。
|
|
|
+移除前端固定 `STALE_AFTER_DAYS`,完全消费服务端状态。
|
|
|
+
|
|
|
+| displayState | 主值 | 辅助说明 |
|
|
|
+|---|---|---|
|
|
|
+| CURRENT_VALUE | 正常显示 | 数据截至 X |
|
|
|
+| LAST_KNOWN_VALUE | 显示最后真实值 | 数据截至 X;最新一期无业务数据/数据源不支持 |
|
|
|
+| NO_DATA | 无业务数据 | 最新尝试原因 |
|
|
|
+| SOURCE_UNSUPPORTED | 数据源不支持 | 最新尝试原因 |
|
|
|
+
|
|
|
+`freshnessState=STALE` 时弱化卡片,但不得隐藏数值。
|
|
|
+
|
|
|
+`freshnessState=POLICY_MISSING` 时显示中性提示“未配置新鲜度规则”,不要擅自变灰。
|
|
|
+
|
|
|
+### C.3 防止窄卡竖排
|
|
|
+
|
|
|
+截图中 S9 的「无业务数据」因窄列换行成竖排。三种布局都应:
|
|
|
+
|
|
|
+- `mapHomeKpiItem` 固定返回 `valueKind: 'number' | 'status'`;
|
|
|
+- 三个布局的 value 元素固定绑定 `--status` class;
|
|
|
+- status class 固定使用 `font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%`;
|
|
|
+- 保持数值、单位、状态三种文本的布局规则分离。
|
|
|
+
|
|
|
+不能仅依赖“以后会显示旧值”掩盖这个布局缺陷,因为从未有值的指标仍会显示状态文本。
|
|
|
+
|
|
|
+### C.4 最新尝试提示
|
|
|
+
|
|
|
+tooltip 固定按非空行依次拼接:
|
|
|
+
|
|
|
+```text
|
|
|
+指标公式(如有)
|
|
|
+最后真实值:YYYY-MM-DD
|
|
|
+最新计算:YYYY-MM-DD / NO_DATA / 原因
|
|
|
+新鲜度:N 天 / SLA M 天
|
|
|
+```
|
|
|
+
|
|
|
+不得把后端 `resultReason` 原样拼出敏感 SQL、连接串或凭据;只显示业务安全文本。
|
|
|
+前端只展示后端已提供的业务原因字段;若文本含 `Password=`、`Pwd=`、`ConnectionString`、
|
|
|
+`Server=` 任一片段,则整条原因替换为「计算状态异常,请查看运行日志」。
|
|
|
+
|
|
|
+### C.5 S8 空态
|
|
|
+
|
|
|
+`home.vue` 当前 `s8TopAlarmsAll=[]` 时列表区域直接空白。改为:
|
|
|
+
|
|
|
+- 报警列表为空:显示「暂无异常告警」;
|
|
|
+- 趋势全 0:保留真实 0 折线/坐标,同时显示轻量“近 7 日无异常”;
|
|
|
+- 请求失败:显示「异常数据加载失败」,不能与“无告警”混为一谈;
|
|
|
+- 不恢复已删除的硬编码演示告警。
|
|
|
+
|
|
|
+### C.6 前端测试
|
|
|
+
|
|
|
+- LAST_KNOWN_VALUE 显示数值而不是“无业务数据”。
|
|
|
+- 最新 `NO_DATA` 徽标可见。
|
|
|
+- STALE 弱化,FRESH 不弱化。
|
|
|
+- 月度值不由前端 7 天规则误判。
|
|
|
+- NO_DATA 状态文本不换成逐字竖排。
|
|
|
+- S8 空数组显示“暂无异常告警”。
|
|
|
+- S8 请求异常显示“加载失败”。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. WP-D:员工状态中立映射根治(P1)
|
|
|
+
|
|
|
+### D.1 改动点
|
|
|
+
|
|
|
+固定修改:
|
|
|
+
|
|
|
+- `DataPlatform/NativeNeutralProjectionService.cs`
|
|
|
+- `DataPlatform/Inbound/MdmMirrorUpsertService.cs`
|
|
|
+- `server/Plugins/Admin.NET.Plugin.AiDOP.Tests/DataPlatform/EmployeeEmploymentStatusMappingTests.cs`(新增)
|
|
|
+
|
|
|
+`T8BaseInboundMdpSyncService.NeutralProjection.cs` 已按 `在职/离职` 正确转换,只做只读回归核查,不改代码。
|
|
|
+
|
|
|
+### D.2 映射规则
|
|
|
+
|
|
|
+必须加入:
|
|
|
+
|
|
|
+```text
|
|
|
+AC → ACTIVE
|
|
|
+DC → LEFT
|
|
|
+```
|
|
|
+
|
|
|
+保留已有规则:
|
|
|
+
|
|
|
+- 在职 / ACTIVE / ONJOB / ON_JOB → ACTIVE
|
|
|
+- 离职类 → LEFT
|
|
|
+- 停用类 → INACTIVE
|
|
|
+- 空值/未知值 → UNKNOWN
|
|
|
+
|
|
|
+不抽取新公共框架。本轮固定同步修改两处:
|
|
|
+
|
|
|
+- Native SQL CASE:`AC → ACTIVE`、`DC → LEFT`
|
|
|
+- Inbound `NormalizeEmployment`:`AC → ACTIVE`、`DC → LEFT`
|
|
|
+
|
|
|
+新增契约测试同时读取两处源码并断言 AC/DC 规则存在,防止以后漂移。
|
|
|
+
|
|
|
+### D.3 数据重投影
|
|
|
+
|
|
|
+代码部署后必须通过正式投影入口重跑,不直接 UPDATE `mdp_std_employee`:
|
|
|
+
|
|
|
+1. 触发 AIDOP_NATIVE 员工中立投影;
|
|
|
+2. 验证 `src_employment_status_raw='AC'` 的行变为 `ACTIVE`;
|
|
|
+3. 验证空状态仍为 `UNKNOWN`;
|
|
|
+4. 再重算 S5/S7。
|
|
|
+
|
|
|
+验收 SQL:
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT source_system, employment_status, position_code, COUNT(*) AS n
|
|
|
+FROM mdp_std_employee
|
|
|
+WHERE tenant_id=838257186181189
|
|
|
+GROUP BY source_system, employment_status, position_code
|
|
|
+ORDER BY source_system, employment_status, position_code;
|
|
|
+```
|
|
|
+
|
|
|
+预期:AIDOP_NATIVE 至少出现 5 个 `ACTIVE + WAREHOUSE`。
|
|
|
+
|
|
|
+### D.4 明确不能承诺的结果
|
|
|
+
|
|
|
+- S5/S7 当前月分子若仍为空,人效卡仍可能只展示 7 月最后真实值。
|
|
|
+- S6 没有 `PRODUCTION` 岗位员工,不能通过 AC/DC 修复得到当前人效。
|
|
|
+- 不得把 11 个空岗位员工批量映射为生产岗位。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. WP-E:T8 密钥与连接终态(P1,安全相关)
|
|
|
+
|
|
|
+### E.1 目标架构
|
|
|
+
|
|
|
+`mdp_source` 保存非秘密元数据;口令由部署环境的密钥提供器解析。环境变量约定固定为:
|
|
|
+
|
|
|
+```text
|
|
|
+AIDOP_MDP_SOURCE_<规范化SOURCE_CODE>_PASSWORD
|
|
|
+```
|
|
|
+
|
|
|
+规范化算法固定为:
|
|
|
+
|
|
|
+```csharp
|
|
|
+Regex.Replace(sourceCode.Trim().ToUpperInvariant(), "[^A-Z0-9]", "_")
|
|
|
+```
|
|
|
+
|
|
|
+因此 T8 的变量名固定为 `AIDOP_MDP_SOURCE_T8_V5_SQLSERVER_PASSWORD`。任务书只允许记录变量名,不允许记录变量值。
|
|
|
+
|
|
|
+### E.2 代码改动
|
|
|
+
|
|
|
+1. 新增 `DataPlatform/MdpSourcePasswordResolver.cs`,同文件定义
|
|
|
+ `IMdpSourcePasswordResolver` 与 `MdpSourcePasswordResolver`:
|
|
|
+ - 优先从环境密钥读取;
|
|
|
+ - 兼容现有 `db_password_enc` 的系统密文;
|
|
|
+ - 旧明文仅作为迁移期兼容,并记录“不含值”的安全告警;
|
|
|
+ - 找不到口令时抛明确的配置异常,禁止用空字符串尝试登录后再报模糊错误。
|
|
|
+2. `MdpSourceScopeFactory` 通过 resolver 获取口令,不自行解密/回退。
|
|
|
+3. `MdpSourceHealthCheckJob` 区分:
|
|
|
+ - `SECRET_MISSING`
|
|
|
+ - `AUTH_FAILED`
|
|
|
+ - `NETWORK_FAILED`
|
|
|
+ 健康信息不得包含连接串和口令。
|
|
|
+4. 删除 `DataPlatform/MdpSourceConnectionImportService.cs`,并删除 `Startup.cs` 中对应
|
|
|
+ `AddHostedService` 注册。该服务只处理 `t8_v5`,终态不再需要。
|
|
|
+5. 从受 Git 跟踪的 `server/Admin.NET.Application/Configuration/Database.json` 中完整删除
|
|
|
+ `ConfigId="t8_v5"` 对象;不得在 diff、日志或交付报告中粘贴原连接串。
|
|
|
+6. 更新所有声称“复用 Database.json 的 t8_v5”的代码注释。
|
|
|
+7. 新增 `server/Plugins/Admin.NET.Plugin.AiDOP.Tests/DataPlatform/MdpSourcePasswordResolverTests.cs`:
|
|
|
+ - 环境变量优先;
|
|
|
+ - 环境缺失时兼容系统密文;
|
|
|
+ - 两者都缺失时抛 `SECRET_MISSING`;
|
|
|
+ - 日志和异常不含测试密码。
|
|
|
+8. **人工门禁 E-M1**:运维在进程环境注入
|
|
|
+ `AIDOP_MDP_SOURCE_T8_V5_SQLSERVER_PASSWORD` 后重启。
|
|
|
+9. **人工门禁 E-M2**:负责人轮换已经进入 Git 历史的旧 T8 口令,并确认旧口令失效。
|
|
|
+
|
|
|
+本包固定文件清单:
|
|
|
+
|
|
|
+- 新增 `server/Plugins/Admin.NET.Plugin.AiDOP/DataPlatform/MdpSourcePasswordResolver.cs`
|
|
|
+- 修改 `server/Plugins/Admin.NET.Plugin.AiDOP/DataPlatform/MdpSourceScopeFactory.cs`
|
|
|
+- 修改 `server/Plugins/Admin.NET.Plugin.AiDOP/Job/MdpSourceHealthCheckJob.cs`
|
|
|
+- 修改 `server/Plugins/Admin.NET.Plugin.AiDOP/Startup.cs`
|
|
|
+- 修改 `server/Admin.NET.Application/Configuration/Database.json`(只删除 T8 注册对象)
|
|
|
+- 删除 `server/Plugins/Admin.NET.Plugin.AiDOP/DataPlatform/MdpSourceConnectionImportService.cs`
|
|
|
+- 更新 `T8BaseInboundMdpSyncService.cs` 等处关于 `t8_v5` 的失效注释
|
|
|
+- 新增 `server/Plugins/Admin.NET.Plugin.AiDOP.Tests/DataPlatform/MdpSourcePasswordResolverTests.cs`
|
|
|
+- 更新所有直接 `new MdpSourceScopeFactory(...)` 的测试调用点,显式传入 fake resolver
|
|
|
+
|
|
|
+resolver 固定解析优先级:
|
|
|
+
|
|
|
+```text
|
|
|
+1. 对应 source_code 的环境变量(非空)
|
|
|
+2. db_password_enc 经 CryptogramUtil.Decrypt 成功得到的非空值
|
|
|
+3. 兼容旧明文 db_password_enc,同时写安全 warning(不含值)
|
|
|
+4. 抛 MdpSourceSecretException,错误码 SECRET_MISSING
|
|
|
+```
|
|
|
+
|
|
|
+禁止把环境变量值缓存进实体、数据库或日志。resolver 可在进程内读取环境变量,每次新建连接时解析。
|
|
|
+
|
|
|
+### E.3 不允许的方案
|
|
|
+
|
|
|
+- 把口令写进新迁移;
|
|
|
+- 把口令写进 `appsettings*.json`、`.env.example` 或任务书;
|
|
|
+- 从 Git 历史读取旧口令继续使用但不轮换;
|
|
|
+- 只把 `MdpSourceConnectionImportService` 条件改成 `db_password_enc IS NULL`,继续复制仓库明文;
|
|
|
+- 在日志中打印脱敏不完整的连接串。
|
|
|
+
|
|
|
+### E.4 验收
|
|
|
+
|
|
|
+- `mdp_source.db_password_enc` 可以继续为空,但健康检查应通过环境密钥连接成功。
|
|
|
+- 缺少环境密钥时,健康状态为 `SECRET_MISSING`,信息可定位但不含秘密。
|
|
|
+- 错误口令时为 `AUTH_FAILED`。
|
|
|
+- Git 当前树不再包含 T8 口令。
|
|
|
+- 外部 T8 账号已轮换;旧口令失效。
|
|
|
+- 连接权限保持只读。
|
|
|
+
|
|
|
+> 主库 MySQL 明文凭据也存在于同一受跟踪配置文件中。若本轮不一并外部化,
|
|
|
+> 必须登记独立 P0 安全任务,不能在交付说明里声称“仓库凭据问题已全部解决”。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. WP-F:S6/S7/S9 当前业务数据补通(P1)
|
|
|
+
|
|
|
+### F.1 先查来源绑定,禁止想当然切源
|
|
|
+
|
|
|
+```sql
|
|
|
+SELECT tenant_id, std_object, source_system, source_scope
|
|
|
+FROM mdp_tenant_std_source
|
|
|
+WHERE tenant_id=838257186181189
|
|
|
+ AND std_object IN ('INV_TRANS','EMPLOYEE','WO_LINE_PROD','WO_LINE_SALES','FQC')
|
|
|
+ORDER BY std_object;
|
|
|
+```
|
|
|
+
|
|
|
+执行规则固定为:
|
|
|
+
|
|
|
+1. 查询命中一条绑定:只沿该来源继续代码/配置/数据取证。
|
|
|
+2. 查询 0 条或同一 std_object 多条:停止 WP-F,输出证据并请求负责人决策。
|
|
|
+3. 已绑定来源不提供所需业务对象:停止 WP-F,列出 T8/165/自建单/标准 API 的能力矩阵,请负责人选权威来源。
|
|
|
+4. 不得为了让卡片出数而自行改绑定。
|
|
|
+
|
|
|
+### F.2 每个缺口必须完成三层取证
|
|
|
+
|
|
|
+#### S6_L1_001 / S6_L1_002
|
|
|
+
|
|
|
+- 代码层:读取 `S6MdpSyncTransformService.cs:566-649` 的真实 SQL。
|
|
|
+- 配置层:核查 `INV_TRANS`、生产工单来源绑定及相关实体配置。
|
|
|
+- 数据层:核查 `PROD_TASK`、`PROD_RECEIPT`、DWD 的最新业务日和可完成订单数。
|
|
|
+
|
|
|
+#### S6_L1_003
|
|
|
+
|
|
|
+- 除上述分子外,必须有 `ACTIVE + PRODUCTION` 员工。
|
|
|
+- 当前 `PRODUCTION=0`,必须由真实员工岗位主数据补齐。
|
|
|
+
|
|
|
+#### S7_L1_001
|
|
|
+
|
|
|
+代码要求同时具备:
|
|
|
+
|
|
|
+- `mdp_std_work_order_line.doc_type='SALES_ORDER'`
|
|
|
+- `mdp_std_inv_trans.biz_doc_type='SALES_SHIP'`
|
|
|
+- `mdp_std_fqc_task.apply_flag=1`
|
|
|
+- 完成判定成立
|
|
|
+
|
|
|
+当前后两项为 0。执行者应检索 T8、165、自建单和标准 API 四条来源,确认代码能力、配置开通和真实数据分别缺在哪一层。
|
|
|
+
|
|
|
+#### S9_L1_001
|
|
|
+
|
|
|
+质量退货率需要 `SALES_RETURN`。当前为 0。只有权威业务源确实存在退货时才补通;若该租户从未发生销售退货,继续显示“最后真实值 + 最新一期无业务数据”是正确结果。
|
|
|
+
|
|
|
+### F.3 补数原则
|
|
|
+
|
|
|
+- 优先重放真实源数据或标准 API 入站;
|
|
|
+- 保留原业务日期、来源系统、业务键和批次;
|
|
|
+- 不把历史值复制成今天;
|
|
|
+- 不复制其它租户;
|
|
|
+- 不直接改 KPI 结果表;
|
|
|
+- 补数后按来源 → stg → std → dwd → KPI 顺序验证。
|
|
|
+
|
|
|
+执行模型在 F.1 第 2/3 条门禁未解除前,只能做只读取证和报告,不能写配置、补数据或改来源选择代码。
|
|
|
+
|
|
|
+### F.4 验收
|
|
|
+
|
|
|
+每个指标只能得出以下三种明确结论之一:
|
|
|
+
|
|
|
+- 代码有 / 配置开 / 数据通;
|
|
|
+- 代码有 / 配置未开(列配置项);
|
|
|
+- 代码有 / 配置开 / 数据未通(列源对象及最新日期);
|
|
|
+- 若代码无,必须给出全仓零匹配范围与拟新增文件。
|
|
|
+
|
|
|
+不得用“未达到”“大概缺数据”收尾。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. WP-G:库存标准层物化互斥缺口与 02:00 死锁(P0)
|
|
|
+
|
|
|
+### G.1 已确认根因
|
|
|
+
|
|
|
+`InventoryMdpSyncService` 的无参 `TransformTransStdFromStgAsync()`、库存余额投影、正常同步都使用 `InventoryInboundLockGuard`。
|
|
|
+
|
|
|
+但 S5/S7 重算调用的是另一个重载:
|
|
|
+
|
|
|
+```csharp
|
|
|
+TransformTransStdFromStgAsync(long tenantId, ...)
|
|
|
+```
|
|
|
+
|
|
|
+该重载在 `InventoryMdpSyncService.cs:257-304` 直接调用
|
|
|
+`MaterializeInvTransStdAsync`,**没有获取同一把锁**。
|
|
|
+
|
|
|
+调用面:
|
|
|
+
|
|
|
+- `S5MdpSyncTransformService.cs:111`
|
|
|
+- `S7MdpSyncTransformService.cs:100`
|
|
|
+
|
|
|
+2026-09-29 02:00,半小时同步与 AUTO S5 同秒运行,在
|
|
|
+`MaterializeInvTransStdAsync:869` 出现 MySQL deadlock;人工错峰重算成功。调度错开只能降低概率,不是根治。
|
|
|
+
|
|
|
+### G.2 最佳修法
|
|
|
+
|
|
|
+1. 为 `TransformTransStdFromStgAsync(long tenantId)` 补同一
|
|
|
+ `InventoryInboundLockGuard.LockKey`。
|
|
|
+2. 该入口是用户/重算链路,不应像后台增量任务一样“抢不到立即静默跳过”:
|
|
|
+ - 在 `InventoryInboundLockGuard` 新增 `AcquireAsync(...)`;
|
|
|
+ - 总等待上限固定 10 分钟;
|
|
|
+ - DB 咨询锁每 2 秒用 `GET_LOCK(key,0)` 非阻塞轮询;
|
|
|
+ - 每轮检查 cancellation token;
|
|
|
+ - 等待期间不得占用错误的共享 SqlSugar 自动关闭连接;
|
|
|
+ - 超时固定抛 `TimeoutException("inventory materialization lock busy")`;
|
|
|
+ - 不允许返回 0 并把“未物化”伪装成成功。
|
|
|
+3. 保留数据库死锁的有限重试作为最后防线:
|
|
|
+ - 仅识别 `MySqlException.Number` 1213(deadlock)与 1205(lock wait timeout);
|
|
|
+ - 最多 3 次总尝试(首次 + 2 次重试);
|
|
|
+ - 两次重试延迟固定 250ms、750ms;
|
|
|
+ - 非并发类 SQL 错误立即抛出。
|
|
|
+4. 全仓检索已知调用方固定为 S5、S7 与管理入口。若执行时发现新增调用方已持锁,停止 G 包并报告调用栈;不得在不同连接上嵌套获取同名锁。
|
|
|
+
|
|
|
+### G.3 测试
|
|
|
+
|
|
|
+固定新增:
|
|
|
+
|
|
|
+- `server/Plugins/Admin.NET.Plugin.AiDOP.Tests/MaterialWarehouse/InventoryInboundLockGuardTests.cs`
|
|
|
+- `server/Plugins/Admin.NET.Plugin.AiDOP.Tests/MaterialWarehouse/InventoryMaterializationRetryTests.cs`
|
|
|
+
|
|
|
+- 同进程两路:第二路等待,第一路释放后获得锁。
|
|
|
+- 跨连接两路:只有一路进入物化区。
|
|
|
+- 取消令牌能终止等待。
|
|
|
+- 超时给出 busy/retryable,不返回假成功。
|
|
|
+- 异常路径释放咨询锁。
|
|
|
+- 非 deadlock SQL 异常不重试。
|
|
|
+- S5 与 S7 同时请求时串行,不死锁。
|
|
|
+
|
|
|
+### G.4 运行验收
|
|
|
+
|
|
|
+- 在半小时同步窗口附近触发 S5/S7 重算;
|
|
|
+- `ado_module_dashboard_rebuild_job` 最终 SUCCESS 或明确 busy/retryable;
|
|
|
+- 后端日志无 `Deadlock found when trying to get lock`;
|
|
|
+- `mdp_source_gate_log` 仍正常写入;
|
|
|
+- 不允许只通过把 02:00 调度改成 02:05 验收。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 10. 执行顺序
|
|
|
+
|
|
|
+固定执行顺序:
|
|
|
+
|
|
|
+```text
|
|
|
+WP-G 互斥根治
|
|
|
+ → WP-A 双时间线取值
|
|
|
+ → WP-B 新鲜度配置
|
|
|
+ → WP-C 前端展示与 S8 空态
|
|
|
+ → WP-D 员工状态
|
|
|
+ → WP-E T8 密钥
|
|
|
+ → WP-F 真实源补通
|
|
|
+ → 全量验收与部署
|
|
|
+```
|
|
|
+
|
|
|
+依赖关系:
|
|
|
+
|
|
|
+- A/B/C 必须同一发布窗口完成,否则 API 与前端契约不一致。
|
|
|
+- D/E/F 可以独立于展示契约,但最终 UAT 放行前必须给出完成状态。
|
|
|
+- G 应先完成,避免后续反复重算时继续死锁。
|
|
|
+- E 涉及密钥轮换,必须与运维约定切换窗口。
|
|
|
+
|
|
|
+### 10.1 固定提交批次
|
|
|
+
|
|
|
+为降低跨模块审查风险,固定拆成 4 个提交,依次 commit + push:
|
|
|
+
|
|
|
+1. **提交 1(server)**:WP-G 互斥与重试;server 版本从 1.0.591 升 1.0.592。
|
|
|
+2. **提交 2(server + Web)**:WP-A/B/C、迁移 `1.0.593`(因为提交 1 已占
|
|
|
+ 1.0.592)、server 升 1.0.593、Web 升 2.4.414。
|
|
|
+3. **提交 3(server)**:WP-D 员工状态;server 升 1.0.594。
|
|
|
+4. **提交 4(server/config)**:WP-E 密钥 resolver 与移除 t8_v5;server 升 1.0.595。
|
|
|
+
|
|
|
+本节覆盖 §0.5 第 8 条的单提交迁移预案:既然现在固定拆包,**新鲜度迁移最终固定为
|
|
|
+`1.0.593.sql` / `1.0.593.verify.sql`**,并在提交 2 登记。若执行前任何基线版本已变化,
|
|
|
+整套版本号按相同相对顺序顺延,执行者先报告新映射再开始,不得自行复用版本。
|
|
|
+
|
|
|
+WP-F 不预设代码提交:先完成只读取证;只有负责人解除来源门禁后,另列改动清单并确认。
|
|
|
+
|
|
|
+当前任务书文件尚未纳入基线提交;执行者把本任务书加入提交 1。每个提交只 stage 本节列出的任务文件和对应版本文件,禁止 `git add .`,不得把工作区其它既有改动带入。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 11. 反向影响推演
|
|
|
+
|
|
|
+### 11.1 代码调用面
|
|
|
+
|
|
|
+**需同步改**:
|
|
|
+
|
|
|
+- `GetHomeGridGeneric` 被 S1–S7、S9 九宫格共同调用;
|
|
|
+- `HomeModuleKpiCard` 与三种布局组件共同影响所有非 S8 模块;
|
|
|
+- 员工状态投影影响 S3 计划人效、S5/S6/S7 人效,不只截图三张卡;
|
|
|
+- `InventoryInboundLockGuard` 被库存同步、后台管理、重算队列抢占等多个入口复用。
|
|
|
+
|
|
|
+执行者必须全仓检索调用点,不能只回归 S5–S9。
|
|
|
+
|
|
|
+### 11.2 数据契约面
|
|
|
+
|
|
|
+**需同步改**:
|
|
|
+
|
|
|
+- `home-grid.items[]` 增加最新尝试、展示状态和新鲜度字段;
|
|
|
+- `ado_smart_ops_kpi_master` 增加新鲜度 SLA;
|
|
|
+- KPI 原始结果表不改、不删、不回填假值;
|
|
|
+- 员工标准层通过正式投影更新。
|
|
|
+
|
|
|
+需要同步核查智慧诊断、ChatBI、导出是否直接调用 `home-grid`。若调用,仅新增字段应向后兼容;若调用方把 `resultStatus` 当“最新计算状态”,必须改读 `latestResultStatus`。
|
|
|
+
|
|
|
+### 11.3 多数据源面
|
|
|
+
|
|
|
+**需同步核查**:
|
|
|
+
|
|
|
+- T8、165 MES/WMS、自建单、标准 API 入站是否都能提供
|
|
|
+ `PROD_RECEIPT/SALES_SHIP/SALES_RETURN/FQC/EMPLOYEE`;
|
|
|
+- 每个租户按 `mdp_tenant_std_source` 选择唯一权威来源;
|
|
|
+- 某来源缺字段时必须落明确 NO_DATA/unsupported,不得跨源拼凑。
|
|
|
+
|
|
|
+### 11.4 多租户 / Domain 面
|
|
|
+
|
|
|
+**无影响(前提是严格保留现有过滤)**:
|
|
|
+
|
|
|
+- 两份值查询都必须带 tenant、factory、module、metric;
|
|
|
+- 补数与重投影只作用于目标租户;
|
|
|
+- 不得复制 tenant 797403760988229 的 T8 数据给 838257186181189;
|
|
|
+- Domain/source_scope 继续由现有 resolver 决定。
|
|
|
+
|
|
|
+### 11.5 运行面
|
|
|
+
|
|
|
+**需同步改**:
|
|
|
+
|
|
|
+- 新迁移、verify 与 csproj 登记;
|
|
|
+- 新环境密钥与口令轮换;
|
|
|
+- 前后端重新构建部署;
|
|
|
+- 截图当前 V2.4.411,必须验证页头版本已变为本次新版本;
|
|
|
+- 02:00/半小时同步并发场景必须现场验收。
|
|
|
+
|
|
|
+### 11.6 验证面
|
|
|
+
|
|
|
+**需同步改**:
|
|
|
+
|
|
|
+- 后端值选择测试;
|
|
|
+- 新鲜度策略测试;
|
|
|
+- 员工状态契约测试;
|
|
|
+- 锁并发测试;
|
|
|
+- 前端三布局 + S8 空态测试;
|
|
|
+- S1–S9 API 快照/语义回归;
|
|
|
+- 真实数据库验收 SQL与截图。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 12. 版本、迁移、构建与提交
|
|
|
+
|
|
|
+### 12.1 版本
|
|
|
+
|
|
|
+严格按 §10.1 四个提交递增:
|
|
|
+
|
|
|
+- 提交 1:server 1.0.592
|
|
|
+- 提交 2:server 1.0.593 / Web 2.4.414;迁移 1.0.593
|
|
|
+- 提交 3:server 1.0.594
|
|
|
+- 提交 4:server 1.0.595
|
|
|
+
|
|
|
+执行前若基线版本不再是 server 1.0.591 / Web 2.4.413,停止并按当前版本整体顺延,先向用户报告,不得覆盖或重复版本。
|
|
|
+
|
|
|
+纯任务书提交不升前后端版本。
|
|
|
+
|
|
|
+### 12.2 构建
|
|
|
+
|
|
|
+```powershell
|
|
|
+dotnet build server\Admin.NET.Web.Entry\Admin.NET.Web.Entry.csproj -v q --nologo
|
|
|
+dotnet build server\Plugins\Admin.NET.Plugin.AiDOP.Tests\Admin.NET.Plugin.AiDOP.Tests.csproj -v q --nologo
|
|
|
+dotnet test server\Plugins\Admin.NET.Plugin.AiDOP.Tests\Admin.NET.Plugin.AiDOP.Tests.csproj `
|
|
|
+ --no-build --nologo --logger "trx;LogFileName=grid-freshness.trx"
|
|
|
+
|
|
|
+Set-Location Web
|
|
|
+npm run test:run
|
|
|
+npm run build
|
|
|
+```
|
|
|
+
|
|
|
+若 DLL 被运行中的后端锁定,先正常停止该进程再构建。不得使用破坏性 Git 命令清理用户工作区。
|
|
|
+
|
|
|
+基线全量测试是 3217 条、7 条失败。新增测试后总数会增加,但失败集合不得扩大。固定基线失败为:
|
|
|
+
|
|
|
+1. `S0DimContractTests.ProductDesign_bom_and_routing_sql_must_carry_tenant_predicate_everywhere`
|
|
|
+2. `S1RequirementExamineDwdContractTests.Table_HasNoS8SpecificColumns`
|
|
|
+3. `S1RequirementExamineDwdContractTests.CurrentSnapshotIndex_ExistsInBothInlineDdlAndMigration`
|
|
|
+4. `S1RequirementExamineDwdContractTests.BooleanColumn_FollowsRepoFlagConvention_NotBit`
|
|
|
+5. `S1RequirementExamineDwdContractTests.NewColumns_ExistInBothInlineDdlAndMigration`
|
|
|
+6. `S1RequirementExamineDwdContractTests.QuantityColumns_DocumentedAsStandardNames`
|
|
|
+7. `S6ProductionInstructionContractTests.Std_Schedule_HasNewNullableColumns`
|
|
|
+
|
|
|
+用 TRX 比较失败用例名,不以控制台中文输出为准。测试完成后删除本轮 `TestResults`。
|
|
|
+
|
|
|
+### 12.3 提交排除项
|
|
|
+
|
|
|
+不得提交:
|
|
|
+
|
|
|
+- `bin/`、`obj/`、`TestResults/`
|
|
|
+- `_tmp_*.log`、`frontend-dev.log`
|
|
|
+- `~$*.docx`
|
|
|
+- `.env` 或任何真实口令
|
|
|
+- 构建产物
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 13. 总验收清单
|
|
|
+
|
|
|
+### 公共取数
|
|
|
+
|
|
|
+- [ ] 最新 NO_DATA 不再遮住区间内最后真实值
|
|
|
+- [ ] 最新尝试状态与最后真实值状态分开返回
|
|
|
+- [ ] target/value/bizDate 来自同一行
|
|
|
+- [ ] 显式日期筛选不跨区间回退
|
|
|
+- [ ] 从未有值仍显示无业务数据
|
|
|
+- [ ] 业务维度筛选不回落未筛选历史值
|
|
|
+
|
|
|
+### 新鲜度
|
|
|
+
|
|
|
+- [ ] 首页启用指标全部配置正数 SLA
|
|
|
+- [ ] 日/月指标按各自 SLA 判定
|
|
|
+- [ ] 前端无固定 7 天常量
|
|
|
+- [ ] POLICY_MISSING 明确可见
|
|
|
+
|
|
|
+### 前端
|
|
|
+
|
|
|
+- [ ] S6_L1_001、002 与 S7_L1_001 显示最后真实值和日期
|
|
|
+- [ ] 7 月人效/退货率值弱化并显示最新 NO_DATA 提示
|
|
|
+- [ ] “无业务数据”不再竖排
|
|
|
+- [ ] S8 0 行显示“暂无异常告警”
|
|
|
+- [ ] S8 请求失败与无告警严格区分
|
|
|
+- [ ] 页头版本不是旧的 V2.4.411
|
|
|
+
|
|
|
+### 员工映射
|
|
|
+
|
|
|
+- [ ] AC → ACTIVE
|
|
|
+- [ ] DC → LEFT
|
|
|
+- [ ] 空状态仍 UNKNOWN
|
|
|
+- [ ] 至少 5 个 ACTIVE + WAREHOUSE
|
|
|
+- [ ] 未伪造 PRODUCTION 岗位
|
|
|
+
|
|
|
+### T8 与安全
|
|
|
+
|
|
|
+- [ ] 口令来自部署密钥,不来自 Git 跟踪文件
|
|
|
+- [ ] health 能区分 SECRET_MISSING / AUTH_FAILED / NETWORK_FAILED
|
|
|
+- [ ] 当前树无 T8 明文口令
|
|
|
+- [ ] 已轮换历史暴露口令
|
|
|
+- [ ] 日志无连接串/口令
|
|
|
+
|
|
|
+### 互斥与运行
|
|
|
+
|
|
|
+- [ ] tenant 重载物化入口也持有库存咨询锁
|
|
|
+- [ ] 抢锁有界等待、可取消、超时不假成功
|
|
|
+- [ ] 02:00 并发场景无 deadlock
|
|
|
+- [ ] S5/S7 重算成功
|
|
|
+
|
|
|
+### 数据真实性
|
|
|
+
|
|
|
+- [ ] 未直接更新 KPI 值表
|
|
|
+- [ ] 未复制其它租户
|
|
|
+- [ ] 未把历史日期改成今天
|
|
|
+- [ ] 每个仍无当前值的指标都有代码/配置/数据三层结论
|
|
|
+
|
|
|
+### 工程
|
|
|
+
|
|
|
+- [ ] 后端构建 0 error
|
|
|
+- [ ] 新增测试通过
|
|
|
+- [ ] 既有失败集合不扩大
|
|
|
+- [ ] 前端测试与构建通过
|
|
|
+- [ ] 迁移与 verify 已登记且执行成功
|
|
|
+- [ ] 版本号正确
|
|
|
+- [ ] commit + push 成功并汇报远端哈希
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 14. 回滚原则
|
|
|
+
|
|
|
+1. API 新字段是增量字段;如需回滚前端,可保留后端字段。
|
|
|
+2. 不回滚或删除历史 KPI 行。
|
|
|
+3. 新鲜度列回滚时先停止读取,不急于 DROP;DROP 必须另发迁移。
|
|
|
+4. 员工状态映射回滚后,通过正式投影重建标准层,禁止手改。
|
|
|
+5. 密钥切换回滚不得把口令重新提交进仓库;只能切回上一安全密钥版本。
|
|
|
+6. 咨询锁改动若出现阻塞,先关闭相关作业并取证锁持有者;不得通过删除业务数据解除。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 15. 交付报告模板
|
|
|
+
|
|
|
+执行者最终必须按以下结构汇报:
|
|
|
+
|
|
|
+```text
|
|
|
+1. 提交与版本
|
|
|
+2. 实际改动文件
|
|
|
+3. 迁移执行结果
|
|
|
+4. 九宫格逐卡验收
|
|
|
+5. 员工投影结果
|
|
|
+6. T8 健康与密钥状态(不得写口令)
|
|
|
+7. 02:00 并发验收
|
|
|
+8. 测试结果及既有失败对比
|
|
|
+9. 反向影响推演
|
|
|
+ - 代码调用面:无影响 / 需同步改(文件)
|
|
|
+ - 数据契约面:无影响 / 需同步改(文件/表)
|
|
|
+ - 多数据源面:无影响 / 需同步改(来源)
|
|
|
+ - 多租户/Domain:无影响 / 需同步改
|
|
|
+ - 运行面:无影响 / 需同步改
|
|
|
+ - 验证面:无影响 / 需同步改
|
|
|
+10. 仍无当前业务数据的指标及三层证据
|
|
|
+```
|
|
|
+
|
|
|
+只有“页面有数字”不算完成;只有在数字来源、业务日期、最新状态、新鲜度和来源链路都可解释时,任务才完成。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 16. 给执行模型的启动指令
|
|
|
+
|
|
|
+把下列文字连同本文件路径交给新的执行模型:
|
|
|
+
|
|
|
+```text
|
|
|
+请严格执行 doc/plan/九宫格最后真实值与数据新鲜度根治执行任务书.md。
|
|
|
+
|
|
|
+先做以下事情,不要立即改文件:
|
|
|
+1. 读取 AGENTS.md 和所有 alwaysApply 规则。
|
|
|
+2. 核对 HEAD、server/Web 版本、工作区全部未提交文件;不得覆盖或提交他人改动。
|
|
|
+3. 重新运行任务书中的只读基线 SQL。
|
|
|
+4. 按 §10.1 列出四个提交的精确文件清单、跨模块影响与不做事项,等待我确认。
|
|
|
+
|
|
|
+确认后严格按 WP-G → WP-A/B/C → WP-D → WP-E 顺序执行并逐提交构建、测试、commit、push。
|
|
|
+不得更改已冻结的设计选择,不得修改历史成功迁移,不得把任何口令打印到对话、日志或 diff 摘要。
|
|
|
+
|
|
|
+到 E-M1/E-M2 人工密钥门禁时停止并只告诉我需要在部署环境完成什么,不得要求我在对话中发送口令。
|
|
|
+WP-F 先只读取证;mdp_tenant_std_source 缺绑定、多绑定或已绑定来源不提供对象时停止并请求负责人决策,禁止自行切源、复制其它租户或直接改 KPI 表。
|
|
|
+
|
|
|
+完成后按 §15 模板交付,并附可重跑的 SQL、测试失败集合对比、远端 commit 哈希和六面反向影响推演。
|
|
|
+```
|
|
|
+
|
|
|
+该启动指令不授权执行模型跳过仓库的“改动前确认”规则;它只固定执行方法与停机条件。
|