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 _logger; private readonly IPublishEndpoint _endPoint; private readonly IUserService _userService; public GroupService(ImContext context, IMapper mapper, ILogger logger, IPublishEndpoint publishEndpoint, IUserService userService) { _context = context; _mapper = mapper; _logger = logger; _endPoint = publishEndpoint; _userService = userService; } private async Task> validFriendshipAsync (int userId, List 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 CreateGroupAsync(int userId, GroupCreateDto groupCreateDto) { List userIds = groupCreateDto.UserIDs ?? []; using var transaction = await _context.Database.BeginTransactionAsync(); try { //先创建群 DateTime dateTime = DateTime.Now; Group group = _mapper.Map(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(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 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> 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(_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> 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(u); _mapper.Map(m, user); return user; }).ToList(); } public async Task 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(groupInfo); } public async Task 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(groupInfo); } public async Task> 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(); // 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(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(); } } }