paiban/NPP.SmartSchedue.Api/Services/Notification/NotificationScheduledService.cs
Asoka.Wang 2b3f9acdce 123
2025-09-22 19:09:47 +08:00

793 lines
31 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NPP.SmartSchedue.Api.Contracts.Core.Enums;
using NPP.SmartSchedue.Api.Contracts.Services.Notification;
using NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
using NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
using NPP.SmartSchedue.Api.Contracts.Services.Work;
using NPP.SmartSchedue.Api.Services.Personnel;
using ZhonTai.Admin.Core.GrpcServices;
using ZhonTai.Admin.Services;
using ZhonTai.DynamicApi;
using ZhonTai.DynamicApi.Attributes;
namespace NPP.SmartSchedue.Api.Services.Notification;
/// <summary>
/// 通知服务 定时消息
/// </summary>
[DynamicApi(Area = "app")]
public class NotificationScheduledService : BaseService, IDynamicApi
{
private readonly ILogger<NotificationScheduledService> _logger;
private readonly IWorkOrderService _workOrderService;
private readonly INotificationService _notificationService;
private readonly IUserGrpcService _userService;
private readonly PersonnelQualificationService _personnelQualificationService;
public NotificationScheduledService(IWorkOrderService workOrderService,
INotificationService notificationService,
IUserGrpcService userService,
ILogger<NotificationScheduledService> logger,
PersonnelQualificationService personnelQualificationService)
{
_logger = logger;
_workOrderService = workOrderService;
_userService = userService;
_notificationService = notificationService;
_personnelQualificationService = personnelQualificationService;
}
/// <summary>
/// 待提交任务提醒
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> CheckWorkOrderByPendingSubmit()
{
var setting = await GetNotificationSettingCache("任务待提交提醒");
if (setting == null || !setting.IsEnabled)
{
_logger.LogWarning("任务待提交提醒未查询到有效的通知配置或已禁用");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
if (!IsWithinTimeWindow(setting, DateTime.Now))
{
_logger.LogInformation("当前不在通知时间窗内,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
var recipientIds = await GetNotificationPersonListAsync(setting);
if (recipientIds == null || recipientIds.Count == 0)
{
_logger.LogInformation("人员组无成员,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
var pendingByCreator = await _workOrderService.GetPendingSubmitCountByCreatorAsync();
if (pendingByCreator == null || pendingByCreator.Count == 0)
{
_logger.LogInformation("无待提交任务,跳过发送");
return new OkObjectResult(new { success = true, sent = 0 });
}
var intersectRecipients = pendingByCreator.Distinct().ToList();
if (intersectRecipients.Count == 0)
{
_logger.LogInformation("人员组成员中无待提交任务创建人,跳过发送");
return new OkObjectResult(new { success = true, sent = 0 });
}
var now = DateTime.Now;
var sendInputs = new List<SendNotificationInput>();
foreach (var personId in intersectRecipients)
{
var lastSentAt = await GetLastSentAt(setting.Id, personId.Key, "WorkOrderPendingSubmit");
if (!ShouldResendForPerson(setting, now, lastSentAt))
{
continue;
}
var count = pendingByCreator.TryGetValue(personId.Key, out var c) ? c : 0;
if (count <= 0) continue;
var variables = new Dictionary<string, string>
{
["UserId"] = personId.ToString(),
["PendingCount"] = count.ToString(),
["Now"] = now.ToString("yyyy-MM-dd HH:mm"),
["count"] = count.ToString(),
};
var sysTitle = string.IsNullOrWhiteSpace(setting.SystemMessageTitleTemplate)
? "任务待提交提醒"
: await Render(setting.SystemMessageTitleTemplate, variables);
var sysContent = string.IsNullOrWhiteSpace(setting.SystemMessageContentTemplate)
? $"您有{count}个任务处于待提交状态,请及时处理"
: await Render(setting.SystemMessageContentTemplate, variables);
if (setting.IsSystemMessageEnabled)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.SystemMessage,
Title = sysTitle,
Content = sysContent,
RecipientPersonnelIds = new List<long> { personId.Key },
BusinessType = "WorkOrderPendingSubmit",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
if (setting.IsEmailEnabled)
{
var emailSubject = string.IsNullOrWhiteSpace(setting.EmailSubjectTemplate)
? sysTitle
: await Render(setting.EmailSubjectTemplate, variables);
var emailContent = string.IsNullOrWhiteSpace(setting.EmailContentTemplate)
? sysContent
: await Render(setting.EmailContentTemplate, variables);
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.Email,
Title = emailSubject,
Content = emailContent,
RecipientPersonnelIds = new List<long> { personId.Key },
BusinessType = "WorkOrderPendingSubmit",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
}
int sent = 0;
foreach (var input in sendInputs)
{
var result = await _notificationService.SendNotificationAsync(input);
sent += result.SuccessCount;
}
return new OkObjectResult(new { success = true, sent, targeted = intersectRecipients.Count });
}
/// <summary>
/// 下周计划与请假提醒
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> CheckNextWeekPlanAndLeaveReminder()
{
var setting = await GetNotificationSettingCache("下周计划与请假提醒");
if (setting == null || !setting.IsEnabled)
{
_logger.LogWarning("下周计划与请假提醒未查询到有效的通知配置或已禁用");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
var now = DateTime.Now;
if (!IsWithinTimeWindow(setting, now))
{
_logger.LogInformation("当前不在通知时间窗内,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
var recipients = await GetNotificationPersonListAsync(setting);
if (recipients == null || recipients.Count == 0)
{
_logger.LogInformation("人员组无成员,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
var nextWeekStart = now.Date.AddDays(7 - (int)now.DayOfWeek + 1);
var nextWeekEnd = nextWeekStart.AddDays(6);
var variables = new Dictionary<string, string>
{
["StartDate"] = nextWeekStart.ToString("yyyy-MM-dd"),
["EndDate"] = nextWeekEnd.ToString("yyyy-MM-dd"),
["Now"] = now.ToString("yyyy-MM-dd HH:mm")
};
var sysTitle = string.IsNullOrWhiteSpace(setting.SystemMessageTitleTemplate)
? "下周计划与请假提醒"
: await Render(setting.SystemMessageTitleTemplate, variables);
var sysContent = string.IsNullOrWhiteSpace(setting.SystemMessageContentTemplate)
? $"请提交下周({variables["StartDate"]} ~ {variables["EndDate"]})的工作计划与请假安排"
: await Render(setting.SystemMessageContentTemplate, variables);
var emailSubject = string.IsNullOrWhiteSpace(setting.EmailSubjectTemplate)
? sysTitle
: await Render(setting.EmailSubjectTemplate, variables);
var emailContent = string.IsNullOrWhiteSpace(setting.EmailContentTemplate)
? sysContent
: await Render(setting.EmailContentTemplate, variables);
// 收集需发送目标并按类型合并为批量收件人,减少发送次数
var systemTargets = new List<long>();
var emailTargets = new List<long>();
foreach (var pid in recipients)
{
var lastSentAt = await GetLastSentAt(setting.Id, pid, "NextWeekPlanAndLeave");
if (!ShouldResendForPerson(setting, now, lastSentAt))
{
continue;
}
if (setting.IsSystemMessageEnabled)
{
systemTargets.Add(pid);
}
if (setting.IsEmailEnabled)
{
emailTargets.Add(pid);
}
}
var sendInputs = new List<SendNotificationInput>();
if (setting.IsSystemMessageEnabled && systemTargets.Count > 0)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.SystemMessage,
Title = sysTitle,
Content = sysContent,
RecipientPersonnelIds = systemTargets.Distinct().ToList(),
BusinessType = "NextWeekPlanAndLeave",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
if (setting.IsEmailEnabled && emailTargets.Count > 0)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.Email,
Title = emailSubject,
Content = emailContent,
RecipientPersonnelIds = emailTargets.Distinct().ToList(),
BusinessType = "NextWeekPlanAndLeave",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
int sent = 0;
foreach (var input in sendInputs)
{
var result = await _notificationService.SendGroupNotificationAsync(input);
sent += result.SuccessCount;
}
// targeted 返回真实被筛选后的人数
var targeted = systemTargets.Union(emailTargets).Distinct().Count();
return new OkObjectResult(new { success = true, sent, targeted });
}
/// <summary>
/// 任务分配提醒(周五提醒排班管理员进行排班)
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> CheckFridayScheduleAssignmentReminder()
{
// 1. 读取通知配置
var setting = await GetNotificationSettingCache("任务分配提醒-周五排班");
if (setting == null || !setting.IsEnabled)
{
_logger.LogWarning("任务分配提醒-周五排班 未查询到有效的通知配置或已禁用");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
// 2. 基于时间窗与星期校验(建议由调度保证周五触发,此处再保护一次)
var now = DateTime.Now;
if (now.DayOfWeek != DayOfWeek.Friday)
{
_logger.LogInformation("今天不是周五,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
if (!IsWithinTimeWindow(setting, now))
{
_logger.LogInformation("当前不在通知时间窗内,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
// 3. 获取人员组成员(排班管理员)
var recipients = await GetNotificationPersonListAsync(setting);
if (recipients == null || recipients.Count == 0)
{
_logger.LogInformation("人员组无成员,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
// 4. 变量渲染
var fridayDate = now.Date.ToString("yyyy-MM-dd");
var weekOfYear = System.Globalization.ISOWeek.GetWeekOfYear(now);
var variables = new Dictionary<string, string>
{
["Now"] = now.ToString("yyyy-MM-dd HH:mm"),
["FridayDate"] = fridayDate,
["WeekOfYear"] = weekOfYear.ToString()
};
var sysTitle = string.IsNullOrWhiteSpace(setting.SystemMessageTitleTemplate)
? "任务分配提醒"
: await Render(setting.SystemMessageTitleTemplate, variables);
var sysContent = string.IsNullOrWhiteSpace(setting.SystemMessageContentTemplate)
? $"今天是周五({fridayDate}),请及时完成下周的排班任务分配。"
: await Render(setting.SystemMessageContentTemplate, variables);
var emailSubject = string.IsNullOrWhiteSpace(setting.EmailSubjectTemplate)
? sysTitle
: await Render(setting.EmailSubjectTemplate, variables);
var emailContent = string.IsNullOrWhiteSpace(setting.EmailContentTemplate)
? sysContent
: await Render(setting.EmailContentTemplate, variables);
// 5. 频率控制 + 组发
var systemTargets = new List<long>();
var emailTargets = new List<long>();
foreach (var pid in recipients)
{
var lastSentAt = await GetLastSentAt(setting.Id, pid, "FridayScheduleAssignmentReminder");
if (!ShouldResendForPerson(setting, now, lastSentAt))
{
continue;
}
if (setting.IsSystemMessageEnabled) systemTargets.Add(pid);
if (setting.IsEmailEnabled) emailTargets.Add(pid);
}
var sendInputs = new List<SendNotificationInput>();
if (setting.IsSystemMessageEnabled && systemTargets.Count > 0)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.SystemMessage,
Title = sysTitle,
Content = sysContent,
RecipientPersonnelIds = systemTargets.Distinct().ToList(),
BusinessType = "FridayScheduleAssignmentReminder",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
if (setting.IsEmailEnabled && emailTargets.Count > 0)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.Email,
Title = emailSubject,
Content = emailContent,
RecipientPersonnelIds = emailTargets.Distinct().ToList(),
BusinessType = "FridayScheduleAssignmentReminder",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
// 6. 发送
int sent = 0;
foreach (var input in sendInputs)
{
// 与“下周计划与请假提醒”保持一致,组发
var result = await _notificationService.SendGroupNotificationAsync(input);
sent += result.SuccessCount;
}
var targeted = systemTargets.Union(emailTargets).Distinct().Count();
return new OkObjectResult(new { success = true, sent, targeted });
}
/// <summary>
/// 人员资质到期预警
/// </summary>
/// <returns></returns>
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> CheckPersonnelQualificationExpiryReminder()
{
var setting = await GetNotificationSettingCache("人员资质到期预警");
if (setting == null || !setting.IsEnabled)
{
_logger.LogWarning("人员资质到期预警未查询到有效的通知配置或已禁用");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
var now = DateTime.Now;
if (!IsWithinTimeWindow(setting, now))
{
_logger.LogInformation("当前不在通知时间窗内,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
// // 人员范围:通知配置的人员组
// var recipients = await GetNotificationPersonListAsync(setting);
// if (recipients == null || recipients.Count == 0)
// {
// _logger.LogInformation("人员组无成员,跳过发送");
// return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
// }
// 查询这些人员的预警期内即将到期的资质
var expiring = await _personnelQualificationService.GetExpiringAsync(DateTime.Today, null, includeExpired: false);
if (expiring == null || expiring.Count == 0)
{
_logger.LogInformation("无资质到期预警数据,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, targeted = 0 });
}
// 按人员聚合
var grouped = expiring.GroupBy(x => x.PersonnelId)
.ToDictionary(g => g.Key, g => g.ToList());
var sendInputs = new List<SendNotificationInput>();
int targeted = 0;
foreach (var pid in grouped.Distinct())
{
if (!grouped.TryGetValue(pid.Key, out var items) || items.Count == 0)
continue;
var lastSentAt = await GetLastSentAt(setting.Id, pid.Key, "PersonnelQualificationExpiry");
if (!ShouldResendForPerson(setting, now, lastSentAt))
continue;
targeted++;
// 组装模板变量
var personName = items.FirstOrDefault()?.PersonnelName ?? "";
var count = items.Count;
var itemLines = items
.OrderBy(i => i.DaysLeft)
.Select(i =>
{
var d = i.ExpiryDate?.ToString("yyyy-MM-dd") ?? "-";
return $"{i.QualificationName} 将于 {d} 到期,剩余 {i.DaysLeft} 天";
});
var itemsText = string.Join("; ", itemLines);
var variables = new Dictionary<string, string>
{
["UserId"] = pid.ToString(),
["PersonnelName"] = personName,
["Count"] = count.ToString(),
["Items"] = itemsText,
["Now"] = now.ToString("yyyy-MM-dd HH:mm")
};
var sysTitle = string.IsNullOrWhiteSpace(setting.SystemMessageTitleTemplate)
? "人员资质到期预警"
: await Render(setting.SystemMessageTitleTemplate, variables);
var sysContent = string.IsNullOrWhiteSpace(setting.SystemMessageContentTemplate)
? $"您有{count}条资质即将到期:{itemsText}"
: await Render(setting.SystemMessageContentTemplate, variables);
if (setting.IsSystemMessageEnabled)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.SystemMessage,
Title = sysTitle,
Content = sysContent,
RecipientPersonnelIds = new List<long> { pid.Key },
BusinessType = "PersonnelQualificationExpiry",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
if (setting.IsEmailEnabled)
{
var emailSubject = string.IsNullOrWhiteSpace(setting.EmailSubjectTemplate)
? sysTitle
: await Render(setting.EmailSubjectTemplate, variables);
var emailContent = string.IsNullOrWhiteSpace(setting.EmailContentTemplate)
? sysContent
: await Render(setting.EmailContentTemplate, variables);
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.Email,
Title = emailSubject,
Content = emailContent,
RecipientPersonnelIds = new List<long> { pid.Key },
BusinessType = "PersonnelQualificationExpiry",
BusinessId = null,
BusinessData = "",
SendImmediately = true
});
}
}
int sent = 0;
foreach (var input in sendInputs)
{
var result = await _notificationService.SendNotificationAsync(input);
sent += result.SuccessCount;
}
return new OkObjectResult(new { success = true, sent, targeted });
}
/// <summary>
/// 统一对外:工作任务操作通知(供 WorkOrderIntegrationOperationService 调用)
/// </summary>
/// <param name="operationType">Modify/Delete/Cancel</param>
/// <param name="taskId">任务ID</param>
/// <param name="operatorUserId">操作者用户ID可空</param>
/// <param name="operatorName">操作者名称(可空)</param>
/// <param name="remarks">备注(可空)</param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> SendWorkOrderOperationNotificationAsync(
string operationType,
long taskId,
long? operatorUserId = null,
string operatorName = null,
string remarks = null,
IList<long> designatedPersonnelIds = null,
IList<string> designatedPersonnelNames = null)
{
// 1. 读取统一通知配置
var setting = await GetNotificationSettingCache("工作任务操作通知");
if (setting == null || !setting.IsEnabled)
{
_logger.LogWarning("工作任务操作通知 未查询到有效的通知配置或已禁用");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
var now = DateTime.Now;
if (!IsWithinTimeWindow(setting, now))
{
_logger.LogInformation("当前不在通知时间窗内,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
// 2. 收件人
var recipients = await GetNotificationPersonListAsync(setting);
recipients.AddRange(designatedPersonnelIds);
recipients = recipients.Distinct().ToList();
if (recipients == null || recipients.Count == 0)
{
_logger.LogInformation("人员组无成员,跳过发送");
return new OkObjectResult(new { success = true, sent = 0, skipped = 0 });
}
// 3. 业务类型区分,便于历史频率控制
string businessType = operationType?.ToLower() switch
{
"modify" => "WorkOrderModified",
"delete" => "WorkOrderDeleted",
"cancel" => "WorkOrderCancelled",
_ => "WorkOrderOperation"
};
// 4. 模板变量
var variables = new Dictionary<string, string>
{
["OperationType"] = operationType ?? string.Empty,
["TaskId"] = taskId.ToString(),
["OperatorUserId"] = operatorUserId?.ToString() ?? string.Empty,
["OperatorName"] = operatorName ?? string.Empty,
["Remarks"] = remarks ?? string.Empty,
["Now"] = now.ToString("yyyy-MM-dd HH:mm"),
["DesignatedPersonnelIds"] = designatedPersonnelIds != null ? string.Join(",", designatedPersonnelIds) : string.Empty,
["DesignatedPersonnelNames"] = designatedPersonnelNames != null ? string.Join(",", designatedPersonnelNames) : string.Empty
};
var sysTitle = string.IsNullOrWhiteSpace(setting.SystemMessageTitleTemplate)
? $"任务{operationType}通知"
: await Render(setting.SystemMessageTitleTemplate, variables);
var sysContent = string.IsNullOrWhiteSpace(setting.SystemMessageContentTemplate)
? $"任务({taskId})已执行{operationType}操作。{(string.IsNullOrEmpty(remarks) ? "" : "" + remarks)}"
: await Render(setting.SystemMessageContentTemplate, variables);
var emailSubject = string.IsNullOrWhiteSpace(setting.EmailSubjectTemplate)
? sysTitle
: await Render(setting.EmailSubjectTemplate, variables);
var emailContent = string.IsNullOrWhiteSpace(setting.EmailContentTemplate)
? sysContent
: await Render(setting.EmailContentTemplate, variables);
// 5. 频率控制 + 组发
var systemTargets = new List<long>();
var emailTargets = new List<long>();
foreach (var pid in recipients)
{
var lastSentAt = await GetLastSentAt(setting.Id, pid, businessType);
if (!ShouldResendForPerson(setting, now, lastSentAt))
{
continue;
}
if (setting.IsSystemMessageEnabled) systemTargets.Add(pid);
if (setting.IsEmailEnabled) emailTargets.Add(pid);
}
var sendInputs = new List<SendNotificationInput>();
if (setting.IsSystemMessageEnabled && systemTargets.Count > 0)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.SystemMessage,
Title = sysTitle,
Content = sysContent,
RecipientPersonnelIds = systemTargets.Distinct().ToList(),
BusinessType = businessType,
BusinessId = taskId,
BusinessData = System.Text.Json.JsonSerializer.Serialize(new {
OperationType = operationType, TaskId = taskId, OperatorUserId = operatorUserId, OperatorName = operatorName, Remarks = remarks
}),
SendImmediately = true
});
}
if (setting.IsEmailEnabled && emailTargets.Count > 0)
{
sendInputs.Add(new SendNotificationInput
{
SettingId = setting.Id,
NotificationType = NotificationTypeEnum.Email,
Title = emailSubject,
Content = emailContent,
RecipientPersonnelIds = emailTargets.Distinct().ToList(),
BusinessType = businessType,
BusinessId = taskId,
BusinessData = System.Text.Json.JsonSerializer.Serialize(new {
OperationType = operationType, TaskId = taskId, OperatorUserId = operatorUserId, OperatorName = operatorName, Remarks = remarks
}),
SendImmediately = true
});
}
int sent = 0;
foreach (var input in sendInputs)
{
var result = await _notificationService.SendGroupNotificationAsync(input);
sent += result.SuccessCount;
}
var targeted = systemTargets.Union(emailTargets).Distinct().Count();
return new OkObjectResult(new { success = true, sent, targeted, businessType });
}
private async Task<string> Render(string template, Dictionary<string, string> variables)
{
var isValid = await _notificationService.ValidateTemplateAsync(template);
if (!isValid) return template;
if (variables == null || variables.Count == 0) return template;
var result = template;
foreach (var kv in variables)
{
var placeholder = $"{{{kv.Key}}}";
result = result.Replace(placeholder, kv.Value ?? string.Empty, StringComparison.Ordinal);
}
return result;
}
private async Task<DateTime?> GetLastSentAt(long settingId, long personnelId, string businessType)
{
try
{
var histories = await _notificationService.GetNotificationHistoryPageAsync(new ZhonTai.Admin.Core.Dto.PageInput<NotificationHistoryPageInput>
{
CurrentPage = 0,
PageSize = 10000,
Filter = new NotificationHistoryPageInput
{
NotificationSettingId = settingId,
RecipientPersonnelId = personnelId,
BusinessType = businessType
}
});
var item = histories.List.FirstOrDefault();
return item?.ActualSendTime ?? item?.PlannedSendTime;
}
catch
{
return null;
}
}
private async Task<NotificationSettingOutput> GetNotificationSettingCache(string key)
{
var settings = await _notificationService.GetNotificationSettingListAsync(true);
return settings.FirstOrDefault(m => m.NotificationName == key);
}
private async Task<List<long>> GetNotificationPersonListAsync(NotificationSettingOutput? setting = null,
List<long>? otherPersons = null)
{
var notificationPersonList = new List<long>();
if (setting != null)
{
notificationPersonList.AddRange(await _notificationService.GetPersonnelGroupMembersAsync(setting.PersonnelGroupId));
}
if (otherPersons != null && otherPersons.Count > 0)
{
notificationPersonList.AddRange(otherPersons);
}
return notificationPersonList.Distinct().ToList();
}
private bool ShouldResendForPerson(NotificationSettingOutput setting, DateTime now, DateTime? lastSentAt)
{
if (!IsWithinTimeWindow(setting, now)) return false;
if (setting.FrequencyType == NotificationFrequencyEnum.Once)
{
return lastSentAt == null;
}
if (setting.FrequencyType == NotificationFrequencyEnum.FixedInterval)
{
if (lastSentAt == null) return true;
var interval = setting.IntervalMinutes.GetValueOrDefault(0);
if (interval <= 0) return false;
return now - lastSentAt.Value >= TimeSpan.FromMinutes(interval);
}
return false;
}
private bool IsWithinTimeWindow(NotificationSettingOutput setting, DateTime now)
{
if (string.IsNullOrWhiteSpace(setting.StartTime) || string.IsNullOrWhiteSpace(setting.EndTime)) return true;
if (!TimeOnly.TryParse(setting.StartTime, out var start)) return true;
if (!TimeOnly.TryParse(setting.EndTime, out var end)) return true;
var t = TimeOnly.FromDateTime(now);
return t >= start && t < end;
}
}