feat: 完成宠物模块重构与社区功能开发
本次提交包含多项核心更新:
1. 重构宠物模块数据结构:拆分皮肤图片为独立表,优化Pet、PetEvolution实体,调整字段类型与冗余字段
2. 新增宠物皮肤图片管理表,支持多进化阶段多图片展示
3. 完善宠物DTO,新增当前形态名称、皮肤信息与图片序列返回
4. 新增社区功能模块:
- 微信端社区Feed流、点赞/取消点赞接口
- 后台社区消息管理接口与服务实现
5. 优化商城与背包模块,替换皮肤图片获取逻辑为从新表读取预览图
6. 重构勋章服务,新增规则校验逻辑
7. 调整命名规范,修复原有控制器命名问题
This commit is contained in:
@ -5,6 +5,7 @@ using QYZH.InteractiveMagazine.Models.Dto.Bag;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.Mall;
|
||||
using QYZH.InteractiveMagazine.Models.Entity;
|
||||
using QYZH.InteractiveMagazine.Repository;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace QYZH.InteractiveMagazine.Service;
|
||||
|
||||
@ -17,6 +18,24 @@ public class WxMallService(
|
||||
ILogger<WxMallService> logger)
|
||||
: BaseRepository<ExchangeRecord>, IWxMallService
|
||||
{
|
||||
/// <summary>
|
||||
/// 从 MetaData JSON 中提取 SkinId
|
||||
/// </summary>
|
||||
private static long GetSkinIdFromMetaData(string? metaData)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(metaData)) return 0;
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(metaData);
|
||||
if (doc.RootElement.TryGetProperty("SkinId", out var skinIdElement))
|
||||
return skinIdElement.GetInt64();
|
||||
if (doc.RootElement.TryGetProperty("skinId", out var skinIdLower))
|
||||
return skinIdLower.GetInt64();
|
||||
}
|
||||
catch { }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取商城商品列表(仅上架 + 在售)
|
||||
/// </summary>
|
||||
@ -29,15 +48,36 @@ public class WxMallService(
|
||||
|
||||
var products = await query.ToListAsync();
|
||||
|
||||
// 批量查询皮肤信息
|
||||
var skinProductIds = products.Where(p => p.Type == "PetBg").Select(p => p.Id).ToList();
|
||||
var skins = skinProductIds.Count > 0
|
||||
// 批量提取 PetBg 商品的 SkinId
|
||||
var skinProductMap = products
|
||||
.Where(p => p.Type == "PetBg")
|
||||
.Select(p => new { ProductId = p.Id, SkinId = GetSkinIdFromMetaData(p.MetaData) })
|
||||
.Where(x => x.SkinId > 0)
|
||||
.ToList();
|
||||
|
||||
var skinIds = skinProductMap.Select(x => x.SkinId).Distinct().ToList();
|
||||
|
||||
// 批量查询皮肤
|
||||
var skins = skinIds.Count > 0
|
||||
? await exchangeRecordRepository.Context.Queryable<PetSkin>()
|
||||
.Where(s => skinProductIds.Contains(s.ProductId) && !s.IsDeleted)
|
||||
.Where(s => skinIds.Contains(s.Id) && !s.IsDeleted)
|
||||
.ToListAsync()
|
||||
: new List<PetSkin>();
|
||||
|
||||
// 批量查询皮肤预览图(取每个皮肤的第一张图)
|
||||
var allPreviewImages = skinIds.Count > 0
|
||||
? await exchangeRecordRepository.Context.Queryable<PetSkinImage>()
|
||||
.Where(i => skinIds.Contains(i.SkinId) && !i.IsDeleted)
|
||||
.OrderBy(i => i.SortOrder)
|
||||
.ToListAsync()
|
||||
: new List<PetSkinImage>();
|
||||
|
||||
var previewImageMap = allPreviewImages
|
||||
.GroupBy(i => i.SkinId)
|
||||
.ToDictionary(g => g.Key, g => g.First().ImageUrl);
|
||||
|
||||
// 批量查询用户背包(判断已拥有)
|
||||
var skinProductIds = skinProductMap.Select(x => x.ProductId).ToList();
|
||||
var bagItems = skinProductIds.Count > 0
|
||||
? await exchangeRecordRepository.Context.Queryable<UserBag>()
|
||||
.Where(b => b.UserId == userId && !b.IsDeleted && b.Status == "Available"
|
||||
@ -47,8 +87,10 @@ public class WxMallService(
|
||||
|
||||
return products.Select(p =>
|
||||
{
|
||||
var skin = skins.FirstOrDefault(s => s.ProductId == p.Id);
|
||||
var skinMapping = skinProductMap.FirstOrDefault(x => x.ProductId == p.Id);
|
||||
var skin = skinMapping != null ? skins.FirstOrDefault(s => s.Id == skinMapping.SkinId) : null;
|
||||
var owned = bagItems.Any(b => b.ItemId == p.Id);
|
||||
var previewImg = skin != null && previewImageMap.ContainsKey(skin.Id) ? previewImageMap[skin.Id] : null;
|
||||
|
||||
return new WxProductOutput
|
||||
{
|
||||
@ -63,9 +105,9 @@ public class WxMallService(
|
||||
{
|
||||
SkinId = skin.Id,
|
||||
SkinName = skin.Name,
|
||||
SkinImage = skin.ImageUrl,
|
||||
Description = skin.Description,
|
||||
Rarity = skin.Rarity
|
||||
Rarity = skin.Rarity,
|
||||
PreviewImage = previewImg
|
||||
} : null
|
||||
};
|
||||
}).ToList();
|
||||
@ -85,20 +127,30 @@ public class WxMallService(
|
||||
PetSkinBrief? skinBrief = null;
|
||||
if (product.Type == "PetBg")
|
||||
{
|
||||
var skin = await exchangeRecordRepository.Context.Queryable<PetSkin>()
|
||||
.Where(s => s.ProductId == product.Id && !s.IsDeleted)
|
||||
.FirstAsync();
|
||||
|
||||
if (skin != null)
|
||||
var skinId = GetSkinIdFromMetaData(product.MetaData);
|
||||
if (skinId > 0)
|
||||
{
|
||||
skinBrief = new PetSkinBrief
|
||||
var skin = await exchangeRecordRepository.Context.Queryable<PetSkin>()
|
||||
.Where(s => s.Id == skinId && !s.IsDeleted)
|
||||
.FirstAsync();
|
||||
|
||||
if (skin != null)
|
||||
{
|
||||
SkinId = skin.Id,
|
||||
SkinName = skin.Name,
|
||||
SkinImage = skin.ImageUrl,
|
||||
Description = skin.Description,
|
||||
Rarity = skin.Rarity
|
||||
};
|
||||
var previewImg = await exchangeRecordRepository.Context.Queryable<PetSkinImage>()
|
||||
.Where(i => i.SkinId == skinId && !i.IsDeleted)
|
||||
.OrderBy(i => i.SortOrder)
|
||||
.Select(i => i.ImageUrl)
|
||||
.FirstAsync();
|
||||
|
||||
skinBrief = new PetSkinBrief
|
||||
{
|
||||
SkinId = skin.Id,
|
||||
SkinName = skin.Name,
|
||||
Description = skin.Description,
|
||||
Rarity = skin.Rarity,
|
||||
PreviewImage = previewImg
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,18 +350,40 @@ public class WxMallService(
|
||||
.Where(p => itemIds.Contains(p.Id) && !p.IsDeleted)
|
||||
.ToListAsync();
|
||||
|
||||
// 批量查询关联皮肤(PetBg 类型)
|
||||
var skinProductIds = bagItems.Where(b => b.ItemType == "PetBg").Select(b => b.ItemId).Distinct().ToList();
|
||||
var skins = skinProductIds.Count > 0
|
||||
// 批量提取 PetBg 背包物品的 SkinId(从 MetaData 解析)
|
||||
var skinIdMap = bagItems
|
||||
.Where(b => b.ItemType == "PetBg")
|
||||
.Select(b => new { BagId = b.Id, SkinId = GetSkinIdFromMetaData(b.MetaData) })
|
||||
.Where(x => x.SkinId > 0)
|
||||
.ToList();
|
||||
|
||||
var skinIds = skinIdMap.Select(x => x.SkinId).Distinct().ToList();
|
||||
|
||||
// 批量查询皮肤
|
||||
var skins = skinIds.Count > 0
|
||||
? await exchangeRecordRepository.Context.Queryable<PetSkin>()
|
||||
.Where(s => skinProductIds.Contains(s.ProductId) && !s.IsDeleted)
|
||||
.Where(s => skinIds.Contains(s.Id) && !s.IsDeleted)
|
||||
.ToListAsync()
|
||||
: new List<PetSkin>();
|
||||
|
||||
// 批量查询皮肤预览图
|
||||
var allBagPreviewImages = skinIds.Count > 0
|
||||
? await exchangeRecordRepository.Context.Queryable<PetSkinImage>()
|
||||
.Where(i => skinIds.Contains(i.SkinId) && !i.IsDeleted)
|
||||
.OrderBy(i => i.SortOrder)
|
||||
.ToListAsync()
|
||||
: new List<PetSkinImage>();
|
||||
|
||||
var bagPreviewImageMap = allBagPreviewImages
|
||||
.GroupBy(i => i.SkinId)
|
||||
.ToDictionary(g => g.Key, g => g.First().ImageUrl);
|
||||
|
||||
return bagItems.Select(b =>
|
||||
{
|
||||
var product = products.FirstOrDefault(p => p.Id == b.ItemId);
|
||||
var skin = skins.FirstOrDefault(s => s.ProductId == b.ItemId);
|
||||
var skinMapping = skinIdMap.FirstOrDefault(x => x.BagId == b.Id);
|
||||
var skin = skinMapping != null ? skins.FirstOrDefault(s => s.Id == skinMapping.SkinId) : null;
|
||||
var previewImg = skin != null && bagPreviewImageMap.ContainsKey(skin.Id) ? bagPreviewImageMap[skin.Id] : null;
|
||||
|
||||
return new UserBagOutput
|
||||
{
|
||||
@ -329,8 +403,8 @@ public class WxMallService(
|
||||
{
|
||||
SkinId = skin.Id,
|
||||
SkinName = skin.Name,
|
||||
SkinImage = skin.ImageUrl,
|
||||
Rarity = skin.Rarity
|
||||
Rarity = skin.Rarity,
|
||||
PreviewImage = previewImg
|
||||
} : null
|
||||
};
|
||||
}).ToList();
|
||||
@ -449,13 +523,15 @@ public class WxMallService(
|
||||
if (skin == null)
|
||||
throw new BusinessException("皮肤不存在", 404);
|
||||
|
||||
// 校验背包中是否拥有该皮肤(通过关联商品Id判断)
|
||||
// 校验背包中是否拥有该皮肤(通过 MetaData 中的 SkinId 判断)
|
||||
var hasSkin = await exchangeRecordRepository.Context.Queryable<UserBag>()
|
||||
.Where(b => b.UserId == userId && b.ItemId == skin.ProductId
|
||||
&& !b.IsDeleted && b.Status == "Available" && b.Quantity > 0)
|
||||
.AnyAsync();
|
||||
.Where(b => b.UserId == userId && !b.IsDeleted && b.Status == "Available" && b.Quantity > 0
|
||||
&& b.ItemType == "PetBg")
|
||||
.ToListAsync();
|
||||
|
||||
if (!hasSkin)
|
||||
var owned = hasSkin.Any(b => GetSkinIdFromMetaData(b.MetaData) == input.SkinId);
|
||||
|
||||
if (!owned)
|
||||
throw new BusinessException("您尚未拥有该皮肤,请先兑换", 400);
|
||||
|
||||
// 换肤
|
||||
|
||||
Reference in New Issue
Block a user