Parcourir la source

feat(s8): configure exception monitoring categories

YY968XX il y a 2 mois
Parent
commit
c9fd5b7722

+ 11 - 0
Web/src/views/aidop/s8/config/S8ExceptionTypeConfigPage.vue

@@ -8,10 +8,19 @@ const columns = [
 	{ key: 'typeCode', label: '类型编码', width: 180 },
 	{ key: 'typeCode', label: '类型编码', width: 180 },
 	{ key: 'typeName', label: '类型名称', width: 180 },
 	{ key: 'typeName', label: '类型名称', width: 180 },
 	{ key: 'sceneCode', label: '场景编码', width: 150 },
 	{ key: 'sceneCode', label: '场景编码', width: 150 },
+	{ key: 'monitoringCategoryKey', label: '大屏统计类别', width: 160 },
 	{ key: 'enabled', label: '启用', width: 90 },
 	{ key: 'enabled', label: '启用', width: 90 },
 	{ key: 'sortNo', label: '排序', width: 90 },
 	{ key: 'sortNo', label: '排序', width: 90 },
 ];
 ];
 
 
+const MONITORING_CATEGORY_OPTIONS = [
+	{ label: '订单评审', value: 'ORDER_REVIEW' },
+	{ label: '产品设计', value: 'PRODUCT_DESIGN' },
+	{ label: '材料采购', value: 'MATERIAL_PURCHASE' },
+	{ label: '本体生产', value: 'BODY_PRODUCTION' },
+	{ label: '总装发货', value: 'FINAL_ASSEMBLY_DELIVERY' },
+];
+
 // S8-CONFIG-CLEANUP-DEMO-1:以下 7 个字段标记 hidden=true:
 // S8-CONFIG-CLEANUP-DEMO-1:以下 7 个字段标记 hidden=true:
 // severityDefault / slaMinutes / ownerRoleCode / escalateRoleCode / statsMode / icon / mobileVisible
 // severityDefault / slaMinutes / ownerRoleCode / escalateRoleCode / statsMode / icon / mobileVisible
 // 渲染层隐藏,但保留在 fields / buildDefault / form / PUT payload,旧值原样回写不丢。
 // 渲染层隐藏,但保留在 fields / buildDefault / form / PUT payload,旧值原样回写不丢。
