build-flow.api/src/modules/im/Application/Chat/Handlers/SearchMessagesQueryHandler.cs
xiangning fb40c47d5b refactor(monitoring): 重构监控模块数据模型为文档模式
- 将 AlertRule 和 ExternalSystem 实体改为使用 JsonData 存储业务字段
- 移除独立的 ExternalSystemCredential 表,凭证信息合并到 ExternalSystem.JsonData
- 添加强类型视图 Data 属性用于访问 JsonData 中的业务数据
- 更新数据库配置将字段存储改为 jsonb 类型并调整索引策略
- 重构告警评估器使用 JsonData 强类型视图读取规则数据
- 更新连接器凭证解析器从 JsonData 中获取凭证信息
- 在 appsettings.json 中添加 monitoring 模块配置
- 将 monitoring 模块集成到主应用程序中并注册服务
- 更新相关处理器使用新的数据模型结构
2026-07-17 12:19:24 +08:00

109 lines
3.7 KiB
C#

using System.Text.RegularExpressions;
using BuildFlow.Shared.Domain.Exceptions;
using IM.Application.Chat.DTOs;
using IM.Application.Chat.Queries;
using IM.Domain.Common;
using IM.Infrastructure.Mongo;
using IM.Infrastructure.Persistence;
using MediatR;
using MongoDB.Bson;
using MongoDB.Entities;
namespace IM.Application.Chat.Handlers;
/// <summary>
/// 历史消息搜索处理器。基于 MongoDB 对消息正文、发送者名称和文件名进行模糊检索。
/// </summary>
public class SearchMessagesQueryHandler(
ImDbContext db,
IUserInfoService userInfoService) : IRequestHandler<SearchMessagesQuery, MessageHistoryDto>
{
public async Task<MessageHistoryDto> Handle(SearchMessagesQuery request, CancellationToken ct)
{
var isParticipant = await db.ConversationParticipants
.AnyAsync(p => p.ConversationId == request.ConversationId && p.UserId == request.UserId, ct);
if (!isParticipant)
{
throw new UnauthorizedException("无权搜索该会话的消息");
}
var keyword = request.Keyword.Trim();
if (keyword.Length == 0)
{
return new MessageHistoryDto([], false);
}
var escaped = Regex.Escape(keyword);
var regex = new BsonRegularExpression(escaped, "i");
var messages = await DB.Default.Find<MessageDocument>()
.Match(f => f.And(
f.Eq(m => m.ConversationId, request.ConversationId),
f.Or(
f.Regex(m => m.Content, regex),
f.Regex(m => m.SenderName, regex),
f.Regex("Metadata.FileName", regex))))
.Sort(m => m.CreatedAt, Order.Descending)
.Limit(request.Limit)
.ExecuteAsync(ct);
var dtos = await ToMessageDtosAsync(messages, ct);
return new MessageHistoryDto(dtos, false);
}
private async Task<List<MessageDto>> ToMessageDtosAsync(List<MessageDocument> messages, CancellationToken ct)
{
var senderIds = messages
.Where(m => string.IsNullOrEmpty(m.SenderAvatar))
.Select(m => m.SenderId)
.Distinct()
.ToList();
var users = senderIds.Count > 0
? await userInfoService.GetUsersAsync(senderIds, ct)
: new Dictionary<string, (string Username, string? Avatar)>();
return messages.Select(m =>
{
var avatar = m.SenderAvatar;
if (string.IsNullOrEmpty(avatar))
{
avatar = users.GetValueOrDefault(m.SenderId.ToString()).Avatar;
}
return new MessageDto(
m.ID,
m.ConversationId,
m.SenderId,
m.SenderName,
avatar,
m.ContentType,
m.Content,
m.Metadata != null ? new MessageMetadataDto(
m.Metadata.FileName,
m.Metadata.FileSize,
m.Metadata.FileUrl,
m.Metadata.ImageUrl,
m.Metadata.ImageWidth,
m.Metadata.ImageHeight,
m.Metadata.EmojiCode)
{
BucketName = m.Metadata.BucketName,
ObjectKey = m.Metadata.ObjectKey,
ContentType = m.Metadata.ContentType
} : null,
m.ReplyToId,
m.CreatedAt,
m.MentionUserIds,
m.MentionAll,
m.IsRecalled,
m.RecalledAt,
m.RecalledBy,
m.ForwardFromMessageId,
m.ForwardFromSenderName,
m.ForwardFromConversationId);
}).ToList();
}
}