SmartOpsKpiMdpBootstrapJob.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. using Admin.NET.Plugin.AiDOP.Infrastructure;
  2. using Admin.NET.Plugin.AiDOP.Order;
  3. using Admin.NET.Plugin.AiDOP.ProcurementExecution;
  4. using Admin.NET.Plugin.AiDOP.Production;
  5. using Admin.NET.Plugin.AiDOP.Supply;
  6. using Furion.Schedule;
  7. using Microsoft.Extensions.DependencyInjection;
  8. using Microsoft.Extensions.Logging;
  9. using SqlSugar;
  10. using System.Text.Json;
  11. namespace Admin.NET.Plugin.AiDOP.Job;
  12. /// <summary>
  13. /// 应用启动后按 S1→S2→S3→S4 顺序执行 MDP 全量同步,构建模块 KPI 汇总与 KPI 原子聚合层。
  14. /// 新环境发布后无需等待 hourly 任务即可在看板筛选中看到数据。
  15. /// </summary>
  16. [JobDetail("job_smart_ops_kpi_mdp_bootstrap", Description = "智慧运营KPI:启动时按序执行S1→S2→S3→S4 MDP同步(含原子聚合层)", GroupName = "default", Concurrent = false)]
  17. [PeriodSeconds(1, TriggerId = "trigger_smart_ops_kpi_mdp_bootstrap", Description = "应用启动后立即执行一次", MaxNumberOfRuns = 1, RunOnStart = true)]
  18. public class SmartOpsKpiMdpBootstrapJob : IJob
  19. {
  20. private const string EnvBootstrapEnabled = "AIDOP_MDP_BOOTSTRAP_ENABLED";
  21. private readonly IServiceScopeFactory _scopeFactory;
  22. private readonly ILogger _logger;
  23. public SmartOpsKpiMdpBootstrapJob(IServiceScopeFactory scopeFactory, ILoggerFactory loggerFactory)
  24. {
  25. _scopeFactory = scopeFactory;
  26. _logger = loggerFactory.CreateLogger(nameof(SmartOpsKpiMdpBootstrapJob));
  27. }
  28. public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
  29. {
  30. if (!IsBootstrapEnabled(Environment.GetEnvironmentVariable(EnvBootstrapEnabled)))
  31. {
  32. _logger.LogInformation(
  33. "SmartOpsKpiMdpBootstrapJob 被环境变量 {EnvironmentVariable}=false 禁用",
  34. EnvBootstrapEnabled);
  35. return;
  36. }
  37. using var scope = _scopeFactory.CreateScope();
  38. var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
  39. try
  40. {
  41. AidopTenantMigration.EnsureKpiAtomicTable(db);
  42. _logger.LogInformation("SmartOpsKpiMdpBootstrapJob: ado_smart_ops_kpi_atomic_day 已就绪");
  43. }
  44. catch (Exception ex)
  45. {
  46. _logger.LogError(ex, "SmartOpsKpiMdpBootstrapJob: 原子聚合表初始化失败");
  47. }
  48. await RunStepAsync("S1", stoppingToken, async sp =>
  49. {
  50. var catalog = sp.GetRequiredService<Admin.NET.Plugin.AiDOP.DataPlatform.S1Refresh.S1MdpScopeCatalog>();
  51. var rebuild = sp.GetRequiredService<Admin.NET.Plugin.AiDOP.DataPlatform.S1Refresh.S1DashboardRebuildService>();
  52. var scopes = await catalog.ListEnabledScopesAsync(stoppingToken);
  53. var accepted = 0;
  54. foreach (var runScope in scopes)
  55. {
  56. var (status, _) = await rebuild.EnqueueAsync(runScope.TenantId, runScope.FactoryId, null, "BOOTSTRAP", stoppingToken);
  57. if (status == 202) accepted++;
  58. }
  59. return new { scopes = scopes.Count, accepted };
  60. });
  61. await RunStepAsync("S2", stoppingToken, async sp =>
  62. {
  63. var payload = await Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.ModuleRebuildJobRunner
  64. .EnqueueEnabledScopesAsync(sp, "S2", "BOOTSTRAP", stoppingToken, _logger);
  65. return payload;
  66. });
  67. await RunStepAsync("S3", stoppingToken, async sp =>
  68. {
  69. var payload = await Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.ModuleRebuildJobRunner
  70. .EnqueueEnabledScopesAsync(sp, "S3", "BOOTSTRAP", stoppingToken, _logger);
  71. return payload;
  72. });
  73. await RunStepAsync("S4", stoppingToken, async sp =>
  74. {
  75. var payload = await Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.ModuleRebuildJobRunner
  76. .EnqueueEnabledScopesAsync(sp, "S4", "BOOTSTRAP", stoppingToken, _logger);
  77. return payload;
  78. });
  79. _logger.LogInformation("SmartOpsKpiMdpBootstrapJob: 全部 MDP 引导同步已完成");
  80. }
  81. internal static bool IsBootstrapEnabled(string? raw) =>
  82. !bool.TryParse(raw, out var enabled) || enabled;
  83. private async Task RunStepAsync(string step, CancellationToken stoppingToken, Func<IServiceProvider, Task<object>> action)
  84. {
  85. using var scope = _scopeFactory.CreateScope();
  86. try
  87. {
  88. stoppingToken.ThrowIfCancellationRequested();
  89. var payload = await action(scope.ServiceProvider);
  90. _logger.LogInformation("SmartOpsKpiMdpBootstrapJob {Step} 完成 {Payload}", step, JsonSerializer.Serialize(payload));
  91. }
  92. catch (Admin.NET.Plugin.AiDOP.DataPlatform.S1Refresh.S1MdpAlreadyRunningException)
  93. {
  94. _logger.LogInformation("SmartOpsKpiMdpBootstrapJob {Step} 跳过:已有全量任务在执行", step);
  95. }
  96. catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
  97. {
  98. _logger.LogInformation("SmartOpsKpiMdpBootstrapJob {Step} 收到停止信号", step);
  99. throw;
  100. }
  101. catch (Exception ex)
  102. {
  103. _logger.LogError(ex, "SmartOpsKpiMdpBootstrapJob {Step} 执行失败,继续后续步骤", step);
  104. }
  105. }
  106. }