Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/PetService.cs
glz 866549873a refactor(pet): 重构宠物模块实体与接口
1. 将原Pet实体重命名为UserPet,新增PetTemplate模板实体拆分宠物模板与实例数据
2. 调整IPetService泛型参数为IBaseService<UserPet>
3. 补充宠物模板相关字段与查询逻辑,完善创建默认宠物的流程
4. 替换所有Pet实体引用为UserPet,修正相关服务层查询与更新逻辑
2026-06-05 18:10:34 +08:00

351 lines
13 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.Dto.Pet;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Repository;
using SqlSugar;
namespace QYZH.InteractiveMagazine.Service;
/// <summary>
/// 宠物服务实现
/// </summary>
public class PetService(
BaseRepository<UserPet> petRepository,
BaseRepository<PetTemplate> petTemplateRepository,
BaseRepository<PetFeedingRecord> feedingRecordRepository,
BaseRepository<PetEvolution> petEvolutionRepository,
BaseRepository<PetSkinImage> petSkinImageRepository,
BaseRepository<PetSkin> petSkinRepository,
ILogger<PetService> logger)
: BaseRepository<UserPet>, IPetService
{
/// <summary>
/// 获取用户宠物信息(含当前形态名称、皮肤图片序列)
/// </summary>
public async Task<PetOutput?> GetPetByUserIdAsync(long userId)
{
logger.LogInformation("获取用户宠物信息UserId: {UserId}", userId);
var pet = await petRepository.Queryable()
.Where(p => p.UserId == userId)
.FirstAsync();
if (pet == null) return null;
// 查询当前进化阶段名称
var evolution = await petEvolutionRepository.Queryable()
.Where(e => e.Id == pet.CurrentEvolutionId)
.FirstAsync();
// 查询宠物模板名称
var template = await petTemplateRepository.Queryable()
.Where(t => t.Id == pet.TemplateId && !t.IsDeleted)
.FirstAsync();
// 查询当前皮肤名称
string? skinName = null;
if (pet.CurrentSkinId > 0)
{
var skin = await petSkinRepository.Queryable()
.Where(s => s.Id == pet.CurrentSkinId && !s.IsDeleted)
.FirstAsync();
skinName = skin?.Name;
}
// 查询当前形态+当前皮肤 的图片序列
var skinId = pet.CurrentSkinId; // 0 = 默认皮肤
var images = await petSkinImageRepository.Queryable()
.Where(i => i.SkinId == skinId
&& i.EvolutionStageId == pet.CurrentEvolutionId
&& !i.IsDeleted)
.OrderBy(i => i.SortOrder)
.Select(i => new SkinImageOutput
{
Id = i.Id,
ImageUrl = i.ImageUrl,
SortOrder = i.SortOrder
})
.ToListAsync();
return new PetOutput
{
Id = pet.Id,
UserId = pet.UserId,
TemplateId = pet.TemplateId,
TemplateName = template?.Name,
Name = pet.Name,
CurrentEvolutionId = pet.CurrentEvolutionId,
EvolutionStageName = evolution?.StageName,
GrowthPoints = pet.GrowthPoints,
FeedingCount = pet.FeedingCount,
CurrentSkinId = pet.CurrentSkinId,
CurrentSkinName = skinName,
CurrentImages = images,
Type = pet.Type,
Status = pet.Status,
CreatedAt = pet.CreatedAt
};
}
/// <summary>
/// 为用户创建默认宠物最低形态、成长值为0、未激活状态
/// </summary>
public async Task CreateDefaultPetAsync(long userId)
{
logger.LogInformation("为用户创建默认宠物UserId: {UserId}", userId);
// 检查用户是否已有宠物
var exists = petRepository.Context.Queryable<UserPet>()
.Any(p => p.UserId == userId);
if (exists)
{
logger.LogWarning("用户已存在宠物跳过创建UserId: {UserId}", userId);
return;
}
// 查询默认宠物模板取排序权重最低且状态为Active的模板
var defaultTemplate = await petTemplateRepository.Queryable()
.Where(t => t.Status == "Active" && !t.IsDeleted)
.OrderBy(t => t.SortOrder)
.FirstAsync();
if (defaultTemplate == null)
{
logger.LogError("未找到可用的宠物模板");
throw new Exception("宠物模板配置缺失");
}
// 查询模板对应的初始进化形态
var initialEvolution = await petEvolutionRepository.Queryable()
.Where(e => e.TemplateId == defaultTemplate.Id
&& e.PreviousEvolutionId == null
&& e.Status == "Active")
.OrderBy(e => e.StageLevel)
.FirstAsync();
var pet = new UserPet
{
UserId = userId,
TemplateId = defaultTemplate.Id,
Name = defaultTemplate.Name,
CurrentEvolutionId = initialEvolution?.Id ?? 0,
GrowthPoints = 0,
FeedingCount = 0,
CurrentSkinId = 0,
Type = "Normal",
Status = "Inactive",
IsDeleted = false,
CreatedBy = userId.ToString(),
CreatedAt = DateTime.Now,
UpdatedBy = userId.ToString(),
UpdatedAt = DateTime.Now
};
var result = await petRepository.InsertAsync(pet);
if (!result)
{
logger.LogError("创建默认宠物失败UserId: {UserId}", userId);
throw new Exception("创建宠物失败");
}
logger.LogInformation("用户默认宠物创建成功UserId: {UserId}, PetId: {PetId}, EvolutionId: {EvolutionId}",
userId, pet.Id, pet.CurrentEvolutionId);
}
/// <summary>
/// 激活宠物(将状态从 Inactive 改为 Active
/// </summary>
public async Task ActivatePetAsync(long userId)
{
logger.LogInformation("激活用户宠物UserId: {UserId}", userId);
var pet = await petRepository.Queryable()
.Where(p => p.UserId == userId)
.FirstAsync();
if (pet == null)
{
logger.LogWarning("用户宠物不存在无法激活UserId: {UserId}", userId);
return;
}
if (pet.Status != "Inactive")
{
logger.LogInformation("用户宠物已非未激活状态跳过激活UserId: {UserId}, Status: {Status}", userId, pet.Status);
return;
}
var result = await petRepository.UpdateAsync(
p => new UserPet { Status = "Active" },
p => p.UserId == userId);
if (!result)
{
logger.LogError("激活宠物失败UserId: {UserId}", userId);
throw new Exception("激活宠物失败");
}
logger.LogInformation("用户宠物激活成功UserId: {UserId}", userId);
}
/// <summary>
/// 喂养宠物(增加成长值 + 记录喂养记录 + 触发进化检查)
/// </summary>
public async Task<FeedPetOutput> FeedPetAsync(long userId, FeedPetInput input)
{
logger.LogInformation("喂养宠物UserId: {UserId}, PetId: {PetId}, GrowthPoints: {GrowthPoints}",
userId, input.PetId, input.GrowthPoints);
if (input.PetId <= 0)
{
throw new BusinessException("宠物Id不能为空", 400);
}
if (input.GrowthPoints <= 0)
{
throw new BusinessException("成长值必须大于0", 400);
}
// 查询宠物
var pet = await petRepository.GetByIdAsync(input.PetId);
if (pet == null || pet.IsDeleted)
{
logger.LogWarning("喂养失败宠物不存在PetId: {PetId}", input.PetId);
throw new BusinessException("宠物不存在", 404);
}
// 校验宠物归属
if (pet.UserId != userId)
{
logger.LogWarning("喂养失败无权操作该宠物UserId: {UserId}, PetUserId: {PetUserId}", userId, pet.UserId);
throw new BusinessException("无权操作该宠物", 403);
}
// 校验宠物状态
if (pet.Status != "Active")
{
logger.LogWarning("喂养失败宠物未激活PetId: {PetId}, Status: {Status}", input.PetId, pet.Status);
throw new BusinessException("宠物未激活,无法喂养", 400);
}
var growthBefore = pet.GrowthPoints;
var growthAfter = growthBefore + input.GrowthPoints;
var hasEvolved = false;
string? evolvedStageName = null;
// 事务保证一致性
await UseTranAsync(async () =>
{
// 累加成长值和喂养次数
var updateResult = await petRepository.Context.Updateable<UserPet>()
.SetColumns(p => p.GrowthPoints == growthAfter)
.SetColumns(p => p.FeedingCount == p.FeedingCount + 1)
.SetColumns(p => p.UpdatedAt == DateTime.Now)
.SetColumns(p => p.UpdatedBy == userId.ToString())
.Where(p => p.Id == input.PetId)
.ExecuteCommandAsync();
if (updateResult <= 0)
{
throw new BusinessException("更新宠物成长值失败", 500);
}
// 进化检查查找下一阶段进化形态PreviousEvolutionId 类型为 long?
var nextEvolution = await petEvolutionRepository.Queryable()
.Where(e => e.PreviousEvolutionId == pet.CurrentEvolutionId
&& e.RequiredGrowth <= growthAfter
&& e.Status == "Active")
.OrderBy(e => e.RequiredGrowth, OrderByType.Desc)
.FirstAsync();
if (nextEvolution != null)
{
// 触发进化
var evolveResult = await petRepository.Context.Updateable<UserPet>()
.SetColumns(p => p.CurrentEvolutionId == nextEvolution.Id)
.SetColumns(p => p.UpdatedAt == DateTime.Now)
.SetColumns(p => p.UpdatedBy == userId.ToString())
.Where(p => p.Id == input.PetId)
.ExecuteCommandAsync();
if (evolveResult > 0)
{
hasEvolved = true;
evolvedStageName = nextEvolution.StageName;
logger.LogInformation("宠物进化成功PetId: {PetId}, 新形态: {StageName} (Level {StageLevel})",
input.PetId, nextEvolution.StageName, nextEvolution.StageLevel);
}
}
// 写入喂养记录
var record = new PetFeedingRecord
{
PetId = input.PetId,
UserId = userId,
PointsUsed = 0, // 预留:后期可扩展为消耗积分喂养
GrowthChange = input.GrowthPoints,
GrowthBefore = growthBefore,
GrowthAfter = growthAfter,
Type = "Normal",
Status = "Success",
IsDeleted = false,
CreatedBy = userId.ToString(),
CreatedAt = DateTime.Now,
UpdatedBy = userId.ToString(),
UpdatedAt = DateTime.Now
};
var insertResult = await feedingRecordRepository.InsertAsync(record);
if (!insertResult)
{
throw new BusinessException("写入喂养记录失败", 500);
}
});
logger.LogInformation("喂养宠物成功PetId: {PetId}, 成长值: {Before} -> {After}, 进化: {HasEvolved}",
input.PetId, growthBefore, growthAfter, hasEvolved);
return new FeedPetOutput
{
PetId = input.PetId,
GrowthBefore = growthBefore,
GrowthAfter = growthAfter,
GrowthChange = input.GrowthPoints,
HasEvolved = hasEvolved,
EvolvedStageName = evolvedStageName
};
}
/// <summary>
/// 获取宠物喂养记录列表
/// </summary>
public async Task<PageListModel<FeedingRecordOutput>> GetFeedingRecordsAsync(long userId, long petId, PageQueryModel pageQuery)
{
logger.LogInformation("查询喂养记录UserId: {UserId}, PetId: {PetId}, PageIndex: {PageIndex}, PageSize: {PageSize}",
userId, petId, pageQuery.PageIndex, pageQuery.PageSize);
RefAsync<int> totalNumber = 0;
var records = await feedingRecordRepository.Queryable()
.Where(r => r.UserId == userId && r.PetId == petId)
.OrderByDescending(r => r.CreatedAt)
.Select(r => new FeedingRecordOutput
{
Id = r.Id,
PetId = r.PetId,
UserId = r.UserId,
GrowthChange = r.GrowthChange,
GrowthBefore = r.GrowthBefore,
GrowthAfter = r.GrowthAfter,
Type = r.Type,
Status = r.Status,
CreatedAt = r.CreatedAt
}, true)
.ToPageListAsync(pageQuery.PageIndex, pageQuery.PageSize, totalNumber);
return new PageListModel<FeedingRecordOutput>(records, pageQuery.PageIndex, pageQuery.PageSize, totalNumber);
}
}