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无效", 400);
}
var message = await messageRepository.GetByIdAsync(input.MessageId);
if (message == null)
{
throw new BusinessException("消息不存在", 404);
}
// 检查是否已点赞
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("您已点赞过该消息", 400);
}
// 插入点赞记录
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("点赞失败", 500);
}
// 更新点赞数
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无效", 400);
}
var message = await messageRepository.GetByIdAsync(messageId);
if (message == null)
{
throw new BusinessException("消息不存在", 404);
}
var existingLike = await Context.Queryable()
.Where(l => l.UserId == userId && l.MessageId == messageId && l.Type == CommunityMessageLikeTypeEnum.Like)
.FirstAsync();
if (existingLike == null)
{
throw new BusinessException("您尚未点赞过该消息", 400);
}
// 软删除点赞记录
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
}