116 lines
3.8 KiB
C#
116 lines
3.8 KiB
C#
using System.Collections.Concurrent;
|
||
using GozareshgirProgramManager.Application._Common.Constants;
|
||
using GozareshgirProgramManager.Application.Interfaces;
|
||
using GozareshgirProgramManager.Domain.ProjectAgg.Entities;
|
||
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
|
||
using Microsoft.AspNetCore.SignalR;
|
||
using ServiceHost.Hubs.ProgramManager;
|
||
|
||
namespace ServiceHost.Notifications.ProgramManager;
|
||
|
||
public class SignalRBoardNotificationPublisher : IBoardNotificationPublisher
|
||
{
|
||
private readonly IHubContext<ProjectBoardHub> _hubContext;
|
||
|
||
public SignalRBoardNotificationPublisher(IHubContext<ProjectBoardHub> hubContext)
|
||
{
|
||
_hubContext = hubContext;
|
||
}
|
||
|
||
public Task NotifyTaskStatusChanged(long userId, TaskSectionStatus oldStatus,
|
||
TaskSectionStatus newStatus, Guid sectionId)
|
||
{
|
||
var payload = new
|
||
{
|
||
UserId = userId,
|
||
OldStatus = oldStatus,
|
||
NewStatus = newStatus,
|
||
SectionId = sectionId
|
||
};
|
||
|
||
// گروه task-specific (برای همه assigneeهای واقعی)
|
||
var taskGroup = $"pm.task:{sectionId}";
|
||
|
||
// گروه permission-based (مثلاً برای Admin ها)
|
||
var permissionGroup = $"pm.perm:{ProgramManagerPermissionCode.Board.All.ViewAll}";
|
||
|
||
// ارسال به هر دو گروه؛ SignalR خودش duplicate connection رو هندل میکنه
|
||
return _hubContext.Clients.Groups(taskGroup, permissionGroup)
|
||
.SendAsync("ReceiveProjectStatusChanged", payload);
|
||
}
|
||
|
||
public async Task NotifyTaskCreated(TaskSection taskSection)
|
||
{
|
||
var sectionId = taskSection.Id;
|
||
var userId = taskSection.CurrentAssignedUserId;
|
||
var taskGroup = $"pm.task:{taskSection.Id}";
|
||
var adminGroup = $"pm.perm:{ProgramManagerPermissionCode.Board.All.ViewAll}";
|
||
|
||
// Add assignee connections to task group automatically
|
||
await AddAssigneeConnectionsToTaskGroup(userId, sectionId);
|
||
|
||
|
||
var payload = new
|
||
{
|
||
TaskId = sectionId,
|
||
AssignedTo = userId,
|
||
|
||
};
|
||
|
||
await _hubContext.Clients.Groups(taskGroup, adminGroup)
|
||
.SendAsync("TaskCreated", payload);
|
||
}
|
||
|
||
public Task NotifyTaskAssigned(long userId, Guid sectionId)
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
|
||
public Task NotifyTaskRemoved(long userId, Guid sectionId)
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
|
||
|
||
private async Task AddAssigneeConnectionsToTaskGroup(long userId, Guid taskId)
|
||
{
|
||
// Retrieve all connectionIds that have this claim
|
||
var connections = ConnectionMapping.GetConnectionsByClaim("pm.userId", userId.ToString());
|
||
|
||
var taskGroup = $"pm.task:{taskId}";
|
||
|
||
var joinTasks = connections
|
||
.Select(connId => _hubContext.Groups.AddToGroupAsync(connId, taskGroup));
|
||
|
||
await Task.WhenAll(joinTasks);
|
||
}
|
||
}
|
||
|
||
public static class ConnectionMapping
|
||
{
|
||
private static readonly ConcurrentDictionary<string, HashSet<string>> _map = new();
|
||
|
||
public static void Add(string claimType, string claimValue, string connectionId)
|
||
{
|
||
var key = $"{claimType}:{claimValue}";
|
||
_map.AddOrUpdate(key,
|
||
_ => new HashSet<string> { connectionId },
|
||
(_, set) => { set.Add(connectionId); return set; });
|
||
}
|
||
|
||
public static void Remove(string claimType, string claimValue, string connectionId)
|
||
{
|
||
var key = $"{claimType}:{claimValue}";
|
||
if (_map.TryGetValue(key, out var set))
|
||
{
|
||
set.Remove(connectionId);
|
||
if (!set.Any()) _map.TryRemove(key, out _);
|
||
}
|
||
}
|
||
|
||
public static IEnumerable<string> GetConnectionsByClaim(string claimType, string claimValue)
|
||
{
|
||
var key = $"{claimType}:{claimValue}";
|
||
return _map.TryGetValue(key, out var set) ? set : Enumerable.Empty<string>();
|
||
}
|
||
} |