552 lines
23 KiB
C#
552 lines
23 KiB
C#
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;
|
||
|
||
/// <summary>
|
||
/// 通知服务 系统消息
|
||
/// </summary>
|
||
[DynamicApi(Area = "app")]
|
||
public class SystemMessageService : BaseService, ISystemMessageService, IDynamicApi
|
||
{
|
||
private readonly INotificationTemplateService _templateService;
|
||
private readonly IRepositoryBase<NotificationHistoryEntity> _notificationHistoryRepository;
|
||
|
||
public SystemMessageService(
|
||
INotificationTemplateService templateService,
|
||
IRepositoryBase<NotificationHistoryEntity> notificationHistoryRepository)
|
||
{
|
||
_templateService = templateService;
|
||
_notificationHistoryRepository = notificationHistoryRepository;
|
||
}
|
||
|
||
#region 单个消息发送
|
||
|
||
/// <summary>
|
||
/// 发送系统消息
|
||
/// 业务场景:工作任务分配、设备状态变更、排班通知等
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<SendSystemMessageOutput> 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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送带操作按钮的系统消息
|
||
/// 业务场景:任务确认、审批流程、操作确认等交互式场景
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<SendSystemMessageOutput> 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 批量消息发送
|
||
|
||
/// <summary>
|
||
/// 批量发送系统消息
|
||
/// 业务场景:排班变更通知、紧急通知、系统维护通知等需要群发的场景
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<BatchSendSystemMessageOutput> BatchSendSystemMessageAsync(BatchSendSystemMessageInput input)
|
||
{
|
||
var results = new Dictionary<long, SendSystemMessageOutput>();
|
||
|
||
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<NotificationHistoryEntity>();
|
||
// 注释:系统消息类型统一使用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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 个性化批量发送系统消息
|
||
/// 业务场景:个性化任务分配通知、个性化提醒等需要不同内容的场景
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<BatchSendSystemMessageOutput> BatchSendPersonalizedSystemMessageAsync(List<SystemMessageItem> messageItems)
|
||
{
|
||
var results = new Dictionary<long, SendSystemMessageOutput>();
|
||
|
||
if (messageItems == null || !messageItems.Any())
|
||
{
|
||
Logger.LogWarning("个性化批量发送系统消息:消息项列表为空");
|
||
return BatchSendSystemMessageOutput.Create(results);
|
||
}
|
||
|
||
try
|
||
{
|
||
var notificationHistories = new List<NotificationHistoryEntity>();
|
||
var currentTime = DateTime.Now;
|
||
var successfulItems = new List<SystemMessageItem>();
|
||
|
||
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 消息模板
|
||
|
||
/// <summary>
|
||
/// 使用模板发送系统消息
|
||
/// 业务场景:标准化通知,如任务分配模板、维护提醒模板等
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<SendSystemMessageOutput> 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 消息管理
|
||
|
||
/// <summary>
|
||
/// 标记消息为已读
|
||
/// 业务场景:用户查看消息后更新状态,用于消息中心的已读/未读状态管理
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<SystemMessageStatusOutput> 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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量标记消息为已读
|
||
/// 业务场景:用户批量处理消息,提高操作效率
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<SystemMessageStatusOutput> BatchMarkMessagesAsReadAsync(List<long> 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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除消息
|
||
/// 业务场景:用户清理不需要的消息,实现软删除以保留审计记录
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<SystemMessageStatusOutput> 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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户未读消息数量
|
||
/// 业务场景:消息中心红点提示、导航栏未读消息数显示
|
||
/// </summary>
|
||
public async Task<int> 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
|
||
} |