first commit

This commit is contained in:
yanpeng200212
2026-10-08 10:43:59 +08:00
commit 8430ba0452
288 changed files with 29595 additions and 0 deletions
@@ -0,0 +1,22 @@
using F9WebAiTest.Api.Hubs;
using F9WebAiTest.Application.Services;
using MediatR;
using Microsoft.AspNetCore.SignalR;
namespace F9WebAiTest.Api.Services;
/// <summary>
/// 业务状态变更事件的 SignalR 推送处理器。
/// 业务服务发布 BusinessChangedEvent 后,推送给所有在线客户端,
/// 各端监听 "BusinessChanged" 后刷新已打开的相关页面,实现多端实时同步。
/// </summary>
public class BusinessChangedEventHandler : INotificationHandler<BusinessChangedEvent>
{
private readonly IHubContext<NotificationHub> _hubContext;
public BusinessChangedEventHandler(IHubContext<NotificationHub> hubContext)
=> _hubContext = hubContext;
public Task Handle(BusinessChangedEvent e, CancellationToken ct)
=> _hubContext.Clients.All.SendAsync("BusinessChanged", e, ct);
}
@@ -0,0 +1,10 @@
// 已移除:消息投递不再使用后台服务(BackgroundService)与轮询。
//
// 现在的投递方式(即时推送):
// CreateNotificationCommandHandler(F9WebAiTest.Application/Services/Notice/NotificationMediatR.cs)
// 在消息创建后立即:
// 1. 确定接收人(ForceReceivers 精确送达,否则按消息类型订阅关系广播);
// 2. 写入 NotificationReceiver(MessageId + UserId 幂等);
// 3. 通过 INotificationPusher(实现在 F9WebAiTest.Api/Services/NotificationPusher.cs)经 SignalR 直接推送。
//
// 本文件已无任何代码,可直接删除。
@@ -0,0 +1,45 @@
using F9WebAiTest.Api.Hubs;
using F9WebAiTest.Application.Dtos.Notice;
using F9WebAiTest.Application.Services.Notice;
using Microsoft.AspNetCore.SignalR;
namespace F9WebAiTest.Api.Services;
/// <summary>
/// 消息实时推送器实现:基于 SignalR 的 IHubContext 直接推送。
/// 无后台队列、无轮询——消息创建时同步完成投递与推送。
///
/// 在线判定:OnlineUserRegistry(Hub 连接/断开时登记的内存表)。
/// 之所以需要显式判定而不能依赖推送异常:SignalR 对"无连接的 Clients.User 调用"是静默成功的,
/// 不判定的话离线用户会被误记为推送成功。
///
/// 用户标识:由 NotificationUserIdProvider 统一取 JWT 的 "id" 声明,
/// 因此 Clients.User(userId) 可精准命中该用户的所有在线连接(支持多端同时在线)。
/// </summary>
public class NotificationPusher : INotificationPusher
{
private readonly IHubContext<NotificationHub> _hubContext;
public NotificationPusher(IHubContext<NotificationHub> hubContext)
{
_hubContext = hubContext;
}
/// <inheritdoc />
public bool IsOnline(long userId) => OnlineUserRegistry.IsOnline(userId);
/// <inheritdoc />
public async Task<bool> 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;
}
/// <inheritdoc />
public Task PushUnreadCountAsync(long userId, int unreadCount, int todoCount, CancellationToken ct = default)
=> _hubContext.Clients.User(userId.ToString())
.SendAsync("UnreadCountChanged", new { unreadCount, todoCount }, ct);
}