refactor: 批量新增枚举类型并完成实体、DTO、服务层枚举替换

- 新增30+业务枚举类型覆盖用户、宠物、商城、社区、积分等模块
- 完成实体类、DTO、服务层的字符串枚举替换为强类型枚举
- 修复用户状态枚举名称变更,将Frozen改为Disabled
- 新增批量发布社区消息接口与控制器实现
- 新增操作日志、积分管理、补偿任务相关服务与DTO
This commit is contained in:
glz
2026-06-08 16:57:44 +08:00
parent 698c404b72
commit 78b686f9e5
116 changed files with 4861 additions and 307 deletions

View File

@ -3,7 +3,9 @@ using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto.Bag;
using QYZH.InteractiveMagazine.Models.Dto.Mall;
using QYZH.InteractiveMagazine.Models.Dto.Points;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;
using System.Text.Json;
@ -15,6 +17,7 @@ namespace QYZH.InteractiveMagazine.Service;
public class WxMallService(
BaseRepository<ExchangeRecord> exchangeRecordRepository,
ICheckInService checkInService,
IPointsService pointsService,
ILogger<WxMallService> logger)
: BaseRepository<ExchangeRecord>, IWxMallService
{
@ -43,14 +46,14 @@ public class WxMallService(
{
var query = exchangeRecordRepository.Context.Queryable<Product>()
.Where(p => !p.IsDeleted && p.IsActive && p.SaleStatus == "OnSale")
.WhereIF(!string.IsNullOrEmpty(type), p => p.Type == type)
.WhereIF(!string.IsNullOrEmpty(type), p => p.Type.ToString() == type)
.OrderByDescending(p => p.CreatedAt);
var products = await query.ToListAsync();
// 批量提取 PetBg 商品的 SkinId
var skinProductMap = products
.Where(p => p.Type == "PetBg")
.Where(p => p.Type == ProductTypeEnum.PetBg)
.Select(p => new { ProductId = p.Id, SkinId = GetSkinIdFromMetaData(p.MetaData) })
.Where(x => x.SkinId > 0)
.ToList();
@ -80,7 +83,7 @@ public class WxMallService(
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"
.Where(b => b.UserId == userId && !b.IsDeleted && b.Status == UserBagStatusEnum.Available
&& skinProductIds.Contains(b.ItemId))
.ToListAsync()
: new List<UserBag>();
@ -99,7 +102,7 @@ public class WxMallService(
Description = p.Description,
ImageUrl = p.ImageUrl,
Price = p.Price,
Type = p.Type,
Type = p.Type.ToString(),
Owned = owned,
Skin = skin != null ? new PetSkinBrief
{
@ -125,7 +128,7 @@ public class WxMallService(
if (product == null) return null;
PetSkinBrief? skinBrief = null;
if (product.Type == "PetBg")
if (product.Type == ProductTypeEnum.PetBg)
{
var skinId = GetSkinIdFromMetaData(product.MetaData);
if (skinId > 0)
@ -155,7 +158,7 @@ public class WxMallService(
}
var owned = await exchangeRecordRepository.Context.Queryable<UserBag>()
.Where(b => b.UserId == userId && b.ItemId == product.Id && !b.IsDeleted && b.Status == "Available")
.Where(b => b.UserId == userId && b.ItemId == product.Id && !b.IsDeleted && b.Status == UserBagStatusEnum.Available)
.AnyAsync();
return new WxProductOutput
@ -165,7 +168,7 @@ public class WxMallService(
Description = product.Description,
ImageUrl = product.ImageUrl,
Price = product.Price,
Type = product.Type,
Type = product.Type.ToString(),
Owned = owned,
Skin = skinBrief
};
@ -195,40 +198,22 @@ public class WxMallService(
var totalCost = product.Price * input.Quantity;
// 查询用户积分
var user = await exchangeRecordRepository.Context.Queryable<Users>()
.Where(u => u.Id == userId && !u.IsDeleted)
.FirstAsync();
if (user == null)
throw new BusinessException("用户不存在", 404);
if (user.Points < totalCost)
throw new BusinessException($"积分不足,需要 {totalCost} 积分,当前余额 {user.Points}", 400);
var newPointsBalance = user.Points - totalCost;
DeductPointsOutput? pointsResult = null;
long recordId = 0;
await exchangeRecordRepository.UseTranAsync(async () =>
{
// 扣除用户积分
await exchangeRecordRepository.Context.Updateable<Users>()
.SetColumns(u => u.Points == newPointsBalance)
.SetColumns(u => u.UpdatedAt == DateTime.Now)
.Where(u => u.Id == userId && !u.IsDeleted)
.ExecuteCommandAsync();
// 创建兑换记录
var record = new ExchangeRecord
{
UserId = userId,
ProductId = product.Id,
ProductName = product.Name,
ProductType = product.Type,
ProductType = product.Type.ToString(),
PointsCost = totalCost,
PointsBalance = newPointsBalance,
PointsBalance = 0, // 稍后赋值
Quantity = input.Quantity,
Status = "Success",
Status = ExchangeRecordStatusEnum.Success,
IsDeleted = false,
CreatedBy = userId.ToString(),
CreatedAt = DateTime.Now,
@ -238,9 +223,25 @@ public class WxMallService(
var inserted = await exchangeRecordRepository.InsertReturnEntityAsync(record);
recordId = inserted.Id;
// 扣除积分
pointsResult = await pointsService.DeductPointsInTranAsync(new DeductPointsInput
{
UserId = userId,
Amount = totalCost,
ChangeType = PointsChangeTypeEnum.Exchange,
RelatedId = recordId,
Description = $"兑换 {product.Name} x{input.Quantity}"
});
// 更新兑换记录的积分余额
await exchangeRecordRepository.Updateable<ExchangeRecord>()
.SetColumns(r => r.PointsBalance == pointsResult.NewBalance)
.Where(r => r.Id == recordId)
.ExecuteCommandAsync();
// 加入背包
var existingBag = await exchangeRecordRepository.Context.Queryable<UserBag>()
.Where(b => b.UserId == userId && b.ItemId == product.Id && !b.IsDeleted && b.Status == "Available")
.Where(b => b.UserId == userId && b.ItemId == product.Id && !b.IsDeleted && b.Status == UserBagStatusEnum.Available)
.FirstAsync();
if (existingBag != null)
@ -259,11 +260,11 @@ public class WxMallService(
{
UserId = userId,
ItemId = product.Id,
ItemType = product.Type,
ItemType = product.Type.ToString(),
Quantity = input.Quantity,
MetaData = product.MetaData,
Type = product.Type,
Status = "Available",
Type = Enum.TryParse<UserBagTypeEnum>(product.Type.ToString(), out var bagType) ? bagType : default,
Status = UserBagStatusEnum.Available,
IsDeleted = false,
CreatedBy = userId.ToString(),
CreatedAt = DateTime.Now,
@ -272,25 +273,6 @@ public class WxMallService(
};
await exchangeRecordRepository.Context.Insertable(bagItem).ExecuteCommandAsync();
}
// 创建积分消耗记录
var pointsRecord = new PointsRecord
{
UserId = userId,
ChangeAmount = -totalCost,
BalanceAfter = newPointsBalance,
ChangeType = "Exchange",
RelatedId = recordId,
Description = $"兑换 {product.Name} x{input.Quantity}",
Type = "Expense",
Status = "Success",
IsDeleted = false,
CreatedBy = userId.ToString(),
CreatedAt = DateTime.Now,
UpdatedBy = userId.ToString(),
UpdatedAt = DateTime.Now
};
await exchangeRecordRepository.Context.Insertable(pointsRecord).ExecuteCommandAsync();
});
logger.LogInformation("兑换成功UserId: {UserId}, Product: {Product}, Cost: {Cost}",
@ -301,7 +283,7 @@ public class WxMallService(
RecordId = recordId,
ProductName = product.Name,
PointsCost = totalCost,
PointsBalance = newPointsBalance,
PointsBalance = pointsResult!.NewBalance,
Message = $"兑换成功!{product.Name} x{input.Quantity} 已放入背包"
};
}
@ -324,7 +306,7 @@ public class WxMallService(
PointsCost = r.PointsCost,
PointsBalance = r.PointsBalance,
Quantity = r.Quantity,
Status = r.Status,
Status = r.Status.ToString(),
CreatedAt = r.CreatedAt
})
.ToListAsync();
@ -336,7 +318,7 @@ public class WxMallService(
public async Task<List<UserBagOutput>> GetBagItemsAsync(long userId, string? itemType = null)
{
var query = exchangeRecordRepository.Context.Queryable<UserBag>()
.Where(b => b.UserId == userId && !b.IsDeleted && b.Status == "Available")
.Where(b => b.UserId == userId && !b.IsDeleted && b.Status == UserBagStatusEnum.Available)
.WhereIF(!string.IsNullOrEmpty(itemType), b => b.ItemType == itemType)
.OrderByDescending(b => b.CreatedAt);
@ -391,7 +373,7 @@ public class WxMallService(
ItemId = b.ItemId,
ItemType = b.ItemType,
Quantity = b.Quantity,
Status = b.Status,
Status = b.Status.ToString(),
CreatedAt = b.CreatedAt,
Product = product != null ? new BagProductBrief
{
@ -421,7 +403,7 @@ public class WxMallService(
throw new BusinessException("背包物品Id无效", 400);
var bagItem = await exchangeRecordRepository.Context.Queryable<UserBag>()
.Where(b => b.Id == input.BagItemId && b.UserId == userId && !b.IsDeleted && b.Status == "Available")
.Where(b => b.Id == input.BagItemId && b.UserId == userId && !b.IsDeleted && b.Status == UserBagStatusEnum.Available)
.FirstAsync();
if (bagItem == null)
@ -462,7 +444,7 @@ public class WxMallService(
if (bagItem.Quantity <= 1)
{
await exchangeRecordRepository.Context.Updateable<UserBag>()
.SetColumns(b => b.Status == "UsedUp")
.SetColumns(b => b.Status == UserBagStatusEnum.Expired)
.SetColumns(b => b.Quantity == 0)
.SetColumns(b => b.UpdatedAt == DateTime.Now)
.Where(b => b.Id == bagItem.Id)
@ -525,7 +507,7 @@ public class WxMallService(
// 校验背包中是否拥有该皮肤(通过 MetaData 中的 SkinId 判断)
var hasSkin = await exchangeRecordRepository.Context.Queryable<UserBag>()
.Where(b => b.UserId == userId && !b.IsDeleted && b.Status == "Available" && b.Quantity > 0
.Where(b => b.UserId == userId && !b.IsDeleted && b.Status == UserBagStatusEnum.Available && b.Quantity > 0
&& b.ItemType == "PetBg")
.ToListAsync();