feat: 新增宠物封面图支持,优化补偿任务查询与宠物模板创建流程
1. 新增PetSkinImageTypeEnum.Cover枚举,添加封面图类型支持 2. 为PetSkin、PetTemplate实体添加封面图片地址字段 3. 新增补偿任务按用户ID筛选功能,优化UsersService查询逻辑 4. 重构宠物模板创建流程,支持批量创建进化链与图片 5. 移除PetEvolution实体的基础属性字段,简化进化形态结构 6. 新增OSS域名辅助工具与SDK注册扩展类 7. 优化宠物皮肤、进化阶段的DTO与服务层逻辑
This commit is contained in:
@ -119,20 +119,38 @@ public class PetService(
|
||||
throw new Exception("宠物模板配置缺失");
|
||||
}
|
||||
|
||||
// 查询模板对应的初始进化形态
|
||||
var initialEvolution = await petEvolutionRepository.Queryable()
|
||||
.Where(e => e.TemplateId == defaultTemplate.Id
|
||||
&& e.PreviousEvolutionId == null
|
||||
&& e.Status == (int)PetEvolutionStatusEnum.Active)
|
||||
.OrderBy(e => e.StageLevel)
|
||||
.FirstAsync();
|
||||
// 确定初始进化形态:优先使用模板的 DefaultEvolutionId,为 0 时动态查询兜底
|
||||
PetEvolution? initialEvolution = null;
|
||||
if (defaultTemplate.DefaultEvolutionId > 0)
|
||||
{
|
||||
initialEvolution = await petEvolutionRepository.Queryable()
|
||||
.Where(e => e.Id == defaultTemplate.DefaultEvolutionId && !e.IsDeleted)
|
||||
.FirstAsync();
|
||||
}
|
||||
|
||||
if (initialEvolution == null)
|
||||
{
|
||||
// 兜底:查询该模板下 PreviousEvolutionId 为 null 的初始形态
|
||||
initialEvolution = await petEvolutionRepository.Queryable()
|
||||
.Where(e => e.TemplateId == defaultTemplate.Id
|
||||
&& e.PreviousEvolutionId == null
|
||||
&& e.Status == (int)PetEvolutionStatusEnum.Active)
|
||||
.OrderBy(e => e.StageLevel)
|
||||
.FirstAsync();
|
||||
}
|
||||
|
||||
if (initialEvolution == null)
|
||||
{
|
||||
logger.LogError("模板 {TemplateId} 未配置初始进化形态", defaultTemplate.Id);
|
||||
throw new Exception("宠物模板初始进化形态配置缺失");
|
||||
}
|
||||
|
||||
var pet = new UserPet
|
||||
{
|
||||
UserId = userId,
|
||||
TemplateId = defaultTemplate.Id,
|
||||
Name = defaultTemplate.Name,
|
||||
CurrentEvolutionId = initialEvolution?.Id ?? 0,
|
||||
CurrentEvolutionId = initialEvolution.Id,
|
||||
GrowthPoints = 0,
|
||||
FeedingCount = 0,
|
||||
CurrentSkinId = 0,
|
||||
@ -352,34 +370,149 @@ public class PetService(
|
||||
// ==================== 后台管理:宠物模板 ====================
|
||||
|
||||
/// <summary>
|
||||
/// 创建宠物模板
|
||||
/// 创建宠物模板(事务:同时创建进化链、默认皮肤及图片)
|
||||
/// </summary>
|
||||
public async Task<PetTemplateOutput> CreateTemplateAsync(PetTemplateInput input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input.Name))
|
||||
throw new BusinessException("模板名称不能为空", 400);
|
||||
|
||||
var template = new PetTemplate
|
||||
if (input.Evolutions == null || input.Evolutions.Count == 0)
|
||||
throw new BusinessException("至少需要一个进化阶段", 400);
|
||||
|
||||
PetTemplate template = null!;
|
||||
|
||||
await UseTranAsync(async () =>
|
||||
{
|
||||
Name = input.Name.Trim(),
|
||||
Description = input.Description,
|
||||
DefaultEvolutionId = input.DefaultEvolutionId,
|
||||
IconUrl = input.IconUrl,
|
||||
SortOrder = input.SortOrder,
|
||||
Type = Enum.Parse<PetTemplateTypeEnum>(input.Type, true),
|
||||
Status = (int)PetTemplateStatusEnum.Active,
|
||||
CreatedBy = "System",
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = "System",
|
||||
UpdatedAt = DateTime.Now,
|
||||
IsDeleted = false
|
||||
};
|
||||
// 1. 创建模板(DefaultEvolutionId 先置 0,稍后回填)
|
||||
template = new PetTemplate
|
||||
{
|
||||
Name = input.Name.Trim(),
|
||||
Description = input.Description,
|
||||
DefaultEvolutionId = 0,
|
||||
IconUrl = input.IconUrl,
|
||||
SortOrder = input.SortOrder,
|
||||
Type = Enum.Parse<PetTemplateTypeEnum>(input.Type, true),
|
||||
Status = (int)PetTemplateStatusEnum.Active,
|
||||
CreatedBy = "System",
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = "System",
|
||||
UpdatedAt = DateTime.Now,
|
||||
IsDeleted = false
|
||||
};
|
||||
|
||||
var result = await petTemplateRepository.InsertAsync(template);
|
||||
if (!result)
|
||||
throw new BusinessException("创建宠物模板失败", 500);
|
||||
var inserted = await petTemplateRepository.InsertAsync(template);
|
||||
if (!inserted)
|
||||
throw new BusinessException("创建宠物模板失败", 500);
|
||||
|
||||
logger.LogInformation("创建宠物模板成功,Id: {Id}, Name: {Name}", template.Id, template.Name);
|
||||
// 2. 按 StageLevel 排序,依次创建进化阶段
|
||||
var sortedEvolutions = input.Evolutions.OrderBy(e => e.StageLevel).ToList();
|
||||
PetEvolution? previousEvolution = null;
|
||||
long initialEvolutionId = 0;
|
||||
string? initialCoverUrl = null;
|
||||
|
||||
foreach (var evoInput in sortedEvolutions)
|
||||
{
|
||||
var evolution = new PetEvolution
|
||||
{
|
||||
TemplateId = template.Id,
|
||||
StageName = evoInput.StageName.Trim(),
|
||||
StageLevel = evoInput.StageLevel,
|
||||
RequiredGrowth = evoInput.RequiredGrowth,
|
||||
PreviousEvolutionId = previousEvolution?.Id, // 第一个为 null
|
||||
Type = Enum.Parse<PetEvolutionTypeEnum>(evoInput.Type, true),
|
||||
Status = (int)PetEvolutionStatusEnum.Active,
|
||||
CreatedBy = "System",
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = "System",
|
||||
UpdatedAt = DateTime.Now,
|
||||
IsDeleted = false
|
||||
};
|
||||
|
||||
var evoInserted = await petEvolutionRepository.InsertAsync(evolution);
|
||||
if (!evoInserted)
|
||||
throw new BusinessException($"创建进化阶段 [{evoInput.StageName}] 失败", 500);
|
||||
|
||||
// 记录初始阶段(第一个)
|
||||
if (previousEvolution == null)
|
||||
{
|
||||
initialEvolutionId = evolution.Id;
|
||||
}
|
||||
|
||||
// 3. 为该阶段创建默认皮肤图片
|
||||
if (evoInput.Images != null && evoInput.Images.Count > 0)
|
||||
{
|
||||
foreach (var imgInput in evoInput.Images.OrderBy(i => i.SortOrder))
|
||||
{
|
||||
var skinImage = new PetSkinImage
|
||||
{
|
||||
SkinId = 0, // 先置 0,等默认皮肤创建后回填
|
||||
EvolutionStageId = evolution.Id,
|
||||
ImageUrl = imgInput.ImageUrl.Trim(),
|
||||
SortOrder = imgInput.SortOrder,
|
||||
Type = Enum.Parse<PetSkinImageTypeEnum>(imgInput.Type, true),
|
||||
CreatedBy = "System",
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = "System",
|
||||
UpdatedAt = DateTime.Now,
|
||||
IsDeleted = false
|
||||
};
|
||||
|
||||
await petSkinImageRepository.InsertAsync(skinImage);
|
||||
|
||||
// 记录初始阶段的封面图
|
||||
if (previousEvolution == null
|
||||
&& imgInput.Type == PetSkinImageTypeEnum.Cover.ToString()
|
||||
&& initialCoverUrl == null)
|
||||
{
|
||||
initialCoverUrl = skinImage.ImageUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousEvolution = evolution;
|
||||
}
|
||||
|
||||
// 4. 创建默认皮肤
|
||||
var defaultSkin = new PetSkin
|
||||
{
|
||||
TemplateId = template.Id,
|
||||
Name = "默认皮肤",
|
||||
Description = "系统默认皮肤",
|
||||
Rarity = "Normal",
|
||||
SortOrder = 0,
|
||||
Type = Enum.Parse<PetSkinTypeEnum>("Normal", true),
|
||||
CoverImageUrl = initialCoverUrl,
|
||||
CreatedBy = "System",
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = "System",
|
||||
UpdatedAt = DateTime.Now,
|
||||
IsDeleted = false
|
||||
};
|
||||
await petSkinRepository.InsertAsync(defaultSkin);
|
||||
|
||||
// 5. 回填图片的 SkinId
|
||||
var evolutionIds = await petEvolutionRepository.Context.Queryable<PetEvolution>()
|
||||
.Where(e => e.TemplateId == template.Id && !e.IsDeleted)
|
||||
.Select(e => e.Id)
|
||||
.ToListAsync();
|
||||
|
||||
if (evolutionIds.Count > 0)
|
||||
{
|
||||
await petSkinImageRepository.Context.Updateable<PetSkinImage>()
|
||||
.SetColumns(i => i.SkinId == defaultSkin.Id)
|
||||
.Where(i => i.SkinId == 0 && i.IsDeleted == false
|
||||
&& evolutionIds.Contains(i.EvolutionStageId))
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
// 6. 回填模板的 DefaultEvolutionId 和 CoverImageUrl
|
||||
template.DefaultEvolutionId = initialEvolutionId;
|
||||
template.CoverImageUrl = initialCoverUrl;
|
||||
await petTemplateRepository.UpdateAsync(template);
|
||||
});
|
||||
|
||||
logger.LogInformation("创建宠物模板成功,Id: {Id}, 初始进化Id: {EvoId}", template.Id, template.DefaultEvolutionId);
|
||||
return BuildTemplateOutput(template);
|
||||
}
|
||||
|
||||
@ -397,7 +530,6 @@ public class PetService(
|
||||
|
||||
template.Name = input.Name.Trim();
|
||||
template.Description = input.Description;
|
||||
template.DefaultEvolutionId = input.DefaultEvolutionId;
|
||||
template.IconUrl = input.IconUrl;
|
||||
template.SortOrder = input.SortOrder;
|
||||
template.Type = Enum.Parse<PetTemplateTypeEnum>(input.Type, true);
|
||||
@ -468,6 +600,7 @@ public class PetService(
|
||||
Description = t.Description,
|
||||
DefaultEvolutionId = t.DefaultEvolutionId,
|
||||
IconUrl = t.IconUrl,
|
||||
CoverImageUrl = t.CoverImageUrl,
|
||||
SortOrder = t.SortOrder,
|
||||
Type = t.Type.ToString(),
|
||||
Status = t.Status.ToString(),
|
||||
@ -509,6 +642,7 @@ public class PetService(
|
||||
Description = t.Description,
|
||||
DefaultEvolutionId = t.DefaultEvolutionId,
|
||||
IconUrl = t.IconUrl,
|
||||
CoverImageUrl = t.CoverImageUrl,
|
||||
SortOrder = t.SortOrder,
|
||||
Type = t.Type.ToString(),
|
||||
Status = t.Status.ToString(),
|
||||
@ -545,10 +679,6 @@ public class PetService(
|
||||
StageLevel = input.StageLevel,
|
||||
RequiredGrowth = input.RequiredGrowth,
|
||||
PreviousEvolutionId = input.PreviousEvolutionId,
|
||||
BaseStrength = input.BaseStrength,
|
||||
BaseAgility = input.BaseAgility,
|
||||
BaseIntelligence = input.BaseIntelligence,
|
||||
BaseCharm = input.BaseCharm,
|
||||
Type = Enum.Parse<PetEvolutionTypeEnum>(input.Type, true),
|
||||
Status = (int)PetEvolutionStatusEnum.Active,
|
||||
CreatedBy = "System",
|
||||
@ -583,10 +713,6 @@ public class PetService(
|
||||
evolution.StageLevel = input.StageLevel;
|
||||
evolution.RequiredGrowth = input.RequiredGrowth;
|
||||
evolution.PreviousEvolutionId = input.PreviousEvolutionId;
|
||||
evolution.BaseStrength = input.BaseStrength;
|
||||
evolution.BaseAgility = input.BaseAgility;
|
||||
evolution.BaseIntelligence = input.BaseIntelligence;
|
||||
evolution.BaseCharm = input.BaseCharm;
|
||||
evolution.Type = Enum.Parse<PetEvolutionTypeEnum>(input.Type, true);
|
||||
evolution.UpdatedBy = "System";
|
||||
evolution.UpdatedAt = DateTime.Now;
|
||||
@ -654,10 +780,6 @@ public class PetService(
|
||||
StageLevel = e.StageLevel,
|
||||
RequiredGrowth = e.RequiredGrowth,
|
||||
PreviousEvolutionId = e.PreviousEvolutionId,
|
||||
BaseStrength = e.BaseStrength,
|
||||
BaseAgility = e.BaseAgility,
|
||||
BaseIntelligence = e.BaseIntelligence,
|
||||
BaseCharm = e.BaseCharm,
|
||||
Type = e.Type.ToString(),
|
||||
Status = e.Status.ToString(),
|
||||
CreatedBy = e.CreatedBy,
|
||||
@ -680,10 +802,6 @@ public class PetService(
|
||||
StageLevel = e.StageLevel,
|
||||
RequiredGrowth = e.RequiredGrowth,
|
||||
PreviousEvolutionId = e.PreviousEvolutionId,
|
||||
BaseStrength = e.BaseStrength,
|
||||
BaseAgility = e.BaseAgility,
|
||||
BaseIntelligence = e.BaseIntelligence,
|
||||
BaseCharm = e.BaseCharm,
|
||||
Type = e.Type.ToString(),
|
||||
Status = e.Status.ToString(),
|
||||
CreatedBy = e.CreatedBy,
|
||||
@ -720,6 +838,7 @@ public class PetService(
|
||||
Rarity = input.Rarity,
|
||||
SortOrder = input.SortOrder,
|
||||
Type = Enum.Parse<PetSkinTypeEnum>(input.Type, true),
|
||||
CoverImageUrl = input.CoverImageUrl,
|
||||
CreatedBy = "System",
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = "System",
|
||||
@ -753,6 +872,7 @@ public class PetService(
|
||||
skin.Rarity = input.Rarity;
|
||||
skin.SortOrder = input.SortOrder;
|
||||
skin.Type = Enum.Parse<PetSkinTypeEnum>(input.Type, true);
|
||||
skin.CoverImageUrl = input.CoverImageUrl;
|
||||
skin.UpdatedBy = "System";
|
||||
skin.UpdatedAt = DateTime.Now;
|
||||
|
||||
@ -834,6 +954,7 @@ public class PetService(
|
||||
Rarity = s.Rarity,
|
||||
SortOrder = s.SortOrder,
|
||||
Type = s.Type.ToString(),
|
||||
CoverImageUrl = s.CoverImageUrl,
|
||||
CreatedBy = s.CreatedBy,
|
||||
CreatedAt = s.CreatedAt,
|
||||
UpdatedBy = s.UpdatedBy,
|
||||
@ -875,6 +996,7 @@ public class PetService(
|
||||
Rarity = s.Rarity,
|
||||
SortOrder = s.SortOrder,
|
||||
Type = s.Type.ToString(),
|
||||
CoverImageUrl = s.CoverImageUrl,
|
||||
CreatedBy = s.CreatedBy,
|
||||
CreatedAt = s.CreatedAt,
|
||||
UpdatedBy = s.UpdatedBy,
|
||||
|
||||
Reference in New Issue
Block a user