@@ -20,6 +29,7 @@ const fields = [
 	{ key: 'typeCode', label: '类型编码', type: 'input', required: true, placeholder: '如 ORDER_CHANGE' },
 	{ key: 'typeCode', label: '类型编码', type: 'input', required: true, placeholder: '如 ORDER_CHANGE' },
 	{ key: 'typeName', label: '类型名称', type: 'input', required: true },
 	{ key: 'typeName', label: '类型名称', type: 'input', required: true },
 	{ key: 'sceneCode', label: '场景编码', type: 'select', required: true, optionsKey: 'scenes' },
 	{ key: 'sceneCode', label: '场景编码', type: 'select', required: true, optionsKey: 'scenes' },
+	{ key: 'monitoringCategoryKey', label: '大屏统计类别', type: 'select', options: MONITORING_CATEGORY_OPTIONS },
 	{ key: 'severityDefault', label: '默认严重度', type: 'select', optionsKey: 'severities', hidden: true },
 	{ key: 'severityDefault', label: '默认严重度', type: 'select', optionsKey: 'severities', hidden: true },
 	{ key: 'slaMinutes', label: 'SLA(分钟)', type: 'number', hidden: true },
 	{ key: 'slaMinutes', label: 'SLA(分钟)', type: 'number', hidden: true },
 	{ key: 'ownerRoleCode', label: '责任角色', type: 'select', optionsKey: 'roles', hidden: true },
 	{ key: 'ownerRoleCode', label: '责任角色', type: 'select', optionsKey: 'roles', hidden: true },
@@ -47,6 +57,7 @@ const buildDefault = () => ({
 	typeCode: '',
 	typeCode: '',
 	typeName: '',
 	typeName: '',
 	sceneCode: '',
 	sceneCode: '',
+	monitoringCategoryKey: '',
 	severityDefault: 'MEDIUM',
 	severityDefault: 'MEDIUM',
 	slaMinutes: 60,
 	slaMinutes: 60,
 	ownerRoleCode: '',
 	ownerRoleCode: '',

+ 5 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S8/AdoS8ExceptionType.cs

@@ -48,6 +48,11 @@ public class AdoS8ExceptionType
     [SugarColumn(ColumnName = "stats_mode", Length = 16)]
     [SugarColumn(ColumnName = "stats_mode", Length = 16)]
     public string StatsMode { get; set; } = "ALL";
     public string StatsMode { get; set; } = "ALL";
 
 
+    /// <summary>大屏统计类别 Key(5 选 1:ORDER_REVIEW / PRODUCT_DESIGN / MATERIAL_PURCHASE / BODY_PRODUCTION / FINAL_ASSEMBLY_DELIVERY)。
+    /// S8 Overview 5 张类别卡聚合依据。空值时回落到 S8MonitoringService.CategoryOfLegacy 兼容映射。</summary>
+    [SugarColumn(ColumnName = "monitoring_category_key", Length = 64, IsNullable = true)]
+    public string? MonitoringCategoryKey { get; set; }
+
     [SugarColumn(ColumnName = "mobile_visible", ColumnDataType = "boolean")]
     [SugarColumn(ColumnName = "mobile_visible", ColumnDataType = "boolean")]
     public bool MobileVisible { get; set; } = true;
     public bool MobileVisible { get; set; } = true;
 
 

+ 44 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/S8/S8MonitoringCategory.cs

@@ -0,0 +1,44 @@
+namespace Admin.NET.Plugin.AiDOP.Infrastructure.S8;
+
+/// <summary>
+/// S8 监控大屏 5 张类别卡稳定 Key 与中文 Label 的双向映射。
+/// Key 写入 ado_s8_exception_type.monitoring_category_key;Label 用于 byCategory.category 输出,
+/// 与前端 CATEGORY_DEFS_FALLBACK / OVERVIEW_CATEGORY_KEY_BY_CELL_CODE 字面 100% 对齐。
+/// </summary>
+public static class S8MonitoringCategory
+{
+    public const string OrderReview = "ORDER_REVIEW";
+    public const string ProductDesign = "PRODUCT_DESIGN";
+    public const string MaterialPurchase = "MATERIAL_PURCHASE";
+    public const string BodyProduction = "BODY_PRODUCTION";
+    public const string FinalAssemblyDelivery = "FINAL_ASSEMBLY_DELIVERY";
+
+    public const string OrderReviewLabel = "订单评审";
+    public const string ProductDesignLabel = "产品设计";
+    public const string MaterialPurchaseLabel = "材料采购";
+    public const string BodyProductionLabel = "本体生产";
+    public const string FinalAssemblyDeliveryLabel = "总装发货";
+
+    public static string? Normalize(string? key)
+    {
+        var normalized = key?.Trim().ToUpperInvariant();
+        return IsValid(normalized) ? normalized : null;
+    }
+
+    public static bool IsValid(string? key) => key is
+        OrderReview or
+        ProductDesign or
+        MaterialPurchase or
+        BodyProduction or
+        FinalAssemblyDelivery;
+
+    public static string? KeyToLabel(string? key) => Normalize(key) switch
+    {
+        OrderReview => OrderReviewLabel,
+        ProductDesign => ProductDesignLabel,
+        MaterialPurchase => MaterialPurchaseLabel,
+        BodyProduction => BodyProductionLabel,
+        FinalAssemblyDelivery => FinalAssemblyDeliveryLabel,
+        _ => null,
+    };
+}

+ 44 - 39
server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/S8ExceptionTypeSeedData.cs

@@ -16,9 +16,13 @@ namespace Admin.NET.Plugin.AiDOP;
 ///   - S8-BASELINE-EXCEPTION-TYPE-RULE-TEMPLATE-UPDATE-1:基于《S1-S7异常类型与三类规则对应表.md》
 ///   - S8-BASELINE-EXCEPTION-TYPE-RULE-TEMPLATE-UPDATE-1:基于《S1-S7异常类型与三类规则对应表.md》
 ///     扩展 baseline 至 44 条;旧 7 条(DIMENSION_DEVIATION / MATERIAL_SHORTAGE / QUALITY_DEFECT /
 ///     扩展 baseline 至 44 条;旧 7 条(DIMENSION_DEVIATION / MATERIAL_SHORTAGE / QUALITY_DEFECT /
 ///     YIELD_DEFICIT / WH_INBOUND_ISSUE / WH_KIT_ISSUE / WH_ISSUE_OUT_ISSUE)暂保留为"待裁剪"。
 ///     YIELD_DEFICIT / WH_INBOUND_ISSUE / WH_KIT_ISSUE / WH_ISSUE_OUT_ISSUE)暂保留为"待裁剪"。
-///   - S8-DEPRECATED-EXCEPTION-TYPE-MIGRATE-1(本轮):迁移 active 异常 / cell_config / watch_rule.params_json,
+///   - S8-DEPRECATED-EXCEPTION-TYPE-MIGRATE-1:迁移 active 异常 / cell_config / watch_rule.params_json,
 ///     旧 7 条 enabled=false 持久化(不删除,保留历史可查 + remark 标注 migrated to ...)。
 ///     旧 7 条 enabled=false 持久化(不删除,保留历史可查 + remark 标注 migrated to ...)。
 ///     enabled baseline 由 44 → 37。
 ///     enabled baseline 由 44 → 37。
+///   - S8-EXCEPTION-TYPE-CATEGORY-CONFIG-2(本轮):异常类型新增 monitoring_category_key 列,
+///     用配置驱动 Overview 5 张类别卡聚合,取代 S8MonitoringService.CategoryOf 内的 hardcode。
+///     37 条 enabled 在 SeedData 显式赋 5 选 1(PRODUCT_DESIGN 当前无对应 type_code,先保留 key 选项);
+///     7 条 deprecated 保持 monitoring_category_key=null,命中 CategoryOfLegacy 兜底。
 ///
 ///
 /// 字段约定:Id 与 DB 现有 1:1 对齐;type_code 全保留;sort_no 与 DB 一致;enabled=true。
 /// 字段约定:Id 与 DB 现有 1:1 对齐;type_code 全保留;sort_no 与 DB 一致;enabled=true。
 /// </summary>
 /// </summary>
@@ -33,13 +37,13 @@ public class S8ExceptionTypeSeedData : ISqlSugarEntitySeedData<Entity.S8.AdoS8Ex
         return new[]
         return new[]
         {
         {
             // ── S1 产销协同 + S7 成品仓储(交付侧) ──
             // ── S1 产销协同 + S7 成品仓储(交付侧) ──
-            T(baseId + 1,  "ORDER_CHANGE",             "订单变更",           S8SceneCode.S1, 60,  "ROLE_ORDER_PLANNER",      "MEDIUM", 100, ct),
-            T(baseId + 2,  "DELIVERY_DELAY",           "交期延迟",           S8SceneCode.S7, 120, "ROLE_ORDER_PLANNER",      "HIGH",   101, ct),
-            T(baseId + 3,  "PENDING_SHIPMENT",         "入库待发",           S8SceneCode.S7, 240, "ROLE_WH_OUTBOUND",        "MEDIUM", 102, ct),
-            T(baseId + 16, "ORDER_REVIEW_DELAY",       "订单评审延迟",       S8SceneCode.S1, 120, "ROLE_ORDER_PLANNER",      "MEDIUM", 400, ct),
+            T(baseId + 1,  "ORDER_CHANGE",             "订单变更",           S8SceneCode.S1, 60,  "ROLE_ORDER_PLANNER",      "MEDIUM", 100, ct, monitoringCategoryKey: S8MonitoringCategory.OrderReview),
+            T(baseId + 2,  "DELIVERY_DELAY",           "交期延迟",           S8SceneCode.S7, 120, "ROLE_ORDER_PLANNER",      "HIGH",   101, ct, monitoringCategoryKey: S8MonitoringCategory.FinalAssemblyDelivery),
+            T(baseId + 3,  "PENDING_SHIPMENT",         "入库待发",           S8SceneCode.S7, 240, "ROLE_WH_OUTBOUND",        "MEDIUM", 102, ct, monitoringCategoryKey: S8MonitoringCategory.FinalAssemblyDelivery),
+            T(baseId + 16, "ORDER_REVIEW_DELAY",       "订单评审延迟",       S8SceneCode.S1, 120, "ROLE_ORDER_PLANNER",      "MEDIUM", 400, ct, monitoringCategoryKey: S8MonitoringCategory.OrderReview),
 
 
             // ── S2 制造协同 + S6 生产执行 ──
             // ── S2 制造协同 + S6 生产执行 ──
-            T(baseId + 4,  "EQUIP_FAULT",              "设备异常",           S8SceneCode.S2, 30,  "ROLE_EQUIP_MAINT",        "HIGH",   200, ct),
+            T(baseId + 4,  "EQUIP_FAULT",              "设备异常",           S8SceneCode.S2, 30,  "ROLE_EQUIP_MAINT",        "HIGH",   200, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
             // S8-DEPRECATED-EXCEPTION-TYPE-MIGRATE-1:DIMENSION_DEVIATION/QUALITY_DEFECT 迁 MFG_QUALITY_ABNORMAL
             // S8-DEPRECATED-EXCEPTION-TYPE-MIGRATE-1:DIMENSION_DEVIATION/QUALITY_DEFECT 迁 MFG_QUALITY_ABNORMAL
             T(baseId + 14, "DIMENSION_DEVIATION",      "尺寸超差",           S8SceneCode.S2, 60,  "ROLE_QC",                 "HIGH",   200, ct, enabled: false, remark: "[DEPRECATED] migrated to MFG_QUALITY_ABNORMAL"),
             T(baseId + 14, "DIMENSION_DEVIATION",      "尺寸超差",           S8SceneCode.S2, 60,  "ROLE_QC",                 "HIGH",   200, ct, enabled: false, remark: "[DEPRECATED] migrated to MFG_QUALITY_ABNORMAL"),
             // MATERIAL_SHORTAGE 迁 MFG_MATERIAL_ABNORMAL
             // MATERIAL_SHORTAGE 迁 MFG_MATERIAL_ABNORMAL
@@ -47,62 +51,62 @@ public class S8ExceptionTypeSeedData : ISqlSugarEntitySeedData<Entity.S8.AdoS8Ex
             T(baseId + 6,  "QUALITY_DEFECT",           "质量异常",           S8SceneCode.S2, 60,  "ROLE_QC",                 "HIGH",   202, ct, enabled: false, remark: "[DEPRECATED] migrated to MFG_QUALITY_ABNORMAL"),
             T(baseId + 6,  "QUALITY_DEFECT",           "质量异常",           S8SceneCode.S2, 60,  "ROLE_QC",                 "HIGH",   202, ct, enabled: false, remark: "[DEPRECATED] migrated to MFG_QUALITY_ABNORMAL"),
             // YIELD_DEFICIT 迁 PRODUCTION_QUALITY_ABNORMAL
             // YIELD_DEFICIT 迁 PRODUCTION_QUALITY_ABNORMAL
             T(baseId + 15, "YIELD_DEFICIT",            "良率不足",           S8SceneCode.S6, 60,  "ROLE_QC",                 "HIGH",   300, ct, enabled: false, remark: "[DEPRECATED] migrated to PRODUCTION_QUALITY_ABNORMAL"),
             T(baseId + 15, "YIELD_DEFICIT",            "良率不足",           S8SceneCode.S6, 60,  "ROLE_QC",                 "HIGH",   300, ct, enabled: false, remark: "[DEPRECATED] migrated to PRODUCTION_QUALITY_ABNORMAL"),
-            T(baseId + 19, "WORK_ORDER_DELAY",         "工单延期",           S8SceneCode.S6, 60,  "ROLE_PRODUCTION_PLANNER", "HIGH",   403, ct),
+            T(baseId + 19, "WORK_ORDER_DELAY",         "工单延期",           S8SceneCode.S6, 60,  "ROLE_PRODUCTION_PLANNER", "HIGH",   403, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
 
 
             // ── S3 供应协同 + S4 采购执行 + S5 物料仓储 ──
             // ── S3 供应协同 + S4 采购执行 + S5 物料仓储 ──
-            T(baseId + 7,  "SUPPLIER_ETA_ISSUE",       "供应商回复交期异常", S8SceneCode.S3, 240, "ROLE_PURCHASER",          "MEDIUM", 300, ct),
-            T(baseId + 8,  "SUPPLIER_SHIP_ISSUE",      "供应商发货异常",     S8SceneCode.S3, 240, "ROLE_PURCHASER",          "MEDIUM", 301, ct),
+            T(baseId + 7,  "SUPPLIER_ETA_ISSUE",       "供应商回复交期异常", S8SceneCode.S3, 240, "ROLE_PURCHASER",          "MEDIUM", 300, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 8,  "SUPPLIER_SHIP_ISSUE",      "供应商发货异常",     S8SceneCode.S3, 240, "ROLE_PURCHASER",          "MEDIUM", 301, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
             // WH_INBOUND_ISSUE 迁 WAREHOUSE_RECEIPT_ABNORMAL
             // WH_INBOUND_ISSUE 迁 WAREHOUSE_RECEIPT_ABNORMAL
             T(baseId + 9,  "WH_INBOUND_ISSUE",         "仓库收货异常",       S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "MEDIUM", 302, ct, enabled: false, remark: "[DEPRECATED] migrated to WAREHOUSE_RECEIPT_ABNORMAL"),
             T(baseId + 9,  "WH_INBOUND_ISSUE",         "仓库收货异常",       S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "MEDIUM", 302, ct, enabled: false, remark: "[DEPRECATED] migrated to WAREHOUSE_RECEIPT_ABNORMAL"),
-            T(baseId + 10, "IQC_ISSUE",                "IQC 检验异常",       S8SceneCode.S3, 120, "ROLE_QC",                 "MEDIUM", 303, ct),
-            T(baseId + 11, "WH_PUTAWAY_ISSUE",         "仓库上架入库异常",   S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "LOW",    304, ct),
+            T(baseId + 10, "IQC_ISSUE",                "IQC 检验异常",       S8SceneCode.S3, 120, "ROLE_QC",                 "MEDIUM", 303, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 11, "WH_PUTAWAY_ISSUE",         "仓库上架入库异常",   S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "LOW",    304, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
             // WH_KIT_ISSUE 迁 WORK_ORDER_KITTING_ABNORMAL
             // WH_KIT_ISSUE 迁 WORK_ORDER_KITTING_ABNORMAL
             T(baseId + 12, "WH_KIT_ISSUE",             "仓库工单备料异常",   S8SceneCode.S5, 60,  "ROLE_WH_OUTBOUND",        "MEDIUM", 305, ct, enabled: false, remark: "[DEPRECATED] migrated to WORK_ORDER_KITTING_ABNORMAL"),
             T(baseId + 12, "WH_KIT_ISSUE",             "仓库工单备料异常",   S8SceneCode.S5, 60,  "ROLE_WH_OUTBOUND",        "MEDIUM", 305, ct, enabled: false, remark: "[DEPRECATED] migrated to WORK_ORDER_KITTING_ABNORMAL"),
             // WH_ISSUE_OUT_ISSUE 迁 WORK_ORDER_ISSUE_ABNORMAL
             // WH_ISSUE_OUT_ISSUE 迁 WORK_ORDER_ISSUE_ABNORMAL
             T(baseId + 13, "WH_ISSUE_OUT_ISSUE",       "仓库工单发料异常",   S8SceneCode.S7, 60,  "ROLE_WH_OUTBOUND",        "MEDIUM", 306, ct, enabled: false, remark: "[DEPRECATED] migrated to WORK_ORDER_ISSUE_ABNORMAL"),
             T(baseId + 13, "WH_ISSUE_OUT_ISSUE",       "仓库工单发料异常",   S8SceneCode.S7, 60,  "ROLE_WH_OUTBOUND",        "MEDIUM", 306, ct, enabled: false, remark: "[DEPRECATED] migrated to WORK_ORDER_ISSUE_ABNORMAL"),
-            T(baseId + 17, "PURCHASE_EXECUTION_DELAY", "采购执行延迟",       S8SceneCode.S4, 120, "ROLE_PURCHASER",          "MEDIUM", 401, ct),
-            T(baseId + 18, "PURCHASE_QUALITY_ABNORMAL","采购质量异常",       S8SceneCode.S4, 60,  "ROLE_QC",                 "MEDIUM", 402, ct),
-            T(baseId + 20, "MATERIAL_STOCK_ABNORMAL",  "库存异常",           S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "MEDIUM", 404, ct),
+            T(baseId + 17, "PURCHASE_EXECUTION_DELAY", "采购执行延迟",       S8SceneCode.S4, 120, "ROLE_PURCHASER",          "MEDIUM", 401, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 18, "PURCHASE_QUALITY_ABNORMAL","采购质量异常",       S8SceneCode.S4, 60,  "ROLE_QC",                 "MEDIUM", 402, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 20, "MATERIAL_STOCK_ABNORMAL",  "库存异常",           S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "MEDIUM", 404, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
 
 
             // ── S8-BASELINE-EXCEPTION-TYPE-RULE-TEMPLATE-UPDATE-1(基于 lwb/S1-S7异常类型与三类规则对应表.md)──
             // ── S8-BASELINE-EXCEPTION-TYPE-RULE-TEMPLATE-UPDATE-1(基于 lwb/S1-S7异常类型与三类规则对应表.md)──
             // S1 产销协同(新增 3)
             // S1 产销协同(新增 3)
-            T(baseId + 21, "ORDER_DUE_DATE_DELAY",                         "订单交期延迟",           S8SceneCode.S1, 240, "ROLE_ORDER_PLANNER",      "HIGH",   110, ct),
-            T(baseId + 22, "ORDER_DELIVERY_DELAY_WARNING",                 "订单交付延期预警",       S8SceneCode.S1, 240, "ROLE_ORDER_PLANNER",      "MEDIUM", 111, ct),
-            T(baseId + 23, "DELIVERY_SATISFACTION_RATE_ABNORMAL",          "交付满足率异常",         S8SceneCode.S1, 240, "ROLE_ORDER_PLANNER",      "HIGH",   112, ct),
+            T(baseId + 21, "ORDER_DUE_DATE_DELAY",                         "订单交期延迟",           S8SceneCode.S1, 240, "ROLE_ORDER_PLANNER",      "HIGH",   110, ct, monitoringCategoryKey: S8MonitoringCategory.OrderReview),
+            T(baseId + 22, "ORDER_DELIVERY_DELAY_WARNING",                 "订单交付延期预警",       S8SceneCode.S1, 240, "ROLE_ORDER_PLANNER",      "MEDIUM", 111, ct, monitoringCategoryKey: S8MonitoringCategory.OrderReview),
+            T(baseId + 23, "DELIVERY_SATISFACTION_RATE_ABNORMAL",          "交付满足率异常",         S8SceneCode.S1, 240, "ROLE_ORDER_PLANNER",      "HIGH",   112, ct, monitoringCategoryKey: S8MonitoringCategory.OrderReview),
             // S2 制造协同(新增 4)
             // S2 制造协同(新增 4)
-            T(baseId + 24, "WORK_ORDER_COMPLETION_DELAY_WARNING",          "工单完工延期预警",       S8SceneCode.S2, 120, "ROLE_PRODUCTION_PLANNER", "MEDIUM", 210, ct),
-            T(baseId + 25, "WORK_ORDER_COMPLETION_RATE_ABNORMAL",          "工单完工满足率异常",     S8SceneCode.S2, 240, "ROLE_PRODUCTION_PLANNER", "HIGH",   211, ct),
-            T(baseId + 26, "MFG_MATERIAL_ABNORMAL",                        "制造协同物料异常",       S8SceneCode.S2,  60, "ROLE_PRODUCTION_PLANNER", "HIGH",   212, ct),
-            T(baseId + 27, "MFG_QUALITY_ABNORMAL",                         "制造协同质量异常",       S8SceneCode.S2,  60, "ROLE_QC",                 "HIGH",   213, ct),
+            T(baseId + 24, "WORK_ORDER_COMPLETION_DELAY_WARNING",          "工单完工延期预警",       S8SceneCode.S2, 120, "ROLE_PRODUCTION_PLANNER", "MEDIUM", 210, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 25, "WORK_ORDER_COMPLETION_RATE_ABNORMAL",          "工单完工满足率异常",     S8SceneCode.S2, 240, "ROLE_PRODUCTION_PLANNER", "HIGH",   211, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 26, "MFG_MATERIAL_ABNORMAL",                        "制造协同物料异常",       S8SceneCode.S2,  60, "ROLE_PRODUCTION_PLANNER", "HIGH",   212, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 27, "MFG_QUALITY_ABNORMAL",                         "制造协同质量异常",       S8SceneCode.S2,  60, "ROLE_QC",                 "HIGH",   213, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
             // S3 供应协同(新增 1)
             // S3 供应协同(新增 1)
-            T(baseId + 28, "SUPPLIER_DELIVERY_DELAY_WARNING",              "供应商交付延期预警",     S8SceneCode.S3, 240, "ROLE_PURCHASER",          "MEDIUM", 310, ct),
+            T(baseId + 28, "SUPPLIER_DELIVERY_DELAY_WARNING",              "供应商交付延期预警",     S8SceneCode.S3, 240, "ROLE_PURCHASER",          "MEDIUM", 310, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
             // S4 采购执行(新增 2)
             // S4 采购执行(新增 2)
-            T(baseId + 29, "PURCHASE_DELIVERY_ABNORMAL",                   "采购交期异常",           S8SceneCode.S4, 120, "ROLE_PURCHASER",          "HIGH",   410, ct),
-            T(baseId + 30, "PURCHASE_ARRIVAL_ABNORMAL",                    "采购到货异常",           S8SceneCode.S4, 120, "ROLE_PURCHASER",          "MEDIUM", 411, ct),
+            T(baseId + 29, "PURCHASE_DELIVERY_ABNORMAL",                   "采购交期异常",           S8SceneCode.S4, 120, "ROLE_PURCHASER",          "HIGH",   410, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 30, "PURCHASE_ARRIVAL_ABNORMAL",                    "采购到货异常",           S8SceneCode.S4, 120, "ROLE_PURCHASER",          "MEDIUM", 411, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
             // S5 物料仓储(新增 7)
             // S5 物料仓储(新增 7)
-            T(baseId + 31, "WAREHOUSE_RECEIPT_ABNORMAL",                   "仓库收货异常",           S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "MEDIUM", 510, ct),
-            T(baseId + 32, "WORK_ORDER_KITTING_ABNORMAL",                  "工单备料异常",           S8SceneCode.S5,  60, "ROLE_WH_OUTBOUND",        "MEDIUM", 511, ct),
-            T(baseId + 33, "WORK_ORDER_ISSUE_ABNORMAL",                    "工单发料异常",           S8SceneCode.S5,  60, "ROLE_WH_OUTBOUND",        "MEDIUM", 512, ct),
-            T(baseId + 34, "MATERIAL_KITTING_ABNORMAL",                    "物料齐套异常",           S8SceneCode.S5,  60, "ROLE_WH_OUTBOUND",        "HIGH",   513, ct),
-            T(baseId + 35, "MATERIAL_KITTING_DELAY_WARNING",               "物料齐套延期预警",       S8SceneCode.S5, 120, "ROLE_WH_OUTBOUND",        "MEDIUM", 514, ct),
-            T(baseId + 36, "INVENTORY_TURNOVER_ABNORMAL",                  "库存周转异常",           S8SceneCode.S5, 240, "ROLE_WH_INBOUND",         "MEDIUM", 515, ct),
-            T(baseId + 37, "INVENTORY_AMOUNT_LEVEL_ABNORMAL",              "库存金额水位异常",       S8SceneCode.S5, 240, "ROLE_WH_INBOUND",         "MEDIUM", 516, ct),
+            T(baseId + 31, "WAREHOUSE_RECEIPT_ABNORMAL",                   "仓库收货异常",           S8SceneCode.S5, 120, "ROLE_WH_INBOUND",         "MEDIUM", 510, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 32, "WORK_ORDER_KITTING_ABNORMAL",                  "工单备料异常",           S8SceneCode.S5,  60, "ROLE_WH_OUTBOUND",        "MEDIUM", 511, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 33, "WORK_ORDER_ISSUE_ABNORMAL",                    "工单发料异常",           S8SceneCode.S5,  60, "ROLE_WH_OUTBOUND",        "MEDIUM", 512, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 34, "MATERIAL_KITTING_ABNORMAL",                    "物料齐套异常",           S8SceneCode.S5,  60, "ROLE_WH_OUTBOUND",        "HIGH",   513, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 35, "MATERIAL_KITTING_DELAY_WARNING",               "物料齐套延期预警",       S8SceneCode.S5, 120, "ROLE_WH_OUTBOUND",        "MEDIUM", 514, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 36, "INVENTORY_TURNOVER_ABNORMAL",                  "库存周转异常",           S8SceneCode.S5, 240, "ROLE_WH_INBOUND",         "MEDIUM", 515, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
+            T(baseId + 37, "INVENTORY_AMOUNT_LEVEL_ABNORMAL",              "库存金额水位异常",       S8SceneCode.S5, 240, "ROLE_WH_INBOUND",         "MEDIUM", 516, ct, monitoringCategoryKey: S8MonitoringCategory.MaterialPurchase),
             // S6 生产执行(新增 4)
             // S6 生产执行(新增 4)
-            T(baseId + 38, "PRODUCTION_MATERIAL_ABNORMAL",                  "生产物料异常",           S8SceneCode.S6,  60, "ROLE_PRODUCTION_PLANNER", "HIGH",   610, ct),
-            T(baseId + 39, "PRODUCTION_QUALITY_ABNORMAL",                   "生产质量异常",           S8SceneCode.S6,  60, "ROLE_QC",                 "HIGH",   611, ct),
-            T(baseId + 40, "PRODUCTION_WORK_ORDER_COMPLETION_DELAY",        "生产工单完工延期",       S8SceneCode.S6,  60, "ROLE_PRODUCTION_PLANNER", "HIGH",   612, ct),
-            T(baseId + 41, "PRODUCTION_WORK_ORDER_COMPLETION_RATE_ABNORMAL","生产工单完工满足率异常", S8SceneCode.S6, 240, "ROLE_PRODUCTION_PLANNER", "HIGH",   613, ct),
+            T(baseId + 38, "PRODUCTION_MATERIAL_ABNORMAL",                  "生产物料异常",           S8SceneCode.S6,  60, "ROLE_PRODUCTION_PLANNER", "HIGH",   610, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 39, "PRODUCTION_QUALITY_ABNORMAL",                   "生产质量异常",           S8SceneCode.S6,  60, "ROLE_QC",                 "HIGH",   611, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 40, "PRODUCTION_WORK_ORDER_COMPLETION_DELAY",        "生产工单完工延期",       S8SceneCode.S6,  60, "ROLE_PRODUCTION_PLANNER", "HIGH",   612, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
+            T(baseId + 41, "PRODUCTION_WORK_ORDER_COMPLETION_RATE_ABNORMAL","生产工单完工满足率异常", S8SceneCode.S6, 240, "ROLE_PRODUCTION_PLANNER", "HIGH",   613, ct, monitoringCategoryKey: S8MonitoringCategory.BodyProduction),
             // S7 成品仓储(新增 3)
             // S7 成品仓储(新增 3)
-            T(baseId + 42, "FINISHED_GOODS_PENDING_SHIPMENT",              "成品待发异常",           S8SceneCode.S7, 240, "ROLE_WH_OUTBOUND",        "MEDIUM", 710, ct),
-            T(baseId + 43, "SHIPMENT_ABNORMAL",                            "出货异常",               S8SceneCode.S7, 120, "ROLE_WH_OUTBOUND",        "HIGH",   711, ct),
-            T(baseId + 44, "ORDER_DELIVERY_RATE_ABNORMAL",                 "订单交付满足率异常",     S8SceneCode.S7, 240, "ROLE_ORDER_PLANNER",      "HIGH",   712, ct),
+            T(baseId + 42, "FINISHED_GOODS_PENDING_SHIPMENT",              "成品待发异常",           S8SceneCode.S7, 240, "ROLE_WH_OUTBOUND",        "MEDIUM", 710, ct, monitoringCategoryKey: S8MonitoringCategory.FinalAssemblyDelivery),
+            T(baseId + 43, "SHIPMENT_ABNORMAL",                            "出货异常",               S8SceneCode.S7, 120, "ROLE_WH_OUTBOUND",        "HIGH",   711, ct, monitoringCategoryKey: S8MonitoringCategory.FinalAssemblyDelivery),
+            T(baseId + 44, "ORDER_DELIVERY_RATE_ABNORMAL",                 "订单交付满足率异常",     S8SceneCode.S7, 240, "ROLE_ORDER_PLANNER",      "HIGH",   712, ct, monitoringCategoryKey: S8MonitoringCategory.FinalAssemblyDelivery),
         };
         };
     }
     }
 
 
     private static Entity.S8.AdoS8ExceptionType T(
     private static Entity.S8.AdoS8ExceptionType T(
         long id, string typeCode, string typeName, string sceneCode,
         long id, string typeCode, string typeName, string sceneCode,
         int slaMinutes, string ownerRoleCode, string severityDefault, int sortNo, DateTime ct,
         int slaMinutes, string ownerRoleCode, string severityDefault, int sortNo, DateTime ct,
-        bool enabled = true, string? remark = null) =>
+        bool enabled = true, string? remark = null, string? monitoringCategoryKey = null) =>
         new()
         new()
         {
         {
             Id = id,
             Id = id,
@@ -121,6 +125,7 @@ public class S8ExceptionTypeSeedData : ISqlSugarEntitySeedData<Entity.S8.AdoS8Ex
             Enabled = enabled,
             Enabled = enabled,
             SortNo = sortNo,
             SortNo = sortNo,
             Remark = remark,
             Remark = remark,
+            MonitoringCategoryKey = monitoringCategoryKey,
             CreatedAt = ct,
             CreatedAt = ct,
         };
         };
 }
 }

+ 13 - 3
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8MonitoringService.cs

@@ -138,9 +138,19 @@ public class S8MonitoringService : ITransient
     }
     }
 
 
     /// <summary>异常类型 → 业务类别(与 Overview 页 5 张类别卡对应)。
     /// <summary>异常类型 → 业务类别(与 Overview 页 5 张类别卡对应)。
-    /// S8-DEPRECATED-EXCEPTION-TYPE-MIGRATE-1:补 7 个迁移目标 type_code 的归类,并对旧 7 条保留兼容
-    /// (历史 active 异常迁移期间 / 重建 DB 期间都不丢类别卡)。</summary>
-    private static string CategoryOf(AdoS8ExceptionType t) => t.TypeCode switch
+    /// 优先级:配置 (AdoS8ExceptionType.MonitoringCategoryKey → KeyToLabel) → legacy hardcode fallback → string.Empty。
+    /// 配置缺失或非法 key 时由 CategoryOfLegacy 兜底,保证演示链路不丢类别卡。</summary>
+    private static string CategoryOf(AdoS8ExceptionType? t)
+    {
+        if (t is null) return string.Empty;
+        var configured = S8MonitoringCategory.KeyToLabel(t.MonitoringCategoryKey);
+        if (!string.IsNullOrEmpty(configured)) return configured;
+        return CategoryOfLegacy(t.TypeCode);
+    }
+
+    /// <summary>Legacy hardcode 映射,保留现有 15 条 enabled + 7 条 deprecated 兼容分支。
+    /// 不在此处补新映射;新 type_code 通过 monitoring_category_key 配置驱动。</summary>
+    private static string CategoryOfLegacy(string? typeCode) => typeCode switch
     {
     {
         // 订单评审
         // 订单评审
         "ORDER_CHANGE"                              => "订单评审",
         "ORDER_CHANGE"                              => "订单评审",