using AutomationBLL.Target; using AutomationDAL.CustomCode.Schedule; using AutomationDAL.DTO.Schedule; using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using Microsoft.Extensions.Logging; namespace AutomationBLL.Schedule; public class ScheduleBLL : IScheduleBLL { private static readonly string[] ValidTriggerTypes = { "Scheduled", "EventDriven", "Manual" }; private readonly IScheduleDAL _dal; private readonly ITargetBLL _targetBll; private readonly IJobEngineScheduleClient _jobEngineClient; private readonly ILogger _logger; public ScheduleBLL(IScheduleDAL dal, ITargetBLL targetBll, IJobEngineScheduleClient jobEngineClient, ILogger logger) { _dal = dal; _targetBll = targetBll; _jobEngineClient = jobEngineClient; _logger = logger; } public async Task GetByTarget(int targetId, LoginDTO login, CancellationToken ct) => await _dal.GetByTarget(targetId, login, ct).ConfigureAwait(false); public async Task GetByJobEngineJobId(int jobEngineJobId, LoginDTO login, CancellationToken ct) => await _dal.GetByJobEngineJobId(jobEngineJobId, login, ct).ConfigureAwait(false); public async Task Save(ScheduleDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-schedule", new { dto.TargetId, dto.TriggerType }); if (dto.TargetId <= 0) throw new InvalidOperationException("TargetId is required."); if (!ValidTriggerTypes.Contains(dto.TriggerType)) throw new InvalidOperationException($"TriggerType must be one of: {string.Join(", ", ValidTriggerTypes)}"); if (dto.TriggerType == "Scheduled" && string.IsNullOrWhiteSpace(dto.CronExpression)) throw new InvalidOperationException("CronExpression is required when TriggerType is Scheduled."); var target = await _targetBll.GetById(dto.TargetId, login, ct).ConfigureAwait(false) ?? throw new InvalidOperationException($"Target {dto.TargetId} not found."); var existing = await _dal.GetByTarget(dto.TargetId, login, ct).ConfigureAwait(false); GB5Trace.Step("save-schedule", new { dto.TargetId, isNew = existing == null }); int scheduleId; if (existing == null) { scheduleId = await _dal.Insert(dto, login, ct).ConfigureAwait(false); } else { scheduleId = existing.ScheduleId; await _dal.Update(dto, login, ct).ConfigureAwait(false); } if (dto.TriggerType == "Scheduled" && dto.CronExpression is not null) { GB5Trace.Step("jobengine-sync", new { scheduleId, dto.TargetId }); var jobId = await _jobEngineClient .SyncAsync(dto.TargetId, $"AutomationTarget-{dto.TargetId}", dto.CronExpression, login, ct) .ConfigureAwait(false); await _dal.UpdateJobEngineJobId(scheduleId, jobId, login, ct).ConfigureAwait(false); return jobId.HasValue ? $"{SuccessResponse.SaveSuccessMessage} {scheduleId} (synced to JobEngine as job {jobId})" : $"{SuccessResponse.SaveSuccessMessage} {scheduleId} (saved locally — JobEngine sync failed, see logs; schedule is not yet active in Quartz)"; } return $"{SuccessResponse.SaveSuccessMessage} {scheduleId}"; } catch (Exception ex) { GB5Trace.MarkFailed("save-schedule-failed", ex); _logger.LogError(ex, "Save failed for TargetId {TargetId}", dto.TargetId); throw; } } }