using FreeSql; using LiveForum.Code.Base; using LiveForum.Code.JwtInfrastructure; using LiveForum.IService.Messages; using LiveForum.IService.Others; using LiveForum.IService.Users; using LiveForum.Model; using LiveForum.Model.Dto.Messages; using LiveForum.Model.Dto.Users; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LiveForum.Service.Messages { public class MessagesService : IMessagesService { private readonly JwtUserInfoModel _userInfoModel; private readonly IBaseRepository _messagesRepository; private readonly IBaseRepository _notificationsRepository; private readonly IBaseRepository _usersRepository; private readonly IBaseRepository _commentsRepository; private readonly IBaseRepository _postsRepository; private readonly IBaseRepository _likesRepository; private readonly IBaseRepository _followsRepository; private readonly IBaseRepository _participantsRepository; private readonly IUserInfoService _userInfoService; private readonly IFreeSql _freeSql; /// /// 构造函数 /// /// JWT用户信息模型 /// 消息仓储 /// 系统通知仓储 /// 用户仓储 /// 评论仓储 /// 帖子仓储 /// 点赞仓储 /// 关注仓储 /// 系统通知参与记录仓储 /// 用户信息转换服务 /// FreeSql实例 public MessagesService( JwtUserInfoModel userInfoModel, IBaseRepository messagesRepository, IBaseRepository notificationsRepository, IBaseRepository usersRepository, IBaseRepository commentsRepository, IBaseRepository postsRepository, IBaseRepository likesRepository, IBaseRepository followsRepository, IBaseRepository participantsRepository, IUserInfoService userInfoService, IFreeSql freeSql) { _userInfoModel = userInfoModel; _messagesRepository = messagesRepository; _notificationsRepository = notificationsRepository; _usersRepository = usersRepository; _commentsRepository = commentsRepository; _postsRepository = postsRepository; _likesRepository = likesRepository; _followsRepository = followsRepository; _participantsRepository = participantsRepository; _userInfoService = userInfoService; _freeSql = freeSql; } #region 快照相关辅助方法 /// /// 尝试从快照反序列化发送者信息 /// private UserInfoDto TryDeserializeSenderInfo(string senderInfoJson) { if (string.IsNullOrEmpty(senderInfoJson)) { return null; } try { var userInfo = JsonConvert.DeserializeObject(senderInfoJson); return userInfo; } catch { return null; } } /// /// 尝试从快照反序列化内容信息 /// private MessageRelatedContentDto TryDeserializeContentSnapshot(string contentSnapshotJson) { if (string.IsNullOrEmpty(contentSnapshotJson)) { return null; } try { var snapshot = JsonConvert.DeserializeObject(contentSnapshotJson); if (snapshot == null) return null; return new MessageRelatedContentDto { PostId = snapshot.PostId, PostTitle = snapshot.PostTitle, CommentContent = snapshot.CommentContent }; } catch { return null; } } /// /// 批量回填消息快照数据 /// private async Task BackfillMessageSnapshots( List messages, Dictionary users, Dictionary posts, Dictionary comments) { var messagesToUpdate = new List(); foreach (var message in messages) { bool needUpdate = false; // 回填发送者信息快照 if (string.IsNullOrEmpty(message.SenderInfo) && message.SenderId.HasValue) { if (users.ContainsKey(message.SenderId.Value)) { var user = users[message.SenderId.Value]; var senderInfo = await _userInfoService.ToUserInfoDtoAsync(user); message.SenderInfo = JsonConvert.SerializeObject(senderInfo, new JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }); needUpdate = true; } else { // 发送者已不存在,保存占位快照 var deletedUserInfo = new UserInfoDto { UserId = 0, NickName = "已删除的用户", Avatar = "", IsVip = false, IsCertified = false, CertifiedType = null }; message.SenderInfo = JsonConvert.SerializeObject(deletedUserInfo, new JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }); needUpdate = true; } } // 回填内容快照 if (string.IsNullOrEmpty(message.ContentSnapshot) && message.ContentId.HasValue) { var contentSnapshot = new Model.Events.ContentSnapshot(); if (message.ContentType == 1 && posts.ContainsKey(message.ContentId.Value)) { // 帖子快照 var post = posts[message.ContentId.Value]; contentSnapshot.PostId = post.Id; contentSnapshot.PostTitle = post.Title; message.ContentSnapshot = JsonConvert.SerializeObject(contentSnapshot); needUpdate = true; } else if (message.ContentType == 2 && comments.ContainsKey(message.ContentId.Value)) { // 评论快照 var comment = comments[message.ContentId.Value]; contentSnapshot.CommentId = comment.Id; contentSnapshot.CommentContent = comment.Content; // 同时获取帖子标题 if (posts.ContainsKey(comment.PostId)) { contentSnapshot.PostId = comment.PostId; contentSnapshot.PostTitle = posts[comment.PostId].Title; } message.ContentSnapshot = JsonConvert.SerializeObject(contentSnapshot); needUpdate = true; } else if (!posts.ContainsKey(message.ContentId.Value) && !comments.ContainsKey(message.ContentId.Value)) { // 内容已被删除,保存占位快照 contentSnapshot.PostTitle = "[已删除]"; contentSnapshot.CommentContent = "[已删除]"; message.ContentSnapshot = JsonConvert.SerializeObject(contentSnapshot); needUpdate = true; } } if (needUpdate) { messagesToUpdate.Add(message); } } // 批量更新数据库 if (messagesToUpdate.Any()) { await _messagesRepository.UpdateAsync(messagesToUpdate); } } #endregion /// /// 确保系统通知消息已创建 /// 查询未创建消息记录的系统通知,并根据目标用户类型筛选,为用户创建消息记录 /// /// 用户ID private async Task EnsureSystemNotificationsCreated(long userId) { var now = DateTime.Now; // 查询未创建消息记录的系统通知 // SQL: select n.* from T_SystemNotifications n // left join T_Messages m on n.Id=m.SenderId and m.MessageType=3 and m.ReceiverId=userId // where m.Id is null and (ExpireTime>NOW() or ExpireTime is null) and PublishTime<=NOW() var allNotificationsWithoutMessages = _freeSql.Select() .LeftJoin((n, m) => n.Id == m.SenderId && m.MessageType == 3 && m.ReceiverId == userId) .Where((n, m) => m == null && (n.ExpireTime > now || n.ExpireTime == null) && n.PublishTime <= now && n.IsActive) .ToList((a, b) => a); if (!allNotificationsWithoutMessages.Any()) { return; } // 根据目标用户类型筛选通知 var notificationsForUser = new List(); foreach (var notification in allNotificationsWithoutMessages) { // TargetUserType: null或0-所有用户,1-指定用户列表,2-按认证类型筛选 if (notification.TargetUserType == null || notification.TargetUserType == 0) { // 所有用户 notificationsForUser.Add(notification); } else if (notification.TargetUserType == 1) { // 指定用户列表 if (!string.IsNullOrEmpty(notification.TargetUserIds)) { try { var targetUserIds = JsonConvert.DeserializeObject>(notification.TargetUserIds); if (targetUserIds != null && targetUserIds.Contains(userId)) { notificationsForUser.Add(notification); } } catch { // JSON解析失败,跳过该通知 } } } else if (notification.TargetUserType == 2) { // 按认证类型筛选 if (notification.TargetCertifiedType.HasValue) { var user = await _usersRepository.Select.Where(u => u.Id == userId).FirstAsync(); if (user != null) { // 直接比较认证类型ID if (user.CertifiedType.HasValue && user.CertifiedType.Value == notification.TargetCertifiedType.Value) { notificationsForUser.Add(notification); } } } } } // 如果存在未创建消息的系统通知,需要为它们创建消息记录 if (notificationsForUser.Any()) { var newMessages = notificationsForUser.Select(n => new T_Messages { ReceiverId = userId, SenderId = n.Id, // 使用通知ID作为SenderId MessageType = 3, // 系统消息 MessageContent = $"{n.Content}", IsRead = false, CreatedAt = DateTime.Now, MessageTitle = n.Title, }).ToList(); await _messagesRepository.InsertAsync(newMessages); } } public async Task> GetMessages(GetMessagesReq request) { var currentUserId = (long)_userInfoModel.UserId; // 构建查询条件 var query = _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId); // 过滤消息类型 if (request.MessageType.HasValue) { query = query.Where(x => x.MessageType == request.MessageType.Value); if (request.MessageType == 3) { // 确保系统消息已创建 await EnsureSystemNotificationsCreated(currentUserId); } } // 过滤是否已读 if (request.IsRead.HasValue) { query = query.Where(x => x.IsRead == request.IsRead.Value); } // 排序 query = query.OrderByDescending(x => x.CreatedAt); // 获取总数 var total = await query.CountAsync(); // 分页 var messages = await query .Skip((request.PageIndex - 1) * request.PageSize) .Take(request.PageSize) .ToListAsync(); if (!messages.Any()) { // 获取未读总数 var unreadCount = await query.Where(x => !x.IsRead) .CountAsync(); return new BaseResponse(new GetMessagesRespDto { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = (int)total, TotalPages = (int)Math.Ceiling((double)total / request.PageSize), UnreadCount = (int)unreadCount, Items = new List() }); } // 分类消息:有快照的和没快照的 var messagesWithSnapshot = messages.Where(x => !string.IsNullOrEmpty(x.SenderInfo) || x.MessageType == 3).ToList(); var messagesWithoutSnapshot = messages.Where(x => string.IsNullOrEmpty(x.SenderInfo) && x.MessageType != 3).ToList(); // 对于没有快照的消息,收集需要查询的ID var senderIds = messagesWithoutSnapshot.Where(x => x.SenderId.HasValue).Select(x => x.SenderId!.Value).Distinct().ToList(); var contentIds = messagesWithoutSnapshot.Where(x => x.ContentId.HasValue && string.IsNullOrEmpty(x.ContentSnapshot)) .Select(x => new { x.ContentType, x.ContentId!.Value }).ToList(); // 获取用户信息(仅查询无快照的消息需要的) var users = new Dictionary(); if (senderIds.Any()) { var usersList = await _usersRepository.Select .Where(x => senderIds.Contains(x.Id)) .ToListAsync(); foreach (var user in usersList) { users[user.Id] = user; } } // 获取帖子信息 var postIds = contentIds.Where(x => x.ContentType == 1).Select(x => x.Value).Distinct().ToList(); var posts = new Dictionary(); if (postIds.Any()) { var postsList = await _postsRepository.Select .Where(x => postIds.Contains(x.Id)) .ToListAsync(); foreach (var post in postsList) { posts[post.Id] = post; } } // 获取评论信息 var commentIds = contentIds.Where(x => x.ContentType == 2).Select(x => x.Value).Distinct().ToList(); var comments = new Dictionary(); if (commentIds.Any()) { var commentsList = await _commentsRepository.Select .Where(x => commentIds.Contains(x.Id)) .ToListAsync(); foreach (var comment in commentsList) { comments[comment.Id] = comment; } // 同时获取评论关联的帖子 var commentPostIds = commentsList.Select(x => x.PostId).Where(x => !posts.ContainsKey(x)).Distinct().ToList(); if (commentPostIds.Any()) { var commentPostsList = await _postsRepository.Select .Where(x => commentPostIds.Contains(x.Id)) .ToListAsync(); foreach (var post in commentPostsList) { posts[post.Id] = post; } } } // 批量回填无快照的消息 if (messagesWithoutSnapshot.Any()) { await BackfillMessageSnapshots(messagesWithoutSnapshot, users, posts, comments); } // 获取系统通知信息(仅当有系统消息时) var systemNotificationIds = messages.Where(x => x.MessageType == 3 && x.SenderId.HasValue).Select(x => x.SenderId!.Value).Distinct().ToList(); var systemNotifications = new Dictionary(); if (systemNotificationIds.Any()) { var notificationsList = await _notificationsRepository.Select .Where(x => systemNotificationIds.Contains(x.Id)) .ToListAsync(); foreach (var notification in notificationsList) { systemNotifications[notification.Id] = notification; } } // 获取用户参与状态(仅当有活动通知时) var activityNotificationIds = systemNotifications.Values .Where(n => n.NotificationType == 2) .Select(n => n.Id) .ToList(); var participants = new Dictionary(); if (activityNotificationIds.Any()) { var participantsList = await _participantsRepository.Select .Where(x => activityNotificationIds.Contains(x.NotificationId) && x.UserId == currentUserId) .ToListAsync(); foreach (var participant in participantsList) { participants[participant.NotificationId] = participant; } } var unreadCountTotal = await _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && !x.IsRead) .CountAsync(); // 批量转换用户信息(自动获取认证类型数据) var usersDict = await _userInfoService.ToUserInfoDtoDictionaryAsync(users.Values); // 构建返回数据 var items = messages.Select(message => { // 优先使用快照,如果没有快照则使用实时查询的数据 UserInfoDto sender = null; if (message.MessageType != 3) { // 先尝试从快照反序列化(非系统消息) sender = TryDeserializeSenderInfo(message.SenderInfo); // 如果快照为空,尝试从实时数据获取 if (sender == null && message.SenderId.HasValue && usersDict.ContainsKey(message.SenderId.Value)) { sender = usersDict[message.SenderId.Value]; } } sender ??= new UserInfoDto(); // 优先使用内容快照 var relatedContent = TryDeserializeContentSnapshot(message.ContentSnapshot); // 如果快照为空,尝试从实时数据获取 if (relatedContent == null && message.ContentId.HasValue) { relatedContent = new MessageRelatedContentDto(); if (message.ContentType == 1 && posts.ContainsKey(message.ContentId.Value)) { var post = posts[message.ContentId.Value]; relatedContent.PostId = post.Id; relatedContent.PostTitle = post.Title; } else if (message.ContentType == 2 && comments.ContainsKey(message.ContentId.Value)) { var comment = comments[message.ContentId.Value]; relatedContent.CommentContent = comment.Content; if (posts.ContainsKey(comment.PostId)) { relatedContent.PostId = comment.PostId; relatedContent.PostTitle = posts[comment.PostId].Title; } } } relatedContent ??= new MessageRelatedContentDto(); // 组装活动信息(仅当系统消息为活动类型时) ActivityInfoDto? activityInfo = null; if (message.MessageType == 3 && message.SenderId.HasValue && systemNotifications.ContainsKey(message.SenderId.Value)) { var notification = systemNotifications[message.SenderId.Value]; if (notification.NotificationType == 2) { var isParticipated = participants.ContainsKey(notification.Id); if (isParticipated) { var _participants = participants[notification.Id]; var status = _participants?.Status ?? 0; if (notification?.ActivityEndTime != null && notification?.ActivityEndTime < DateTime.Now) { //活动已经结束 status = 3; } activityInfo = new ActivityInfoDto { IsActivity = true, ActivityEndTime = notification.ActivityEndTime, ActivityButtonText = "已提交", IsParticipated = isParticipated, ParticipatedAt = _participants.SubmittedAt, ContactInfo = _participants.ContactInfo ?? "", ShippingAddress = _participants.ShippingAddress ?? "", Status = status }; } else { activityInfo = new ActivityInfoDto { IsActivity = true, ActivityEndTime = notification.ActivityEndTime, ActivityButtonText = notification.ActivityButtonText ?? "参与活动", IsParticipated = isParticipated, ParticipatedAt = null, ContactInfo = "", ShippingAddress = "", Status = 0 }; } } } return new MessageListItemDto { MessageId = message.Id, MessageType = message.MessageType, MessageTitle = message.MessageTitle ?? "", MessageContent = message.MessageContent ?? "", ContentType = message.ContentType, ContentId = message.ContentId ?? 0, IsRead = message.IsRead, CreatedAt = message.CreatedAt, Sender = sender, RelatedContent = relatedContent, ActivityInfo = activityInfo }; }).ToList(); return new BaseResponse(new GetMessagesRespDto { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = (int)total, TotalPages = (int)Math.Ceiling((double)total / request.PageSize), UnreadCount = (int)unreadCountTotal, Items = items }); } public async Task> GetSystemNotifications(GetSystemNotificationsReq request) { var currentUserId = (long)_userInfoModel.UserId; var now = DateTime.Now; // 构建查询条件 var query = _notificationsRepository.Select .Where(x => x.IsActive && (x.ExpireTime == null || x.ExpireTime > now) && (x.PublishTime == null || x.PublishTime <= now)); // 过滤通知类型 //if (request.NotificationType.HasValue) //{ // query = query.Where(x => x.NotificationType == request.NotificationType.Value); //} // 排序 query = query.OrderByDescending(x => x.PublishTime ?? x.CreatedAt); // 获取总数 var total = await query.CountAsync(); // 分页 var notifications = await query .Skip((request.PageIndex - 1) * request.PageSize) .Take(request.PageSize) .ToListAsync(); var items = notifications.Select(n => new SystemNotificationDto { NotificationId = n.Id, Title = n.Title, Content = n.Content, //NotificationType = n.NotificationType, PublishTime = n.PublishTime ?? n.CreatedAt, ExpireTime = n.ExpireTime }).ToList(); return new BaseResponse(new GetSystemNotificationsRespDto { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = (int)total, TotalPages = (int)Math.Ceiling((double)total / request.PageSize), Items = items }); } public async Task> ReadMessage(ReadMessageReq request) { var currentUserId = (long)_userInfoModel.UserId; // 获取消息 var message = await _messagesRepository.Select .Where(x => x.Id == request.MessageId && x.ReceiverId == currentUserId) .FirstAsync(); if (message == null) { return new BaseResponse(ResponseCode.Error, "消息不存在或无权限访问"); } // 标记为已读 message.IsRead = true; message.ReadAt = DateTime.Now; await _messagesRepository.UpdateAsync(message); return new BaseResponse(new ReadMessageRespDto { MessageId = message.Id, IsRead = true, ReadAt = message.ReadAt!.Value }); } public async Task> ReadBatchMessages(ReadBatchMessagesReq request) { var currentUserId = (long)_userInfoModel.UserId; if (request.MessageIds == null || !request.MessageIds.Any()) { return new BaseResponse(ResponseCode.Error, "消息ID列表不能为空"); } if (request.MessageIds.Count > 100) { return new BaseResponse(ResponseCode.Error, "批量操作数量超过限制"); } var now = DateTime.Now; // 更新消息状态 var result = await _messagesRepository.UpdateDiy .Set(x => x.IsRead == true) .Set(x => x.ReadAt == now) .Where(x => x.ReceiverId == currentUserId && request.MessageIds.Contains(x.Id)) .ExecuteAffrowsAsync(); return new BaseResponse(new ReadBatchMessagesRespDto { SuccessCount = (int)result, ReadAt = now }); } public async Task> ReadAllMessages(ReadAllMessagesReq request) { var currentUserId = (long)_userInfoModel.UserId; var now = DateTime.Now; // 构建查询条件 ISelect query = _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && !x.IsRead); // 如果指定了消息类型 if (request.MessageType.HasValue) { query = query.Where(x => x.MessageType == request.MessageType.Value); } // 获取未读消息 var messages = await query.ToListAsync(); if (!messages.Any()) { return new BaseResponse(new ReadAllMessagesRespDto { SuccessCount = 0, ReadAt = now }); } // 批量更新消息 foreach (var message in messages) { message.IsRead = true; message.ReadAt = now; } await _messagesRepository.UpdateAsync(messages); return new BaseResponse(new ReadAllMessagesRespDto { SuccessCount = messages.Count, ReadAt = now }); } public async Task DeleteMessage(DeleteMessageReq request) { var currentUserId = (long)_userInfoModel.UserId; // 检查消息是否存在且属于当前用户 var message = await _messagesRepository.Select .Where(x => x.Id == request.MessageId && x.ReceiverId == currentUserId) .FirstAsync(); if (message == null) { return new BaseResponseBool { Code = ResponseCode.Error, Message = "消息不存在或无权限删除" }; } // 软删除消息(设置为已读并标记为已删除,或者物理删除) await _messagesRepository.DeleteAsync(message); return new BaseResponseBool { Code = ResponseCode.Success, Data = true }; } public async Task> GetUnreadCount() { var currentUserId = (long)_userInfoModel.UserId; // 确保系统通知消息已创建,以便统计未读数量 await EnsureSystemNotificationsCreated(currentUserId); var result = await _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && !x.IsRead) .ToListAsync(x => new { x.MessageType }); var totalUnread = result.Count; var commentReplyUnread = result.Count(x => x.MessageType == 1); var likeUnread = result.Count(x => x.MessageType == 2); var systemUnread = result.Count(x => x.MessageType == 3); return new BaseResponse(new UnreadCountRespDto { TotalUnread = totalUnread, CommentReplyUnread = commentReplyUnread, LikeUnread = likeUnread, SystemUnread = systemUnread }); } public async Task> GetCommentRepliesMessages(GetCommentRepliesMessagesReq request) { var currentUserId = (long)_userInfoModel.UserId; // 构建查询条件 - 只获取评论回复消息 var query = _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && x.MessageType == 1); // 过滤是否已读 if (request.IsRead.HasValue) { query = query.Where(x => x.IsRead == request.IsRead.Value); } // 排序 query = query.OrderByDescending(x => x.CreatedAt); // 获取总数 var total = await query.CountAsync(); // 分页 var messages = await query .Skip((request.PageIndex - 1) * request.PageSize) .Take(request.PageSize) .ToListAsync(); if (!messages.Any()) { var unreadCount = await _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && x.MessageType == 1 && !x.IsRead) .CountAsync(); return new BaseResponse(new GetCommentRepliesMessagesRespDto { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = (int)total, TotalPages = (int)Math.Ceiling((double)total / request.PageSize), UnreadCount = (int)unreadCount, Items = new List() }); } // 分类消息:有快照的和没快照的 var messagesWithoutSnapshot = messages.Where(x => string.IsNullOrEmpty(x.SenderInfo) || string.IsNullOrEmpty(x.ContentSnapshot)).ToList(); // 收集需要查询的ID(仅针对无快照的消息) var senderIds = messagesWithoutSnapshot.Where(x => x.SenderId.HasValue && string.IsNullOrEmpty(x.SenderInfo)) .Select(x => x.SenderId!.Value).Distinct().ToList(); var commentIds = messagesWithoutSnapshot.Where(x => x.ContentType == 2 && x.ContentId.HasValue && string.IsNullOrEmpty(x.ContentSnapshot)) .Select(x => x.ContentId!.Value).ToList(); // 获取用户信息 var users = new Dictionary(); if (senderIds.Any()) { var usersList = await _usersRepository.Select .Where(x => senderIds.Contains(x.Id)) .ToListAsync(); foreach (var user in usersList) { users[user.Id] = user; } } // 获取评论信息 var comments = new Dictionary(); var posts = new Dictionary(); if (commentIds.Any()) { var commentsList = await _commentsRepository.Select .Where(x => commentIds.Contains(x.Id)) .ToListAsync(); foreach (var comment in commentsList) { comments[comment.Id] = comment; if (!posts.ContainsKey(comment.PostId)) { var post = await _postsRepository.Select .Where(x => x.Id == comment.PostId) .FirstAsync(); if (post != null) { posts[comment.PostId] = post; } } } } // 获取当前用户被回复的评论(父评论) var myCommentIds = new List(); foreach (var message in messagesWithoutSnapshot) { if (message.ContentType == 2 && message.ContentId.HasValue) { if (comments.ContainsKey(message.ContentId.Value)) { var comment = comments[message.ContentId.Value]; if (comment.ParentCommentId.HasValue && !myCommentIds.Contains(comment.ParentCommentId.Value)) { myCommentIds.Add(comment.ParentCommentId.Value); } } } } var myComments = new Dictionary(); if (myCommentIds.Any()) { var myCommentsList = await _commentsRepository.Select .Where(x => myCommentIds.Contains(x.Id)) .ToListAsync(); foreach (var comment in myCommentsList) { myComments[comment.Id] = comment; } } // 批量回填无快照的消息 if (messagesWithoutSnapshot.Any()) { await BackfillMessageSnapshots(messagesWithoutSnapshot, users, posts, comments); } var unreadCountTotal = await _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && x.MessageType == 1 && !x.IsRead) .CountAsync(); // 批量转换用户信息(自动获取认证类型数据) var usersDict = await _userInfoService.ToUserInfoDtoDictionaryAsync(users.Values); // 构建返回数据 var items = messages.Select(message => { // 优先使用快照 var sender = TryDeserializeSenderInfo(message.SenderInfo); // 如果快照为空,使用实时数据 if (sender == null && message.SenderId.HasValue && usersDict.ContainsKey(message.SenderId.Value)) { sender = usersDict[message.SenderId.Value]; } sender ??= new UserInfoDto(); // 优先从快照获取评论信息 var contentSnapshot = TryDeserializeContentSnapshot(message.ContentSnapshot); var commentDto = new CommentInfoDto(); var myCommentDto = new MyCommentInfoDto(); if (contentSnapshot != null) { // 使用快照数据 commentDto = new CommentInfoDto { //CommentId = contentSnapshot.Id ?? 0, Content = contentSnapshot.CommentContent ?? "", PostId = contentSnapshot.PostId ?? 0, PostTitle = contentSnapshot.PostTitle ?? "" }; // 父评论信息也在快照中(ContentSnapshot 中的 CommentContent 是父评论) myCommentDto = new MyCommentInfoDto { CommentId = 0, // 快照中不保存父评论ID Content = "" // 需要单独查询或从消息内容中提取 }; } else if (message.ContentType == 2 && message.ContentId.HasValue) { // 使用实时数据 var comment = comments.ContainsKey(message.ContentId.Value) ? comments[message.ContentId.Value] : null; var myComment = comment != null && comment.ParentCommentId.HasValue && myComments.ContainsKey(comment.ParentCommentId.Value) ? myComments[comment.ParentCommentId.Value] : null; commentDto = comment != null ? new CommentInfoDto { CommentId = comment.Id, Content = comment.Content, PostId = comment.PostId, PostTitle = posts.ContainsKey(comment.PostId) ? posts[comment.PostId].Title : "" } : new CommentInfoDto { CommentId = 0, Content = "", PostId = 0, PostTitle = "" }; myCommentDto = myComment != null ? new MyCommentInfoDto { CommentId = myComment.Id, Content = myComment.Content } : new MyCommentInfoDto { CommentId = 0, Content = "" }; } return new CommentReplyMessageDto { MessageId = message.Id, IsRead = message.IsRead, CreatedAt = message.CreatedAt, Sender = sender, Comment = commentDto, MyComment = myCommentDto }; }).ToList(); return new BaseResponse(new GetCommentRepliesMessagesRespDto { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = (int)total, TotalPages = (int)Math.Ceiling((double)total / request.PageSize), UnreadCount = (int)unreadCountTotal, Items = items }); } public async Task> GetLikesMessages(GetLikesMessagesReq request) { var currentUserId = (long)_userInfoModel.UserId; // 构建查询条件 - 只获取点赞消息 var query = _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && x.MessageType == 2); // 过滤是否已读 if (request.IsRead.HasValue) { query = query.Where(x => x.IsRead == request.IsRead.Value); } // 排序 query = query.OrderByDescending(x => x.CreatedAt); // 获取总数 var total = await query.CountAsync(); // 分页 var messages = await query .Skip((request.PageIndex - 1) * request.PageSize) .Take(request.PageSize) .ToListAsync(); if (!messages.Any()) { var unreadCount = await _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && x.MessageType == 2 && !x.IsRead) .CountAsync(); return new BaseResponse(new GetLikesMessagesRespDto { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = (int)total, TotalPages = (int)Math.Ceiling((double)total / request.PageSize), UnreadCount = (int)unreadCount, Items = new List() }); } // 分类消息:有快照的和没快照的 var messagesWithoutSnapshot = messages.Where(x => string.IsNullOrEmpty(x.SenderInfo) || string.IsNullOrEmpty(x.ContentSnapshot)).ToList(); // 收集需要查询的ID var senderIds = messagesWithoutSnapshot.Where(x => x.SenderId.HasValue && string.IsNullOrEmpty(x.SenderInfo)) .Select(x => x.SenderId!.Value).Distinct().ToList(); var contentIds = messagesWithoutSnapshot.Where(x => x.ContentId.HasValue && string.IsNullOrEmpty(x.ContentSnapshot)) .Select(x => new { x.ContentType, x.ContentId!.Value }).ToList(); // 获取用户信息 var users = new Dictionary(); if (senderIds.Any()) { var usersList = await _usersRepository.Select .Where(x => senderIds.Contains(x.Id)) .ToListAsync(); foreach (var user in usersList) { users[user.Id] = user; } } // 获取帖子信息 var postIds = contentIds.Where(x => x.ContentType == 1).Select(x => x.Value).Distinct().ToList(); var posts = new Dictionary(); if (postIds.Any()) { var postsList = await _postsRepository.Select .Where(x => postIds.Contains(x.Id)) .ToListAsync(); foreach (var post in postsList) { posts[post.Id] = post; } } // 获取评论信息 var commentIds = contentIds.Where(x => x.ContentType == 2).Select(x => x.Value).Distinct().ToList(); var comments = new Dictionary(); if (commentIds.Any()) { var commentsList = await _commentsRepository.Select .Where(x => commentIds.Contains(x.Id)) .ToListAsync(); foreach (var comment in commentsList) { comments[comment.Id] = comment; } // 获取评论对应的帖子 var commentPostIds = commentsList.Select(x => x.PostId).Where(x => !posts.ContainsKey(x)).Distinct().ToList(); if (commentPostIds.Any()) { var commentPostsList = await _postsRepository.Select .Where(x => commentPostIds.Contains(x.Id)) .ToListAsync(); foreach (var post in commentPostsList) { posts[post.Id] = post; } } } // 批量回填无快照的消息 if (messagesWithoutSnapshot.Any()) { await BackfillMessageSnapshots(messagesWithoutSnapshot, users, posts, comments); } var unreadCountTotal = await _messagesRepository.Select .Where(x => x.ReceiverId == currentUserId && x.MessageType == 2 && !x.IsRead) .CountAsync(); // 批量转换用户信息(自动获取认证类型数据) var usersDict = await _userInfoService.ToUserInfoDtoDictionaryAsync(users.Values); // 构建返回数据 var items = messages.Select(message => { // 优先使用快照 var sender = TryDeserializeSenderInfo(message.SenderInfo); // 如果快照为空,使用实时数据 if (sender == null && message.SenderId.HasValue && usersDict.ContainsKey(message.SenderId.Value)) { sender = usersDict[message.SenderId.Value]; } sender ??= new UserInfoDto(); // 优先从快照获取内容信息 var contentSnapshot = TryDeserializeContentSnapshot(message.ContentSnapshot); var likedContent = new LikedContentDto { ContentType = message.ContentType, ContentId = message.ContentId ?? 0 }; if (contentSnapshot != null) { // 使用快照数据 likedContent.PostTitle = contentSnapshot.PostTitle; likedContent.CommentContent = contentSnapshot.CommentContent; } else if (message.ContentId.HasValue) { // 使用实时数据 if (message.ContentType == 1 && posts.ContainsKey(message.ContentId.Value)) { likedContent.PostTitle = posts[message.ContentId.Value].Title; } else if (message.ContentType == 2 && comments.ContainsKey(message.ContentId.Value)) { likedContent.CommentContent = comments[message.ContentId.Value].Content; // 评论的帖子标题需要额外查询 var comment = comments[message.ContentId.Value]; if (posts.ContainsKey(comment.PostId)) { likedContent.PostTitle = posts[comment.PostId].Title; } } } return new LikeMessageDto { MessageId = message.Id, IsRead = message.IsRead, CreatedAt = message.CreatedAt, Sender = sender, LikedContent = likedContent }; }).ToList(); return new BaseResponse(new GetLikesMessagesRespDto { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = (int)total, TotalPages = (int)Math.Ceiling((double)total / request.PageSize), UnreadCount = (int)unreadCountTotal, Items = items }); } public async Task> ParticipateActivity(ParticipateActivityReq request) { var currentUserId = (long)_userInfoModel.UserId; var now = DateTime.Now; // 验证通知是否存在且为活动类型 var message = await _messagesRepository.Select.Where(it => it.Id == request.NotificationId).FirstAsync(); if (message == null || message.SenderId == null) { return new BaseResponse(ResponseCode.Error, "消息不存在或已删除"); } var notification = await _notificationsRepository.Select .Where(x => x.Id == message.SenderId && x.IsActive) .FirstAsync(); if (notification == null) { return new BaseResponse(ResponseCode.Error, "活动通知不存在或已禁用"); } if (notification.NotificationType != 2) { return new BaseResponse(ResponseCode.Error, "该通知不是活动类型"); } // 验证活动是否已过期 if (notification.ActivityEndTime.HasValue && notification.ActivityEndTime.Value < now) { return new BaseResponse(ResponseCode.Error, "活动已结束"); } // 验证活动是否已发布 if (notification.PublishTime.HasValue && notification.PublishTime.Value > now) { return new BaseResponse(ResponseCode.Error, "活动尚未开始"); } // 验证用户是否已参与(防止重复提交) var existingParticipant = await _participantsRepository.Select .Where(x => x.NotificationId == notification.Id && x.UserId == currentUserId) .FirstAsync(); if (existingParticipant != null && existingParticipant.Status == 3) { return new BaseResponse(ResponseCode.Error, "活动奖品已发货,无法修改地址!"); } // 验证必填字段 if (string.IsNullOrWhiteSpace(request.ContactInfo)) { return new BaseResponse(ResponseCode.Error, "联系方式不能为空"); } if (string.IsNullOrWhiteSpace(request.ShippingAddress)) { return new BaseResponse(ResponseCode.Error, "收货地址不能为空"); } // 验证字段长度 if (request.ContactInfo.Length > 100) { return new BaseResponse(ResponseCode.Error, "联系方式长度不能超过100字符"); } if (request.ShippingAddress.Length > 500) { return new BaseResponse(ResponseCode.Error, "收货地址长度不能超过500字符"); } // 判断是新增还是更新 var isUpdate = existingParticipant != null; if (existingParticipant == null) { // 创建新的参与记录 existingParticipant = new T_SystemNotificationParticipants { NotificationId = notification.Id, UserId = currentUserId, ContactInfo = request.ContactInfo, ShippingAddress = request.ShippingAddress, Status = 1, // 已提交 SubmittedAt = now, CreatedAt = now, UpdatedAt = now, SenerId = message.Id, SenerTitle = notification.Title, }; } else { // 更新现有参与记录(保留 Id、CreatedAt、SubmittedAt 等字段) existingParticipant.ContactInfo = request.ContactInfo; existingParticipant.ShippingAddress = request.ShippingAddress; existingParticipant.Status = 1; // 已提交 existingParticipant.UpdatedAt = now; existingParticipant.SenerId = message.Id; existingParticipant.SenerTitle = notification.Title; // 注意:不更新 SubmittedAt,保持首次提交时间 } await _participantsRepository.InsertOrUpdateAsync(existingParticipant); return new BaseResponse(isUpdate ? "修改成功!" : "提交成功!", new ParticipateActivityResp { ParticipantId = existingParticipant.Id, SubmittedAt = existingParticipant.SubmittedAt }); } } }