using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using ZhonTai.Admin.Services; using ZhonTai.DynamicApi; using ZhonTai.DynamicApi.Attributes; using NPP.SmartSchedue.Api.Contracts.Services.Notification; using NPP.SmartSchedue.Api.Contracts.Services.Notification.Input; using NPP.SmartSchedue.Api.Contracts.Services.Notification.Output; using ZhonTai.Admin.Core.Repositories; using NPP.SmartSchedue.Api.Contracts.Domain.Notification; using NPP.SmartSchedue.Api.Contracts.Core.Enums; namespace NPP.SmartSchedue.Api.Services.Notification; /// /// 通知服务 系统消息 /// [DynamicApi(Area = "app")] public class SystemMessageService : BaseService, ISystemMessageService, IDynamicApi { private readonly INotificationTemplateService _templateService; private readonly IRepositoryBase _notificationHistoryRepository; public SystemMessageService( INotificationTemplateService templateService, IRepositoryBase notificationHistoryRepository) { _templateService = templateService; _notificationHistoryRepository = notificationHistoryRepository; } #region 单个消息发送 /// /// 发送系统消息 /// 业务场景:工作任务分配、设备状态变更、排班通知等 /// [HttpPost] public async Task SendSystemMessageAsync(SendSystemMessageInput input) { try { if (input.RecipientPersonnelId <= 0) { return SendSystemMessageOutput.CreateFailure(input.RecipientPersonnelId, "收件人ID无效"); } if (string.IsNullOrWhiteSpace(input.Title) || string.IsNullOrWhiteSpace(input.Content)) { return SendSystemMessageOutput.CreateFailure(input.RecipientPersonnelId, "消息标题或内容不能为空"); } var currentTime = DateTime.Now; // 创建通知历史记录 var notificationHistory = new NotificationHistoryEntity { NotificationSettingId = input.NotificationSettingId, // 临时设置,实际应该从配置获取 NotificationType = (int)NotificationTypeEnum.SystemMessage, RecipientPersonnelId = input.RecipientPersonnelId, NotificationTitle = input.Title, NotificationContent = input.Content, BusinessType = input.BusinessType, BusinessId = input.BusinessId, SendStatus = (int)NotificationStatusEnum.Success, ActualSendTime = currentTime, PlannedSendTime = currentTime, CreatedTime = currentTime }; var result = await _notificationHistoryRepository.InsertAsync(notificationHistory); return SendSystemMessageOutput.CreateSuccess((long)result.Id, input.RecipientPersonnelId, currentTime); } catch (Exception ex) { Logger.LogError(ex, "发送系统消息失败:收件人 {RecipientId},标题:{Title}", input.RecipientPersonnelId, input.Title); return SendSystemMessageOutput.CreateFailure(input.RecipientPersonnelId, $"发送失败:{ex.Message}"); } } /// /// 发送带操作按钮的系统消息 /// 业务场景:任务确认、审批流程、操作确认等交互式场景 /// [HttpPost] public async Task SendSystemMessageWithActionsAsync(SendSystemMessageWithActionsInput input) { try { if (input.RecipientPersonnelId <= 0) { return SendSystemMessageOutput.CreateFailure(input.RecipientPersonnelId, "收件人ID无效"); } if (string.IsNullOrWhiteSpace(input.Title) || string.IsNullOrWhiteSpace(input.Content)) { return SendSystemMessageOutput.CreateFailure(input.RecipientPersonnelId, "消息标题或内容不能为空"); } var currentTime = DateTime.Now; // 序列化操作按钮 var actionsJson = input.Actions != null && input.Actions.Any() ? System.Text.Json.JsonSerializer.Serialize(input.Actions) : ""; // 创建通知历史记录 var notificationHistory = new NotificationHistoryEntity { NotificationSettingId = 1, // 临时设置,实际应该从配置获取 NotificationType = (int)NotificationTypeEnum.SystemMessage, RecipientPersonnelId = input.RecipientPersonnelId, NotificationTitle = input.Title, NotificationContent = input.Content, BusinessType = input.BusinessType, BusinessId = input.BusinessId, SendStatus = (int)NotificationStatusEnum.Success, ActualSendTime = currentTime, PlannedSendTime = currentTime, BusinessData = actionsJson, // 存储操作按钮数据 CreatedTime = currentTime }; var result = await _notificationHistoryRepository.InsertAsync(notificationHistory); return SendSystemMessageOutput.CreateSuccess((long)result.Id, input.RecipientPersonnelId, currentTime); } catch (Exception ex) { Logger.LogError(ex, "发送带操作按钮的系统消息失败:收件人 {RecipientId},标题:{Title}", input.RecipientPersonnelId, input.Title); return SendSystemMessageOutput.CreateFailure(input.RecipientPersonnelId, $"发送失败:{ex.Message}"); } } #endregion #region 批量消息发送 /// /// 批量发送系统消息 /// 业务场景:排班变更通知、紧急通知、系统维护通知等需要群发的场景 /// [HttpPost] public async Task BatchSendSystemMessageAsync(BatchSendSystemMessageInput input) { var results = new Dictionary(); if (input.RecipientPersonnelIds == null || !input.RecipientPersonnelIds.Any()) { Logger.LogWarning("批量发送系统消息:收件人列表为空"); return BatchSendSystemMessageOutput.Create(results); } if (string.IsNullOrWhiteSpace(input.Title) || string.IsNullOrWhiteSpace(input.Content)) { foreach (var recipientId in input.RecipientPersonnelIds) { results[recipientId] = SendSystemMessageOutput.CreateFailure(recipientId, "消息标题或内容不能为空"); } return BatchSendSystemMessageOutput.Create(results); } // 去重收件人 var uniqueRecipients = input.RecipientPersonnelIds.Distinct().Where(id => id > 0).ToList(); try { var notificationHistories = new List(); // 注释:系统消息类型统一使用NotificationTypeEnum.SystemMessage var currentTime = DateTime.Now; // 批量创建通知记录 foreach (var recipientId in uniqueRecipients) { var notificationHistory = new NotificationHistoryEntity { NotificationSettingId = 1, // 临时设置,实际应该从配置获取 NotificationType = (int)NotificationTypeEnum.SystemMessage, RecipientPersonnelId = recipientId, NotificationTitle = input.Title, NotificationContent = input.Content, BusinessType = input.BusinessType, BusinessId = input.BusinessId, SendStatus = (int)NotificationStatusEnum.Success, ActualSendTime = currentTime, PlannedSendTime = currentTime, CreatedTime = currentTime }; notificationHistories.Add(notificationHistory); } // 批量插入 var insertedEntities = await _notificationHistoryRepository.InsertAsync(notificationHistories); // 创建成功结果 for (int i = 0; i < uniqueRecipients.Count; i++) { var recipientId = uniqueRecipients[i]; var entity = insertedEntities[i]; results[recipientId] = SendSystemMessageOutput.CreateSuccess((long)entity.Id, recipientId, currentTime); } Logger.LogInformation("批量发送系统消息完成:总数 {Total},成功 {Success}", input.RecipientPersonnelIds.Count, results.Values.Count(r => r.Success)); } catch (Exception ex) { Logger.LogError(ex, "批量发送系统消息失败"); // 如果批量操作失败,将所有结果设为失败 foreach (var recipientId in uniqueRecipients) { results[recipientId] = SendSystemMessageOutput.CreateFailure(recipientId, $"批量发送失败:{ex.Message}"); } } return BatchSendSystemMessageOutput.Create(results); } /// /// 个性化批量发送系统消息 /// 业务场景:个性化任务分配通知、个性化提醒等需要不同内容的场景 /// [HttpPost] public async Task BatchSendPersonalizedSystemMessageAsync(List messageItems) { var results = new Dictionary(); if (messageItems == null || !messageItems.Any()) { Logger.LogWarning("个性化批量发送系统消息:消息项列表为空"); return BatchSendSystemMessageOutput.Create(results); } try { var notificationHistories = new List(); var currentTime = DateTime.Now; var successfulItems = new List(); foreach (var item in messageItems) { try { if (item.RecipientPersonnelId <= 0 || string.IsNullOrWhiteSpace(item.Title) || string.IsNullOrWhiteSpace(item.Content)) { results[item.RecipientPersonnelId] = SendSystemMessageOutput.CreateFailure( item.RecipientPersonnelId, "消息项无效:收件人ID、标题或内容为空"); continue; } // 渲染模板变量(如果有) var title = item.Title; var content = item.Content; if (item.Variables != null && item.Variables.Any()) { var titleResult = await _templateService.RenderTemplateAsync(new RenderTemplateInput { Template = item.Title, Variables = item.Variables }); var contentResult = await _templateService.RenderTemplateAsync(new RenderTemplateInput { Template = item.Content, Variables = item.Variables }); title = titleResult.RenderedContent; content = contentResult.RenderedContent; } // 序列化操作按钮 var actionsJson = item.Actions != null && item.Actions.Any() ? System.Text.Json.JsonSerializer.Serialize(item.Actions) : ""; var notificationHistory = new NotificationHistoryEntity { NotificationSettingId = 1, // 临时设置,实际应该从配置获取 NotificationType = (int)NotificationTypeEnum.SystemMessage, RecipientPersonnelId = item.RecipientPersonnelId, NotificationTitle = title, NotificationContent = content, BusinessType = item.BusinessType, BusinessId = item.BusinessId, SendStatus = (int)NotificationStatusEnum.Success, ActualSendTime = currentTime, PlannedSendTime = currentTime, BusinessData = actionsJson, CreatedTime = currentTime }; notificationHistories.Add(notificationHistory); successfulItems.Add(item); } catch (Exception ex) { Logger.LogError(ex, "处理个性化消息项失败:收件人 {RecipientId}", item.RecipientPersonnelId); results[item.RecipientPersonnelId] = SendSystemMessageOutput.CreateFailure( item.RecipientPersonnelId, $"处理失败:{ex.Message}"); } } // 批量插入成功的记录 if (notificationHistories.Any()) { var insertedEntities = await _notificationHistoryRepository.InsertAsync(notificationHistories); // 创建成功结果 for (int i = 0; i < successfulItems.Count; i++) { var item = successfulItems[i]; var entity = insertedEntities[i]; results[item.RecipientPersonnelId] = SendSystemMessageOutput.CreateSuccess( (long)entity.Id, item.RecipientPersonnelId, currentTime); } } Logger.LogInformation("个性化批量发送系统消息完成:总数 {Total},成功 {Success},失败 {Failed}", messageItems.Count, results.Values.Count(r => r.Success), results.Values.Count(r => !r.Success)); } catch (Exception ex) { Logger.LogError(ex, "个性化批量发送系统消息失败"); // 如果批量操作失败,将所有结果设为失败 foreach (var item in messageItems) { if (!results.ContainsKey(item.RecipientPersonnelId)) { results[item.RecipientPersonnelId] = SendSystemMessageOutput.CreateFailure( item.RecipientPersonnelId, $"批量操作失败:{ex.Message}"); } } } return BatchSendSystemMessageOutput.Create(results); } #endregion #region 消息模板 /// /// 使用模板发送系统消息 /// 业务场景:标准化通知,如任务分配模板、维护提醒模板等 /// [HttpPost] public async Task SendSystemMessageByTemplateAsync(SendSystemMessageByTemplateInput input) { try { // 渲染模板 var titleResult = await _templateService.RenderTemplateAsync(new RenderTemplateInput { Template = input.TitleTemplate, Variables = input.Variables }); var contentResult = await _templateService.RenderTemplateAsync(new RenderTemplateInput { Template = input.ContentTemplate, Variables = input.Variables }); var title = titleResult.RenderedContent; var content = contentResult.RenderedContent; // 创建发送消息的输入模型 var sendInput = new SendSystemMessageInput { RecipientPersonnelId = input.RecipientPersonnelId, Title = title, Content = content, MessageType = input.MessageType, BusinessType = input.BusinessType, BusinessId = input.BusinessId }; // 发送消息 return await SendSystemMessageAsync(sendInput); } catch (Exception ex) { Logger.LogError(ex, "使用模板发送系统消息失败:收件人 {RecipientId}", input.RecipientPersonnelId); return SendSystemMessageOutput.CreateFailure(input.RecipientPersonnelId, $"模板发送失败:{ex.Message}"); } } #endregion #region 消息管理 /// /// 标记消息为已读 /// 业务场景:用户查看消息后更新状态,用于消息中心的已读/未读状态管理 /// [HttpPost] public async Task MarkMessageAsReadAsync(long messageId, long recipientPersonnelId) { try { var message = await _notificationHistoryRepository .Where(x => x.Id == messageId && x.RecipientPersonnelId == recipientPersonnelId && !x.IsDeleted) .FirstAsync(); if (message == null) { Logger.LogWarning("消息不存在或已被删除:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return SystemMessageStatusOutput.CreateFailure(recipientPersonnelId, "消息不存在或已被删除"); } // 系统消息的已读状态通过SendStatus管理,这里暂时不做处理 // 实际项目中可能需要扩展实体或使用其他方式管理已读状态 // 这里更新ModifiedTime表示已读 message.ModifiedTime = DateTime.Now; await _notificationHistoryRepository.UpdateAsync(message); Logger.LogDebug("消息标记为已读:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return SystemMessageStatusOutput.CreateSuccess(recipientPersonnelId, 1, "消息已标记为已读"); } catch (Exception ex) { Logger.LogError(ex, "标记消息为已读失败:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return SystemMessageStatusOutput.CreateFailure(recipientPersonnelId, $"标记为已读失败:{ex.Message}"); } } /// /// 批量标记消息为已读 /// 业务场景:用户批量处理消息,提高操作效率 /// [HttpPost] public async Task BatchMarkMessagesAsReadAsync(List messageIds, long recipientPersonnelId) { if (messageIds == null || !messageIds.Any()) { Logger.LogWarning("批量标记消息为已读:消息ID列表为空"); return SystemMessageStatusOutput.CreateFailure(recipientPersonnelId, "消息ID列表为空"); } try { var currentTime = DateTime.Now; // 查询消息 var messages = await _notificationHistoryRepository .Where(x => messageIds.Contains(x.Id) && x.RecipientPersonnelId == recipientPersonnelId && !x.IsDeleted) .ToListAsync(); if (!messages.Any()) { Logger.LogInformation("没有找到需要标记为已读的消息:收件人 {RecipientId}", recipientPersonnelId); return SystemMessageStatusOutput.CreateSuccess(recipientPersonnelId, 0, "没有找到需要标记为已读的消息"); } // 批量更新 foreach (var message in messages) { message.ModifiedTime = currentTime; } await _notificationHistoryRepository.UpdateAsync(messages); Logger.LogInformation("批量标记消息为已读完成:收件人 {RecipientId},更新数量 {Count}", recipientPersonnelId, messages.Count); return SystemMessageStatusOutput.CreateSuccess(recipientPersonnelId, messages.Count, $"成功标记{messages.Count}条消息为已读"); } catch (Exception ex) { Logger.LogError(ex, "批量标记消息为已读失败:收件人 {RecipientId}", recipientPersonnelId); return SystemMessageStatusOutput.CreateFailure(recipientPersonnelId, $"批量标记为已读失败:{ex.Message}"); } } /// /// 删除消息 /// 业务场景:用户清理不需要的消息,实现软删除以保留审计记录 /// [HttpPost] public async Task DeleteMessageAsync(long messageId, long recipientPersonnelId) { try { var message = await _notificationHistoryRepository .Where(x => x.Id == messageId && x.RecipientPersonnelId == recipientPersonnelId && !x.IsDeleted) .FirstAsync(); if (message == null) { Logger.LogWarning("消息不存在或已被删除:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return SystemMessageStatusOutput.CreateFailure(recipientPersonnelId, "消息不存在或已被删除"); } // 软删除 message.IsDeleted = true; message.ModifiedTime = DateTime.Now; await _notificationHistoryRepository.UpdateAsync(message); Logger.LogInformation("消息删除成功:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return SystemMessageStatusOutput.CreateSuccess(recipientPersonnelId, 1, "消息删除成功"); } catch (Exception ex) { Logger.LogError(ex, "删除消息失败:消息ID {MessageId},收件人 {RecipientId}", messageId, recipientPersonnelId); return SystemMessageStatusOutput.CreateFailure(recipientPersonnelId, $"删除消息失败:{ex.Message}"); } } /// /// 获取用户未读消息数量 /// 业务场景:消息中心红点提示、导航栏未读消息数显示 /// public async Task GetUnreadMessageCountAsync(long recipientPersonnelId) { try { var count = await _notificationHistoryRepository .Where(x => x.RecipientPersonnelId == recipientPersonnelId && x.NotificationType == (int)NotificationTypeEnum.SystemMessage && !x.IsDeleted) .CountAsync(); return (int)count; } catch (Exception ex) { Logger.LogError(ex, "获取用户未读消息数量失败:收件人 {RecipientId}", recipientPersonnelId); return 0; } } #endregion #region 私有辅助方法 // 注:原有的消息类型转换方法已移除,因为实体结构调整后不再需要 #endregion }