This commit is contained in:
2026-02-12 21:53:26 +08:00
parent 77db20dc38
commit b96d895809
339 changed files with 46655 additions and 46657 deletions
+53 -53
View File
@@ -1,53 +1,53 @@
using AutoMapper;
using IM_API.Dtos.Auth;
using IM_API.Dtos.User;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.EntityFrameworkCore;
namespace IM_API.Services
{
public class AuthService : IAuthService
{
private readonly ImContext _context;
private readonly ILogger<AuthService> _logger;
private readonly IMapper _mapper;
private readonly ICacheService _cache;
public AuthService(ImContext context, ILogger<AuthService> logger, IMapper mapper, ICacheService cache)
{
_context = context;
_logger = logger;
_mapper = mapper;
_cache = cache;
}
public async Task<User> LoginAsync(LoginRequestDto dto)
{
var userinfo = await _cache.GetUserCacheAsync(dto.Username);
if (userinfo != null && userinfo.Password == dto.Password) return userinfo;
string username = dto.Username;
string password = dto.Password;
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == username && x.Password == password);
if(user is null)
{
throw new BaseException(CodeDefine.PASSWORD_ERROR);
}
await _cache.SetUserCacheAsync(user);
return user;
}
public async Task<UserInfoDto> RegisterAsync(RegisterRequestDto dto)
{
string username = dto.Username;
//用户是否存在
bool isExist = await _context.Users.AnyAsync(x => x.Username == username);
if (isExist) throw new BaseException(CodeDefine.USER_ALREADY_EXISTS);
User user = _mapper.Map<User>(dto);
_context.Users.Add(user);
await _context.SaveChangesAsync();
return _mapper.Map<UserInfoDto>(user);
}
}
}
using AutoMapper;
using IM_API.Dtos.Auth;
using IM_API.Dtos.User;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.EntityFrameworkCore;
namespace IM_API.Services
{
public class AuthService : IAuthService
{
private readonly ImContext _context;
private readonly ILogger<AuthService> _logger;
private readonly IMapper _mapper;
private readonly ICacheService _cache;
public AuthService(ImContext context, ILogger<AuthService> logger, IMapper mapper, ICacheService cache)
{
_context = context;
_logger = logger;
_mapper = mapper;
_cache = cache;
}
public async Task<User> LoginAsync(LoginRequestDto dto)
{
var userinfo = await _cache.GetUserCacheAsync(dto.Username);
if (userinfo != null && userinfo.Password == dto.Password) return userinfo;
string username = dto.Username;
string password = dto.Password;
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == username && x.Password == password);
if(user is null)
{
throw new BaseException(CodeDefine.PASSWORD_ERROR);
}
await _cache.SetUserCacheAsync(user);
return user;
}
public async Task<UserInfoDto> RegisterAsync(RegisterRequestDto dto)
{
string username = dto.Username;
//用户是否存在
bool isExist = await _context.Users.AnyAsync(x => x.Username == username);
if (isExist) throw new BaseException(CodeDefine.USER_ALREADY_EXISTS);
User user = _mapper.Map<User>(dto);
_context.Users.Add(user);
await _context.SaveChangesAsync();
return _mapper.Map<UserInfoDto>(user);
}
}
}
+181 -181
View File
@@ -1,182 +1,182 @@
using AutoMapper;
using IM_API.Dtos.Conversation;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using IM_API.VOs.Conversation;
using Microsoft.EntityFrameworkCore;
namespace IM_API.Services
{
public class ConversationService : IConversationService
{
private readonly ImContext _context;
private readonly IMapper _mapper;
public ConversationService(ImContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
#region
public async Task<bool> ClearConversationsAsync(int userId)
{
await _context.Conversations.Where(x => x.UserId == userId).ExecuteDeleteAsync();
return true;
}
#endregion
#region
public async Task<List<ConversationVo>> GetConversationsAsync(int userId)
{
// 1. 获取私聊会话
var privateList = await (from c in _context.Conversations
join f in _context.Friends on new { c.UserId, c.TargetId }
equals new { UserId = f.UserId, TargetId = f.FriendId }
where c.UserId == userId && c.ChatType == ChatType.PRIVATE
select new { c, f.Avatar, f.RemarkName })
.ToListAsync();
// 2. 获取群聊会话
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,g.MaxSequenceId,g.LastMessage })
.ToListAsync();
var privateDtos = privateList.Select(x =>
{
var dto = _mapper.Map<ConversationVo>(x.c);
dto.TargetAvatar = x.Avatar;
dto.TargetName = x.RemarkName;
return dto;
});
var groupDtos = groupList.Select(x =>
{
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;
});
// 4. 合并并排序
return privateDtos.Concat(groupDtos)
.OrderByDescending(x => x.DateTime)
.ToList();
}
#endregion
#region
public async Task<bool> DeleteConversationAsync(int conversationId)
{
var conversation = await _context.Conversations.FirstOrDefaultAsync(x => x.Id == conversationId);
if (conversation == null) throw new BaseException(CodeDefine.CONVERSATION_NOT_FOUND);
_context.Conversations.Remove(conversation);
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<List<string>> GetUserAllStreamKeyAsync(int userId)
{
return await _context.Conversations.Where(x => x.UserId == userId)
.Select(x => x.StreamKey)
.Distinct()
.ToListAsync();
}
#endregion
#region
public async Task<ConversationVo> GetConversationByIdAsync(int userId, int conversationId)
{
var conversation = await _context.Conversations
.FirstOrDefaultAsync(
x => x.UserId == userId && x.Id == conversationId
);
if (conversation is null) throw new BaseException(CodeDefine.CONVERSATION_NOT_FOUND);
var dto = _mapper.Map<ConversationVo>(conversation);
if(conversation.ChatType == ChatType.PRIVATE)
{
var friendInfo = await _context.Friends.Include(n => n.FriendNavigation).FirstOrDefaultAsync(
x => x.UserId == conversation.UserId && x.FriendId == conversation.TargetId
);
if (friendInfo is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_mapper.Map(friendInfo,dto);
}
if(conversation.ChatType == ChatType.GROUP)
{
var groupInfo = await _context.Groups.FirstOrDefaultAsync(
x => x.Id == conversation.TargetId
);
if (groupInfo is null) throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
_mapper.Map(groupInfo, dto);
}
return dto;
}
#endregion
public async Task<bool> ClearUnreadCountAsync(int userId, int conversationId)
{
var conversation = await _context.Conversations.FirstOrDefaultAsync(x => x.UserId == userId && x.Id == conversationId);
if (conversation is null) throw new BaseException(CodeDefine.CONVERSATION_NOT_FOUND);
var message = await _context.Messages
.Where(x => x.StreamKey == conversation.StreamKey)
.OrderByDescending(x => x.SequenceId)
.FirstOrDefaultAsync();
if(message != null)
{
conversation.UnreadCount = 0;
conversation.LastMessage = message.Content;
conversation.LastReadSequenceId = message.SequenceId;
conversation.LastMessageTime = message.Created;
_context.Conversations.Update(conversation);
await _context.SaveChangesAsync();
}
return true;
}
public async Task MakeConversationAsync(int userAId, int userBId, ChatType chatType)
{
var userAcExist = await _context.Conversations.AnyAsync(x => x.UserId == userAId && x.TargetId == userBId);
if (userAcExist) return;
var streamKey = chatType == ChatType.PRIVATE ?
StreamKeyBuilder.Private(userAId, userBId) : StreamKeyBuilder.Group(userBId);
var conversation = new Conversation()
{
ChatType = chatType,
LastMessage = "",
LastMessageTime = DateTime.Now,
LastReadSequenceId = null,
StreamKey = streamKey,
TargetId = userBId,
UnreadCount = 0,
UserId = userAId
};
_context.Conversations.Add(conversation);
await _context.SaveChangesAsync();
}
public async Task UpdateConversationAfterSentAsync(UpdateConversationDto dto)
{
var cList = await _context.Conversations.Where(x => x.StreamKey == dto.StreamKey).ToListAsync();
foreach(var c in cList)
{
bool isSender = dto.SenderId == c.UserId;
c.LastMessage = dto.LastMessage;
c.LastMessageTime = dto.DateTime;
c.LastReadSequenceId = isSender ? dto.LastSequenceId : c.LastReadSequenceId;
c.UnreadCount = isSender ? 0 : c.UnreadCount + 1;
}
_context.Conversations.UpdateRange(cList);
await _context.SaveChangesAsync();
}
}
using AutoMapper;
using IM_API.Dtos.Conversation;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using IM_API.VOs.Conversation;
using Microsoft.EntityFrameworkCore;
namespace IM_API.Services
{
public class ConversationService : IConversationService
{
private readonly ImContext _context;
private readonly IMapper _mapper;
public ConversationService(ImContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
#region
public async Task<bool> ClearConversationsAsync(int userId)
{
await _context.Conversations.Where(x => x.UserId == userId).ExecuteDeleteAsync();
return true;
}
#endregion
#region
public async Task<List<ConversationVo>> GetConversationsAsync(int userId)
{
// 1. 获取私聊会话
var privateList = await (from c in _context.Conversations
join f in _context.Friends on new { c.UserId, c.TargetId }
equals new { UserId = f.UserId, TargetId = f.FriendId }
where c.UserId == userId && c.ChatType == ChatType.PRIVATE
select new { c, f.Avatar, f.RemarkName })
.ToListAsync();
// 2. 获取群聊会话
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,g.MaxSequenceId,g.LastMessage })
.ToListAsync();
var privateDtos = privateList.Select(x =>
{
var dto = _mapper.Map<ConversationVo>(x.c);
dto.TargetAvatar = x.Avatar;
dto.TargetName = x.RemarkName;
return dto;
});
var groupDtos = groupList.Select(x =>
{
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;
});
// 4. 合并并排序
return privateDtos.Concat(groupDtos)
.OrderByDescending(x => x.DateTime)
.ToList();
}
#endregion
#region
public async Task<bool> DeleteConversationAsync(int conversationId)
{
var conversation = await _context.Conversations.FirstOrDefaultAsync(x => x.Id == conversationId);
if (conversation == null) throw new BaseException(CodeDefine.CONVERSATION_NOT_FOUND);
_context.Conversations.Remove(conversation);
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<List<string>> GetUserAllStreamKeyAsync(int userId)
{
return await _context.Conversations.Where(x => x.UserId == userId)
.Select(x => x.StreamKey)
.Distinct()
.ToListAsync();
}
#endregion
#region
public async Task<ConversationVo> GetConversationByIdAsync(int userId, int conversationId)
{
var conversation = await _context.Conversations
.FirstOrDefaultAsync(
x => x.UserId == userId && x.Id == conversationId
);
if (conversation is null) throw new BaseException(CodeDefine.CONVERSATION_NOT_FOUND);
var dto = _mapper.Map<ConversationVo>(conversation);
if(conversation.ChatType == ChatType.PRIVATE)
{
var friendInfo = await _context.Friends.Include(n => n.FriendNavigation).FirstOrDefaultAsync(
x => x.UserId == conversation.UserId && x.FriendId == conversation.TargetId
);
if (friendInfo is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_mapper.Map(friendInfo,dto);
}
if(conversation.ChatType == ChatType.GROUP)
{
var groupInfo = await _context.Groups.FirstOrDefaultAsync(
x => x.Id == conversation.TargetId
);
if (groupInfo is null) throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
_mapper.Map(groupInfo, dto);
}
return dto;
}
#endregion
public async Task<bool> ClearUnreadCountAsync(int userId, int conversationId)
{
var conversation = await _context.Conversations.FirstOrDefaultAsync(x => x.UserId == userId && x.Id == conversationId);
if (conversation is null) throw new BaseException(CodeDefine.CONVERSATION_NOT_FOUND);
var message = await _context.Messages
.Where(x => x.StreamKey == conversation.StreamKey)
.OrderByDescending(x => x.SequenceId)
.FirstOrDefaultAsync();
if(message != null)
{
conversation.UnreadCount = 0;
conversation.LastMessage = message.Content;
conversation.LastReadSequenceId = message.SequenceId;
conversation.LastMessageTime = message.Created;
_context.Conversations.Update(conversation);
await _context.SaveChangesAsync();
}
return true;
}
public async Task MakeConversationAsync(int userAId, int userBId, ChatType chatType)
{
var userAcExist = await _context.Conversations.AnyAsync(x => x.UserId == userAId && x.TargetId == userBId);
if (userAcExist) return;
var streamKey = chatType == ChatType.PRIVATE ?
StreamKeyBuilder.Private(userAId, userBId) : StreamKeyBuilder.Group(userBId);
var conversation = new Conversation()
{
ChatType = chatType,
LastMessage = "",
LastMessageTime = DateTime.Now,
LastReadSequenceId = null,
StreamKey = streamKey,
TargetId = userBId,
UnreadCount = 0,
UserId = userAId
};
_context.Conversations.Add(conversation);
await _context.SaveChangesAsync();
}
public async Task UpdateConversationAfterSentAsync(UpdateConversationDto dto)
{
var cList = await _context.Conversations.Where(x => x.StreamKey == dto.StreamKey).ToListAsync();
foreach(var c in cList)
{
bool isSender = dto.SenderId == c.UserId;
c.LastMessage = dto.LastMessage;
c.LastMessageTime = dto.DateTime;
c.LastReadSequenceId = isSender ? dto.LastSequenceId : c.LastReadSequenceId;
c.UnreadCount = isSender ? 0 : c.UnreadCount + 1;
}
_context.Conversations.UpdateRange(cList);
await _context.SaveChangesAsync();
}
}
}
+220 -220
View File
@@ -1,220 +1,220 @@
using AutoMapper;
using IM_API.Domain.Events;
using IM_API.Dtos.Friend;
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
{
public class FriendService : IFriendSerivce
{
private readonly ImContext _context;
private readonly ILogger<FriendService> _logger;
private readonly 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)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.Id == friendId);
if (friend == null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
friend.StatusEnum = FriendStatus.Blocked;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region id拉黑好友
public async Task<bool> BlockFriendByUserIdAsync(int userId, int toUserId)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == userId && x.FriendId == toUserId);
if (friend == null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
friend.StatusEnum = FriendStatus.Blocked;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<bool> DeleteFriendAsync(int friendId)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.Id == friendId);
if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_context.Friends.Remove(friend);
await _context.SaveChangesAsync();
return true;
}
#endregion
#region id删除好友关系
public async Task<bool> DeleteFriendByUserIdAsync(int userId, int toUserId)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == userId && x.FriendId == toUserId);
if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_context.Friends.Remove(friend);
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<List<FriendInfoDto>> GetFriendListAsync(int userId, int page, int limit, bool desc)
{
var query = _context.Friends.Include(u => u.FriendNavigation).Where(x => x.UserId == userId && x.Status == (sbyte)FriendStatus.Added);
if (desc)
{
query = query.OrderByDescending(x => x.UserId);
}
var friendList = await query.Skip(((page - 1) * limit)).Take(limit).ToListAsync();
return _mapper.Map<List<FriendInfoDto>>(friendList);
}
#endregion
#region
public async Task<List<FriendRequestResDto>> GetFriendRequestListAsync(int userId, int page, int limit, bool desc)
{
var query = _context.FriendRequests
.Include(x => x.ResponseUserNavigation)
.Include(x => x.RequestUserNavigation)
.Where(
x => (x.ResponseUser == userId) ||
x.RequestUser == userId
)
.Select(s => new FriendRequestResDto
{
Id = s.Id,
RequestUser = s.RequestUser,
ResponseUser = s.ResponseUser,
Avatar = s.RequestUser == userId ? s.ResponseUserNavigation.Avatar : s.RequestUserNavigation.Avatar,
Created = s.Created,
NickName = s.RequestUser == userId ? s.ResponseUserNavigation.NickName : s.RequestUserNavigation.NickName,
Description = s.Description,
State = (FriendRequestState)s.State
})
;
query = query.OrderByDescending(x => x.Id);
var friendRequestList = await query.Skip(((page - 1) * limit)).Take(limit).ToListAsync();
return friendRequestList;
}
#endregion
#region
public async Task<bool> HandleFriendRequestAsync(HandleFriendRequestDto requestDto)
{
//查询好友请求记录
var friendRequest = await _context.FriendRequests
.Include(e => e.ResponseUserNavigation)
.FirstOrDefaultAsync(x => x.Id == requestDto.RequestId);
if (friendRequest is null) throw new BaseException(CodeDefine.FRIEND_REQUEST_NOT_FOUND);
//查询好友关系
var friend = await _context.Friends.FirstOrDefaultAsync(
x => x.UserId == friendRequest.RequestUser && x.FriendId == friendRequest.ResponseUser
);
if (friend != null) throw new BaseException(CodeDefine.ALREADY_FRIENDS);
//处理好友请求操作
switch (requestDto.Action)
{
//拒绝后标记
case HandleFriendRequestAction.Reject:
friendRequest.StateEnum = FriendRequestState.Declined;
break;
//同意后标记
case HandleFriendRequestAction.Accept:
friendRequest.StateEnum = FriendRequestState.Passed;
await _endpoint.Publish(new FriendAddEvent()
{
AggregateId = friendRequest.Id.ToString(),
OccurredAt = DateTime.Now,
Created = DateTime.Now,
EventId = Guid.NewGuid(),
OperatorId = friendRequest.ResponseUser,
RequestInfo = _mapper.Map<FriendRequestDto>(friendRequest),
requestUserRemarkname = requestDto.RemarkName,
RequestUserId = friendRequest.RequestUser,
ResponseUserId = friendRequest.ResponseUser
});
break;
//无效操作
default:
throw new BaseException(CodeDefine.INVALID_ACTION);
}
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<bool> SendFriendRequestAsync(FriendRequestDto dto)
{
//查询用户是否存在
bool isExist = await _context.Users.AnyAsync(x => x.Id == dto.ToUserId);
if (!isExist) throw new BaseException(CodeDefine.USER_NOT_FOUND);
bool isExistUser2 = await _context.Users.AnyAsync(x => x.Id == dto.FromUserId);
if(!isExistUser2) throw new BaseException(CodeDefine.USER_NOT_FOUND);
// 检查是否已有好友关系或待处理请求
bool alreadyExists = await _context.FriendRequests.AnyAsync(x =>
x.RequestUser == dto.FromUserId && x.ResponseUser == dto.ToUserId && x.State == (sbyte)FriendRequestState.Pending
);
if (alreadyExists)
throw new BaseException(CodeDefine.FRIEND_REQUEST_EXISTS);
var friendShip = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == dto.FromUserId && x.FriendId == dto.ToUserId);
//检查是否被对方拉黑
bool isBlocked = friendShip != null && friendShip.StatusEnum == FriendStatus.Blocked;
if (isBlocked)
throw new BaseException(CodeDefine.FRIEND_REQUEST_REJECTED);
if (friendShip != null)
throw new BaseException(CodeDefine.ALREADY_FRIENDS);
//生成实体
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.UtcDateTime,
Description = friendRequst.Description,
EventId = Guid.NewGuid(),
FromUserId = friendRequst.RequestUser,
ToUserId = friendRequst.ResponseUser,
OperatorId = friendRequst.RequestUser
});
return true;
}
#endregion
#region
public async Task MakeFriendshipAsync(int userAId, int userBId, string? remarkName)
{
bool userAexist = await _context.Friends.AnyAsync(x => x.UserId == userAId && x.FriendId == userBId);
if (!userAexist)
{
User? userbInfo = await _context.Users.FirstOrDefaultAsync(x => x.Id == userBId);
if (userbInfo is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
Friend friendA = new Friend()
{
Avatar = userbInfo.Avatar,
Created = DateTime.Now,
FriendId = userbInfo.Id,
RemarkName = remarkName ?? userbInfo.NickName,
StatusEnum = FriendStatus.Added,
UserId = userAId
};
_context.Friends.Add(friendA);
await _context.SaveChangesAsync();
}
}
#endregion
}
}
using AutoMapper;
using IM_API.Domain.Events;
using IM_API.Dtos.Friend;
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
{
public class FriendService : IFriendSerivce
{
private readonly ImContext _context;
private readonly ILogger<FriendService> _logger;
private readonly 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)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.Id == friendId);
if (friend == null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
friend.StatusEnum = FriendStatus.Blocked;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region id拉黑好友
public async Task<bool> BlockFriendByUserIdAsync(int userId, int toUserId)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == userId && x.FriendId == toUserId);
if (friend == null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
friend.StatusEnum = FriendStatus.Blocked;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<bool> DeleteFriendAsync(int friendId)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.Id == friendId);
if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_context.Friends.Remove(friend);
await _context.SaveChangesAsync();
return true;
}
#endregion
#region id删除好友关系
public async Task<bool> DeleteFriendByUserIdAsync(int userId, int toUserId)
{
var friend = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == userId && x.FriendId == toUserId);
if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
_context.Friends.Remove(friend);
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<List<FriendInfoDto>> GetFriendListAsync(int userId, int page, int limit, bool desc)
{
var query = _context.Friends.Include(u => u.FriendNavigation).Where(x => x.UserId == userId && x.Status == (sbyte)FriendStatus.Added);
if (desc)
{
query = query.OrderByDescending(x => x.UserId);
}
var friendList = await query.Skip(((page - 1) * limit)).Take(limit).ToListAsync();
return _mapper.Map<List<FriendInfoDto>>(friendList);
}
#endregion
#region
public async Task<List<FriendRequestResDto>> GetFriendRequestListAsync(int userId, int page, int limit, bool desc)
{
var query = _context.FriendRequests
.Include(x => x.ResponseUserNavigation)
.Include(x => x.RequestUserNavigation)
.Where(
x => (x.ResponseUser == userId) ||
x.RequestUser == userId
)
.Select(s => new FriendRequestResDto
{
Id = s.Id,
RequestUser = s.RequestUser,
ResponseUser = s.ResponseUser,
Avatar = s.RequestUser == userId ? s.ResponseUserNavigation.Avatar : s.RequestUserNavigation.Avatar,
Created = s.Created,
NickName = s.RequestUser == userId ? s.ResponseUserNavigation.NickName : s.RequestUserNavigation.NickName,
Description = s.Description,
State = (FriendRequestState)s.State
})
;
query = query.OrderByDescending(x => x.Id);
var friendRequestList = await query.Skip(((page - 1) * limit)).Take(limit).ToListAsync();
return friendRequestList;
}
#endregion
#region
public async Task<bool> HandleFriendRequestAsync(HandleFriendRequestDto requestDto)
{
//查询好友请求记录
var friendRequest = await _context.FriendRequests
.Include(e => e.ResponseUserNavigation)
.FirstOrDefaultAsync(x => x.Id == requestDto.RequestId);
if (friendRequest is null) throw new BaseException(CodeDefine.FRIEND_REQUEST_NOT_FOUND);
//查询好友关系
var friend = await _context.Friends.FirstOrDefaultAsync(
x => x.UserId == friendRequest.RequestUser && x.FriendId == friendRequest.ResponseUser
);
if (friend != null) throw new BaseException(CodeDefine.ALREADY_FRIENDS);
//处理好友请求操作
switch (requestDto.Action)
{
//拒绝后标记
case HandleFriendRequestAction.Reject:
friendRequest.StateEnum = FriendRequestState.Declined;
break;
//同意后标记
case HandleFriendRequestAction.Accept:
friendRequest.StateEnum = FriendRequestState.Passed;
await _endpoint.Publish(new FriendAddEvent()
{
AggregateId = friendRequest.Id.ToString(),
OccurredAt = DateTime.Now,
Created = DateTime.Now,
EventId = Guid.NewGuid(),
OperatorId = friendRequest.ResponseUser,
RequestInfo = _mapper.Map<FriendRequestDto>(friendRequest),
requestUserRemarkname = requestDto.RemarkName,
RequestUserId = friendRequest.RequestUser,
ResponseUserId = friendRequest.ResponseUser
});
break;
//无效操作
default:
throw new BaseException(CodeDefine.INVALID_ACTION);
}
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<bool> SendFriendRequestAsync(FriendRequestDto dto)
{
//查询用户是否存在
bool isExist = await _context.Users.AnyAsync(x => x.Id == dto.ToUserId);
if (!isExist) throw new BaseException(CodeDefine.USER_NOT_FOUND);
bool isExistUser2 = await _context.Users.AnyAsync(x => x.Id == dto.FromUserId);
if(!isExistUser2) throw new BaseException(CodeDefine.USER_NOT_FOUND);
// 检查是否已有好友关系或待处理请求
bool alreadyExists = await _context.FriendRequests.AnyAsync(x =>
x.RequestUser == dto.FromUserId && x.ResponseUser == dto.ToUserId && x.State == (sbyte)FriendRequestState.Pending
);
if (alreadyExists)
throw new BaseException(CodeDefine.FRIEND_REQUEST_EXISTS);
var friendShip = await _context.Friends.FirstOrDefaultAsync(x => x.UserId == dto.FromUserId && x.FriendId == dto.ToUserId);
//检查是否被对方拉黑
bool isBlocked = friendShip != null && friendShip.StatusEnum == FriendStatus.Blocked;
if (isBlocked)
throw new BaseException(CodeDefine.FRIEND_REQUEST_REJECTED);
if (friendShip != null)
throw new BaseException(CodeDefine.ALREADY_FRIENDS);
//生成实体
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.UtcDateTime,
Description = friendRequst.Description,
EventId = Guid.NewGuid(),
FromUserId = friendRequst.RequestUser,
ToUserId = friendRequst.ResponseUser,
OperatorId = friendRequst.RequestUser
});
return true;
}
#endregion
#region
public async Task MakeFriendshipAsync(int userAId, int userBId, string? remarkName)
{
bool userAexist = await _context.Friends.AnyAsync(x => x.UserId == userAId && x.FriendId == userBId);
if (!userAexist)
{
User? userbInfo = await _context.Users.FirstOrDefaultAsync(x => x.Id == userBId);
if (userbInfo is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
Friend friendA = new Friend()
{
Avatar = userbInfo.Avatar,
Created = DateTime.Now,
FriendId = userbInfo.Id,
RemarkName = remarkName ?? userbInfo.NickName,
StatusEnum = FriendStatus.Added,
UserId = userAId
};
_context.Friends.Add(friendA);
await _context.SaveChangesAsync();
}
}
#endregion
}
}
+251 -251
View File
@@ -1,251 +1,251 @@
using AutoMapper;
using AutoMapper.QueryableExtensions;
using IM_API.Domain.Events;
using IM_API.Dtos.Group;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using MassTransit;
using Microsoft.EntityFrameworkCore;
using System;
namespace IM_API.Services
{
public class GroupService : IGroupService
{
private readonly ImContext _context;
private readonly IMapper _mapper;
private readonly ILogger<GroupService> _logger;
private readonly IPublishEndpoint _endPoint;
private readonly IUserService _userService;
public GroupService(ImContext context, IMapper mapper, ILogger<GroupService> logger,
IPublishEndpoint publishEndpoint, IUserService userService)
{
_context = context;
_mapper = mapper;
_logger = logger;
_endPoint = publishEndpoint;
_userService = userService;
}
private async Task<List<int>> validFriendshipAsync (int userId, List<int> ids)
{
DateTime dateTime = DateTime.UtcNow;
//验证被邀请用户是否为好友
return await _context.Friends
.Where(f => f.UserId == userId && ids.Contains(f.FriendId))
.Select(f => f.FriendId)
.ToListAsync();
}
public async Task<GroupInfoDto> CreateGroupAsync(int userId, GroupCreateDto groupCreateDto)
{
List<int> userIds = groupCreateDto.UserIDs ?? [];
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
//先创建群
DateTime dateTime = DateTime.Now;
Group group = _mapper.Map<Group>(groupCreateDto);
group.GroupMaster = userId;
_context.Groups.Add(group);
await _context.SaveChangesAsync();
if (userIds.Count > 0)
{
//邀请好友
await InviteUsersAsync(userId, group.Id ,userIds);
}
await transaction.CommitAsync();
await _endPoint.Publish(new GroupJoinEvent
{
EventId = Guid.NewGuid(),
AggregateId = userId.ToString(),
GroupId = group.Id,
OccurredAt = dateTime,
OperatorId = userId,
UserId = userId,
IsCreated = true
});
return _mapper.Map<GroupInfoDto>(group);
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
public Task DeleteGroupAsync(int userId, int groupId)
{
throw new NotImplementedException();
}
public async Task InviteUsersAsync(int userId, int groupId, List<int> userIds)
{
var group = await _context.Groups.FirstOrDefaultAsync(
x => x.Id == groupId) ?? throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
//过滤非好友
var groupInviteIds = await validFriendshipAsync(userId, userIds);
var inviteList = groupInviteIds.Select(id => new GroupInvite
{
Created = DateTime.UtcNow,
GroupId = group.Id,
InviteUser = userId,
InvitedUser = id,
StateEnum = GroupInviteState.Pending
}).ToList();
_context.GroupInvites.AddRange(inviteList);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupInviteEvent
{
GroupId = groupId,
AggregateId = userId.ToString(),
OccurredAt = DateTime.UtcNow,
EventId = Guid.NewGuid(),
Ids = userIds,
OperatorId = userId,
UserId = userId
});
}
public async Task MakeGroupMemberAsync(int userId, int groupId ,GroupMemberRole? role)
{
var isExist = await _context.GroupMembers.AnyAsync(x => x.GroupId == groupId && x.UserId == userId);
if (isExist) return;
var groupMember = new GroupMember
{
UserId = userId,
Created = DateTime.UtcNow,
RoleEnum = role ?? GroupMemberRole.Normal,
GroupId = groupId
};
_context.GroupMembers.Add(groupMember);
await _context.SaveChangesAsync();
}
public Task JoinGroupAsync(int userId, int groupId)
{
throw new NotImplementedException();
}
public async Task<List<GroupInfoDto>> GetGroupListAsync(int userId, int page, int limit, bool desc)
{
var query = _context.GroupMembers
.Where(x => x.UserId == userId)
.Select(s => s.Group);
if (desc)
{
query = query.OrderByDescending(x => x.Id);
}
var list = await query
.Skip((page - 1) * limit)
.Take(limit)
.ProjectTo<GroupInfoDto>(_mapper.ConfigurationProvider)
.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();
}
public async Task HandleGroupInviteAsync(int userid, HandleGroupInviteDto dto)
{
var user = _userService.GetUserInfoAsync(userid);
var inviteInfo = await _context.GroupInvites.FirstOrDefaultAsync(x => x.Id == dto.InviteId)
?? throw new BaseException(CodeDefine.INVALID_ACTION);
if (inviteInfo.InvitedUser != userid) throw new BaseException(CodeDefine.AUTH_FAILED);
inviteInfo.StateEnum = dto.Action;
_context.GroupInvites.Update(inviteInfo);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupInviteActionUpdateEvent
{
Action = dto.Action,
AggregateId = userid.ToString(),
OccurredAt = DateTime.UtcNow,
EventId = Guid.NewGuid(),
GroupId = inviteInfo.GroupId,
InviteId = inviteInfo.Id,
InviteUserId = inviteInfo.InviteUser.Value,
OperatorId = userid,
UserId = userid
});
}
public async Task HandleGroupRequestAsync(int userid, HandleGroupRequestDto dto)
{
var user = _userService.GetUserInfoAsync(userid);
//判断请求存在
var requestInfo = await _context.GroupRequests.FirstOrDefaultAsync(x => x.Id == dto.RequestId)
?? throw new BaseException(CodeDefine.INVALID_ACTION);
//判断成员存在
var memberInfo = await _context.GroupMembers.FirstOrDefaultAsync(x => x.UserId == userid)
?? throw new BaseException(CodeDefine.NO_GROUP_PERMISSION);
//判断成员权限
if (memberInfo.RoleEnum != GroupMemberRole.Master && memberInfo.RoleEnum != GroupMemberRole.Administrator)
throw new BaseException(CodeDefine.NO_GROUP_PERMISSION);
requestInfo.StateEnum = dto.Action;
_context.GroupRequests.Update(requestInfo);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupRequestUpdateEvent
{
Action = requestInfo.StateEnum,
AdminUserId = userid,
AggregateId = userid.ToString(),
OccurredAt = DateTime.UtcNow,
EventId = Guid.NewGuid(),
GroupId = requestInfo.GroupId,
OperatorId = userid,
UserId = requestInfo.UserId,
RequestId = requestInfo.Id
});
}
public async Task MakeGroupRequestAsync(int userId, int? adminUserId, int groupId)
{
var requestInfo = await _context.GroupRequests
.FirstOrDefaultAsync(x => x.UserId == userId && x.GroupId == groupId);
if (requestInfo != null) return;
var member = await _context.GroupMembers.FirstOrDefaultAsync(
x => x.UserId == adminUserId && x.GroupId == groupId);
var request = new GroupRequest
{
Created = DateTime.UtcNow,
Description = string.Empty,
GroupId = groupId,
UserId = userId,
StateEnum = GroupRequestState.Pending
};
if(member != null && (
member.RoleEnum == GroupMemberRole.Administrator || member.RoleEnum == GroupMemberRole.Master))
{
request.StateEnum = GroupRequestState.Passed;
}
_context.GroupRequests.Add(request);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupRequestEvent
{
OccurredAt = DateTime.UtcNow,
Description = request.Description,
GroupId = request.GroupId,
Action = request.StateEnum,
UserId = userId,
AggregateId = userId.ToString(),
EventId = Guid.NewGuid(),
OperatorId = userId
});
return;
}
}
}
using AutoMapper;
using AutoMapper.QueryableExtensions;
using IM_API.Domain.Events;
using IM_API.Dtos.Group;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using MassTransit;
using Microsoft.EntityFrameworkCore;
using System;
namespace IM_API.Services
{
public class GroupService : IGroupService
{
private readonly ImContext _context;
private readonly IMapper _mapper;
private readonly ILogger<GroupService> _logger;
private readonly IPublishEndpoint _endPoint;
private readonly IUserService _userService;
public GroupService(ImContext context, IMapper mapper, ILogger<GroupService> logger,
IPublishEndpoint publishEndpoint, IUserService userService)
{
_context = context;
_mapper = mapper;
_logger = logger;
_endPoint = publishEndpoint;
_userService = userService;
}
private async Task<List<int>> validFriendshipAsync (int userId, List<int> ids)
{
DateTime dateTime = DateTime.UtcNow;
//验证被邀请用户是否为好友
return await _context.Friends
.Where(f => f.UserId == userId && ids.Contains(f.FriendId))
.Select(f => f.FriendId)
.ToListAsync();
}
public async Task<GroupInfoDto> CreateGroupAsync(int userId, GroupCreateDto groupCreateDto)
{
List<int> userIds = groupCreateDto.UserIDs ?? [];
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
//先创建群
DateTime dateTime = DateTime.Now;
Group group = _mapper.Map<Group>(groupCreateDto);
group.GroupMaster = userId;
_context.Groups.Add(group);
await _context.SaveChangesAsync();
if (userIds.Count > 0)
{
//邀请好友
await InviteUsersAsync(userId, group.Id ,userIds);
}
await transaction.CommitAsync();
await _endPoint.Publish(new GroupJoinEvent
{
EventId = Guid.NewGuid(),
AggregateId = userId.ToString(),
GroupId = group.Id,
OccurredAt = dateTime,
OperatorId = userId,
UserId = userId,
IsCreated = true
});
return _mapper.Map<GroupInfoDto>(group);
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
public Task DeleteGroupAsync(int userId, int groupId)
{
throw new NotImplementedException();
}
public async Task InviteUsersAsync(int userId, int groupId, List<int> userIds)
{
var group = await _context.Groups.FirstOrDefaultAsync(
x => x.Id == groupId) ?? throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
//过滤非好友
var groupInviteIds = await validFriendshipAsync(userId, userIds);
var inviteList = groupInviteIds.Select(id => new GroupInvite
{
Created = DateTime.UtcNow,
GroupId = group.Id,
InviteUser = userId,
InvitedUser = id,
StateEnum = GroupInviteState.Pending
}).ToList();
_context.GroupInvites.AddRange(inviteList);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupInviteEvent
{
GroupId = groupId,
AggregateId = userId.ToString(),
OccurredAt = DateTime.UtcNow,
EventId = Guid.NewGuid(),
Ids = userIds,
OperatorId = userId,
UserId = userId
});
}
public async Task MakeGroupMemberAsync(int userId, int groupId ,GroupMemberRole? role)
{
var isExist = await _context.GroupMembers.AnyAsync(x => x.GroupId == groupId && x.UserId == userId);
if (isExist) return;
var groupMember = new GroupMember
{
UserId = userId,
Created = DateTime.UtcNow,
RoleEnum = role ?? GroupMemberRole.Normal,
GroupId = groupId
};
_context.GroupMembers.Add(groupMember);
await _context.SaveChangesAsync();
}
public Task JoinGroupAsync(int userId, int groupId)
{
throw new NotImplementedException();
}
public async Task<List<GroupInfoDto>> GetGroupListAsync(int userId, int page, int limit, bool desc)
{
var query = _context.GroupMembers
.Where(x => x.UserId == userId)
.Select(s => s.Group);
if (desc)
{
query = query.OrderByDescending(x => x.Id);
}
var list = await query
.Skip((page - 1) * limit)
.Take(limit)
.ProjectTo<GroupInfoDto>(_mapper.ConfigurationProvider)
.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();
}
public async Task HandleGroupInviteAsync(int userid, HandleGroupInviteDto dto)
{
var user = _userService.GetUserInfoAsync(userid);
var inviteInfo = await _context.GroupInvites.FirstOrDefaultAsync(x => x.Id == dto.InviteId)
?? throw new BaseException(CodeDefine.INVALID_ACTION);
if (inviteInfo.InvitedUser != userid) throw new BaseException(CodeDefine.AUTH_FAILED);
inviteInfo.StateEnum = dto.Action;
_context.GroupInvites.Update(inviteInfo);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupInviteActionUpdateEvent
{
Action = dto.Action,
AggregateId = userid.ToString(),
OccurredAt = DateTime.UtcNow,
EventId = Guid.NewGuid(),
GroupId = inviteInfo.GroupId,
InviteId = inviteInfo.Id,
InviteUserId = inviteInfo.InviteUser.Value,
OperatorId = userid,
UserId = userid
});
}
public async Task HandleGroupRequestAsync(int userid, HandleGroupRequestDto dto)
{
var user = _userService.GetUserInfoAsync(userid);
//判断请求存在
var requestInfo = await _context.GroupRequests.FirstOrDefaultAsync(x => x.Id == dto.RequestId)
?? throw new BaseException(CodeDefine.INVALID_ACTION);
//判断成员存在
var memberInfo = await _context.GroupMembers.FirstOrDefaultAsync(x => x.UserId == userid)
?? throw new BaseException(CodeDefine.NO_GROUP_PERMISSION);
//判断成员权限
if (memberInfo.RoleEnum != GroupMemberRole.Master && memberInfo.RoleEnum != GroupMemberRole.Administrator)
throw new BaseException(CodeDefine.NO_GROUP_PERMISSION);
requestInfo.StateEnum = dto.Action;
_context.GroupRequests.Update(requestInfo);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupRequestUpdateEvent
{
Action = requestInfo.StateEnum,
AdminUserId = userid,
AggregateId = userid.ToString(),
OccurredAt = DateTime.UtcNow,
EventId = Guid.NewGuid(),
GroupId = requestInfo.GroupId,
OperatorId = userid,
UserId = requestInfo.UserId,
RequestId = requestInfo.Id
});
}
public async Task MakeGroupRequestAsync(int userId, int? adminUserId, int groupId)
{
var requestInfo = await _context.GroupRequests
.FirstOrDefaultAsync(x => x.UserId == userId && x.GroupId == groupId);
if (requestInfo != null) return;
var member = await _context.GroupMembers.FirstOrDefaultAsync(
x => x.UserId == adminUserId && x.GroupId == groupId);
var request = new GroupRequest
{
Created = DateTime.UtcNow,
Description = string.Empty,
GroupId = groupId,
UserId = userId,
StateEnum = GroupRequestState.Pending
};
if(member != null && (
member.RoleEnum == GroupMemberRole.Administrator || member.RoleEnum == GroupMemberRole.Master))
{
request.StateEnum = GroupRequestState.Passed;
}
_context.GroupRequests.Add(request);
await _context.SaveChangesAsync();
await _endPoint.Publish(new GroupRequestEvent
{
OccurredAt = DateTime.UtcNow,
Description = request.Description,
GroupId = request.GroupId,
Action = request.StateEnum,
UserId = userId,
AggregateId = userId.ToString(),
EventId = Guid.NewGuid(),
OperatorId = userId
});
return;
}
}
}
+55 -55
View File
@@ -1,55 +1,55 @@
using IM_API.Interface.Services;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace IM_API.Services
{
public class JWTService : IJWTService
{
private readonly IConfiguration _config;
private readonly string _key;
private readonly string _issuer;
private readonly string _audience;
private readonly int _accessMinutes;
public JWTService(IConfiguration config)
{
_config = config;
_key = _config["Jwt:Key"]!;
_issuer = _config["Jwt:Issuer"]!;
_audience = _config["Jwt:Audience"]!;
_accessMinutes = int.Parse(_config["Jwt:AccessTokenMinutes"] ?? "15");
}
public string GenerateAccessToken(IEnumerable<Claim> claims, DateTime expiresAt)
{
var keyBytes = Encoding.UTF8.GetBytes(_key);
var creds = new SigningCredentials(new SymmetricSecurityKey(keyBytes), SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _issuer,
audience: _audience,
claims: claims,
expires: expiresAt,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public (string token, DateTime expiresAt) CreateAccessTokenForUser(int userId, string username, string role)
{
var expiresAt = DateTime.Now.AddMinutes(_accessMinutes);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, role)
};
var token = GenerateAccessToken(claims, expiresAt);
return (token, expiresAt);
}
}
}
using IM_API.Interface.Services;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace IM_API.Services
{
public class JWTService : IJWTService
{
private readonly IConfiguration _config;
private readonly string _key;
private readonly string _issuer;
private readonly string _audience;
private readonly int _accessMinutes;
public JWTService(IConfiguration config)
{
_config = config;
_key = _config["Jwt:Key"]!;
_issuer = _config["Jwt:Issuer"]!;
_audience = _config["Jwt:Audience"]!;
_accessMinutes = int.Parse(_config["Jwt:AccessTokenMinutes"] ?? "15");
}
public string GenerateAccessToken(IEnumerable<Claim> claims, DateTime expiresAt)
{
var keyBytes = Encoding.UTF8.GetBytes(_key);
var creds = new SigningCredentials(new SymmetricSecurityKey(keyBytes), SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _issuer,
audience: _audience,
claims: claims,
expires: expiresAt,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public (string token, DateTime expiresAt) CreateAccessTokenForUser(int userId, string username, string role)
{
var expiresAt = DateTime.Now.AddMinutes(_accessMinutes);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, role)
};
var token = GenerateAccessToken(claims, expiresAt);
return (token, expiresAt);
}
}
}
+164 -164
View File
@@ -1,164 +1,164 @@
using AutoMapper;
using IM_API.Application.Interfaces;
using IM_API.Domain.Events;
using IM_API.Dtos;
using IM_API.Dtos.Message;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
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;
namespace IM_API.Services
{
public class MessageService : IMessageSevice
{
private readonly ImContext _context;
private readonly ILogger<MessageService> _logger;
private readonly IMapper _mapper;
//废弃,此处已使用rabbitMQ替代
//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,
IUserService userService
)
{
_context = context;
_logger = logger;
_mapper = mapper;
//_eventBus = eventBus;
_endpoint = publishEndpoint;
_sequenceIdService = sequenceIdService;
_userService = userService;
}
public async Task<List<MessageBaseVo>> GetMessagesAsync(int userId,MessageQueryDto dto)
{
//获取会话信息,用于获取双方聊天的唯一标识streamkey
Conversation? conversation = await _context.Conversations.FirstOrDefaultAsync(
x => x.Id == dto.ConversationId && x.UserId == userId
);
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)
baseQuery = baseQuery.Where(m => m.SequenceId < dto.Cursor.Value);
var list = await baseQuery
.OrderByDescending(m => m.SequenceId) // 最新消息在最前
.Take(dto.Limit)
.Select(m => _mapper.Map<MessageBaseVo>(m))
.ToListAsync();
messages = list.OrderBy(s => s.SequenceId).ToList();
}
else // After: 找比锚点大的,按正序排(用于补洞或刷新)
{
// 如果 Cursor 为空且是 After,逻辑上说不通,通常直接返回空或报错
if (!dto.Cursor.HasValue) return new List<MessageBaseVo>();
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)
{
throw new NotImplementedException();
}
public Task<List<MessageBaseDto>> GetUnreadMessagesAsync(int userId)
{
throw new NotImplementedException();
}
public Task<bool> MarkAsReadAsync(int userId, long messageId)
{
throw new NotImplementedException();
}
public Task<bool> MarkConversationAsReadAsync(int userId, int? userBId, int? groupId)
{
throw new NotImplementedException();
}
public Task<bool> RecallMessageAsync(int userId, int messageId)
{
throw new NotImplementedException();
}
public async Task MakeMessageAsync(Message message)
{
_context.Messages.Add(message);
await _context.SaveChangesAsync();
}
#region
public async Task<MessageBaseVo> SendGroupMessageAsync(int senderId, int groupId, MessageBaseDto dto)
{
//判断群存在
var isExist = await _context.Groups.AnyAsync(x => x.Id == groupId);
if (!isExist) throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
//判断是否是群成员
var isMember = await _context.GroupMembers.AnyAsync(x => x.GroupId == groupId && x.UserId == senderId);
if (!isMember) throw new BaseException(CodeDefine.NO_GROUP_PERMISSION);
var message = _mapper.Map<Message>(dto);
message.StreamKey = StreamKeyBuilder.Group(groupId);
message.SequenceId = await _sequenceIdService.GetNextSquenceIdAsync(message.StreamKey);
await _endpoint.Publish(_mapper.Map<MessageCreatedEvent>(message));
return _mapper.Map<MessageBaseVo>(message);
}
#endregion
#region
public async Task<MessageBaseVo> SendPrivateMessageAsync(int senderId, int receiverId, MessageBaseDto dto)
{
bool isExist = await _context.Friends.AnyAsync(x => x.FriendId == receiverId);
if (!isExist) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
var message = _mapper.Map<Message>(dto);
message.StreamKey = StreamKeyBuilder.Private(senderId, receiverId);
message.SequenceId = await _sequenceIdService.GetNextSquenceIdAsync(message.StreamKey);
await _endpoint.Publish(_mapper.Map<MessageCreatedEvent>(message));
return _mapper.Map<MessageBaseVo>(message);
}
#endregion
}
}
using AutoMapper;
using IM_API.Application.Interfaces;
using IM_API.Domain.Events;
using IM_API.Dtos;
using IM_API.Dtos.Message;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
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;
namespace IM_API.Services
{
public class MessageService : IMessageSevice
{
private readonly ImContext _context;
private readonly ILogger<MessageService> _logger;
private readonly IMapper _mapper;
//废弃,此处已使用rabbitMQ替代
//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,
IUserService userService
)
{
_context = context;
_logger = logger;
_mapper = mapper;
//_eventBus = eventBus;
_endpoint = publishEndpoint;
_sequenceIdService = sequenceIdService;
_userService = userService;
}
public async Task<List<MessageBaseVo>> GetMessagesAsync(int userId,MessageQueryDto dto)
{
//获取会话信息,用于获取双方聊天的唯一标识streamkey
Conversation? conversation = await _context.Conversations.FirstOrDefaultAsync(
x => x.Id == dto.ConversationId && x.UserId == userId
);
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)
baseQuery = baseQuery.Where(m => m.SequenceId < dto.Cursor.Value);
var list = await baseQuery
.OrderByDescending(m => m.SequenceId) // 最新消息在最前
.Take(dto.Limit)
.Select(m => _mapper.Map<MessageBaseVo>(m))
.ToListAsync();
messages = list.OrderBy(s => s.SequenceId).ToList();
}
else // After: 找比锚点大的,按正序排(用于补洞或刷新)
{
// 如果 Cursor 为空且是 After,逻辑上说不通,通常直接返回空或报错
if (!dto.Cursor.HasValue) return new List<MessageBaseVo>();
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)
{
throw new NotImplementedException();
}
public Task<List<MessageBaseDto>> GetUnreadMessagesAsync(int userId)
{
throw new NotImplementedException();
}
public Task<bool> MarkAsReadAsync(int userId, long messageId)
{
throw new NotImplementedException();
}
public Task<bool> MarkConversationAsReadAsync(int userId, int? userBId, int? groupId)
{
throw new NotImplementedException();
}
public Task<bool> RecallMessageAsync(int userId, int messageId)
{
throw new NotImplementedException();
}
public async Task MakeMessageAsync(Message message)
{
_context.Messages.Add(message);
await _context.SaveChangesAsync();
}
#region
public async Task<MessageBaseVo> SendGroupMessageAsync(int senderId, int groupId, MessageBaseDto dto)
{
//判断群存在
var isExist = await _context.Groups.AnyAsync(x => x.Id == groupId);
if (!isExist) throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
//判断是否是群成员
var isMember = await _context.GroupMembers.AnyAsync(x => x.GroupId == groupId && x.UserId == senderId);
if (!isMember) throw new BaseException(CodeDefine.NO_GROUP_PERMISSION);
var message = _mapper.Map<Message>(dto);
message.StreamKey = StreamKeyBuilder.Group(groupId);
message.SequenceId = await _sequenceIdService.GetNextSquenceIdAsync(message.StreamKey);
await _endpoint.Publish(_mapper.Map<MessageCreatedEvent>(message));
return _mapper.Map<MessageBaseVo>(message);
}
#endregion
#region
public async Task<MessageBaseVo> SendPrivateMessageAsync(int senderId, int receiverId, MessageBaseDto dto)
{
bool isExist = await _context.Friends.AnyAsync(x => x.FriendId == receiverId);
if (!isExist) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND);
var message = _mapper.Map<Message>(dto);
message.StreamKey = StreamKeyBuilder.Private(senderId, receiverId);
message.SequenceId = await _sequenceIdService.GetNextSquenceIdAsync(message.StreamKey);
await _endpoint.Publish(_mapper.Map<MessageCreatedEvent>(message));
return _mapper.Map<MessageBaseVo>(message);
}
#endregion
}
}
+62 -62
View File
@@ -1,62 +1,62 @@
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
namespace IM_API.Services
{
public class RedisCacheService:ICacheService
{
private readonly IDistributedCache _cache;
public RedisCacheService(IDistributedCache cache)
{
_cache = cache;
}
public async Task<T?> GetAsync<T>(string key)
{
var valueBytes= await _cache.GetAsync(key);
if (valueBytes is null || valueBytes.Length == 0) return default;
return JsonSerializer.Deserialize<T>(valueBytes);
}
public async Task<User?> GetUserCacheAsync(string username)
{
var usernameKey = RedisKeys.GetUserinfoKeyByUsername(username);
var userid = await GetAsync<string>(usernameKey);
if (userid is null) return default;
var key = RedisKeys.GetUserinfoKey(userid);
return await GetAsync<User>(key);
}
public async Task RemoveAsync(string key) => await _cache.RemoveAsync(key);
public async Task RemoveUserCacheAsync(string username)
{
var usernameKey = RedisKeys.GetUserinfoKeyByUsername(username);
var userid = await GetAsync<string>(usernameKey);
if (userid is null) return;
var key = RedisKeys.GetUserinfoKey(userid);
await RemoveAsync(key);
}
public async Task SetAsync<T>(string key, T value, TimeSpan? expiration = null)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expiration ?? TimeSpan.FromHours(1)
};
var valueBytes = JsonSerializer.SerializeToUtf8Bytes(value);
await _cache.SetAsync(key, valueBytes, options);
}
public async Task SetUserCacheAsync(User user)
{
var idKey = RedisKeys.GetUserinfoKey(user.Id.ToString());
await SetAsync(idKey, user);
var usernameKey = RedisKeys.GetUserinfoKeyByUsername(user.Username);
await SetAsync(usernameKey, user.Id.ToString());
}
}
}
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
namespace IM_API.Services
{
public class RedisCacheService:ICacheService
{
private readonly IDistributedCache _cache;
public RedisCacheService(IDistributedCache cache)
{
_cache = cache;
}
public async Task<T?> GetAsync<T>(string key)
{
var valueBytes= await _cache.GetAsync(key);
if (valueBytes is null || valueBytes.Length == 0) return default;
return JsonSerializer.Deserialize<T>(valueBytes);
}
public async Task<User?> GetUserCacheAsync(string username)
{
var usernameKey = RedisKeys.GetUserinfoKeyByUsername(username);
var userid = await GetAsync<string>(usernameKey);
if (userid is null) return default;
var key = RedisKeys.GetUserinfoKey(userid);
return await GetAsync<User>(key);
}
public async Task RemoveAsync(string key) => await _cache.RemoveAsync(key);
public async Task RemoveUserCacheAsync(string username)
{
var usernameKey = RedisKeys.GetUserinfoKeyByUsername(username);
var userid = await GetAsync<string>(usernameKey);
if (userid is null) return;
var key = RedisKeys.GetUserinfoKey(userid);
await RemoveAsync(key);
}
public async Task SetAsync<T>(string key, T value, TimeSpan? expiration = null)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expiration ?? TimeSpan.FromHours(1)
};
var valueBytes = JsonSerializer.SerializeToUtf8Bytes(value);
await _cache.SetAsync(key, valueBytes, options);
}
public async Task SetUserCacheAsync(User user)
{
var idKey = RedisKeys.GetUserinfoKey(user.Id.ToString());
await SetAsync(idKey, user);
var usernameKey = RedisKeys.GetUserinfoKeyByUsername(user.Username);
await SetAsync(usernameKey, user.Id.ToString());
}
}
}
@@ -1,66 +1,66 @@
using IM_API.Interface.Services;
using Microsoft.AspNetCore.Connections;
using Newtonsoft.Json;
using StackExchange.Redis;
using System.Numerics;
using System.Security.Cryptography;
using System.Text.Json;
namespace IM_API.Services
{
public class RedisRefreshTokenService : IRefreshTokenService
{
private readonly ILogger<RedisRefreshTokenService> _logger;
//redis数据库
private readonly IDatabase _db;
private IConfiguration configuration;
//过期时长
private readonly TimeSpan _refreshTTL;
public RedisRefreshTokenService(ILogger<RedisRefreshTokenService> logger, IConnectionMultiplexer multiplexer, IConfiguration configuration)
{
_logger = logger;
_db = multiplexer.GetDatabase();
this.configuration = configuration;
//设置refresh过期时间
var days = int.Parse(this.configuration["Jwt:RefreshTokenDays"] ?? "30");
_refreshTTL = TimeSpan.FromDays(days);
}
private static string GenerateTokenStr()
{
var bytes = RandomNumberGenerator.GetBytes(32);
return Convert.ToBase64String(bytes);
}
public async Task<string> CreateRefreshTokenAsync(int userId, CancellationToken ct = default)
{
string token = GenerateTokenStr();
var payload = new { UserId = userId,CreateAt = DateTime.Now};
string json = JsonConvert.SerializeObject(payload);
//token写入redis
await _db.StringSetAsync(token,json,_refreshTTL);
return token;
}
public async Task RevokeRefreshTokenAsync(string token, CancellationToken ct = default)
{
await _db.KeyDeleteAsync(token);
}
public async Task<(bool ok, int userId)> ValidateRefreshTokenAsync(string token, CancellationToken ct = default)
{
var json = await _db.StringGetAsync(token);
if (json.IsNullOrEmpty) return (false,-1);
try
{
using var doc = JsonDocument.Parse(json.ToString());
var userId = doc.RootElement.GetProperty("UserId").GetInt32();
return (true,userId);
}
catch
{
return (false,-1);
}
}
}
}
using IM_API.Interface.Services;
using Microsoft.AspNetCore.Connections;
using Newtonsoft.Json;
using StackExchange.Redis;
using System.Numerics;
using System.Security.Cryptography;
using System.Text.Json;
namespace IM_API.Services
{
public class RedisRefreshTokenService : IRefreshTokenService
{
private readonly ILogger<RedisRefreshTokenService> _logger;
//redis数据库
private readonly IDatabase _db;
private IConfiguration configuration;
//过期时长
private readonly TimeSpan _refreshTTL;
public RedisRefreshTokenService(ILogger<RedisRefreshTokenService> logger, IConnectionMultiplexer multiplexer, IConfiguration configuration)
{
_logger = logger;
_db = multiplexer.GetDatabase();
this.configuration = configuration;
//设置refresh过期时间
var days = int.Parse(this.configuration["Jwt:RefreshTokenDays"] ?? "30");
_refreshTTL = TimeSpan.FromDays(days);
}
private static string GenerateTokenStr()
{
var bytes = RandomNumberGenerator.GetBytes(32);
return Convert.ToBase64String(bytes);
}
public async Task<string> CreateRefreshTokenAsync(int userId, CancellationToken ct = default)
{
string token = GenerateTokenStr();
var payload = new { UserId = userId,CreateAt = DateTime.Now};
string json = JsonConvert.SerializeObject(payload);
//token写入redis
await _db.StringSetAsync(token,json,_refreshTTL);
return token;
}
public async Task RevokeRefreshTokenAsync(string token, CancellationToken ct = default)
{
await _db.KeyDeleteAsync(token);
}
public async Task<(bool ok, int userId)> ValidateRefreshTokenAsync(string token, CancellationToken ct = default)
{
var json = await _db.StringGetAsync(token);
if (json.IsNullOrEmpty) return (false,-1);
try
{
using var doc = JsonDocument.Parse(json.ToString());
var userId = doc.RootElement.GetProperty("UserId").GetInt32();
return (true,userId);
}
catch
{
return (false,-1);
}
}
}
}
+46 -46
View File
@@ -1,46 +1,46 @@
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.EntityFrameworkCore;
using RedLockNet;
using StackExchange.Redis;
namespace IM_API.Services
{
public class SequenceIdService : ISequenceIdService
{
private IDatabase _database;
private IDistributedLockFactory _lockFactory;
private ImContext _context;
public SequenceIdService(IConnectionMultiplexer connectionMultiplexer,
IDistributedLockFactory distributedLockFactory, ImContext imContext)
{
_database = connectionMultiplexer.GetDatabase();
_lockFactory = distributedLockFactory;
_context = imContext;
}
public async Task<long> GetNextSquenceIdAsync(string streamKey)
{
string key = RedisKeys.GetSequenceIdKey(streamKey);
string lockKey = RedisKeys.GetSequenceIdLockKey(streamKey);
var exists = await _database.KeyExistsAsync(key);
if (!exists)
{
using (var _lock = await _lockFactory.CreateLockAsync(lockKey, TimeSpan.FromSeconds(5)))
{
if (_lock.IsAcquired)
{
if(!await _database.KeyExistsAsync(key))
{
var max = await _context.Messages
.Where(x => x.StreamKey == streamKey)
.MaxAsync(m => (long?)m.SequenceId) ?? 0;
await _database.StringSetAsync(key, max, TimeSpan.FromDays(7));
}
}
}
}
return await _database.StringIncrementAsync(key);
}
}
}
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.EntityFrameworkCore;
using RedLockNet;
using StackExchange.Redis;
namespace IM_API.Services
{
public class SequenceIdService : ISequenceIdService
{
private IDatabase _database;
private IDistributedLockFactory _lockFactory;
private ImContext _context;
public SequenceIdService(IConnectionMultiplexer connectionMultiplexer,
IDistributedLockFactory distributedLockFactory, ImContext imContext)
{
_database = connectionMultiplexer.GetDatabase();
_lockFactory = distributedLockFactory;
_context = imContext;
}
public async Task<long> GetNextSquenceIdAsync(string streamKey)
{
string key = RedisKeys.GetSequenceIdKey(streamKey);
string lockKey = RedisKeys.GetSequenceIdLockKey(streamKey);
var exists = await _database.KeyExistsAsync(key);
if (!exists)
{
using (var _lock = await _lockFactory.CreateLockAsync(lockKey, TimeSpan.FromSeconds(5)))
{
if (_lock.IsAcquired)
{
if(!await _database.KeyExistsAsync(key))
{
var max = await _context.Messages
.Where(x => x.StreamKey == streamKey)
.MaxAsync(m => (long?)m.SequenceId) ?? 0;
await _database.StringSetAsync(key, max, TimeSpan.FromDays(7));
}
}
}
}
return await _database.StringIncrementAsync(key);
}
}
}
+129 -129
View File
@@ -1,129 +1,129 @@
using AutoMapper;
using IM_API.Dtos.User;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;
using System.Text.Json;
namespace IM_API.Services
{
public class UserService : IUserService
{
private readonly ImContext _context;
private readonly ILogger<UserService> _logger;
private readonly IMapper _mapper;
private readonly ICacheService _cacheService;
private readonly IDatabase _redis;
public UserService(
ImContext imContext,ILogger<UserService> logger,
IMapper mapper, ICacheService cacheService, IConnectionMultiplexer connectionMultiplexer)
{
this._context = imContext;
this._logger = logger;
this._mapper = mapper;
_cacheService = cacheService;
_redis = connectionMultiplexer.GetDatabase();
}
#region
public async Task<UserInfoDto> GetUserInfoAsync(int userId)
{
//查询redis缓存,如果存在直接返回不走查库逻辑
var key = RedisKeys.GetUserinfoKey(userId.ToString());
var userinfoCache = await _cacheService.GetAsync<User>(key);
if (userinfoCache != null) return _mapper.Map<UserInfoDto>(userinfoCache);
//无缓存查库
var user = await _context.Users
.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
{
throw new BaseException(CodeDefine.USER_NOT_FOUND);
}
await _cacheService.SetUserCacheAsync(user);
return _mapper.Map<UserInfoDto>(user);
}
#endregion
#region
public async Task<UserInfoDto> GetUserInfoByUsernameAsync(string username)
{
var userinfo = await _cacheService.GetUserCacheAsync(username);
if (userinfo != null) return _mapper.Map<UserInfoDto>(userinfo);
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == username);
if (user == null)
{
throw new BaseException(CodeDefine.USER_NOT_FOUND);
}
await _cacheService.SetUserCacheAsync(user);
return _mapper.Map<UserInfoDto>(user);
}
#endregion
#region
public async Task<bool> ResetPasswordAsync(int userId, string oldPassword, string password)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
//验证原密码
if (user.Password != oldPassword) throw new BaseException(CodeDefine.PASSWORD_ERROR);
user.Password = password;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region 线
public async Task<bool> UpdateOlineStatusAsync(int userId,UserOnlineStatus onlineStatus)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
user.OnlineStatusEnum = onlineStatus;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<UserInfoDto> UpdateUserAsync(int userId,UpdateUserDto dto)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
_mapper.Map(dto,user);
await _context.SaveChangesAsync();
await _cacheService.SetUserCacheAsync(user);
return _mapper.Map<UserInfoDto>(user);
}
#endregion
#region
public async Task<List<UserInfoDto>> GetUserInfoListAsync(List<int> ids)
{
//读取缓存中存在的用户,存在直接添加到结果列表
var idKeyArr = ids.Select(s => (RedisKey)RedisKeys.GetUserinfoKey(s.ToString())).ToArray();
var values = await _redis.StringGetAsync(idKeyArr);
List<User> results = [];
List<int> missingIds = [];
for(int i = 0; i < ids.Count; i++)
{
if (values[i].HasValue)
{
results.Add(JsonSerializer.Deserialize<User>(values[i]));
}
else
{
missingIds.Add(ids[i]);
}
}
//如果存在没有缓存的用户则进行查库
if (missingIds.Any())
{
var dbUsers = await _context.Users
.Where(x => ids.Contains(x.Id)).ToListAsync();
results.AddRange(dbUsers);
var setTasks = dbUsers.Select(s => _cacheService.SetUserCacheAsync(s)).ToList();
await Task.WhenAll(setTasks);
}
return _mapper.Map<List<UserInfoDto>>(results);
}
#endregion
}
}
using AutoMapper;
using IM_API.Dtos.User;
using IM_API.Exceptions;
using IM_API.Interface.Services;
using IM_API.Models;
using IM_API.Tools;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;
using System.Text.Json;
namespace IM_API.Services
{
public class UserService : IUserService
{
private readonly ImContext _context;
private readonly ILogger<UserService> _logger;
private readonly IMapper _mapper;
private readonly ICacheService _cacheService;
private readonly IDatabase _redis;
public UserService(
ImContext imContext,ILogger<UserService> logger,
IMapper mapper, ICacheService cacheService, IConnectionMultiplexer connectionMultiplexer)
{
this._context = imContext;
this._logger = logger;
this._mapper = mapper;
_cacheService = cacheService;
_redis = connectionMultiplexer.GetDatabase();
}
#region
public async Task<UserInfoDto> GetUserInfoAsync(int userId)
{
//查询redis缓存,如果存在直接返回不走查库逻辑
var key = RedisKeys.GetUserinfoKey(userId.ToString());
var userinfoCache = await _cacheService.GetAsync<User>(key);
if (userinfoCache != null) return _mapper.Map<UserInfoDto>(userinfoCache);
//无缓存查库
var user = await _context.Users
.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
{
throw new BaseException(CodeDefine.USER_NOT_FOUND);
}
await _cacheService.SetUserCacheAsync(user);
return _mapper.Map<UserInfoDto>(user);
}
#endregion
#region
public async Task<UserInfoDto> GetUserInfoByUsernameAsync(string username)
{
var userinfo = await _cacheService.GetUserCacheAsync(username);
if (userinfo != null) return _mapper.Map<UserInfoDto>(userinfo);
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == username);
if (user == null)
{
throw new BaseException(CodeDefine.USER_NOT_FOUND);
}
await _cacheService.SetUserCacheAsync(user);
return _mapper.Map<UserInfoDto>(user);
}
#endregion
#region
public async Task<bool> ResetPasswordAsync(int userId, string oldPassword, string password)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
//验证原密码
if (user.Password != oldPassword) throw new BaseException(CodeDefine.PASSWORD_ERROR);
user.Password = password;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region 线
public async Task<bool> UpdateOlineStatusAsync(int userId,UserOnlineStatus onlineStatus)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
user.OnlineStatusEnum = onlineStatus;
await _context.SaveChangesAsync();
return true;
}
#endregion
#region
public async Task<UserInfoDto> UpdateUserAsync(int userId,UpdateUserDto dto)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user is null) throw new BaseException(CodeDefine.USER_NOT_FOUND);
_mapper.Map(dto,user);
await _context.SaveChangesAsync();
await _cacheService.SetUserCacheAsync(user);
return _mapper.Map<UserInfoDto>(user);
}
#endregion
#region
public async Task<List<UserInfoDto>> GetUserInfoListAsync(List<int> ids)
{
//读取缓存中存在的用户,存在直接添加到结果列表
var idKeyArr = ids.Select(s => (RedisKey)RedisKeys.GetUserinfoKey(s.ToString())).ToArray();
var values = await _redis.StringGetAsync(idKeyArr);
List<User> results = [];
List<int> missingIds = [];
for(int i = 0; i < ids.Count; i++)
{
if (values[i].HasValue)
{
results.Add(JsonSerializer.Deserialize<User>(values[i]));
}
else
{
missingIds.Add(ids[i]);
}
}
//如果存在没有缓存的用户则进行查库
if (missingIds.Any())
{
var dbUsers = await _context.Users
.Where(x => ids.Contains(x.Id)).ToListAsync();
results.AddRange(dbUsers);
var setTasks = dbUsers.Select(s => _cacheService.SetUserCacheAsync(s)).ToList();
await Task.WhenAll(setTasks);
}
return _mapper.Map<List<UserInfoDto>>(results);
}
#endregion
}
}