using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq; using Microsoft.Extensions.Logging; using FreeSql; using ZhonTai.Admin.Core.Db.Transaction; using NPP.SmartSchedue.Api.Contracts.Services.Notification; using NPP.SmartSchedue.Api.Contracts.Core.Repositories; using NPP.SmartSchedue.Api.Contracts.Domain.Notification; namespace NPP.SmartSchedue.Api.Services.Notification; /// /// 系统消息服务实现 /// 决策点1:基础通知方式 - 系统消息通知 /// 业务思考:系统消息是内部通知的核心方式,需要支持实时推送、状态管理、操作交互等功能 /// 设计原则: /// 1. 消息持久化存储,确保不丢失 /// 2. 支持消息状态管理(未读/已读/已删除) /// 3. 支持操作按钮,实现交互式消息 /// 4. 支持批量操作,提高效率 /// 5. 支持模板化消息,统一格式 /// public class SystemMessageService : ISystemMessageService { private readonly ILogger _logger; private readonly INotificationTemplateService _templateService; private readonly IBaseRepository _notificationHistoryRepository; private readonly IUnitOfWorkManager _uowManager; public SystemMessageService( ILogger logger, INotificationTemplateService templateService, IBaseRepository notificationHistoryRepository, IUnitOfWorkManager uowManager) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _templateService = templateService ?? throw new ArgumentNullException(nameof(templateService)); _notificationHistoryRepository = notificationHistoryRepository ?? throw new ArgumentNullException(nameof(notificationHistoryRepository)); _uowManager = uowManager ?? throw new ArgumentNullException(nameof(uowManager)); } #region 单个消息发送 /// /// 发送系统消息 /// 业务场景:工作任务分配、设备状态变更、排班通知等 /// public async Task SendSystemMessageAsync( long recipientPersonnelId, string title, string content, SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info, string businessType = "", long? businessId = null) { try { if (recipientPersonnelId <= 0) { _logger.LogWarning("收件人ID无效:{RecipientId}", recipientPersonnelId); return false; } if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(content)) { _logger.LogWarning("消息标题或内容不能为空"); return false; } using var uow = _uowManager.Begin(); // 创建通知历史记录 var notificationHistory = new NotificationHistoryEntity { NotificationType = Core.Enums.NotificationTypeEnum.SystemMessage, RecipientType = "Personnel", RecipientId = recipientPersonnelId.ToString(), Subject = title, Content = content, BusinessType = businessType, BusinessId = businessId, Status = Core.Enums.NotificationStatusEnum.Sent, SentTime = DateTime.Now, MessageType = GetNotificationMessageType(messageType), IsRead = false, IsDeleted = false, CreatedTime = DateTime.Now }; await _notificationHistoryRepository.InsertAsync(notificationHistory); await uow.CommitAsync(); _logger.LogInformation("系统消息发送成功:收件人 {RecipientId},标题:{Title}", recipientPersonnelId, title); return true; } catch (Exception ex) { _logger.LogError(ex, "发送系统消息失败:收件人 {RecipientId},标题:{Title}", recipientPersonnelId, title); return false; } } /// /// 发送带操作按钮的系统消息 /// 业务场景:任务确认、审批流程、操作确认等交互式场景 /// public async Task SendSystemMessageWithActionsAsync( long recipientPersonnelId, string title, string content, List actions, SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info, string businessType = "", long? businessId = null) { try { if (recipientPersonnelId <= 0) { _logger.LogWarning("收件人ID无效:{RecipientId}", recipientPersonnelId); return false; } if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(content)) { _logger.LogWarning("消息标题或内容不能为空"); return false; } using var uow = _uowManager.Begin(); // 序列化操作按钮 var actionsJson = actions != null && actions.Any() ? System.Text.Json.JsonSerializer.Serialize(actions) : ""; // 创建通知历史记录 var notificationHistory = new NotificationHistoryEntity { NotificationType = Core.Enums.NotificationTypeEnum.SystemMessage, RecipientType = "Personnel", RecipientId = recipientPersonnelId.ToString(), Subject = title, Content = content, BusinessType = businessType, BusinessId = businessId, Status = Core.Enums.NotificationStatusEnum.Sent, SentTime = DateTime.Now, MessageType = GetNotificationMessageType(messageType), IsRead = false, IsDeleted = false, ActionsData = actionsJson, // 存储操作按钮数据 CreatedTime = DateTime.Now }; await _notificationHistoryRepository.InsertAsync(notificationHistory); await uow.CommitAsync(); _logger.LogInformation("带操作按钮的系统消息发送成功:收件人 {RecipientId},标题:{Title},操作数量:{ActionCount}", recipientPersonnelId, title, actions?.Count ?? 0); return true; } catch (Exception ex) { _logger.LogError(ex, "发送带操作按钮的系统消息失败:收件人 {RecipientId},标题:{Title}", recipientPersonnelId, title); return false; } } #endregion #region 批量消息发送 /// /// 批量发送系统消息 /// 业务场景:排班变更通知、紧急通知、系统维护通知等需要群发的场景 /// public async Task> BatchSendSystemMessageAsync( List recipientPersonnelIds, string title, string content, SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info, string businessType = "", long? businessId = null) { var results = new Dictionary(); if (recipientPersonnelIds == null || !recipientPersonnelIds.Any()) { _logger.LogWarning("批量发送系统消息:收件人列表为空"); return results; } if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(content)) { _logger.LogWarning("消息标题或内容不能为空"); foreach (var recipientId in recipientPersonnelIds) { results[recipientId] = false; } return results; } // 去重收件人 var uniqueRecipients = recipientPersonnelIds.Distinct().Where(id => id > 0).ToList(); try { using var uow = _uowManager.Begin(); var notificationHistories = new List(); var messageTypeValue = GetNotificationMessageType(messageType); var currentTime = DateTime.Now; // 批量创建通知记录 foreach (var recipientId in uniqueRecipients) { var notificationHistory = new NotificationHistoryEntity { NotificationType = Core.Enums.NotificationTypeEnum.SystemMessage, RecipientType = "Personnel", RecipientId = recipientId.ToString(), Subject = title, Content = content, BusinessType = businessType, BusinessId = businessId, Status = Core.Enums.NotificationStatusEnum.Sent, SentTime = currentTime, MessageType = messageTypeValue, IsRead = false, IsDeleted = false, CreatedTime = currentTime }; notificationHistories.Add(notificationHistory); results[recipientId] = true; // 预设为成功,如果出错会被重置 } // 批量插入 await _notificationHistoryRepository.InsertAsync(notificationHistories); await uow.CommitAsync(); var successCount = results.Values.Count(r => r); _logger.LogInformation("批量发送系统消息完成:总数 {Total},成功 {Success},失败 {Failed}", recipientPersonnelIds.Count, successCount, recipientPersonnelIds.Count - successCount); } catch (Exception ex) { _logger.LogError(ex, "批量发送系统消息失败"); // 如果批量操作失败,将所有结果设为失败 foreach (var recipientId in uniqueRecipients) { results[recipientId] = false; } } return results; } /// /// 个性化批量发送系统消息 /// 业务场景:个性化任务分配通知、个性化提醒等需要不同内容的场景 /// public async Task> BatchSendPersonalizedSystemMessageAsync(List messageItems) { var results = new Dictionary(); if (messageItems == null || !messageItems.Any()) { _logger.LogWarning("个性化批量发送系统消息:消息项列表为空"); return results; } try { using var uow = _uowManager.Begin(); var notificationHistories = new List(); var currentTime = DateTime.Now; foreach (var item in messageItems) { try { if (item.RecipientPersonnelId <= 0 || string.IsNullOrWhiteSpace(item.Title) || string.IsNullOrWhiteSpace(item.Content)) { _logger.LogWarning("消息项无效:收件人ID {RecipientId},标题:{Title}", item.RecipientPersonnelId, item.Title); results[item.RecipientPersonnelId] = false; continue; } // 渲染模板变量(如果有) var title = item.Title; var content = item.Content; if (item.Variables != null && item.Variables.Any()) { title = await _templateService.RenderTemplateAsync(item.Title, item.Variables); content = await _templateService.RenderTemplateAsync(item.Content, item.Variables); } // 序列化操作按钮 var actionsJson = item.Actions != null && item.Actions.Any() ? System.Text.Json.JsonSerializer.Serialize(item.Actions) : ""; var notificationHistory = new NotificationHistoryEntity { NotificationType = Core.Enums.NotificationTypeEnum.SystemMessage, RecipientType = "Personnel", RecipientId = item.RecipientPersonnelId.ToString(), Subject = title, Content = content, BusinessType = item.BusinessType, BusinessId = item.BusinessId, Status = Core.Enums.NotificationStatusEnum.Sent, SentTime = currentTime, MessageType = GetNotificationMessageType(item.MessageType), IsRead = false, IsDeleted = false, ActionsData = actionsJson, CreatedTime = currentTime }; notificationHistories.Add(notificationHistory); results[item.RecipientPersonnelId] = true; } catch (Exception ex) { _logger.LogError(ex, "处理个性化消息项失败:收件人 {RecipientId}", item.RecipientPersonnelId); results[item.RecipientPersonnelId] = false; } } // 批量插入成功的记录 if (notificationHistories.Any()) { await _notificationHistoryRepository.InsertAsync(notificationHistories); await uow.CommitAsync(); } var successCount = results.Values.Count(r => r); _logger.LogInformation("个性化批量发送系统消息完成:总数 {Total},成功 {Success},失败 {Failed}", messageItems.Count, successCount, messageItems.Count - successCount); } catch (Exception ex) { _logger.LogError(ex, "个性化批量发送系统消息失败"); // 如果批量操作失败,将所有结果设为失败 foreach (var item in messageItems) { results[item.RecipientPersonnelId] = false; } } return results; } #endregion #region 消息模板 /// /// 使用模板发送系统消息 /// 业务场景:标准化通知,如任务分配模板、维护提醒模板等 /// public async Task SendSystemMessageByTemplateAsync( long recipientPersonnelId, string titleTemplate, string contentTemplate, Dictionary variables, SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info, string businessType = "", long? businessId = null) { try { // 渲染模板 var title = await _templateService.RenderTemplateAsync(titleTemplate, variables); var content = await _templateService.RenderTemplateAsync(contentTemplate, variables); // 发送消息 return await SendSystemMessageAsync(recipientPersonnelId, title, content, messageType, businessType, businessId); } catch (Exception ex) { _logger.LogError(ex, "使用模板发送系统消息失败:收件人 {RecipientId}", recipientPersonnelId); return false; } } #endregion #region 消息管理 /// /// 标记消息为已读 /// 业务场景:用户查看消息后更新状态,用于消息中心的已读/未读状态管理 /// public async Task MarkMessageAsReadAsync(long messageId, long recipientPersonnelId) { try { using var uow = _uowManager.Begin(); var message = await _notificationHistoryRepository .Where(x => x.Id == messageId && x.RecipientId == recipientPersonnelId.ToString() && !x.IsDeleted) .FirstAsync(); if (message == null) { _logger.LogWarning("消息不存在或已被删除:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return false; } if (!message.IsRead) { message.IsRead = true; message.ReadTime = DateTime.Now; message.ModifiedTime = DateTime.Now; await _notificationHistoryRepository.UpdateAsync(message); await uow.CommitAsync(); _logger.LogDebug("消息标记为已读:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); } return true; } catch (Exception ex) { _logger.LogError(ex, "标记消息为已读失败:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return false; } } /// /// 批量标记消息为已读 /// 业务场景:用户批量处理消息,提高操作效率 /// public async Task BatchMarkMessagesAsReadAsync(List messageIds, long recipientPersonnelId) { if (messageIds == null || !messageIds.Any()) { _logger.LogWarning("批量标记消息为已读:消息ID列表为空"); return 0; } try { using var uow = _uowManager.Begin(); var currentTime = DateTime.Now; // 查询未读的消息 var messages = await _notificationHistoryRepository .Where(x => messageIds.Contains(x.Id) && x.RecipientId == recipientPersonnelId.ToString() && !x.IsDeleted && !x.IsRead) .ToListAsync(); if (!messages.Any()) { _logger.LogInformation("没有找到需要标记为已读的消息:收件人 {RecipientId}", recipientPersonnelId); return 0; } // 批量更新 foreach (var message in messages) { message.IsRead = true; message.ReadTime = currentTime; message.ModifiedTime = currentTime; } await _notificationHistoryRepository.UpdateAsync(messages); await uow.CommitAsync(); _logger.LogInformation("批量标记消息为已读完成:收件人 {RecipientId},更新数量 {Count}", recipientPersonnelId, messages.Count); return messages.Count; } catch (Exception ex) { _logger.LogError(ex, "批量标记消息为已读失败:收件人 {RecipientId}", recipientPersonnelId); return 0; } } /// /// 删除消息 /// 业务场景:用户清理不需要的消息,实现软删除以保留审计记录 /// public async Task DeleteMessageAsync(long messageId, long recipientPersonnelId) { try { using var uow = _uowManager.Begin(); var message = await _notificationHistoryRepository .Where(x => x.Id == messageId && x.RecipientId == recipientPersonnelId.ToString() && !x.IsDeleted) .FirstAsync(); if (message == null) { _logger.LogWarning("消息不存在或已被删除:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return false; } // 软删除 message.IsDeleted = true; message.ModifiedTime = DateTime.Now; await _notificationHistoryRepository.UpdateAsync(message); await uow.CommitAsync(); _logger.LogInformation("消息删除成功:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return true; } catch (Exception ex) { _logger.LogError(ex, "删除消息失败:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return false; } } /// /// 获取用户未读消息数量 /// 业务场景:消息中心红点提示、导航栏未读消息数显示 /// public async Task GetUnreadMessageCountAsync(long recipientPersonnelId) { try { var count = await _notificationHistoryRepository .Where(x => x.RecipientId == recipientPersonnelId.ToString() && x.NotificationType == Core.Enums.NotificationTypeEnum.SystemMessage && !x.IsRead && !x.IsDeleted) .CountAsync(); return count; } catch (Exception ex) { _logger.LogError(ex, "获取用户未读消息数量失败:收件人 {RecipientId}", recipientPersonnelId); return 0; } } #endregion #region 私有辅助方法 /// /// 将SystemMessageTypeEnum转换为通知历史实体的MessageType字段 /// private string GetNotificationMessageType(SystemMessageTypeEnum messageType) { return messageType switch { SystemMessageTypeEnum.Info => "信息", SystemMessageTypeEnum.Success => "成功", SystemMessageTypeEnum.Warning => "警告", SystemMessageTypeEnum.Error => "错误", SystemMessageTypeEnum.Urgent => "紧急", _ => "信息" }; } #endregion }