Developer 058d8edffa 添加通知系统和工作任务分配功能
- 新增通知系统完整架构,包含通知设置、历史记录、任务管理等核心功能
- 实现工作任务分配服务,支持人员和设备的智能分配
- 添加人员分组管理功能,支持灵活的通知目标配置
- 完善相关枚举定义和数据传输对象

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-05 08:34:01 +08:00

580 lines
22 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.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;
/// <summary>
/// 系统消息服务实现
/// 决策点1基础通知方式 - 系统消息通知
/// 业务思考:系统消息是内部通知的核心方式,需要支持实时推送、状态管理、操作交互等功能
/// 设计原则:
/// 1. 消息持久化存储,确保不丢失
/// 2. 支持消息状态管理(未读/已读/已删除)
/// 3. 支持操作按钮,实现交互式消息
/// 4. 支持批量操作,提高效率
/// 5. 支持模板化消息,统一格式
/// </summary>
public class SystemMessageService : ISystemMessageService
{
private readonly ILogger<SystemMessageService> _logger;
private readonly INotificationTemplateService _templateService;
private readonly IBaseRepository<NotificationHistoryEntity> _notificationHistoryRepository;
private readonly IUnitOfWorkManager _uowManager;
public SystemMessageService(
ILogger<SystemMessageService> logger,
INotificationTemplateService templateService,
IBaseRepository<NotificationHistoryEntity> 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
/// <summary>
/// 发送系统消息
/// 业务场景:工作任务分配、设备状态变更、排班通知等
/// </summary>
public async Task<bool> 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;
}
}
/// <summary>
/// 发送带操作按钮的系统消息
/// 业务场景:任务确认、审批流程、操作确认等交互式场景
/// </summary>
public async Task<bool> SendSystemMessageWithActionsAsync(
long recipientPersonnelId,
string title,
string content,
List<SystemMessageAction> 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
/// <summary>
/// 批量发送系统消息
/// 业务场景:排班变更通知、紧急通知、系统维护通知等需要群发的场景
/// </summary>
public async Task<Dictionary<long, bool>> BatchSendSystemMessageAsync(
List<long> recipientPersonnelIds,
string title,
string content,
SystemMessageTypeEnum messageType = SystemMessageTypeEnum.Info,
string businessType = "",
long? businessId = null)
{
var results = new Dictionary<long, bool>();
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<NotificationHistoryEntity>();
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;
}
/// <summary>
/// 个性化批量发送系统消息
/// 业务场景:个性化任务分配通知、个性化提醒等需要不同内容的场景
/// </summary>
public async Task<Dictionary<long, bool>> BatchSendPersonalizedSystemMessageAsync(List<SystemMessageItem> messageItems)
{
var results = new Dictionary<long, bool>();
if (messageItems == null || !messageItems.Any())
{
_logger.LogWarning("个性化批量发送系统消息:消息项列表为空");
return results;
}
try
{
using var uow = _uowManager.Begin();
var notificationHistories = new List<NotificationHistoryEntity>();
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
/// <summary>
/// 使用模板发送系统消息
/// 业务场景:标准化通知,如任务分配模板、维护提醒模板等
/// </summary>
public async Task<bool> SendSystemMessageByTemplateAsync(
long recipientPersonnelId,
string titleTemplate,
string contentTemplate,
Dictionary<string, string> 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
/// <summary>
/// 标记消息为已读
/// 业务场景:用户查看消息后更新状态,用于消息中心的已读/未读状态管理
/// </summary>
public async Task<bool> 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;
}
}
/// <summary>
/// 批量标记消息为已读
/// 业务场景:用户批量处理消息,提高操作效率
/// </summary>
public async Task<int> BatchMarkMessagesAsReadAsync(List<long> 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;
}
}
/// <summary>
/// 删除消息
/// 业务场景:用户清理不需要的消息,实现软删除以保留审计记录
/// </summary>
public async Task<bool> 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;
}
}
/// <summary>
/// 获取用户未读消息数量
/// 业务场景:消息中心红点提示、导航栏未读消息数显示
/// </summary>
public async Task<int> 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
/// <summary>
/// 将SystemMessageTypeEnum转换为通知历史实体的MessageType字段
/// </summary>
private string GetNotificationMessageType(SystemMessageTypeEnum messageType)
{
return messageType switch
{
SystemMessageTypeEnum.Info => "信息",
SystemMessageTypeEnum.Success => "成功",
SystemMessageTypeEnum.Warning => "警告",
SystemMessageTypeEnum.Error => "错误",
SystemMessageTypeEnum.Urgent => "紧急",
_ => "信息"
};
}
#endregion
}