KpiTargetMigrationService.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using Admin.NET.Plugin.AiDOP.Dto.SmartOps;
  2. namespace Admin.NET.Plugin.AiDOP.SmartOps;
  3. public sealed class KpiTargetMigrationPreviewDto
  4. {
  5. public List<KpiTargetCreateDto> WillCreate { get; set; } = new();
  6. public List<string> Skipped { get; set; } = new();
  7. public List<string> Conflicts { get; set; } = new();
  8. public List<string> Missing { get; set; } = new();
  9. public List<string> DemoSuspect { get; set; } = new();
  10. }
  11. public sealed class KpiTargetMigrationService : ITransient
  12. {
  13. private readonly ISqlSugarClient _db;
  14. private readonly IKpiTargetConfigStore _store;
  15. private readonly KpiTargetService _targets;
  16. private readonly UserManager _userManager;
  17. public KpiTargetMigrationService(
  18. ISqlSugarClient db,
  19. IKpiTargetConfigStore store,
  20. KpiTargetService targets,
  21. UserManager userManager)
  22. {
  23. _db = db;
  24. _store = store;
  25. _targets = targets;
  26. _userManager = userManager;
  27. }
  28. public async Task<KpiTargetMigrationPreviewDto> PreviewAsync(CancellationToken ct = default)
  29. {
  30. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  31. return await BuildAsync(tenantId, commit: false, ct);
  32. }
  33. public async Task<KpiTargetMigrationPreviewDto> CommitAsync(CancellationToken ct = default)
  34. {
  35. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  36. return await BuildAsync(tenantId, commit: true, ct);
  37. }
  38. private async Task<KpiTargetMigrationPreviewDto> BuildAsync(long tenantId, bool commit, CancellationToken ct)
  39. {
  40. var result = new KpiTargetMigrationPreviewDto();
  41. var rows = await _db.Ado.SqlQueryAsync<LegacyHit>(
  42. """
  43. SELECT factory_id AS FactoryId, metric_code AS MetricCode, module_code AS ModuleCode,
  44. target_value AS TargetValue, biz_date AS BizDate, target_source AS TargetSource
  45. FROM ado_s9_kpi_value_l1_day
  46. WHERE tenant_id=@TenantId AND is_deleted=0 AND target_value IS NOT NULL AND target_value>0
  47. ORDER BY metric_code, factory_id, biz_date DESC, id DESC
  48. """,
  49. new SugarParameter("@TenantId", tenantId));
  50. var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  51. foreach (var row in rows)
  52. {
  53. var key = $"{row.FactoryId}|{row.MetricCode}";
  54. if (!seen.Add(key))
  55. continue;
  56. if (string.Equals(row.TargetSource, KpiTargetSnapshotSource.DemoImport, StringComparison.OrdinalIgnoreCase))
  57. {
  58. result.DemoSuspect.Add($"{row.MetricCode} factory={row.FactoryId}");
  59. continue;
  60. }
  61. var existing = await _store.ListActiveScopeAsync(tenantId, row.FactoryId, row.MetricCode, ct);
  62. if (existing.Count > 0)
  63. {
  64. result.Skipped.Add($"{row.MetricCode} factory={row.FactoryId}");
  65. continue;
  66. }
  67. var dto = new KpiTargetCreateDto
  68. {
  69. MetricCode = row.MetricCode,
  70. FactoryId = row.FactoryId,
  71. TargetValue = row.TargetValue,
  72. EffectiveFrom = DateTime.Today,
  73. ClosePrevious = false,
  74. Remark = $"LEGACY_MIGRATION from {row.BizDate:yyyy-MM-dd}"
  75. };
  76. result.WillCreate.Add(dto);
  77. if (!commit)
  78. continue;
  79. try
  80. {
  81. await _targets.CreateCoreAsync(tenantId, dto, KpiTargetConfigSource.LegacyMigration, null, _userManager.UserId, ct);
  82. }
  83. catch (KpiTargetConflictException ex)
  84. {
  85. result.Conflicts.Add($"{row.MetricCode}: {ex.Message}");
  86. }
  87. }
  88. var masters = await _db.Queryable<Entity.AdoSmartOpsKpiMaster>()
  89. .ClearFilter<ITenantIdFilter>()
  90. .Where(x => x.TenantId == tenantId && x.IsEnabled)
  91. .ToListAsync(ct);
  92. var createdKeys = result.WillCreate.Select(x => x.MetricCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
  93. foreach (var m in masters)
  94. {
  95. if (createdKeys.Contains(m.MetricCode) || seen.Contains($"1|{m.MetricCode}") || seen.Contains($"0|{m.MetricCode}"))
  96. continue;
  97. result.Missing.Add($"{m.ModuleCode}:{m.MetricCode}");
  98. }
  99. return result;
  100. }
  101. private sealed class LegacyHit
  102. {
  103. public long FactoryId { get; set; }
  104. public string MetricCode { get; set; } = string.Empty;
  105. public string ModuleCode { get; set; } = string.Empty;
  106. public decimal TargetValue { get; set; }
  107. public DateTime BizDate { get; set; }
  108. public string? TargetSource { get; set; }
  109. }
  110. }