S8KpiSnapshotService.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. if (!bizDate.HasValue)
  24. {
  25. var totalAffected = 0;
  26. for (var dayOffset = 13; dayOffset >= 0; dayOffset--)
  27. {
  28. totalAffected += await RefreshAsync(
  29. tenantId, factoryId, DateTime.Today.AddDays(-dayOffset), cancellationToken);
  30. }
  31. return totalAffected;
  32. }
  33. var day = bizDate.Value.Date;
  34. var now = DateTime.Now;
  35. var count = await _db.Ado.GetIntAsync(
  36. """
  37. SELECT COUNT(1)
  38. FROM ado_s8_exception
  39. WHERE tenant_id=@TenantId
  40. AND IFNULL(is_deleted,0)=0
  41. AND IFNULL(exception_code,'')<>''
  42. AND (factory_id IS NULL OR factory_id=0 OR factory_id=@FactoryId
  43. OR (@FactoryId IN (0,1) AND factory_id<>@TenantId))
  44. """,
  45. new SugarParameter("@TenantId", tenantId),
  46. new SugarParameter("@FactoryId", factoryId));
  47. var avgHours = await _db.Ado.GetDecimalAsync(
  48. """
  49. SELECT ROUND(AVG(TIMESTAMPDIFF(MINUTE, created_at, closed_at) / 60), 4)
  50. FROM ado_s8_exception
  51. WHERE tenant_id=@TenantId
  52. AND IFNULL(is_deleted,0)=0
  53. AND UPPER(IFNULL(status,''))='CLOSED'
  54. AND closed_at IS NOT NULL
  55. AND closed_at >= created_at
  56. AND (factory_id IS NULL OR factory_id=0 OR factory_id=@FactoryId
  57. OR (@FactoryId IN (0,1) AND factory_id<>@TenantId))
  58. """,
  59. new SugarParameter("@TenantId", tenantId),
  60. new SugarParameter("@FactoryId", factoryId));
  61. var affected = await UpsertAsync("S8_L1_001", tenantId, factoryId, day, now, count);
  62. affected += await UpsertAsync(
  63. "S8_L1_002",
  64. tenantId,
  65. factoryId,
  66. day,
  67. now,
  68. avgHours > 0 ? avgHours : null);
  69. return affected;
  70. }
  71. private async Task<int> UpsertAsync(string metricCode, long tenantId, long factoryId, DateTime bizDate, DateTime now, decimal? metricValue)
  72. {
  73. var snap = await _kpiTargetResolver.ResolveAsync(tenantId, factoryId, metricCode, ModuleCode, bizDate);
  74. var existingId = await _db.Ado.GetLongAsync(
  75. $"""
  76. SELECT IFNULL((SELECT id FROM {ValueTable}
  77. WHERE tenant_id=@TenantId AND factory_id=@FactoryId
  78. AND module_code=@ModuleCode AND metric_code=@MetricCode AND biz_date=@BizDate AND is_deleted=0
  79. ORDER BY id LIMIT 1), 0)
  80. """,
  81. new List<SugarParameter>
  82. {
  83. new("@TenantId", tenantId),
  84. new("@FactoryId", factoryId),
  85. new("@ModuleCode", ModuleCode),
  86. new("@MetricCode", metricCode),
  87. new("@BizDate", bizDate)
  88. });
  89. if (existingId > 0)
  90. {
  91. return await _db.Ado.ExecuteCommandAsync(
  92. $"""
  93. UPDATE {ValueTable}
  94. SET metric_value=@MetricValue, target_value=@TargetValue,
  95. target_config_id=@TargetConfigId, target_source=@TargetSource, target_resolved_at=@TargetResolvedAt,
  96. calc_time=@Now, update_time=@Now, is_deleted=0, is_active=1
  97. WHERE id=@Id
  98. """,
  99. new SugarParameter("@MetricValue", metricValue),
  100. new SugarParameter("@TargetValue", KpiTargetSnapshotSql.ValueOrDbNull(snap)),
  101. new SugarParameter("@TargetConfigId", KpiTargetSnapshotSql.ConfigIdOrDbNull(snap)),
  102. new SugarParameter("@TargetSource", KpiTargetSnapshotSql.SourceOrDbNull(snap)),
  103. new SugarParameter("@TargetResolvedAt", snap.ResolvedAt),
  104. new SugarParameter("@Now", now),
  105. new SugarParameter("@Id", existingId));
  106. }
  107. var nextId = await _db.Ado.GetLongAsync($"SELECT COALESCE(MAX(id), 0) + 1 FROM {ValueTable}");
  108. return await _db.Ado.ExecuteCommandAsync(
  109. $"""
  110. INSERT INTO {ValueTable}
  111. (id, tenant_id, org_id, company_id, factory_id, status, biz_date,
  112. create_time, update_time, is_deleted, is_active,
  113. module_code, metric_code, metric_value, target_value, calc_time,
  114. target_config_id, target_source, target_resolved_at)
  115. VALUES
  116. (@Id, @TenantId, NULL, NULL, @FactoryId, NULL, @BizDate,
  117. @Now, @Now, 0, 1,
  118. @ModuleCode, @MetricCode, @MetricValue, @TargetValue, @Now,
  119. @TargetConfigId, @TargetSource, @TargetResolvedAt)
  120. """,
  121. new SugarParameter("@Id", nextId),
  122. new SugarParameter("@TenantId", tenantId),
  123. new SugarParameter("@FactoryId", factoryId),
  124. new SugarParameter("@BizDate", bizDate),
  125. new SugarParameter("@Now", now),
  126. new SugarParameter("@ModuleCode", ModuleCode),
  127. new SugarParameter("@MetricCode", metricCode),
  128. new SugarParameter("@MetricValue", metricValue),
  129. new SugarParameter("@TargetValue", KpiTargetSnapshotSql.ValueOrDbNull(snap)),
  130. new SugarParameter("@TargetConfigId", KpiTargetSnapshotSql.ConfigIdOrDbNull(snap)),
  131. new SugarParameter("@TargetSource", KpiTargetSnapshotSql.SourceOrDbNull(snap)),
  132. new SugarParameter("@TargetResolvedAt", snap.ResolvedAt));
  133. }
  134. }