using AutoMapper; using IM.Commons; using MessageService.Domain.Enums; using MessageService.Domain.IReposities; using MessageService.Domain.KeyObjects; using MessageService.Domain.Tools; using MessageService.WebApi.Application.Dtos; using MessageService.WebApi.Application.IntegrationServices; namespace MessageService.WebApi.Application.Message { public class MessageService { private readonly IMessageReposity reposity; private readonly IConversationReposity conversationReposity; private readonly IMapper mapper; private readonly IGroupMemberIntegrationService memberService; private readonly IContactIntegrationService contactService; private readonly SquenceService squenceService; private readonly IM.InitCommon.Management.RuntimePolicy runtime; public MessageService(IMessageReposity reposity, IConversationReposity conversationReposity, IMapper mapper, IGroupMemberIntegrationService memberService, IContactIntegrationService contactService, SquenceService squenceService, IM.InitCommon.Management.RuntimePolicy runtime) { this.reposity = reposity; this.conversationReposity = conversationReposity; this.mapper = mapper; this.memberService = memberService; this.contactService = contactService; this.squenceService = squenceService; this.runtime = runtime; } public async Task> SendMsgAsync(SendMsgCommand command) { if (runtime.Current.TextLimit > 0 && command.MsgType == MessageType.Text && command.Text?.Length > runtime.Current.TextLimit) return Result.Fail(ResultCode.PARAMETER_ERROR, "文本超过平台长度限制"); if (command.ChatType == Domain.Enums.ChatType.PRIVATE) { bool passed = await contactService.CheckContactAsync(command.SenderId, command.TargetId); if (!passed) return Result.Fail(ResultCode.FRIEND_RELATION_NOT_FOUND); } else { bool passed = await memberService.CheckGroupMemberAsync(command.SenderId, command.TargetId); if (!passed) return Result.Fail(ResultCode.NO_GROUP_PERMISSION); } var ctx = new MessageCreateContext(command.ChatType, command.ClientMsgId, command.SenderId, command.TargetId); var streamKey = command.ChatType == ChatType.GROUP ? StreamKeyBuilder.Group(ctx.TargetId) : StreamKeyBuilder.Private(ctx.SenderId, ctx.TargetId); long sequenceId = await squenceService.GetNextSquenceIdAsync(streamKey); Domain.Entities.Message message = command.MsgType switch { MessageType.Text => Domain.Entities.Message.BuildTxt(ctx, command.Text!, sequenceId), MessageType.Image => Domain.Entities.Message.BuildImg(ctx, command.Url, command.Width ?? 0, command.Height ?? 0, command.Thumb!, sequenceId, command.FileId), MessageType.Video => Domain.Entities.Message.BuildVideo(ctx, command.Url, command.Width ?? 0, command.Height ?? 0, command.Thumb!, sequenceId, command.FileId), MessageType.Voice => Domain.Entities.Message.BuildVoice(ctx, command.Url, command.Duration ?? 0, sequenceId, command.FileId), MessageType.File => Domain.Entities.Message.BuildFile(ctx, command.FileId!.Value, command.Url, command.FileName!, command.FileSize!.Value, command.FileFormat!, sequenceId), _ => null }; if (message == null) { return Result.Fail(ResultCode.UNSUPPORTED_MESSAGE_TYPE); } if (command.QuoteMessageId.HasValue) { // 2. 处理引用逻辑(如果传了 QuoteMessageId) QuoteInfo? quote = null; var originMsg = await reposity.FindByIdAsync(command.QuoteMessageId.Value); if (originMsg != null) { quote = new QuoteInfo { MessageId = originMsg.Id, SenderId = originMsg.SenderId, SenderName = "未知昵称", // 这里建议从缓存或DB获取发送者昵称 MessageType = originMsg.MsgType, Preview = originMsg.Content.Fallback }; } message.WithQuote(quote); } message.Send(); reposity.Create(message); return Result.Success(mapper.Map(message)); } public async Task> WithDrawMsgAsync(Guid msgId, Guid senderId) { var msg = await reposity.FindByIdAsync(msgId); if (msg == null || msg.SenderId != senderId) { return Result.Fail(ResultCode.MESSAGE_NOT_FOUND); } if (runtime.Current.RecallMinutes > 0 && DateTimeOffset.UtcNow - msg.CreationTime > TimeSpan.FromMinutes(runtime.Current.RecallMinutes)) return Result.Fail(ResultCode.PERMISSION_DENIED, "已超过平台撤回时限"); msg.Withdraw(); return Result.Success(); } public async Task> GetMessagesAsync(GetMessageCommand command, CancellationToken cancellationToken = default) { if (command.direction is not 0 and not 1 || command.limit is < 1 or > 100) { return Result.Fail(ResultCode.PARAMETER_ERROR); } var conversation = await conversationReposity.FindByIdAsync(command.conversationId, cancellationToken); if(conversation is null || conversation.UserId != command.userId) { return Result.Fail(ResultCode.PERMISSION_DENIED); } var messages = await reposity.GetAsync(conversation.StreamKey, command.cusor, command.direction, command.limit, cancellationToken); return Result.Success(new GetMessagesResponse(mapper.Map>(messages.messages.ToList()),messages.hasMore)); } public async Task> SearchMessagesAsync( SearchMessageCommand command, CancellationToken cancellationToken = default) { var keyword = command.Keyword?.Trim() ?? string.Empty; if (keyword.Length is < 1 or > 50 || command.Limit is < 1 or > 50) { return Result.Fail(ResultCode.PARAMETER_ERROR); } var conversation = await conversationReposity.FindByIdAsync(command.ConversationId, cancellationToken); if (conversation is null || conversation.UserId != command.UserId) { return Result.Fail(ResultCode.PERMISSION_DENIED); } var messages = await reposity.SearchAsync( conversation.StreamKey, keyword, command.Cursor, command.Limit, cancellationToken); return Result.Success(new GetMessagesResponse( mapper.Map>(messages.messages.ToList()), messages.hasMore)); } } }