fix:修复已知问题
This commit is contained in:
@@ -8,6 +8,7 @@ using IM_API.Tools;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
@@ -38,7 +39,8 @@ public class UserServiceTests
|
||||
var loggerMock = new Mock<ILogger<UserService>>();
|
||||
var mapper = CreateMapper();
|
||||
var mockCache = new Mock<ICacheService>();
|
||||
return new UserService(context, loggerMock.Object, mapper , mockCache.Object);
|
||||
var res = new Mock<IConnectionMultiplexer>();
|
||||
return new UserService(context, loggerMock.Object, mapper , mockCache.Object, res.Object);
|
||||
}
|
||||
|
||||
// ========== GetUserInfoAsync ==========
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -14,7 +14,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("IMTest")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+139037085bdb672bb115b4b9506955f7bc733672")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+10f79fb537b71581876f17031e09898ddf99e367")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("IMTest")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("IMTest")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
07f6bfbf17c54c7629f2ad097053140e01b65bf5b6fd786f5da1ceb943f92ba1
|
||||
6a1eca496991f1712085223e3079932051f23c0e5f7568bc971c393fde95395b
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10
-8
@@ -18,15 +18,18 @@ namespace IM_API.Application.EventHandlers.MessageCreatedHandler
|
||||
private readonly IConversationService _conversationService;
|
||||
private readonly ILogger<ConversationEventHandler> _logger;
|
||||
private readonly IUserService _userSerivce;
|
||||
private readonly IGroupService _groupService;
|
||||
public ConversationEventHandler(
|
||||
IConversationService conversationService,
|
||||
ILogger<ConversationEventHandler> logger,
|
||||
IUserService userService
|
||||
IUserService userService,
|
||||
IGroupService groupService
|
||||
)
|
||||
{
|
||||
_conversationService = conversationService;
|
||||
_logger = logger;
|
||||
_userSerivce = userService;
|
||||
_groupService = groupService;
|
||||
}
|
||||
|
||||
public async Task Consume(ConsumeContext<MessageCreatedEvent> context)
|
||||
@@ -35,14 +38,13 @@ namespace IM_API.Application.EventHandlers.MessageCreatedHandler
|
||||
if (@event.ChatType == ChatType.GROUP)
|
||||
{
|
||||
var userinfo = await _userSerivce.GetUserInfoAsync(@event.MsgSenderId);
|
||||
await _conversationService.UpdateConversationAfterSentAsync(new Dtos.Conversation.UpdateConversationDto
|
||||
await _groupService.UpdateGroupConversationAsync(new Dtos.Group.GroupUpdateConversationDto
|
||||
{
|
||||
LastMessage = $"{userinfo.NickName}:{@event.MessageContent}",
|
||||
LastSequenceId = @event.SequenceId,
|
||||
ReceiptId = @event.MsgRecipientId,
|
||||
SenderId = @event.MsgSenderId,
|
||||
StreamKey = @event.StreamKey,
|
||||
DateTime = @event.MessageCreated
|
||||
GroupId = @event.MsgRecipientId,
|
||||
LastMessage = @event.MessageContent,
|
||||
LastSenderName = userinfo.NickName,
|
||||
LastUpdateTime = @event.MessageCreated,
|
||||
MaxSequenceId = @event.SequenceId
|
||||
});
|
||||
}
|
||||
else
|
||||
|
||||
+7
-1
@@ -3,6 +3,7 @@ using IM_API.Application.Interfaces;
|
||||
using IM_API.Domain.Events;
|
||||
using IM_API.Dtos;
|
||||
using IM_API.Hubs;
|
||||
using IM_API.Interface.Services;
|
||||
using IM_API.Models;
|
||||
using IM_API.Tools;
|
||||
using IM_API.VOs.Message;
|
||||
@@ -15,10 +16,12 @@ namespace IM_API.Application.EventHandlers.MessageCreatedHandler
|
||||
{
|
||||
private readonly IHubContext<ChatHub> _hub;
|
||||
private readonly IMapper _mapper;
|
||||
public SignalREventHandler(IHubContext<ChatHub> hub, IMapper mapper)
|
||||
private readonly IUserService _userService;
|
||||
public SignalREventHandler(IHubContext<ChatHub> hub, IMapper mapper,IUserService userService)
|
||||
{
|
||||
_hub = hub;
|
||||
_mapper = mapper;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public async Task Consume(ConsumeContext<MessageCreatedEvent> context)
|
||||
@@ -29,6 +32,9 @@ namespace IM_API.Application.EventHandlers.MessageCreatedHandler
|
||||
{
|
||||
var entity = _mapper.Map<Message>(@event);
|
||||
var messageBaseVo = _mapper.Map<MessageBaseVo>(entity);
|
||||
var senderinfo = await _userService.GetUserInfoAsync(@event.MsgSenderId);
|
||||
messageBaseVo.SenderName = senderinfo.NickName;
|
||||
messageBaseVo.SenderAvatar = senderinfo.Avatar ?? "";
|
||||
await _hub.Clients.Group(@event.StreamKey).SendAsync("ReceiveMessage", new HubResponse<MessageBaseVo>("Event", messageBaseVo));
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace IM_API.Dtos.Group
|
||||
{
|
||||
public class GroupUpdateConversationDto
|
||||
{
|
||||
public int GroupId { get; set; }
|
||||
public long MaxSequenceId { get; set; }
|
||||
public string LastMessage { get; set; }
|
||||
public string LastSenderName { get; set; }
|
||||
public DateTimeOffset LastUpdateTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -43,5 +43,6 @@ namespace IM_API.Interface.Services
|
||||
/// <param name="desc"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<GroupInfoDto>> GetGroupListAsync(int userId, int page, int limit, bool desc);
|
||||
Task UpdateGroupConversationAsync(GroupUpdateConversationDto dto);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IM_API.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class updategroup : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LastMessage",
|
||||
table: "groups",
|
||||
type: "longtext",
|
||||
nullable: false,
|
||||
collation: "utf8mb4_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LastSenderName",
|
||||
table: "groups",
|
||||
type: "longtext",
|
||||
nullable: false,
|
||||
collation: "utf8mb4_general_ci")
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AddColumn<DateTimeOffset>(
|
||||
name: "LastUpdateTime",
|
||||
table: "groups",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "MaxSequenceId",
|
||||
table: "groups",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastMessage",
|
||||
table: "groups");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastSenderName",
|
||||
table: "groups");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastUpdateTime",
|
||||
table: "groups");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MaxSequenceId",
|
||||
table: "groups");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,6 +354,20 @@ namespace IM_API.Migrations
|
||||
.HasColumnType("int(11)")
|
||||
.HasComment("群主");
|
||||
|
||||
b.Property<string>("LastMessage")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("LastSenderName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTimeOffset>("LastUpdateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<long>("MaxSequenceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
|
||||
@@ -50,6 +50,11 @@ public partial class Group
|
||||
/// 群头像
|
||||
/// </summary>
|
||||
public string Avatar { get; set; } = null!;
|
||||
public long MaxSequenceId { get; set; } = 0;
|
||||
public string LastMessage { get; set; } = string.Empty;
|
||||
public string LastSenderName { get; set; } = string.Empty;
|
||||
public DateTimeOffset LastUpdateTime { get; set; } = DateTime.UtcNow;
|
||||
|
||||
|
||||
public virtual ICollection<GroupInvite> GroupInvites { get; set; } = new List<GroupInvite>();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace IM_API.Models;
|
||||
|
||||
@@ -58,32 +59,33 @@ public partial class User
|
||||
/// 用户头像链接
|
||||
/// </summary>
|
||||
public string? Avatar { get; set; }
|
||||
[JsonIgnore]
|
||||
|
||||
public virtual ICollection<Conversation> Conversations { get; set; } = new List<Conversation>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<Device> Devices { get; set; } = new List<Device>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<Friend> FriendFriendNavigations { get; set; } = new List<Friend>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<FriendRequest> FriendRequestRequestUserNavigations { get; set; } = new List<FriendRequest>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<FriendRequest> FriendRequestResponseUserNavigations { get; set; } = new List<FriendRequest>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<Friend> FriendUsers { get; set; } = new List<Friend>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<GroupInvite> GroupInviteInviteUserNavigations { get; set; } = new List<GroupInvite>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<GroupInvite> GroupInviteInvitedUserNavigations { get; set; } = new List<GroupInvite>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<GroupMember> GroupMembers { get; set; } = new List<GroupMember>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<GroupRequest> GroupRequests { get; set; } = new List<GroupRequest>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<Group> Groups { get; set; } = new List<Group>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<LoginLog> LoginLogs { get; set; } = new List<LoginLog>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<Message> Messages { get; set; } = new List<Message>();
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<Notification> Notifications { get; set; } = new List<Notification>();
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace IM_API.Services
|
||||
var groupList = await (from c in _context.Conversations
|
||||
join g in _context.Groups on c.TargetId equals g.Id
|
||||
where c.UserId == userId && c.ChatType == ChatType.GROUP
|
||||
select new { c, g.Avatar, g.Name })
|
||||
select new { c, g.Avatar, g.Name,g.MaxSequenceId,g.LastMessage })
|
||||
.ToListAsync();
|
||||
|
||||
var privateDtos = privateList.Select(x =>
|
||||
@@ -58,6 +58,9 @@ namespace IM_API.Services
|
||||
var dto = _mapper.Map<ConversationVo>(x.c);
|
||||
dto.TargetAvatar = x.Avatar;
|
||||
dto.TargetName = x.Name;
|
||||
dto.UnreadCount = (int)(x.MaxSequenceId - x.c.LastReadSequenceId ?? 0);
|
||||
dto.LastSequenceId = x.MaxSequenceId;
|
||||
dto.LastMessage = x.LastMessage;
|
||||
return dto;
|
||||
});
|
||||
|
||||
|
||||
@@ -125,5 +125,16 @@ namespace IM_API.Services
|
||||
.ToListAsync();
|
||||
return list;
|
||||
}
|
||||
public async Task UpdateGroupConversationAsync(GroupUpdateConversationDto dto)
|
||||
{
|
||||
var group = await _context.Groups.FirstOrDefaultAsync(x => x.Id == dto.GroupId);
|
||||
if (group is null) return;
|
||||
group.LastMessage = dto.LastMessage;
|
||||
group.MaxSequenceId = dto.MaxSequenceId;
|
||||
group.LastSenderName = dto.LastSenderName;
|
||||
group.LastUpdateTime = dto.LastUpdateTime;
|
||||
_context.Groups.Update(group);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using IM_API.VOs.Message;
|
||||
using MassTransit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StackExchange.Redis;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using static MassTransit.Monitoring.Performance.BuiltInCounters;
|
||||
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
|
||||
@@ -26,9 +27,11 @@ namespace IM_API.Services
|
||||
//private readonly IEventBus _eventBus;
|
||||
private readonly IPublishEndpoint _endpoint;
|
||||
private readonly ISequenceIdService _sequenceIdService;
|
||||
private readonly IUserService _userService;
|
||||
public MessageService(
|
||||
ImContext context, ILogger<MessageService> logger, IMapper mapper,
|
||||
IPublishEndpoint publishEndpoint, ISequenceIdService sequenceIdService
|
||||
IPublishEndpoint publishEndpoint, ISequenceIdService sequenceIdService,
|
||||
IUserService userService
|
||||
)
|
||||
{
|
||||
_context = context;
|
||||
@@ -37,6 +40,7 @@ namespace IM_API.Services
|
||||
//_eventBus = eventBus;
|
||||
_endpoint = publishEndpoint;
|
||||
_sequenceIdService = sequenceIdService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public async Task<List<MessageBaseVo>> GetMessagesAsync(int userId,MessageQueryDto dto)
|
||||
@@ -48,6 +52,7 @@ namespace IM_API.Services
|
||||
if (conversation is null) throw new BaseException(CodeDefine.CONVERSATION_NOT_FOUND);
|
||||
|
||||
var baseQuery = _context.Messages.Where(x => x.StreamKey == conversation.StreamKey);
|
||||
List<MessageBaseVo> messages = new List<MessageBaseVo>();
|
||||
if (dto.Direction == 0) // Before: 找比锚点小的,按倒序排
|
||||
{
|
||||
if (dto.Cursor.HasValue)
|
||||
@@ -59,20 +64,41 @@ namespace IM_API.Services
|
||||
.Select(m => _mapper.Map<MessageBaseVo>(m))
|
||||
.ToListAsync();
|
||||
|
||||
return list.OrderBy(s => s.SequenceId).ToList();
|
||||
messages = list.OrderBy(s => s.SequenceId).ToList();
|
||||
}
|
||||
else // After: 找比锚点大的,按正序排(用于补洞或刷新)
|
||||
{
|
||||
// 如果 Cursor 为空且是 After,逻辑上说不通,通常直接返回空或报错
|
||||
if (!dto.Cursor.HasValue) return new List<MessageBaseVo>();
|
||||
|
||||
return await baseQuery
|
||||
messages = await baseQuery
|
||||
.Where(m => m.SequenceId > dto.Cursor.Value)
|
||||
.OrderBy(m => m.SequenceId) // 按时间线正序
|
||||
.Take(dto.Limit)
|
||||
.Select(m => _mapper.Map<MessageBaseVo>(m))
|
||||
.ToListAsync();
|
||||
}
|
||||
//取发送者信息,用于前端展示
|
||||
if(messages.Count > 0)
|
||||
{
|
||||
var ids = messages
|
||||
.Select(s => s.SenderId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
var userinfoList = await _userService.GetUserInfoListAsync(ids);
|
||||
// 转为字典,提高查询效率
|
||||
var userDict = userinfoList.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
foreach (var item in messages)
|
||||
{
|
||||
if(userDict.TryGetValue(item.SenderId, out var user))
|
||||
{
|
||||
item.SenderName = user.NickName;
|
||||
item.SenderAvatar = user.Avatar ?? "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
public Task<int> GetUnreadCountAsync(int userId)
|
||||
|
||||
@@ -5,5 +5,7 @@ namespace IM_API.VOs.Message
|
||||
public record MessageBaseVo:MessageBaseDto
|
||||
{
|
||||
public int SequenceId { get; set; }
|
||||
public string SenderName { get; set; } = "";
|
||||
public string SenderAvatar { get; set; } = "";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user