166 lines
7.4 KiB
C#
166 lines
7.4 KiB
C#
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<Result<MessageResponse>> SendMsgAsync(SendMsgCommand command)
|
||
{
|
||
if (runtime.Current.TextLimit > 0 && command.MsgType == MessageType.Text && command.Text?.Length > runtime.Current.TextLimit)
|
||
return Result.Fail<MessageResponse>(ResultCode.PARAMETER_ERROR, "文本超过平台长度限制");
|
||
if (command.ChatType == Domain.Enums.ChatType.PRIVATE)
|
||
{
|
||
bool passed = await contactService.CheckContactAsync(command.SenderId, command.TargetId);
|
||
if (!passed)
|
||
return Result.Fail<MessageResponse>(ResultCode.FRIEND_RELATION_NOT_FOUND);
|
||
}
|
||
else
|
||
{
|
||
bool passed = await memberService.CheckGroupMemberAsync(command.SenderId, command.TargetId);
|
||
if (!passed)
|
||
return Result.Fail<MessageResponse>(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<MessageResponse>(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<MessageResponse>(message));
|
||
|
||
}
|
||
|
||
public async Task<Result<object>> WithDrawMsgAsync(Guid msgId, Guid senderId)
|
||
{
|
||
var msg = await reposity.FindByIdAsync(msgId);
|
||
if (msg == null || msg.SenderId != senderId)
|
||
{
|
||
return Result.Fail<object>(ResultCode.MESSAGE_NOT_FOUND);
|
||
}
|
||
|
||
if (runtime.Current.RecallMinutes > 0 && DateTimeOffset.UtcNow - msg.CreationTime > TimeSpan.FromMinutes(runtime.Current.RecallMinutes))
|
||
return Result.Fail<object>(ResultCode.PERMISSION_DENIED, "已超过平台撤回时限");
|
||
msg.Withdraw();
|
||
|
||
return Result.Success();
|
||
}
|
||
|
||
public async Task<Result<GetMessagesResponse>> GetMessagesAsync(GetMessageCommand command, CancellationToken cancellationToken = default)
|
||
{
|
||
if (command.direction is not 0 and not 1 || command.limit is < 1 or > 100)
|
||
{
|
||
return Result.Fail<GetMessagesResponse>(ResultCode.PARAMETER_ERROR);
|
||
}
|
||
|
||
var conversation = await conversationReposity.FindByIdAsync(command.conversationId, cancellationToken);
|
||
|
||
if(conversation is null || conversation.UserId != command.userId)
|
||
{
|
||
return Result.Fail<GetMessagesResponse>(ResultCode.PERMISSION_DENIED);
|
||
}
|
||
|
||
|
||
var messages = await reposity.GetAsync(conversation.StreamKey, command.cusor, command.direction, command.limit, cancellationToken);
|
||
|
||
return Result.Success(new GetMessagesResponse(mapper.Map<List<MessageResponse>>(messages.messages.ToList()),messages.hasMore));
|
||
}
|
||
|
||
public async Task<Result<GetMessagesResponse>> 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<GetMessagesResponse>(ResultCode.PARAMETER_ERROR);
|
||
}
|
||
|
||
var conversation = await conversationReposity.FindByIdAsync(command.ConversationId, cancellationToken);
|
||
if (conversation is null || conversation.UserId != command.UserId)
|
||
{
|
||
return Result.Fail<GetMessagesResponse>(ResultCode.PERMISSION_DENIED);
|
||
}
|
||
|
||
var messages = await reposity.SearchAsync(
|
||
conversation.StreamKey,
|
||
keyword,
|
||
command.Cursor,
|
||
command.Limit,
|
||
cancellationToken);
|
||
|
||
return Result.Success(new GetMessagesResponse(
|
||
mapper.Map<List<MessageResponse>>(messages.messages.ToList()),
|
||
messages.hasMore));
|
||
}
|
||
}
|
||
}
|