376 lines
15 KiB
C#
376 lines
15 KiB
C#
using AutoMapper;
|
|
using AutoMapper.QueryableExtensions;
|
|
using IM_API.Domain.Events;
|
|
using IM_API.Dtos.Group;
|
|
using IM_API.Dtos.User;
|
|
using IM_API.Exceptions;
|
|
using IM_API.Interface.Services;
|
|
using IM_API.Models;
|
|
using IM_API.Tools;
|
|
using IM_API.VOs.Group;
|
|
using MassTransit;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System;
|
|
using System.Linq;
|
|
|
|
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 GroupRequest
|
|
{
|
|
Created = DateTime.UtcNow,
|
|
GroupId = group.Id,
|
|
UserId = id,
|
|
InviteUserId = userId,
|
|
StateEnum = GroupRequestState.TargetPending
|
|
}).ToList();
|
|
_context.GroupRequests.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 = 1, int limit = 50, bool desc = false)
|
|
{
|
|
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.GroupRequests
|
|
.FirstOrDefaultAsync(x => x.UserId == userid && x.StateEnum == GroupRequestState.TargetPending)
|
|
?? throw new BaseException(CodeDefine.INVALID_ACTION);
|
|
if (!(dto.Action == GroupRequestState.TargetPassed ||
|
|
dto.Action == GroupRequestState.TargetDeclined))
|
|
return;
|
|
inviteInfo.StateEnum = dto.Action;
|
|
_context.GroupRequests.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.InviteUserId!.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;
|
|
}
|
|
|
|
public async Task<List<GroupMemberVo>> GetGroupMembers(int userId, int groupId)
|
|
{
|
|
var members = await _context.GroupMembers
|
|
.Where(x => x.GroupId == groupId).ToListAsync();
|
|
if (members is null || members.Count() == 0)
|
|
return [];
|
|
|
|
var users = await _userService.GetUserInfoListAsync(members.Select(x => x.UserId).ToList());
|
|
return users.Zip(members, (u, m) =>
|
|
{
|
|
var user = _mapper.Map<GroupMemberVo>(u);
|
|
_mapper.Map(m, user);
|
|
return user;
|
|
}).ToList();
|
|
}
|
|
|
|
public async Task<GroupInfoVo> UpdateGroupInfoAsync(int userId, int groupId, GroupUpdateDto updateDto)
|
|
{
|
|
//判断群存在
|
|
var groupInfo = await _context.Groups.FirstOrDefaultAsync(x => x.Id == groupId);
|
|
if (groupInfo is null)
|
|
throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
|
|
|
|
//判断操作者权限
|
|
var memberInfo = await _context.GroupMembers
|
|
.FirstOrDefaultAsync(x => x.UserId == userId && x.GroupId == groupId);
|
|
|
|
if (memberInfo is null || memberInfo.RoleEnum == GroupMemberRole.Normal)
|
|
throw new BaseException(CodeDefine.NO_GROUP_PERMISSION);
|
|
|
|
groupInfo.Name = updateDto.GroupName ?? groupInfo.Name;
|
|
groupInfo.Avatar = updateDto.Avatar ?? groupInfo.Avatar;
|
|
groupInfo.Announcement = updateDto.Description ?? groupInfo.Announcement;
|
|
|
|
|
|
_context.Groups.Update(groupInfo);
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
return _mapper.Map<GroupInfoVo>(groupInfo);
|
|
}
|
|
|
|
public async Task<GroupInfoVo> GetGroupInfoAsync(int groupId)
|
|
{
|
|
var groupInfo = await _context.Groups.FirstOrDefaultAsync(x => x.Id == groupId);
|
|
|
|
if (groupInfo is null)
|
|
throw new BaseException(CodeDefine.GROUP_NOT_FOUND);
|
|
|
|
return _mapper.Map<GroupInfoVo>(groupInfo);
|
|
}
|
|
|
|
public async Task<List<GroupNotificationVo>> GetGroupNotificationAsync(int userId)
|
|
{
|
|
// 1. 查询群请求记录
|
|
var groupList = await _context.GroupMembers
|
|
.Where(x => x.UserId == userId &&
|
|
(x.Role == (sbyte)GroupMemberRole.Master || x.Role == (sbyte)GroupMemberRole.Administrator))
|
|
.Select(s => s.GroupId)
|
|
.ToListAsync();
|
|
|
|
var groupRequest = await _context.GroupRequests
|
|
.Where(x => groupList.Contains(x.GroupId) || x.UserId == userId || x.InviteUserId == userId)
|
|
.OrderByDescending(o => o.Id)
|
|
.ToListAsync();
|
|
|
|
if (!groupRequest.Any()) return new List<GroupNotificationVo>();
|
|
|
|
// 2. 收集所有需要的 ID 并去重
|
|
var userIds = groupRequest.Select(s => s.UserId).Distinct().ToList();
|
|
var inviteUserIds = groupRequest.Where(x => x.InviteUserId != null).Select(s => s.InviteUserId.Value).Distinct().ToList();
|
|
var groupIds = groupRequest.Select(s => s.GroupId).Distinct().ToList();
|
|
|
|
var userList = await _userService.GetUserInfoListAsync(userIds);
|
|
var inviteUserList = await _userService.GetUserInfoListAsync(inviteUserIds);
|
|
var groupInfoList = await _context.Groups
|
|
.Where(x => groupIds.Contains(x.Id))
|
|
.ToListAsync();
|
|
|
|
// 2. 转换为字典
|
|
var userDict = userList.ToDictionary(u => u.Id);
|
|
var inviteUserDict = inviteUserList.ToDictionary(u => u.Id);
|
|
var groupDict = groupInfoList.ToDictionary(g => g.Id);
|
|
|
|
// 3. 组装数据 (Select 逻辑不变)
|
|
return groupRequest.Select(g =>
|
|
{
|
|
var gnv = _mapper.Map<GroupNotificationVo>(g);
|
|
|
|
// 匹配用户信息
|
|
if (userDict.TryGetValue(g.UserId, out var u))
|
|
{
|
|
gnv.UserAvatar = u.Avatar;
|
|
gnv.NickName = u.NickName;
|
|
}
|
|
|
|
// 匹配邀请人信息
|
|
if (g.InviteUserId.HasValue && inviteUserDict.TryGetValue(g.InviteUserId.Value, out var i))
|
|
{
|
|
gnv.InviteUserAvatar = i.Avatar;
|
|
gnv.InviteUserNickname = i.NickName;
|
|
}
|
|
|
|
// 匹配群信息
|
|
if (groupDict.TryGetValue(g.GroupId, out var gi))
|
|
{
|
|
gnv.GroupAvatar = gi.Avatar;
|
|
gnv.GroupName = gi.Name;
|
|
}
|
|
|
|
return gnv;
|
|
}).ToList();
|
|
|
|
}
|
|
}
|
|
}
|