using Microsoft.Extensions.Logging; using QYZH.InteractiveMagazine.IService; using QYZH.InteractiveMagazine.Models.Common; using QYZH.InteractiveMagazine.Models.Dto; using QYZH.InteractiveMagazine.Models.Entity; using QYZH.InteractiveMagazine.Models.Enum; using QYZH.InteractiveMagazine.Repository; using SqlSugar; namespace QYZH.InteractiveMagazine.Service; /// /// 社区微信端服务实现 /// public class WeChatCommunityService( BaseRepository messageRepository, ILogger logger) : BaseRepository, IWeChatCommunityService { private const int FeaturedCount = 5; private const int NormalCount = 5; /// /// 获取社区Feed流(翻页) /// public async Task GetFeedAsync(long userId, long? cursor) { logger.LogInformation("正在获取社区Feed,用户ID: {UserId}, Cursor: {Cursor}", userId, cursor); var journalIds = await GetUserJournalIds(userId); if (!journalIds.Any()) { return new WxFeedOutput { Messages = new List(), HasMore = false }; } // 查询精选消息 var featuredQuery = messageRepository.Queryable() .Where(m => journalIds.Contains(m.JournalId) && m.Status == 1 && m.IsActive && m.UserId != userId && m.IsFeatured == 1); // 查询普通消息 var normalQuery = messageRepository.Queryable() .Where(m => journalIds.Contains(m.JournalId) && m.Status == 1 && m.IsActive && m.UserId != userId && m.IsFeatured == 0); // 游标分页:按ID降序(ID越大越新) if (cursor.HasValue) { featuredQuery = featuredQuery.Where(m => m.Id < cursor.Value); normalQuery = normalQuery.Where(m => m.Id < cursor.Value); } var featured = await featuredQuery .OrderByDescending(m => m.SortOrder) .Take(FeaturedCount) .ToListAsync(); var normal = await normalQuery .OrderByDescending(m => m.SortOrder) .Take(NormalCount) .ToListAsync(); // 混排 var mixed = MixMessages(featured, normal); // 查询当前用户点赞状态 var messageIds = mixed.Select(m => m.Id).ToList(); var likedIds = await GetLikedMessageIds(userId, messageIds); var output = mixed.Select(m => new WxFeedMessageOutput { Id = m.Id, JournalId = m.JournalId, UserId = m.UserId, Content = m.Content, ImageUrl = m.ImageUrl, Type = m.Type.ToString(), LikeCount = m.LikeCount, IsFeatured = m.IsFeatured, IsLiked = likedIds.Contains(m.Id), CreatedAt = m.CreatedAt }).ToList(); var nextCursor = mixed.Any() ? mixed.Min(m => m.Id) : (long?)null; var hasMore = featured.Count >= FeaturedCount || normal.Count >= NormalCount; return new WxFeedOutput { Messages = output, NextCursor = hasMore ? nextCursor : null, HasMore = hasMore }; } /// /// 下拉刷新(重置游标,返回最新列表) /// public async Task RefreshFeedAsync(long userId) { logger.LogInformation("正在刷新社区Feed,用户ID: {UserId}", userId); return await GetFeedAsync(userId, null); } /// /// 点赞 /// public async Task LikeAsync(long userId, WxLikeInput input) { logger.LogInformation("用户正在点赞,用户ID: {UserId}, 消息ID: {MessageId}", userId, input.MessageId); if (input.MessageId <= 0) { throw new BusinessException("消息ID无效", ResultCode.BAD_REQUEST); } var message = await messageRepository.GetByIdAsync(input.MessageId); if (message == null) { throw new BusinessException("消息不存在", ResultCode.NOT_FOUND); } // 检查是否已点赞 var existingLike = await Context.Queryable() .Where(l => l.UserId == userId && l.MessageId == input.MessageId && l.Type == CommunityMessageLikeTypeEnum.Like) .FirstAsync(); if (existingLike != null) { throw new BusinessException("您已点赞过该消息", ResultCode.BAD_REQUEST); } // 插入点赞记录 var like = new CommunityMessageLike { UserId = userId, MessageId = input.MessageId, Type = CommunityMessageLikeTypeEnum.Like, CreatedBy = "System", UpdatedBy = "System", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, IsDeleted = false }; var insertResult = await Context.Insertable(like).ExecuteCommandAsync(); if (insertResult <= 0) { throw new BusinessException("点赞失败", ResultCode.GLOBAL_ERROR); } // 更新点赞数 await Context.Updateable() .SetColumns(m => new CommunityMessage { LikeCount = m.LikeCount + 1, UpdatedAt = DateTime.Now }) .Where(m => m.Id == input.MessageId) .ExecuteCommandAsync(); logger.LogInformation("点赞成功,用户ID: {UserId}, 消息ID: {MessageId}", userId, input.MessageId); return new WxLikeOutput { MessageId = input.MessageId, LikeCount = message.LikeCount + 1, IsLiked = true }; } /// /// 取消点赞 /// public async Task UnlikeAsync(long userId, long messageId) { logger.LogInformation("用户正在取消点赞,用户ID: {UserId}, 消息ID: {MessageId}", userId, messageId); if (messageId <= 0) { throw new BusinessException("消息ID无效", ResultCode.BAD_REQUEST); } var message = await messageRepository.GetByIdAsync(messageId); if (message == null) { throw new BusinessException("消息不存在", ResultCode.NOT_FOUND); } var existingLike = await Context.Queryable() .Where(l => l.UserId == userId && l.MessageId == messageId && l.Type == CommunityMessageLikeTypeEnum.Like) .FirstAsync(); if (existingLike == null) { throw new BusinessException("您尚未点赞过该消息", ResultCode.BAD_REQUEST); } // 软删除点赞记录 await Context.Updateable() .SetColumns(l => new CommunityMessageLike { IsDeleted = true, UpdatedAt = DateTime.Now }) .Where(l => l.Id == existingLike.Id) .ExecuteCommandAsync(); // 更新点赞数(不小于0) var newLikeCount = Math.Max(0, message.LikeCount - 1); await Context.Updateable() .SetColumns(m => new CommunityMessage { LikeCount = newLikeCount, UpdatedAt = DateTime.Now }) .Where(m => m.Id == messageId) .ExecuteCommandAsync(); logger.LogInformation("取消点赞成功,用户ID: {UserId}, 消息ID: {MessageId}", userId, messageId); return new WxLikeOutput { MessageId = messageId, LikeCount = newLikeCount, IsLiked = false }; } #region 私有辅助方法 /// /// 获取用户拥有的期刊ID列表 /// private async Task> GetUserJournalIds(long userId) { return await Context.Queryable() .Where(uj => uj.UserId == userId && uj.Status == (int)UserJournalStatusEnum.Active) .Select(uj => uj.JournalId) .ToListAsync(); } /// /// 获取当前用户已点赞的消息ID集合 /// private async Task> GetLikedMessageIds(long userId, List messageIds) { if (!messageIds.Any()) return new HashSet(); var likes = await Context.Queryable() .Where(l => l.UserId == userId && messageIds.Contains(l.MessageId) && l.Type == CommunityMessageLikeTypeEnum.Like && !l.IsDeleted) .Select(l => l.MessageId) .ToListAsync(); return likes.ToHashSet(); } /// /// 混排精选消息和普通消息(按权重随机插入) /// private static List MixMessages(List featured, List normal) { var result = new List(); var allMessages = new List<(CommunityMessage msg, bool isFeatured)>(); allMessages.AddRange(featured.Select(m => (m, true))); allMessages.AddRange(normal.Select(m => (m, false))); // 按SortOrder加权随机排序:SortOrder越大出现越靠前 var random = new Random(); while (allMessages.Any()) { var totalWeight = allMessages.Sum(x => Math.Max(1, x.msg.SortOrder)); var pick = random.Next(0, totalWeight + 1); var cumulative = 0; var selectedIndex = 0; for (var i = 0; i < allMessages.Count; i++) { cumulative += Math.Max(1, allMessages[i].msg.SortOrder); if (pick <= cumulative) { selectedIndex = i; break; } } result.Add(allMessages[selectedIndex].msg); allMessages.RemoveAt(selectedIndex); } return result; } #endregion }