using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using JobEngineDAL.DTOs; using JobEngineDAL.Interfaces; using JobEngineBLL.Interfaces; using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace JobEngineBLL.Implementations { public class JobDefineBLL : IJobDefineBLL { private readonly IJobDefineDAL _dal; private readonly IJobExecutionDAL _executionDal; private readonly IJobQueueDAL _queueDal; private readonly IQuartzSyncService _quartzSync; private readonly ILogger _logger; public JobDefineBLL( IJobDefineDAL dal, IJobExecutionDAL executionDal, IJobQueueDAL queueDal, IQuartzSyncService quartzSync, ILogger logger) { _dal = dal; _executionDal = executionDal; _queueDal = queueDal; _quartzSync = quartzSync; _logger = logger; } public async Task GetJobsAsync(LoginDTO login, CancellationToken ct) { try { var jobs = await _dal.GetJobsAsync(login, ct).ConfigureAwait(false); return JsonConvert.SerializeObject(jobs); } catch (Exception ex) { GB5Trace.MarkFailed("get-jobs-failed", ex); _logger.LogError(ex, "GetJobs failed for tenant {TenantId}", login.ClientId); throw; } } public async Task GetJobDetailAsync(int jobId, LoginDTO login, CancellationToken ct) { try { var job = await _dal.GetJobDetailAsync(jobId, login, ct).ConfigureAwait(false); return JsonConvert.SerializeObject(job); } catch (Exception ex) { GB5Trace.MarkFailed("get-job-detail-failed", ex); _logger.LogError(ex, "GetJobDetail failed for JobId {JobId}", jobId); throw; } } public async Task SaveJobAsync(JobDefineDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-job-define", new { dto.JobId }); if (string.IsNullOrWhiteSpace(dto.JobName)) throw new ArgumentException("JobName is required"); bool isNew = dto.JobId == 0; GB5Trace.Step("save-job-define", new { dto.JobId, isNew }); var savedId = await _dal.SaveJobAsync(dto, login, ct).ConfigureAwait(false); GB5Trace.Step("sync-quartz", new { savedId }); await _quartzSync.SyncJobAsync(savedId, ct).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {savedId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("save-job-define-failed", ex); _logger.LogError(ex, "SaveJob failed for JobId {JobId}", dto.JobId); throw; } } public async Task DeleteJobAsync(int jobId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("delete-job-define", new { jobId }); await _dal.DeleteJobAsync(jobId, login.UserId, login, ct).ConfigureAwait(false); await _quartzSync.RemoveJobAsync(jobId, ct).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-job-define-failed", ex); _logger.LogError(ex, "DeleteJob failed for JobId {JobId}", jobId); throw; } } public async Task TriggerJobAsync(int jobId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("trigger-job-manual", new { jobId }); await _quartzSync.TriggerJobNowAsync(jobId, ct).ConfigureAwait(false); return SuccessResponse.TriggerSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("trigger-job-failed", ex); _logger.LogError(ex, "TriggerJob failed for JobId {JobId}", jobId); throw; } } public async Task PauseJobAsync(int jobId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("pause-job-define", new { jobId }); await _dal.PauseJobAsync(jobId, login.UserId, login, ct).ConfigureAwait(false); await _quartzSync.RemoveJobAsync(jobId, ct).ConfigureAwait(false); return SuccessResponse.PauseSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("pause-job-failed", ex); _logger.LogError(ex, "PauseJob failed for JobId {JobId}", jobId); throw; } } public async Task ResumeJobAsync(int jobId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("resume-job-define", new { jobId }); await _dal.ResumeJobAsync(jobId, login.UserId, login, ct).ConfigureAwait(false); await _quartzSync.SyncJobAsync(jobId, ct).ConfigureAwait(false); return SuccessResponse.ResumeSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("resume-job-failed", ex); _logger.LogError(ex, "ResumeJob failed for JobId {JobId}", jobId); throw; } } public async Task GetDashboardAsync(LoginDTO login, CancellationToken ct) { try { var runningTask = _executionDal.GetRunningJobsAsync(login, ct); var statsTask = _executionDal.GetStats7DaysAsync(login, ct); var avgTask = _executionDal.GetAvgDurationsAsync(login, ct); var queueStats = _queueDal.GetQueueStatsAsync(login, ct); var dlqTotal = _queueDal.GetDLQTotalAsync(login, ct); var pendingTotal = _queueDal.GetPendingTotalAsync(login, ct); await Task.WhenAll(runningTask, statsTask, avgTask, queueStats, dlqTotal, pendingTotal) .ConfigureAwait(false); var (successCount, failedCount, totalCount) = await statsTask; var successRate = totalCount > 0 ? Math.Round((double)successCount / totalCount * 100, 1) : 0d; var runningJobs = (await runningTask).ToList(); var dashboard = new JobDashboardDTO { RunningCount = runningJobs.Count, FailedCount7Days = failedCount, SuccessCount7Days = successCount, SuccessRate7Days = successRate, DlqCount = await dlqTotal, PendingQueueCount = await pendingTotal, RunningJobs = runningJobs, QueueStats = (await queueStats).ToList(), AvgDurations = (await avgTask).ToList() }; return JsonConvert.SerializeObject(dashboard); } catch (Exception ex) { GB5Trace.MarkFailed("get-dashboard-failed", ex); _logger.LogError(ex, "GetDashboard failed for tenant {TenantId}", login.ClientId); throw; } } public async Task RegisterJobAsync(JobDefineDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("register-job-module", new { dto.SourceModule, dto.SourceObjectId }); if (string.IsNullOrWhiteSpace(dto.JobName)) throw new ArgumentException("JobName is required for module job registration"); if (string.IsNullOrWhiteSpace(dto.SourceModule)) throw new ArgumentException("SourceModule is required for module job registration"); var savedId = await _dal.UpsertBySourceAsync(dto, login, ct).ConfigureAwait(false); GB5Trace.Step("sync-quartz-module", new { savedId }); await _quartzSync.SyncJobAsync(savedId, ct).ConfigureAwait(false); } catch (Exception ex) { GB5Trace.MarkFailed("register-job-module-failed", ex); _logger.LogError(ex, "RegisterJob failed for module {Module} objectId {Id}", dto.SourceModule, dto.SourceObjectId); throw; } } } }