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
}