using F9WebAiTest.Api.Hubs; using F9WebAiTest.Application.Dtos.Notice; using F9WebAiTest.Application.Services.Notice; using Microsoft.AspNetCore.SignalR; namespace F9WebAiTest.Api.Services; /// /// 消息实时推送器实现:基于 SignalR 的 IHubContext 直接推送。 /// 无后台队列、无轮询——消息创建时同步完成投递与推送。 /// /// 在线判定:OnlineUserRegistry(Hub 连接/断开时登记的内存表)。 /// 之所以需要显式判定而不能依赖推送异常:SignalR 对"无连接的 Clients.User 调用"是静默成功的, /// 不判定的话离线用户会被误记为推送成功。 /// /// 用户标识:由 NotificationUserIdProvider 统一取 JWT 的 "id" 声明, /// 因此 Clients.User(userId) 可精准命中该用户的所有在线连接(支持多端同时在线)。 /// public class NotificationPusher : INotificationPusher { private readonly IHubContext _hubContext; public NotificationPusher(IHubContext hubContext) { _hubContext = hubContext; } /// public bool IsOnline(long userId) => OnlineUserRegistry.IsOnline(userId); /// public async Task PushNewMessageAsync(long userId, NotificationMessageDto message, CancellationToken ct = default) { // 离线用户:不推送(消息已持久化,上线后拉取兜底),返回 false 由调用方标记 IsOffline if (!OnlineUserRegistry.IsOnline(userId)) return false; await _hubContext.Clients.User(userId.ToString()).SendAsync("ReceiveNotification", message, ct); return true; } /// public Task PushUnreadCountAsync(long userId, int unreadCount, int todoCount, CancellationToken ct = default) => _hubContext.Clients.User(userId.ToString()) .SendAsync("UnreadCountChanged", new { unreadCount, todoCount }, ct); }