后端:

新增好友请求事件和好友已添加事件
This commit is contained in:
2026-02-01 13:21:21 +08:00
committed by nanxun
40 changed files with 550 additions and 194 deletions
@@ -1,13 +1,26 @@
using IM_API.Domain.Events;
using IM_API.Interface.Services;
using MassTransit;
namespace IM_API.Application.EventHandlers.FriendAddHandler
{
public class FriendAddConversationHandler : IConsumer<FriendAddEvent>
{
public Task Consume(ConsumeContext<FriendAddEvent> context)
private readonly IFriendSerivce _friendService;
public FriendAddConversationHandler(IFriendSerivce friendService)
{
throw new NotImplementedException();
_friendService = friendService;
}
public async Task Consume(ConsumeContext<FriendAddEvent> context)
{
var @event = context.Message;
//为请求发起人添加好友记录
await _friendService.MakeFriendshipAsync(
@event.RequestUserId, @event.ResponseUserId, @event.RequestInfo.RemarkName);
//为接收人添加好友记录
await _friendService.MakeFriendshipAsync(
@event.ResponseUserId, @event.RequestUserId, @event.requestUserRemarkname);
}
}
}
@@ -1,28 +1,37 @@
using IM_API.Domain.Events;
using IM_API.Dtos;
using IM_API.Hubs;
using IM_API.Interface.Services;
using IM_API.Models;
using MassTransit;
using Microsoft.AspNetCore.SignalR;
namespace IM_API.Application.EventHandlers.FriendAddHandler
{
public class FriendAddSignalRHandler : IConsumer<FriendAddEvent>
{
private readonly IFriendSerivce _friendService;
public FriendAddSignalRHandler(IFriendSerivce friendSerivce)
private readonly IHubContext<ChatHub> _chathub;
public FriendAddSignalRHandler(IHubContext<ChatHub> chathub)
{
_friendService = friendSerivce;
_chathub = chathub;
}
public async Task Consume(ConsumeContext<FriendAddEvent> context)
{
var @event = context.Message;
//为请求发起人添加好友记录
await _friendService.MakeFriendshipAsync(
@event.RequestUser.Id, @event.ResponseUser.Id, @event.RequestInfo.RemarkName);
//为接收人添加好友记录
await _friendService.MakeFriendshipAsync(
@event.ResponseUser.Id, @event.RequestUser.Id, @event.requestUserRemarkname);
var usersList = new List<string> {
@event.RequestUserId.ToString(), @event.ResponseUserId.ToString()
};
var res = new HubResponse<MessageBaseDto>("Event", new MessageBaseDto()
{
ChatType = ChatType.PRIVATE,
Content = "您有新的好友关系已添加",
MsgId = @event.EventId.ToString(),
ReceiverId = @event.ResponseUserId,
SenderId = @event.RequestUserId,
TimeStamp = DateTime.UtcNow
});
await _chathub.Clients.Users(usersList).SendAsync("ReceiveMessage", res);
}
}
}
@@ -28,13 +28,13 @@ namespace IM_API.Application.EventHandlers.MessageCreatedHandler
MessageBaseDto messageBaseDto = new MessageBaseDto
{
MsgId = @event.MessageId.ToString(),
ChatType = @event.ChatType.ToString(),
ChatType = @event.ChatType,
Content = @event.MessageContent,
GroupMemberId = null,
ReceiverId = @event.MsgRecipientId,
SenderId = @event.MsgSenderId,
TimeStamp = @event.MessageCreated,
Type = @event.MessageMsgType.ToString()
Type = @event.MessageMsgType
};
await _hub.Clients.Users(@event.MsgRecipientId.ToString()).SendAsync("ReceiveMessage", messageBaseDto);
}
@@ -0,0 +1,36 @@
using IM_API.Domain.Events;
using IM_API.Dtos;
using IM_API.Hubs;
using IM_API.Interface.Services;
using MassTransit;
using Microsoft.AspNetCore.SignalR;
namespace IM_API.Application.EventHandlers.RequestFriendHandler
{
public class RequestFriendSignalRHandler:IConsumer<RequestFriendEvent>
{
private readonly IHubContext<ChatHub> _hub;
private readonly IUserService _userService;
public RequestFriendSignalRHandler(IHubContext<ChatHub> hubContext, IUserService userService)
{
_hub = hubContext;
_userService = userService;
}
public async Task Consume(ConsumeContext<RequestFriendEvent> context)
{
var @event = context.Message;
var userInfo = await _userService.GetUserInfoAsync(@event.FromUserId);
var res = new HubResponse<FriendRequestResDto>("Event", new FriendRequestResDto()
{
RequestUser = @event.FromUserId,
ResponseUser = @event.ToUserId,
Created = DateTime.UtcNow,
Description = @event.Description,
Avatar = userInfo.Avatar,
NickName = userInfo.NickName
});
await _hub.Clients.User(@event.ToUserId.ToString()).SendAsync("ReceiveMessage", res);
}
}
}
+9 -2
View File
@@ -46,6 +46,13 @@ namespace IM_API.Configs
.ForMember(dest => dest.StateEnum , opt => opt.MapFrom(src => FriendRequestState.Pending))
.ForMember(dest => dest.Description , opt => opt.MapFrom(src => src.Description))
;
CreateMap<FriendRequest, FriendRequestDto>()
.ForMember(dest => dest.ToUserId, opt => opt.MapFrom(src => src.ResponseUser))
.ForMember(dest => dest.FromUserId, opt => opt.MapFrom(src => src.RequestUser))
.ForMember(dest => dest.RemarkName, opt => opt.MapFrom(src => src.RemarkName))
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description))
;
//消息模型转换
CreateMap<Message, MessageBaseDto>()
.ForMember(dest => dest.Type , opt => opt.MapFrom(src => src.MsgTypeEnum.ToString()))
@@ -59,8 +66,8 @@ namespace IM_API.Configs
;
CreateMap<MessageBaseDto, Message>()
.ForMember(dest => dest.Sender, opt => opt.MapFrom(src => src.SenderId))
.ForMember(dest => dest.ChatTypeEnum,opt => opt.MapFrom(src => Enum.Parse<ChatType>(src.ChatType,true)))
.ForMember(dest => dest.MsgTypeEnum, opt => opt.MapFrom(src => Enum.Parse<MessageMsgType>(src.Type,true)))
.ForMember(dest => dest.ChatTypeEnum,opt => opt.MapFrom(src => src.ChatType))
.ForMember(dest => dest.MsgTypeEnum, opt => opt.MapFrom(src => src.Type))
.ForMember(dest => dest.Created, opt => opt.MapFrom(src => src.TimeStamp))
.ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.Content))
.ForMember(dest => dest.Recipient, opt => opt.MapFrom(src => src.ReceiverId))
@@ -8,12 +8,12 @@ namespace IM_API.Domain.Events
/// <summary>
/// 发起请求用户
/// </summary>
public UserInfoDto RequestUser { get; init; }
public int RequestUserId { get; init; }
public string? requestUserRemarkname { get; init; }
/// <summary>
/// 接受请求用户
/// </summary>
public UserInfoDto ResponseUser { get; init; }
public int ResponseUserId { get; init; }
public FriendRequestDto RequestInfo { get; init; }
/// <summary>
@@ -0,0 +1,10 @@
namespace IM_API.Domain.Events
{
public record RequestFriendEvent : DomainEvent
{
public override string EventType => "IM.FRIENDS_FRIEND_REQUEST";
public int FromUserId { get; init; }
public int ToUserId { get; init; }
public string Description { get; init; }
}
}
+47
View File
@@ -0,0 +1,47 @@
using IM_API.Tools;
namespace IM_API.Dtos
{
public class HubResponse<T>
{
public int Code { get; init; }
public string Method { get; init; }
public HubResponseType Type { get; init; }
public string Message { get; init; }
public T? Data { get; init; }
public HubResponse(string method)
{
Code = CodeDefine.SUCCESS.Code;
Message = CodeDefine.SUCCESS.Message;
Type = HubResponseType.ActionStatus;
}
public HubResponse(string method,T data)
{
Code = CodeDefine.SUCCESS.Code;
Message = CodeDefine.SUCCESS.Message;
Type = HubResponseType.ActionStatus;
Data = data;
}
public HubResponse(CodeDefine codedefine,string method)
{
Code = codedefine.Code;
Method = method;
Message = codedefine.Message;
Type = HubResponseType.ActionStatus;
}
public HubResponse(CodeDefine codeDefine, string method, HubResponseType type, T? data)
{
Code = codeDefine.Code;
Method = method;
Type = type;
Message = codeDefine.Message;
Data = data;
}
}
public enum HubResponseType
{
ChatMsg = 1, // 聊天内容
SystemNotice = 2, // 系统通知(如:申请好友成功)
ActionStatus = 3 // 状态变更(如:对方正在输入、已读回执)
}
}
+5 -3
View File
@@ -1,10 +1,12 @@
namespace IM_API.Dtos
using IM_API.Models;
namespace IM_API.Dtos
{
public record MessageBaseDto
{
// 使用 { get; init; } 确保对象创建后不可修改,且支持无参构造
public string Type { get; init; } = default!;
public string ChatType { get; init; } = default!;
public MessageMsgType Type { get; init; } = default!;
public ChatType ChatType { get; init; } = default!;
public string? MsgId { get; init; }
public int SenderId { get; init; }
public int ReceiverId { get; init; }
+7 -7
View File
@@ -40,17 +40,17 @@ namespace IM_API.Hubs
}
await base.OnConnectedAsync();
}
public async Task SendMessage(MessageBaseDto dto)
public async Task<HubResponse<MessageBaseDto?>> SendMessage(MessageBaseDto dto)
{
if (!Context.User.Identity.IsAuthenticated)
{
await Clients.Caller.SendAsync("ReceiveMessage", new BaseResponse<object?>(CodeDefine.AUTH_FAILED));
Context.Abort();
return;
return new HubResponse<MessageBaseDto?>(CodeDefine.AUTH_FAILED, "SendMessage");
}
var userIdStr = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
MessageBaseDto msgInfo = null;
if(dto.ChatType.ToLower() == ChatType.PRIVATE.ToString().ToLower())
if(dto.ChatType == ChatType.PRIVATE)
{
msgInfo = await _messageService.SendPrivateMessageAsync(int.Parse(userIdStr), dto.ReceiverId, dto);
}
@@ -58,19 +58,19 @@ namespace IM_API.Hubs
{
msgInfo = await _messageService.SendGroupMessageAsync(int.Parse(userIdStr), dto.ReceiverId, dto);
}
return;
return new HubResponse<MessageBaseDto?>("SendMessage", msgInfo);
}
public async Task ClearUnreadCount(int conversationId)
public async Task<HubResponse<object?>> ClearUnreadCount(int conversationId)
{
if (!Context.User.Identity.IsAuthenticated)
{
await Clients.Caller.SendAsync("ReceiveMessage", new BaseResponse<object?>(CodeDefine.AUTH_FAILED));
Context.Abort();
return;
return new HubResponse<object?>(CodeDefine.AUTH_FAILED, "ClearUnreadCount"); ;
}
var userIdStr = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
await _conversationService.ClearUnreadCountAsync(int.Parse(userIdStr), conversationId);
return;
return new HubResponse<object?>("ClearUnreadCount");
}
}
}
+5
View File
@@ -32,6 +32,11 @@ public partial class FriendRequest
/// </summary>
public sbyte State { get; set; }
/// <summary>
/// 备注
/// </summary>
public string RemarkName { get; set; } = null!;
public virtual User RequestUserNavigation { get; set; } = null!;
public virtual User ResponseUserNavigation { get; set; } = null!;
+3
View File
@@ -281,6 +281,9 @@ public partial class ImContext : DbContext
entity.Property(e => e.Description)
.HasComment("申请附言 ")
.HasColumnType("text");
entity.Property(e => e.RemarkName)
.HasMaxLength(20)
.HasComment("备注");
entity.Property(e => e.RequestUser)
.HasComment("申请人 ")
.HasColumnType("int(11)");
+12 -1
View File
@@ -9,6 +9,8 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using StackExchange.Redis;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace IM_API
{
@@ -38,7 +40,12 @@ namespace IM_API
builder.Services.AddAllService(configuration);
builder.Services.AddSignalR();
builder.Services.AddSignalR().AddJsonProtocol(options =>
{
// 枚举输出字符串
options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
//允许所有来源(跨域)
builder.Services.AddCors(options =>
{
@@ -110,6 +117,10 @@ namespace IM_API
{
// 保持 ISO 8601 格式
options.JsonSerializerOptions.Converters.Add(new UtcDateTimeConverter());
// 将枚举转换为字符串
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
// 建议:保持驼峰命名
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
builder.Services.AddModelValidation(configuration);
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+25 -6
View File
@@ -1,9 +1,11 @@
using AutoMapper;
using IM_API.Domain.Events;
using IM_API.Dtos;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using MassTransit;
using Microsoft.EntityFrameworkCore;
namespace IM_API.Services
@@ -13,11 +15,13 @@ namespace IM_API.Services
private readonly ImContext _context;
private readonly ILogger<FriendService> _logger;
private readonly IMapper _mapper;
public FriendService(ImContext context, ILogger<FriendService> logger, IMapper mapper)
private readonly IPublishEndpoint _endpoint;
public FriendService(ImContext context, ILogger<FriendService> logger, IMapper mapper, IPublishEndpoint endpoint)
{
_context = context;
_logger = logger;
_mapper = mapper;
_endpoint = endpoint;
}
#region
public async Task<bool> BlockeFriendAsync(int friendId)
@@ -127,13 +131,18 @@ namespace IM_API.Services
//同意后标记
case HandleFriendRequestAction.Accept:
friend.StatusEnum = FriendStatus.Added;
friendRequest.StateEnum = FriendRequestState.Passed;
await _endpoint.Publish(new FriendAddEvent()
{
AggregateId = friendRequest.Id.ToString(),
OccurredAt = DateTime.UtcNow,
Created = DateTime.UtcNow,
EventId = Guid.NewGuid(),
OperatorId = friendRequest.ResponseUser,
RequestInfo = _mapper.Map<FriendRequestDto>(friendRequest),
requestUserRemarkname = requestDto.RemarkName,
//根据当前好友请求为被申请方添加一条好友记录(注意:好友记录为双向)
var ResponseFriend = _mapper.Map<Friend>(friendRequest);
if (!string.IsNullOrEmpty(requestDto.RemarkName)) ResponseFriend.RemarkName = requestDto.RemarkName;
_context.Friends.Add(ResponseFriend);
});
break;
//无效操作
@@ -172,6 +181,16 @@ namespace IM_API.Services
var friendRequst = _mapper.Map<FriendRequest>(dto);
_context.FriendRequests.Add(friendRequst);
await _context.SaveChangesAsync();
await _endpoint.Publish(new RequestFriendEvent()
{
AggregateId = friendRequst.Id.ToString(),
OccurredAt = friendRequst.Created,
Description = friendRequst.Description,
EventId = Guid.NewGuid(),
FromUserId = friendRequst.RequestUser,
ToUserId = friendRequst.ResponseUser,
OperatorId = friendRequst.RequestUser
});
return true;
}
#endregion