using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq; using NPP.SmartSchedue.Api.Contracts.Core.Repositories; using NPP.SmartSchedue.Api.Contracts.Domain.Notification; using NPP.SmartSchedue.Api.Contracts.Core.Enums; using NPP.SmartSchedue.Api.Core.Repositories; using ZhonTai.Admin.Core.Db.Transaction; namespace NPP.SmartSchedue.Api.Repositories.Notification; /// /// 通知任务仓储实现 /// 决策点6:定时任务,支持定时检查和触发通知 /// 业务思考: /// 1. 支持一次性任务和周期性任务(Cron表达式) /// 2. 任务状态管理(待执行、执行中、已完成、失败、取消) /// 3. 任务重试机制和失效时间控制 /// 4. 任务执行结果记录和错误处理 /// public class NotificationTaskRepository : AppRepositoryBase, INotificationTaskRepository { public NotificationTaskRepository(UnitOfWorkManagerCloud uowm) : base(uowm) { } /// /// 根据通知设置ID获取任务列表 /// public async Task> GetByNotificationSettingIdAsync(long notificationSettingId) { return await Select .Where(t => t.NotificationSettingId == notificationSettingId) .Include(t => t.NotificationSetting) .OrderByDescending(t => t.CreatedTime) .ToListAsync(); } /// /// 根据任务状态获取任务列表 /// public async Task> GetByTaskStatusAsync(NotificationStatusEnum taskStatus) { return await Select .Where(t => t.TaskStatus == (int)taskStatus) .Include(t => t.NotificationSetting) .OrderByDescending(t => t.CreatedTime) .ToListAsync(); } /// /// 根据启用状态获取任务列表 /// public async Task> GetByEnabledAsync(bool enabled) { return await Select .Where(t => t.IsEnabled == enabled) .Include(t => t.NotificationSetting) .OrderByDescending(t => t.CreatedTime) .ToListAsync(); } /// /// 根据业务类型和业务ID获取任务列表 /// public async Task> GetByBusinessAsync(string businessType, long? businessId = null) { var query = Select.Where(t => t.BusinessType == businessType); if (businessId.HasValue) query = query.Where(t => t.BusinessId == businessId.Value); return await query .Include(t => t.NotificationSetting) .OrderByDescending(t => t.CreatedTime) .ToListAsync(); } /// /// 获取待执行的任务列表 /// 业务逻辑:查询计划执行时间已到达且任务状态为待执行的任务 /// public async Task> GetPendingTasksAsync(DateTime currentTime) { return await Select .Where(t => t.IsEnabled == true) .Where(t => t.TaskStatus == (int)NotificationStatusEnum.Pending) .Where(t => t.PlannedExecutionTime <= currentTime) .Where(t => t.ExpirationTime == null || t.ExpirationTime > currentTime) // 排除已过期的任务 .Include(t => t.NotificationSetting) .OrderBy(t => t.PlannedExecutionTime) // 按计划时间升序排序 .ToListAsync(); } /// /// 获取需要执行的定时任务列表(基于Cron表达式) /// 业务逻辑: /// 1. 查询启用的周期性任务 /// 2. 检查下次执行时间是否已到达 /// 3. 验证任务未过期且未达到最大执行次数 /// public async Task> GetCronTasksForExecutionAsync(DateTime currentTime) { return await Select .Where(t => t.IsEnabled == true) .Where(t => !string.IsNullOrWhiteSpace(t.CronExpression)) // 有Cron表达式的周期性任务 .Where(t => t.TaskStatus != (int)NotificationStatusEnum.Cancelled) .Where(t => t.NextExecutionTime <= currentTime) // 下次执行时间已到 .Where(t => t.ExpirationTime == null || t.ExpirationTime > currentTime) // 未过期 .Where(t => t.MaxExecutionCount == null || t.ExecutionCount < t.MaxExecutionCount) // 未达到最大执行次数 .Include(t => t.NotificationSetting) .OrderBy(t => t.NextExecutionTime) .ToListAsync(); } /// /// 更新任务执行状态 /// 业务逻辑:更新任务状态、执行结果、错误信息等 /// public async Task UpdateExecutionStatusAsync(long taskId, NotificationStatusEnum taskStatus, string executionResult = "", string errorMessage = "") { var currentTime = DateTime.Now; await UpdateDiy .Set(t => t.TaskStatus, (int)taskStatus) .Set(t => t.LastExecutionTime, currentTime) .SetIf(!string.IsNullOrWhiteSpace(executionResult), t => t.ExecutionResult, executionResult) .SetIf(!string.IsNullOrWhiteSpace(errorMessage), t => t.ErrorMessage, errorMessage) .Set(t => t.ModifiedTime, currentTime) .Where(t => t.Id == taskId) .ExecuteAffrowsAsync(); } /// /// 更新任务下次执行时间 /// 业务场景:周期性任务完成后,根据Cron表达式计算下次执行时间 /// public async Task UpdateNextExecutionTimeAsync(long taskId, DateTime? nextExecutionTime) { await UpdateDiy .Set(t => t.NextExecutionTime, nextExecutionTime) .Set(t => t.ModifiedTime, DateTime.Now) .Where(t => t.Id == taskId) .ExecuteAffrowsAsync(); } /// /// 增加任务执行次数 /// 业务逻辑:记录任务执行统计,用于控制最大执行次数和成功率分析 /// public async Task IncrementExecutionCountAsync(long taskId, bool isSuccess) { var task = await Select.Where(t => t.Id == taskId).FirstAsync(); if (task == null) return; await UpdateDiy .Set(t => t.ExecutionCount, task.ExecutionCount + 1) .SetIf(isSuccess, t => t.SuccessCount, task.SuccessCount + 1) .SetIf(!isSuccess, t => t.FailureCount, task.FailureCount + 1) .Set(t => t.ModifiedTime, DateTime.Now) .Where(t => t.Id == taskId) .ExecuteAffrowsAsync(); } /// /// 检查任务是否应该停止执行 /// 业务规则: /// 1. 任务被禁用 /// 2. 任务已取消 /// 3. 已达到最大执行次数 /// 4. 任务已过期 /// 5. 连续失败次数过多 /// public async Task ShouldStopExecutionAsync(long taskId) { var task = await Select.Where(t => t.Id == taskId).FirstAsync(); if (task == null) return true; var currentTime = DateTime.Now; // 检查基本停止条件 if (!task.IsEnabled || task.TaskStatus == (int)NotificationStatusEnum.Cancelled || (task.ExpirationTime.HasValue && task.ExpirationTime.Value <= currentTime) || (task.MaxExecutionCount.HasValue && task.ExecutionCount >= task.MaxExecutionCount.Value)) { return true; } // 检查连续失败次数(如果设置了最大重试次数) if (task.MaxRetryCount.HasValue && task.FailureCount >= task.MaxRetryCount.Value) { return true; } return false; } /// /// 获取过期的任务列表 /// 业务场景:定期清理过期任务,释放系统资源 /// public async Task> GetExpiredTasksAsync(DateTime expiredBefore) { return await Select .Where(t => t.ExpirationTime <= expiredBefore) .Where(t => t.TaskStatus != (int)NotificationStatusEnum.Cancelled) // 排除已取消的任务 .OrderBy(t => t.ExpirationTime) .ToListAsync(); } /// /// 清理过期的任务 /// 业务逻辑:将过期任务状态设为已取消,而不是物理删除,保留审计记录 /// public async Task CleanupExpiredTasksAsync(DateTime expiredBefore) { var currentTime = DateTime.Now; var affectedRows = await UpdateDiy .Set(t => t.TaskStatus, (int)NotificationStatusEnum.Cancelled) .Set(t => t.ErrorMessage, "任务已过期自动取消") .Set(t => t.ModifiedTime, currentTime) .Where(t => t.ExpirationTime <= expiredBefore) .Where(t => t.TaskStatus != (int)NotificationStatusEnum.Cancelled) .ExecuteAffrowsAsync(); return (int)affectedRows; } /// /// 获取需要重试的失败任务列表 /// 业务场景:定时重试失败的任务,提高通知送达率 /// public async Task> GetFailedTasksForRetryAsync() { var currentTime = DateTime.Now; return await Select .Where(t => t.TaskStatus == (int)NotificationStatusEnum.Failed) .Where(t => t.IsEnabled == true) .Where(t => t.NextRetryTime <= currentTime) // 重试时间已到 .Where(t => t.MaxRetryCount == null || t.RetryCount < t.MaxRetryCount) // 未达到最大重试次数 .Where(t => t.ExpirationTime == null || t.ExpirationTime > currentTime) // 未过期 .Include(t => t.NotificationSetting) .OrderBy(t => t.NextRetryTime) .ToListAsync(); } /// /// 更新任务重试信息 /// 业务逻辑:更新重试次数和下次重试时间 /// public async Task UpdateRetryInfoAsync(long taskId, int retryCount, DateTime? nextRetryTime) { await UpdateDiy .Set(t => t.RetryCount, retryCount) .Set(t => t.NextRetryTime, nextRetryTime) .Set(t => t.ModifiedTime, DateTime.Now) .Where(t => t.Id == taskId) .ExecuteAffrowsAsync(); } /// /// 批量更新任务状态 /// 业务场景:批量操作,提高性能 /// public async Task BatchUpdateTaskStatusAsync(List taskIds, NotificationStatusEnum status, string reason = "") { if (!taskIds.Any()) return 0; var currentTime = DateTime.Now; var affectedRows = await UpdateDiy .Set(t => t.TaskStatus, (int)status) .SetIf(!string.IsNullOrWhiteSpace(reason), t => t.ErrorMessage, reason) .Set(t => t.ModifiedTime, currentTime) .Where(t => taskIds.Contains(t.Id)) .ExecuteAffrowsAsync(); return (int)affectedRows; } }