fix: align backend APIs and upload flow
This commit is contained in:
@@ -8,7 +8,7 @@ namespace MessageService.WebApi.Application.Conversation
|
||||
public ConversationMapperConfig()
|
||||
{
|
||||
CreateMap<Domain.Entities.Conversation, ConversationResponse>()
|
||||
.ForMember(dest => dest.DateTime, opt => opt.MapFrom(src => src.ModificationTime))
|
||||
.ForMember(dest => dest.DateTime, opt => opt.MapFrom(src => src.ModificationTime ?? src.CreationTime))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,6 @@ namespace MessageService.WebApi.Application.Dtos
|
||||
/// 最后一条最新消息
|
||||
/// </summary>
|
||||
public string LastMessage { get; set; }
|
||||
public DateTime DateTime { get; set; }
|
||||
public DateTimeOffset DateTime { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using MessageService.Infrastructure;
|
||||
namespace MessageService.WebApi.Application.EventHandlers
|
||||
{
|
||||
public class ConversationAddHandler : IConsumer<GroupMemberJoinedEvent>,
|
||||
IConsumer<FriendAddedEvent>
|
||||
IConsumer<FriendAddedEvent>, IConsumer<GroupMemberLeftEvent>
|
||||
{
|
||||
|
||||
private readonly IConversationReposity reposity;
|
||||
@@ -21,6 +21,11 @@ namespace MessageService.WebApi.Application.EventHandlers
|
||||
public async Task Consume(ConsumeContext<GroupMemberJoinedEvent> context)
|
||||
{
|
||||
var @event = context.Message;
|
||||
var existing = await reposity.FindByTargetIdAsync(@event.GroupId);
|
||||
if (existing.Any(x => x.UserId == @event.UserId && x.ChatType == Domain.Enums.ChatType.GROUP))
|
||||
{
|
||||
return;
|
||||
}
|
||||
reposity.Create(new Domain.Entities.Conversation(
|
||||
userId: @event.UserId,
|
||||
targetId: @event.GroupId,
|
||||
@@ -32,12 +37,17 @@ namespace MessageService.WebApi.Application.EventHandlers
|
||||
lastMessage: string.Empty
|
||||
));
|
||||
|
||||
await messageDb.SaveChangesAsync();
|
||||
await messageDb.SaveChangesAsync(context.CancellationToken);
|
||||
}
|
||||
|
||||
public async Task Consume(ConsumeContext<FriendAddedEvent> context)
|
||||
{
|
||||
var @event = context.Message;
|
||||
var existing = await reposity.FindByUserIdAsync(@event.OwnerId);
|
||||
if (existing.Any(x => x.TargetId == @event.TargetId && x.ChatType == Domain.Enums.ChatType.PRIVATE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
reposity.Create(new Domain.Entities.Conversation(
|
||||
userId: @event.OwnerId,
|
||||
targetId: @event.TargetId,
|
||||
@@ -49,7 +59,18 @@ namespace MessageService.WebApi.Application.EventHandlers
|
||||
lastMessage: string.Empty
|
||||
));
|
||||
|
||||
await messageDb.SaveChangesAsync();
|
||||
await messageDb.SaveChangesAsync(context.CancellationToken);
|
||||
}
|
||||
|
||||
public async Task Consume(ConsumeContext<GroupMemberLeftEvent> context)
|
||||
{
|
||||
var conversations = await reposity.FindByTargetIdAsync(context.Message.GroupId);
|
||||
foreach (var conversation in conversations.Where(x =>
|
||||
x.UserId == context.Message.UserId && x.ChatType == Domain.Enums.ChatType.GROUP))
|
||||
{
|
||||
conversation.SoftDelete();
|
||||
}
|
||||
await messageDb.SaveChangesAsync(context.CancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,20 +24,24 @@ namespace MessageService.WebApi.Application.EventHandlers
|
||||
{
|
||||
var message = notification.Message;
|
||||
|
||||
if(message.ChatType == Domain.Enums.ChatType.PRIVATE)
|
||||
var conversations = await reposity.FindByStreamKeyAsync(message.StreamKey);
|
||||
foreach (var conversation in conversations)
|
||||
{
|
||||
var list = await reposity.FindByStreamKeyAsync(message.StreamKey);
|
||||
var owner = list.First(x => x.UserId == message.SenderId);
|
||||
var target = list.First(x => x.UserId == message.TargetId);
|
||||
|
||||
owner.Update(message.SequenceId, 0, message.Content.Fallback);
|
||||
target.Update(target.LastReadSequenceId, target.UnreadCount + 1, message.Content.Fallback);
|
||||
|
||||
messageDb.Conversations.UpdateRange(owner,target);
|
||||
await messageDb.SaveChangesAsync(cancellationToken);
|
||||
conversation.UpdateLastMessage(message.Content.Fallback);
|
||||
if (conversation.UserId == message.SenderId)
|
||||
{
|
||||
conversation.SetLastReadSequence(message.SequenceId);
|
||||
}
|
||||
else
|
||||
{
|
||||
conversation.IncrementUnread();
|
||||
}
|
||||
}
|
||||
|
||||
await endpoint.Publish(message.ToIntegrationEvent());
|
||||
messageDb.Conversations.UpdateRange(conversations);
|
||||
await messageDb.SaveChangesAsync(cancellationToken);
|
||||
|
||||
await endpoint.Publish(message.ToIntegrationEvent(), cancellationToken);
|
||||
}
|
||||
|
||||
public async Task Handle(MessageWithdrawDomainEvent notification, CancellationToken cancellationToken)
|
||||
|
||||
@@ -52,13 +52,16 @@ namespace MessageService.WebApi.Application.Message
|
||||
{
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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
|
||||
};
|
||||
@@ -109,6 +112,11 @@ namespace MessageService.WebApi.Application.Message
|
||||
|
||||
public async Task<Result<GetMessagesResponse>> GetMessagesAsync(GetMessageCommand command)
|
||||
{
|
||||
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);
|
||||
|
||||
if(conversation is null || conversation.UserId != command.userId)
|
||||
|
||||
@@ -22,8 +22,12 @@ namespace MessageService.WebApi.Application.Message
|
||||
public int? Height { get; init; }
|
||||
public string? Thumb { get; init; }
|
||||
public int? Duration { get; init; }
|
||||
public Guid? FileId { get; init; }
|
||||
public string? FileName { get; init; }
|
||||
public long? FileSize { get; init; }
|
||||
public string? FileFormat { get; init; }
|
||||
|
||||
public SendMsgCommand(Guid senderId, Guid targetId, ChatType chatType, MessageType msgType, Guid clientMsgId, Guid? quoteMessageId, Dictionary<string, string>? ext, string? text, string? url, int? width, int? height, string? thumb, int? duration)
|
||||
public SendMsgCommand(Guid senderId, Guid targetId, ChatType chatType, MessageType msgType, Guid clientMsgId, Guid? quoteMessageId, Dictionary<string, string>? ext, string? text, string? url, int? width, int? height, string? thumb, int? duration, Guid? fileId, string? fileName, long? fileSize, string? fileFormat)
|
||||
{
|
||||
SenderId = senderId;
|
||||
TargetId = targetId;
|
||||
@@ -38,6 +42,10 @@ namespace MessageService.WebApi.Application.Message
|
||||
Height = height;
|
||||
Thumb = thumb;
|
||||
Duration = duration;
|
||||
FileId = fileId;
|
||||
FileName = fileName;
|
||||
FileSize = fileSize;
|
||||
FileFormat = fileFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ namespace MessageService.WebApi.Controllers.Message
|
||||
public int? Height { get; init; }
|
||||
public string? Thumb { get; init; }
|
||||
public int? Duration { get; init; }
|
||||
public Guid? FileId { get; init; }
|
||||
public string? FileName { get; init; }
|
||||
public long? FileSize { get; init; }
|
||||
public string? FileFormat { get; init; }
|
||||
|
||||
public SendMsgCommand ToCommand(Guid senderId)
|
||||
{
|
||||
@@ -39,7 +43,11 @@ namespace MessageService.WebApi.Controllers.Message
|
||||
Width,
|
||||
Height,
|
||||
Thumb,
|
||||
Duration
|
||||
Duration,
|
||||
FileId,
|
||||
FileName,
|
||||
FileSize,
|
||||
FileFormat
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -63,9 +71,19 @@ namespace MessageService.WebApi.Controllers.Message
|
||||
|
||||
// 图片/视频/语音消息:url 必填
|
||||
When(r => r.MsgType == MessageType.Image || r.MsgType == MessageType.Video
|
||||
|| r.MsgType == MessageType.Voice || r.MsgType == MessageType.File, () =>
|
||||
|| r.MsgType == MessageType.Voice, () =>
|
||||
{
|
||||
RuleFor(r => r.Url).NotEmpty().WithMessage("媒体消息的 url 字段不能为空");
|
||||
RuleFor(r => r)
|
||||
.Must(request => !string.IsNullOrWhiteSpace(request.Url) || request.FileId.HasValue)
|
||||
.WithMessage("媒体消息必须提供 url 或 fileId");
|
||||
});
|
||||
|
||||
When(r => r.MsgType == MessageType.File, () =>
|
||||
{
|
||||
RuleFor(r => r.FileId).NotNull().NotEmpty().WithMessage("文件消息的 fileId 字段不能为空");
|
||||
RuleFor(r => r.FileName).NotEmpty().WithMessage("文件消息的 fileName 字段不能为空");
|
||||
RuleFor(r => r.FileSize).NotNull().GreaterThanOrEqualTo(0).WithMessage("文件消息的 fileSize 字段不合法");
|
||||
RuleFor(r => r.FileFormat).NotEmpty().WithMessage("文件消息的 fileFormat 字段不能为空");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,16 @@ namespace MessageService.WebApi
|
||||
services.AddScoped<ConversationService>();
|
||||
services.AddScoped<SquenceService>();
|
||||
services.AddScoped<IContactIntegrationService, ContactIntegrationService>();
|
||||
services.AddHttpClient<IGroupMemberIntegrationService, GroupMemberIntegrationService>(c =>
|
||||
services.AddHttpClient<IGroupMemberIntegrationService, GroupMemberIntegrationService>((sp, c) =>
|
||||
{
|
||||
c.BaseAddress = new Uri("http://im-group-service:8080/");
|
||||
var configuration = sp.GetRequiredService<IConfiguration>();
|
||||
c.BaseAddress = new Uri(configuration["InternalServices:GroupServiceBaseUrl"]
|
||||
?? "http://im-group-service:8080/");
|
||||
var internalApiKey = configuration["InternalApiKey"];
|
||||
if (!string.IsNullOrWhiteSpace(internalApiKey))
|
||||
{
|
||||
c.DefaultRequestHeaders.Add("X-Internal-Api-Key", internalApiKey);
|
||||
}
|
||||
});
|
||||
services.AddGrpcClient<ContactInternal.ContactInternalClient>((sp, o) =>
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"InternalApiKey": "development-only-change-me",
|
||||
"InternalServices": {
|
||||
"GroupServiceBaseUrl": "http://localhost:5070/"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
||||
@@ -5,5 +5,9 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"AllowedHosts": "*",
|
||||
"InternalApiKey": "",
|
||||
"InternalServices": {
|
||||
"GroupServiceBaseUrl": "http://im-group-service:8080/"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user