AidopMonitorDictionarySeed.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using System.Diagnostics;
  2. using Admin.NET.Plugin.AiDOP.Entity.S8;
  3. using SqlSugar;
  4. namespace Admin.NET.Plugin.AiDOP.Infrastructure;
  5. /// <summary>
  6. /// CONFIG-MONITOR-DICT-READONLY-SEED-1:S8 监控对象/指标 baseline 种子。
  7. /// 6 个对象 + 10 条指标,与原前端 BUSINESS_MONITOR_OPTIONS 一一对应。
  8. /// 幂等:按 (tenant_id=0, factory_id=0, object_code/metric_code) Any() 检查后插入。
  9. /// 已存在记录会按 SOURCE-MAPPING-1 打补丁:补 source_table / 修正 demo 字段映射。
  10. /// RATIO 类指标 enabled=false + is_result_kpi=true(S9 结果 KPI 范畴),不进入演示路径。
  11. /// </summary>
  12. public static class AidopMonitorDictionarySeed
  13. {
  14. private const string DefaultObjectIdField = "source_object_id";
  15. private const string DefaultObjectCodeField = "related_object_code";
  16. private const string DefaultObjectNameField = "related_object_name";
  17. /// <summary>CONFIG-MONITOR-DICT-SOURCE-TABLE-SCHEMA-1:DEMO 演示用单表桥接。</summary>
  18. private const string DemoSourceTable = "demo_test_order";
  19. public static void EnsureSeed(ISqlSugarClient db)
  20. {
  21. try
  22. {
  23. var ct = DateTime.Parse("2026-05-09 00:00:00");
  24. // 6 监控对象(仅 ORDER_DELIVERY/IQC_INSPECTION 桥接到 demo_test_order,便于 DATE/VR 真实 SQL 演示)
  25. EnsureObject(db, "ORDER_DELIVERY", "ORDER", "订单交付", DemoSourceTable, 10, ct);
  26. EnsureObject(db, "ORDER_CHANGE", "ORDER", "订单变更", null, 20, ct);
  27. EnsureObject(db, "PURCHASE_DELIVERY", "PURCHASE_ORDER", "供应商交付", null, 30, ct);
  28. EnsureObject(db, "IQC_INSPECTION", "IQC", "IQC 检验", DemoSourceTable, 40, ct);
  29. EnsureObject(db, "WORK_ORDER_PRODUCTION", "WORK_ORDER", "生产工单", null, 50, ct);
  30. EnsureObject(db, "INVENTORY_STOCK", "INVENTORY", "库存", null, 60, ct);
  31. // DATE 指标 enabled=true / is_result_kpi=false
  32. // ORDER_DUE_AT 走 demo_test_order:id/order_no 真实列名(覆盖默认 alias 占位)
  33. EnsureMetricDate(db, "ORDER_DUE_AT", "计划交付时间", "ORDER_DELIVERY",
  34. "分钟", "due_at", "status",
  35. objectIdCol: "id", objectCodeCol: "order_no", objectNameCol: "order_no",
  36. sortNo: 110, ct: ct);
  37. EnsureMetricDate(db, "PO_DUE_AT", "计划到货时间", "PURCHASE_DELIVERY",
  38. "分钟", "due_at", "status", null, null, null, 310, ct);
  39. EnsureMetricDate(db, "WO_DUE_AT", "计划完工时间", "WORK_ORDER_PRODUCTION",
  40. "分钟", "due_at", "status", null, null, null, 510, ct);
  41. // VALUE_RANGE 指标 enabled=true / is_result_kpi=false
  42. // IQC_VALUE 走 demo_test_order:measured_size 是 demo 实际列;id/order_no 同上
  43. EnsureMetricValueRange(db, "ORDER_CHANGE_COUNT", "订单变更次数", "ORDER_CHANGE",
  44. "次", "measured_value", null, null, null, 210, ct);
  45. EnsureMetricValueRange(db, "IQC_VALUE", "检验值", "IQC_INSPECTION",
  46. null, "measured_size",
  47. objectIdCol: "id", objectCodeCol: "order_no", objectNameCol: "order_no",
  48. sortNo: 410, ct: ct);
  49. EnsureMetricValueRange(db, "INV_QTY", "当前库存量", "INVENTORY_STOCK",
  50. null, "measured_value", null, null, null, 610, ct);
  51. // RATIO 指标 enabled=false / is_result_kpi=true(S9 结果 KPI 范畴;本轮 seed 入库但默认禁用)
  52. EnsureMetricRatio(db, "ORDER_DELIVERY_RATE", "订单交付满足率", "ORDER_DELIVERY", "%", "measured_value", 120, ct);
  53. EnsureMetricRatio(db, "PO_DELIVERY_RATE", "到货达成率", "PURCHASE_DELIVERY", "%", "measured_value", 320, ct);
  54. EnsureMetricRatio(db, "IQC_PASS_RATE", "检验合格率", "IQC_INSPECTION", "%", "measured_value", 420, ct);
  55. EnsureMetricRatio(db, "WO_COMPLETION_RATE", "工单完工率", "WORK_ORDER_PRODUCTION", "%", "measured_value", 520, ct);
  56. }
  57. catch (Exception ex)
  58. {
  59. Trace.TraceWarning("AidopMonitorDictionarySeed: " + ex);
  60. }
  61. }
  62. private static void EnsureObject(ISqlSugarClient db, string objectCode, string objectType, string objectName, string? sourceTable, int sortNo, DateTime ct)
  63. {
  64. var existing = db.Queryable<AdoS8MonitorObject>()
  65. .First(x => x.TenantId == 0 && x.FactoryId == 0 && x.ObjectCode == objectCode);
  66. if (existing == null)
  67. {
  68. db.Insertable(new AdoS8MonitorObject
  69. {
  70. TenantId = 0,
  71. FactoryId = 0,
  72. ObjectCode = objectCode,
  73. ObjectType = objectType,
  74. ObjectName = objectName,
  75. SourceTable = sourceTable,
  76. Enabled = true,
  77. SortNo = sortNo,
  78. CreatedAt = ct,
  79. }).ExecuteCommand();
  80. return;
  81. }
  82. // 已存在记录:仅当 source_table 为空时回填,避免覆盖运维已修改的值
  83. if (string.IsNullOrWhiteSpace(existing.SourceTable) && !string.IsNullOrWhiteSpace(sourceTable))
  84. {
  85. db.Updateable<AdoS8MonitorObject>()
  86. .SetColumns(x => x.SourceTable == sourceTable)
  87. .SetColumns(x => x.UpdatedAt == DateTime.Now)
  88. .Where(x => x.Id == existing.Id)
  89. .ExecuteCommand();
  90. }
  91. }
  92. private static void EnsureMetricDate(ISqlSugarClient db, string metricCode, string metricName, string objectCode, string? unit, string dueAtField, string statusField, string? objectIdCol, string? objectCodeCol, string? objectNameCol, int sortNo, DateTime ct)
  93. {
  94. var existing = db.Queryable<AdoS8MonitorMetric>()
  95. .First(x => x.TenantId == 0 && x.FactoryId == 0 && x.MetricCode == metricCode);
  96. if (existing == null)
  97. {
  98. db.Insertable(new AdoS8MonitorMetric
  99. {
  100. TenantId = 0,
  101. FactoryId = 0,
  102. ObjectCode = objectCode,
  103. MetricCode = metricCode,
  104. MetricName = metricName,
  105. Mechanism = "DATE",
  106. Unit = unit,
  107. DueAtField = dueAtField,
  108. StatusField = statusField,
  109. ObjectIdField = objectIdCol ?? DefaultObjectIdField,
  110. ObjectCodeField = objectCodeCol ?? DefaultObjectCodeField,
  111. ObjectNameField = objectNameCol ?? DefaultObjectNameField,
  112. IsResultKpi = false,
  113. Enabled = true,
  114. SortNo = sortNo,
  115. CreatedAt = ct,
  116. }).ExecuteCommand();
  117. return;
  118. }
  119. BackfillMetricColumns(db, existing, objectIdCol, objectCodeCol, objectNameCol);
  120. }
  121. private static void EnsureMetricValueRange(ISqlSugarClient db, string metricCode, string metricName, string objectCode, string? unit, string measuredValueField, string? objectIdCol, string? objectCodeCol, string? objectNameCol, int sortNo, DateTime ct)
  122. {
  123. var existing = db.Queryable<AdoS8MonitorMetric>()
  124. .First(x => x.TenantId == 0 && x.FactoryId == 0 && x.MetricCode == metricCode);
  125. if (existing == null)
  126. {
  127. db.Insertable(new AdoS8MonitorMetric
  128. {
  129. TenantId = 0,
  130. FactoryId = 0,
  131. ObjectCode = objectCode,
  132. MetricCode = metricCode,
  133. MetricName = metricName,
  134. Mechanism = "VALUE_RANGE",
  135. Unit = unit,
  136. MeasuredValueField = measuredValueField,
  137. ObjectIdField = objectIdCol ?? DefaultObjectIdField,
  138. ObjectCodeField = objectCodeCol ?? DefaultObjectCodeField,
  139. ObjectNameField = objectNameCol ?? DefaultObjectNameField,
  140. IsResultKpi = false,
  141. Enabled = true,
  142. SortNo = sortNo,
  143. CreatedAt = ct,
  144. }).ExecuteCommand();
  145. return;
  146. }
  147. BackfillMetricColumns(db, existing, objectIdCol, objectCodeCol, objectNameCol, measuredValueField);
  148. }
  149. private static void EnsureMetricRatio(ISqlSugarClient db, string metricCode, string metricName, string objectCode, string? unit, string measuredValueField, int sortNo, DateTime ct)
  150. {
  151. if (db.Queryable<AdoS8MonitorMetric>()
  152. .Any(x => x.TenantId == 0 && x.FactoryId == 0 && x.MetricCode == metricCode)) return;
  153. db.Insertable(new AdoS8MonitorMetric
  154. {
  155. TenantId = 0,
  156. FactoryId = 0,
  157. ObjectCode = objectCode,
  158. MetricCode = metricCode,
  159. MetricName = metricName,
  160. Mechanism = "RATIO",
  161. Unit = unit,
  162. MeasuredValueField = measuredValueField,
  163. ObjectIdField = DefaultObjectIdField,
  164. ObjectCodeField = DefaultObjectCodeField,
  165. ObjectNameField = DefaultObjectNameField,
  166. IsResultKpi = true,
  167. Enabled = false,
  168. SortNo = sortNo,
  169. CreatedAt = ct,
  170. Remark = "[RESERVED] S9 result KPI; enable explicitly when S8/S9 boundary task lands",
  171. }).ExecuteCommand();
  172. }
  173. /// <summary>已存在指标行:仅在原值仍是默认 alias 占位时回填真实列名,避免覆盖运维改过的字段。</summary>
  174. private static void BackfillMetricColumns(ISqlSugarClient db, AdoS8MonitorMetric existing, string? objectIdCol, string? objectCodeCol, string? objectNameCol, string? measuredValueCol = null)
  175. {
  176. var changed = false;
  177. if (!string.IsNullOrWhiteSpace(objectIdCol)
  178. && (string.IsNullOrWhiteSpace(existing.ObjectIdField) || existing.ObjectIdField == DefaultObjectIdField))
  179. {
  180. existing.ObjectIdField = objectIdCol;
  181. changed = true;
  182. }
  183. if (!string.IsNullOrWhiteSpace(objectCodeCol)
  184. && (string.IsNullOrWhiteSpace(existing.ObjectCodeField) || existing.ObjectCodeField == DefaultObjectCodeField))
  185. {
  186. existing.ObjectCodeField = objectCodeCol;
  187. changed = true;
  188. }
  189. if (!string.IsNullOrWhiteSpace(objectNameCol)
  190. && (string.IsNullOrWhiteSpace(existing.ObjectNameField) || existing.ObjectNameField == DefaultObjectNameField))
  191. {
  192. existing.ObjectNameField = objectNameCol;
  193. changed = true;
  194. }
  195. if (!string.IsNullOrWhiteSpace(measuredValueCol)
  196. && (string.IsNullOrWhiteSpace(existing.MeasuredValueField) || existing.MeasuredValueField == "measured_value"))
  197. {
  198. // 仅当原值是默认占位 measured_value 时才覆盖
  199. if (existing.MeasuredValueField != measuredValueCol)
  200. {
  201. existing.MeasuredValueField = measuredValueCol;
  202. changed = true;
  203. }
  204. }
  205. if (!changed) return;
  206. existing.UpdatedAt = DateTime.Now;
  207. db.Updateable(existing).ExecuteCommand();
  208. }
  209. }