Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/WeChatCommunityService.cs
glz bdaa6a0dc8 refactor: 统一业务异常处理,标准化结果码和错误响应
1.  新增并完善ResultCode枚举,补充标准HTTP状态码对应的业务状态码
2.  重构BusinessException,新增基于ResultCode的构造函数和ThrowIf扩展方法
3.  替换所有硬编码的HTTP状态码为统一的ResultCode枚举
4.  优化全局异常中间件,根据业务状态码映射对应HTTP状态码并规范化JSON响应
5.  修复OssImageHelper和AutoDotCodeConsumer中的OSS文件处理逻辑
6.  新增用户答题快照实体类
7.  清理废弃的宠物模块迁移脚本
2026-06-29 16:34:26 +08:00

298 lines
9.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// 社区微信端服务实现
/// </summary>
public class WeChatCommunityService(
BaseRepository<CommunityMessage> messageRepository,
ILogger<WeChatCommunityService> logger)
: BaseRepository<CommunityMessage>, IWeChatCommunityService
{
private const int FeaturedCount = 5;
private const int NormalCount = 5;
/// <summary>
/// 获取社区Feed流翻页
/// </summary>
public async Task<WxFeedOutput> 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<WxFeedMessageOutput>(), 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
};
}
/// <summary>
/// 下拉刷新(重置游标,返回最新列表)
/// </summary>
public async Task<WxFeedOutput> RefreshFeedAsync(long userId)
{
logger.LogInformation("正在刷新社区Feed用户ID: {UserId}", userId);
return await GetFeedAsync(userId, null);
}
/// <summary>
/// 点赞
/// </summary>
public async Task<WxLikeOutput> 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<CommunityMessageLike>()
.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<CommunityMessage>()
.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
};
}
/// <summary>
/// 取消点赞
/// </summary>
public async Task<WxLikeOutput> 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<CommunityMessageLike>()
.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<CommunityMessageLike>()
.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<CommunityMessage>()
.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
/// <summary>
/// 获取用户拥有的期刊ID列表
/// </summary>
private async Task<List<long>> GetUserJournalIds(long userId)
{
return await Context.Queryable<UserJournal>()
.Where(uj => uj.UserId == userId && uj.Status == (int)UserJournalStatusEnum.Active)
.Select(uj => uj.JournalId)
.ToListAsync();
}
/// <summary>
/// 获取当前用户已点赞的消息ID集合
/// </summary>
private async Task<HashSet<long>> GetLikedMessageIds(long userId, List<long> messageIds)
{
if (!messageIds.Any()) return new HashSet<long>();
var likes = await Context.Queryable<CommunityMessageLike>()
.Where(l => l.UserId == userId && messageIds.Contains(l.MessageId) && l.Type == CommunityMessageLikeTypeEnum.Like && !l.IsDeleted)
.Select(l => l.MessageId)
.ToListAsync();
return likes.ToHashSet();
}
/// <summary>
/// 混排精选消息和普通消息(按权重随机插入)
/// </summary>
private static List<CommunityMessage> MixMessages(List<CommunityMessage> featured, List<CommunityMessage> normal)
{
var result = new List<CommunityMessage>();
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
}