build-flow.api/src/modules/im/Application/Chat/Handlers/GetUnreadCountQueryHandler.cs
xiangning 1aeaf7b1a6 feat(platform): 实现菜单管理功能并完善权限控制
- 将菜单实体的 ID 类型从 Guid 改为 long,并配置为自增主键
- 添加菜单创建、删除、树形结构构建等完整的 CRUD 操作支持
- 为文件上传相关端点添加 file:upload 权限验证
- 为文件操作端点添加 file:read 和 file:write 权限控制
- 为通知功能添加 notification:read 权限限制
- 实现 BuildFlow 按钮执行的权限检查机制
- 在 IM 模块中集成 Redis 未读计数器优化性能
- 更新用户主页路径从 /workspace 到 /dashboard/workspace
- 添加 SignalR Hub 授权和 WebSocket 连接安全保护
- 新增平台测试项目并完善按钮权限测试用例
2026-07-04 17:21:07 +08:00

51 lines
2.0 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 IM.Application.Chat.DTOs;
using IM.Application.Chat.Queries;
using IM.Domain.Common;
using IM.Infrastructure.Persistence;
using MediatR;
namespace IM.Application.Chat.Handlers;
/// <summary>
/// 未读计数查询。读扩散热路径:Redis 优先,未命中回退 PostgreSQL 冷备。
/// </summary>
/// <remarks>
/// 兼容策略:Redis 命中的会话用 Redis 值(权威);Redis 未命中的会话(老数据/降级期间写入)回退 PG 的 UnreadCount。
/// 二者合并去重,最终结果保证不丢未读。
/// </remarks>
public class GetUnreadCountQueryHandler(ImDbContext db, IUnreadCounter unreadCounter) : IRequestHandler<GetUnreadCountQuery, UnreadCountsDto>
{
public async Task<UnreadCountsDto> Handle(GetUnreadCountQuery request, CancellationToken ct)
{
// 先从 PG 取用户参与的所有会话及其冷备未读数(也作为 Redis 全故障时的兜底数据源)。
// 注意不能用 p.UnreadCount > 0 预过滤:发送链路已把未读累加移到 Redis,PG 冷备可能仍为 0。
var participants = await db.ConversationParticipants
.Where(p => p.UserId == request.UserId)
.Select(p => new { p.ConversationId, p.UnreadCount })
.ToListAsync(ct);
if (participants.Count == 0)
{
return new UnreadCountsDto([], 0);
}
var pgUnread = participants.ToDictionary(p => p.ConversationId, p => p.UnreadCount);
// 批量查 Redis 热数据
var redisUnread = await unreadCounter.GetByUserAsync(
request.UserId,
pgUnread.Keys,
ct);
// 合并:Redis 命中用 Redis 值,未命中回退 PG
var items = pgUnread.Keys
.Select(convId => new UnreadCountDto(
convId,
redisUnread.TryGetValue(convId, out var redisValue) ? redisValue : pgUnread[convId]))
.Where(i => i.UnreadCount > 0)
.ToList();
return new UnreadCountsDto(items, items.Sum(i => i.UnreadCount));
}
}