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; /// /// 宠物服务实现 /// public class PetService( BaseRepository petRepository, BaseRepository petTemplateRepository, BaseRepository feedingRecordRepository, BaseRepository petEvolutionRepository, BaseRepository petSkinImageRepository, BaseRepository petSkinRepository, ILogger logger) : BaseRepository, IPetService { /// /// 获取用户宠物信息(含当前形态名称、皮肤图片序列) /// public async Task 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 }; } /// /// 为用户创建默认宠物(最低形态、成长值为0、未激活状态) /// public async Task CreateDefaultPetAsync(long userId) { logger.LogInformation("为用户创建默认宠物,UserId: {UserId}", userId); // 检查用户是否已有宠物 var exists = petRepository.Context.Queryable() .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); } /// /// 激活宠物(将状态从 Inactive 改为 Active) /// 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); } /// /// 喂养宠物(增加成长值 + 记录喂养记录 + 触发进化检查) /// public async Task 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() .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() .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 }; } /// /// 获取宠物喂养记录列表 /// public async Task> GetFeedingRecordsAsync(long userId, long petId, PageQueryModel pageQuery) { logger.LogInformation("查询喂养记录,UserId: {UserId}, PetId: {PetId}, PageIndex: {PageIndex}, PageSize: {PageSize}", userId, petId, pageQuery.PageIndex, pageQuery.PageSize); RefAsync 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(records, pageQuery.PageIndex, pageQuery.PageSize, totalNumber); } }