S8KpiSnapshotService.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. using Admin.NET.Plugin.AiDOP.SmartOps;
  2. namespace Admin.NET.Plugin.AiDOP.Service.S8;
  3. /// <summary>
  4. /// S8 独立异常链汇总:把 ado_s8_exception 写成九宫格可读的 L1 日值。
  5. /// 不把未闭环异常的处理时效伪装成 0。
  6. /// </summary>
  7. public class S8KpiSnapshotService : ITransient
  8. {
  9. private const string ModuleCode = "S8";
  10. private const string ValueTable = "ado_s9_kpi_value_l1_day";
  11. private readonly ISqlSugarClient _db;
  12. private readonly IKpiTargetResolver _kpiTargetResolver;
  13. public S8KpiSnapshotService(ISqlSugarClient db, IKpiTargetResolver kpiTargetResolver)
  14. {
  15. _db = db;
  16. _kpiTargetResolver = kpiTargetResolver;
  17. }
  18. public async Task<int> RefreshAsync(long tenantId, long factoryId, DateTime? bizDate = null, CancellationToken cancellationToken = default)
  19. {
  20. cancellationToken.ThrowIfCancellationRequested();
  21. if (tenantId <= 0 || factoryId <= 0 || factoryId == tenantId)
  22. return 0;
  23. var day = (bizDate ?? DateTime.Now).Date;
  24. var now = DateTime.Now;
  25. var count = await _db.Ado.GetIntAsync(
  26. """
  27. SELECT COUNT(1)
  28. FROM ado_s8_exception
  29. WHERE tenant_id=@TenantId
  30. AND IFNULL(is_deleted,0)=0
  31. AND IFNULL(exception_code,'')<>''
  32. AND (factory_id IS NULL OR factory_id=0 OR factory_id=@FactoryId
  33. OR (@FactoryId IN (0,1) AND factory_id<>@TenantId))
  34. """,
  35. new SugarParameter("@TenantId", tenantId),
  36. new SugarParameter("@FactoryId", factoryId));
  37. var avgHours = await _db.Ado.GetDecimalAsync(
  38. """
  39. SELECT ROUND(AVG(TIMESTAMPDIFF(MINUTE, created_at, closed_at) / 60), 4)
  40. FROM ado_s8_exception
  41. WHERE tenant_id=@TenantId
  42. AND IFNULL(is_deleted,0)=0
  43. AND UPPER(IFNULL(status,''))='CLOSED'
  44. AND closed_at IS NOT NULL
  45. AND closed_at >= created_at
  46. AND (factory_id IS NULL OR factory_id=0 OR factory_id=@FactoryId
  47. OR (@FactoryId IN (0,1) AND factory_id<>@TenantId))
  48. """,
  49. new SugarParameter("@TenantId", tenantId),
  50. new SugarParameter("@FactoryId", factoryId));
  51. var affected = await UpsertAsync("S8_L1_001", tenantId, factoryId, day, now, count);
  52. affected += await UpsertAsync(
  53. "S8_L1_002",
  54. tenantId,
  55. factoryId,
  56. day,
  57. now,
  58. avgHours > 0 ? avgHours : null);
  59. return affected;
  60. }
  61. private async Task<int> UpsertAsync(string metricCode, long tenantId, long factoryId, DateTime bizDate, DateTime now, decimal? metricValue)
  62. {
  63. var snap = await _kpiTargetResolver.ResolveAsync(tenantId, factoryId, metricCode, ModuleCode, bizDate);
  64. var existingId = await _db.Ado.GetLongAsync(
  65. $"""
  66. SELECT IFNULL((SELECT id FROM {ValueTable}
  67. WHERE tenant_id=@TenantId AND factory_id=@FactoryId
  68. AND module_code=@ModuleCode AND metric_code=@MetricCode AND biz_date=@BizDate AND is_deleted=0
  69. ORDER BY id LIMIT 1), 0)
  70. """,
  71. new List<SugarParameter>
  72. {
  73. new("@TenantId", tenantId),
  74. new("@FactoryId", factoryId),
  75. new("@ModuleCode", ModuleCode),
  76. new("@MetricCode", metricCode),
  77. new("@BizDate", bizDate)
  78. });
  79. if (existingId > 0)
  80. {
  81. return await _db.Ado.ExecuteCommandAsync(
  82. $"""
  83. UPDATE {ValueTable}
  84. SET metric_value=@MetricValue, target_value=@TargetValue,
  85. target_config_id=@TargetConfigId, target_source=@TargetSource, target_resolved_at=@TargetResolvedAt,
  86. calc_time=@Now, update_time=@Now, is_deleted=0, is_active=1
  87. WHERE id=@Id
  88. """,
  89. new SugarParameter("@MetricValue", metricValue),
  90. new SugarParameter("@TargetValue", KpiTargetSnapshotSql.ValueOrDbNull(snap)),
  91. new SugarParameter("@TargetConfigId", KpiTargetSnapshotSql.ConfigIdOrDbNull(snap)),
  92. new SugarParameter("@TargetSource", KpiTargetSnapshotSql.SourceOrDbNull(snap)),
  93. new SugarParameter("@TargetResolvedAt", snap.ResolvedAt),
  94. new SugarParameter("@Now", now),
  95. new SugarParameter("@Id", existingId));
  96. }
  97. var nextId = await _db.Ado.GetLongAsync($"SELECT COALESCE(MAX(id), 0) + 1 FROM {ValueTable}");
  98. return await _db.Ado.ExecuteCommandAsync(
  99. $"""
  100. INSERT INTO {ValueTable}
  101. (id, tenant_id, org_id, company_id, factory_id, status, biz_date,
  102. create_time, update_time, is_deleted, is_active,
  103. module_code, metric_code, metric_value, target_value, calc_time,
  104. target_config_id, target_source, target_resolved_at)
  105. VALUES
  106. (@Id, @TenantId, NULL, NULL, @FactoryId, NULL, @BizDate,
  107. @Now, @Now, 0, 1,
  108. @ModuleCode, @MetricCode, @MetricValue, @TargetValue, @Now,
  109. @TargetConfigId, @TargetSource, @TargetResolvedAt)
  110. """,
  111. new SugarParameter("@Id", nextId),
  112. new SugarParameter("@TenantId", tenantId),
  113. new SugarParameter("@FactoryId", factoryId),
  114. new SugarParameter("@BizDate", bizDate),
  115. new SugarParameter("@Now", now),
  116. new SugarParameter("@ModuleCode", ModuleCode),
  117. new SugarParameter("@MetricCode", metricCode),
  118. new SugarParameter("@MetricValue", metricValue),
  119. new SugarParameter("@TargetValue", KpiTargetSnapshotSql.ValueOrDbNull(snap)),
  120. new SugarParameter("@TargetConfigId", KpiTargetSnapshotSql.ConfigIdOrDbNull(snap)),
  121. new SugarParameter("@TargetSource", KpiTargetSnapshotSql.SourceOrDbNull(snap)),
  122. new SugarParameter("@TargetResolvedAt", snap.ResolvedAt));
  123. }
  124. }