refactor: 统一业务异常处理,标准化结果码和错误响应

1.  新增并完善ResultCode枚举,补充标准HTTP状态码对应的业务状态码
2.  重构BusinessException,新增基于ResultCode的构造函数和ThrowIf扩展方法
3.  替换所有硬编码的HTTP状态码为统一的ResultCode枚举
4.  优化全局异常中间件,根据业务状态码映射对应HTTP状态码并规范化JSON响应
5.  修复OssImageHelper和AutoDotCodeConsumer中的OSS文件处理逻辑
6.  新增用户答题快照实体类
7.  清理废弃的宠物模块迁移脚本
This commit is contained in:
glz
2026-06-29 16:34:26 +08:00
parent 4579fefef9
commit bdaa6a0dc8
35 changed files with 693 additions and 364 deletions

View File

@ -223,12 +223,12 @@ public class PetService(
if (input.PetId <= 0)
{
throw new BusinessException("宠物Id不能为空", 400);
throw new BusinessException("宠物Id不能为空", ResultCode.BAD_REQUEST);
}
if (input.GrowthPoints <= 0)
{
throw new BusinessException("成长值必须大于0", 400);
throw new BusinessException("成长值必须大于0", ResultCode.BAD_REQUEST);
}
// 查询宠物
@ -236,21 +236,21 @@ public class PetService(
if (pet == null || pet.IsDeleted)
{
logger.LogWarning("喂养失败宠物不存在PetId: {PetId}", input.PetId);
throw new BusinessException("宠物不存在", 404);
throw new BusinessException("宠物不存在", ResultCode.NOT_FOUND);
}
// 校验宠物归属
if (pet.UserId != userId)
{
logger.LogWarning("喂养失败无权操作该宠物UserId: {UserId}, PetUserId: {PetUserId}", userId, pet.UserId);
throw new BusinessException("无权操作该宠物", 403);
throw new BusinessException("无权操作该宠物", ResultCode.FORBIDDEN);
}
// 校验宠物状态
if (pet.Status != (int)UserPetStatusEnum.Active)
{
logger.LogWarning("喂养失败宠物未激活PetId: {PetId}, Status: {Status}", input.PetId, pet.Status);
throw new BusinessException("宠物未激活,无法喂养", 400);
throw new BusinessException("宠物未激活,无法喂养", ResultCode.BAD_REQUEST);
}
FeedPetOutput result = new FeedPetOutput();
// 事务保证一致性
@ -273,13 +273,13 @@ public class PetService(
// 查询宠物
var pet = await petRepository.GetByIdAsync(input.PetId);
if (pet == null || pet.IsDeleted)
throw new BusinessException("宠物不存在", 404);
throw new BusinessException("宠物不存在", ResultCode.NOT_FOUND);
if (pet.UserId != userId)
throw new BusinessException("无权操作该宠物", 403);
throw new BusinessException("无权操作该宠物", ResultCode.FORBIDDEN);
if (pet.Status != (int)UserPetStatusEnum.Active)
throw new BusinessException("宠物未激活,无法喂养", 400);
throw new BusinessException("宠物未激活,无法喂养", ResultCode.BAD_REQUEST);
var growthBefore = pet.GrowthPoints;
var growthAfter = growthBefore + input.GrowthPoints;
@ -297,7 +297,7 @@ public class PetService(
if (updateResult <= 0)
{
throw new BusinessException("更新宠物成长值失败", 500);
throw new BusinessException("更新宠物成长值失败", ResultCode.GLOBAL_ERROR);
}
// 进化检查查找下一阶段进化形态PreviousEvolutionId 类型为 long?
@ -348,7 +348,7 @@ public class PetService(
var insertResult = await feedingRecordRepository.InsertAsync(record);
if (!insertResult)
{
throw new BusinessException("写入喂养记录失败", 500);
throw new BusinessException("写入喂养记录失败", ResultCode.GLOBAL_ERROR);
}
logger.LogInformation("喂养宠物成功PetId: {PetId}, 成长值: {Before} -> {After}, 进化: {HasEvolved}",
@ -402,10 +402,10 @@ public class PetService(
public async Task<PetTemplateOutput> CreateTemplateAsync(PetTemplateInput input)
{
if (string.IsNullOrWhiteSpace(input.Name))
throw new BusinessException("模板名称不能为空", 400);
throw new BusinessException("模板名称不能为空", ResultCode.BAD_REQUEST);
if (input.Evolutions == null || input.Evolutions.Count == 0)
throw new BusinessException("至少需要一个进化阶段", 400);
throw new BusinessException("至少需要一个进化阶段", ResultCode.BAD_REQUEST);
PetTemplate template = null!;
@ -430,7 +430,7 @@ public class PetService(
var inserted = await petTemplateRepository.InsertAsync(template);
if (!inserted)
throw new BusinessException("创建宠物模板失败", 500);
throw new BusinessException("创建宠物模板失败", ResultCode.GLOBAL_ERROR);
// 2. 按 StageLevel 排序,依次创建进化阶段
var sortedEvolutions = input.Evolutions.OrderBy(e => e.StageLevel).ToList();
@ -458,7 +458,7 @@ public class PetService(
var evoInserted = await petEvolutionRepository.InsertAsync(evolution);
if (!evoInserted)
throw new BusinessException($"创建进化阶段 [{evoInput.StageName}] 失败", 500);
throw new BusinessException($"创建进化阶段 [{evoInput.StageName}] 失败", ResultCode.GLOBAL_ERROR);
// 记录初始阶段(第一个)
if (previousEvolution == null)
@ -560,10 +560,10 @@ public class PetService(
{
var template = await petTemplateRepository.GetByIdAsync(id);
if (template == null || template.IsDeleted)
throw new BusinessException("宠物模板不存在", 404);
throw new BusinessException("宠物模板不存在", ResultCode.NOT_FOUND);
if (string.IsNullOrWhiteSpace(input.Name))
throw new BusinessException("模板名称不能为空", 400);
throw new BusinessException("模板名称不能为空", ResultCode.BAD_REQUEST);
template.Name = input.Name.Trim();
template.Description = input.Description;
@ -575,7 +575,7 @@ public class PetService(
var result = await petTemplateRepository.UpdateAsync(template);
if (!result)
throw new BusinessException("更新宠物模板失败", 500);
throw new BusinessException("更新宠物模板失败", ResultCode.GLOBAL_ERROR);
logger.LogInformation("更新宠物模板成功Id: {Id}", id);
return BuildTemplateOutput(template);
@ -588,18 +588,18 @@ public class PetService(
{
var template = await petTemplateRepository.GetByIdAsync(id);
if (template == null || template.IsDeleted)
throw new BusinessException("宠物模板不存在", 404);
throw new BusinessException("宠物模板不存在", ResultCode.NOT_FOUND);
if (template.Type == PetTemplateTypeEnum.Default)
throw new BusinessException("默认模板不允许删除", 400);
throw new BusinessException("默认模板不允许删除", ResultCode.BAD_REQUEST);
// 校验是否有用户宠物实例关联
var hasUserPet = petRepository.Context.Queryable<UserPet>()
.Any(p => p.TemplateId == id && !p.IsDeleted);
if (hasUserPet)
throw new BusinessException("该模板下存在用户宠物实例,无法删除", 400);
throw new BusinessException("该模板下存在用户宠物实例,无法删除", ResultCode.BAD_REQUEST);
template.IsDeleted = true;
template.UpdatedBy = "System";
@ -616,7 +616,7 @@ public class PetService(
{
var template = await petTemplateRepository.GetByIdAsync(id);
if (template == null || template.IsDeleted)
throw new BusinessException("宠物模板不存在", 404);
throw new BusinessException("宠物模板不存在", ResultCode.NOT_FOUND);
return BuildTemplateOutput(template);
}
@ -663,7 +663,7 @@ public class PetService(
{
var template = await petTemplateRepository.GetByIdAsync(id);
if (template == null || template.IsDeleted)
throw new BusinessException("宠物模板不存在", 404);
throw new BusinessException("宠物模板不存在", ResultCode.NOT_FOUND);
template.Status = template.Status == (int)DefaultStatusEnum.Active
? (int)DefaultStatusEnum.Inactive
@ -703,16 +703,16 @@ public class PetService(
public async Task<PetEvolutionOutput> CreateEvolutionAsync(PetEvolutionInput input)
{
if (input.TemplateId <= 0)
throw new BusinessException("模板Id不能为空", 400);
throw new BusinessException("模板Id不能为空", ResultCode.BAD_REQUEST);
if (string.IsNullOrWhiteSpace(input.StageName))
throw new BusinessException("阶段名称不能为空", 400);
throw new BusinessException("阶段名称不能为空", ResultCode.BAD_REQUEST);
// 校验模板是否存在
var templateExists = petTemplateRepository.Context.Queryable<PetTemplate>()
.Any(t => t.Id == input.TemplateId && !t.IsDeleted);
if (!templateExists)
throw new BusinessException("宠物模板不存在", 404);
throw new BusinessException("宠物模板不存在", ResultCode.NOT_FOUND);
var evolution = new PetEvolution
{
@ -732,7 +732,7 @@ public class PetService(
var result = await petEvolutionRepository.InsertAsync(evolution);
if (!result)
throw new BusinessException("创建进化阶段失败", 500);
throw new BusinessException("创建进化阶段失败", ResultCode.GLOBAL_ERROR);
logger.LogInformation("创建进化阶段成功Id: {Id}, StageName: {StageName}", evolution.Id, evolution.StageName);
return BuildEvolutionOutput(evolution);
@ -745,10 +745,10 @@ public class PetService(
{
var evolution = await petEvolutionRepository.GetByIdAsync(id);
if (evolution == null || evolution.IsDeleted)
throw new BusinessException("进化阶段不存在", 404);
throw new BusinessException("进化阶段不存在", ResultCode.NOT_FOUND);
if (string.IsNullOrWhiteSpace(input.StageName))
throw new BusinessException("阶段名称不能为空", 400);
throw new BusinessException("阶段名称不能为空", ResultCode.BAD_REQUEST);
evolution.TemplateId = input.TemplateId;
evolution.StageName = input.StageName.Trim();
@ -761,7 +761,7 @@ public class PetService(
var result = await petEvolutionRepository.UpdateAsync(evolution);
if (!result)
throw new BusinessException("更新进化阶段失败", 500);
throw new BusinessException("更新进化阶段失败", ResultCode.GLOBAL_ERROR);
logger.LogInformation("更新进化阶段成功Id: {Id}", id);
return BuildEvolutionOutput(evolution);
@ -774,13 +774,13 @@ public class PetService(
{
var evolution = await petEvolutionRepository.GetByIdAsync(id);
if (evolution == null || evolution.IsDeleted)
throw new BusinessException("进化阶段不存在", 404);
throw new BusinessException("进化阶段不存在", ResultCode.NOT_FOUND);
// 校验是否有用户宠物处于该形态
var hasUserPet = petRepository.Context.Queryable<UserPet>()
.Any(p => p.CurrentEvolutionId == id && !p.IsDeleted);
if (hasUserPet)
throw new BusinessException("有用户宠物正处于该形态,无法删除", 400);
throw new BusinessException("有用户宠物正处于该形态,无法删除", ResultCode.BAD_REQUEST);
evolution.IsDeleted = true;
evolution.UpdatedBy = "System";
@ -797,7 +797,7 @@ public class PetService(
{
var evolution = await petEvolutionRepository.GetByIdAsync(id);
if (evolution == null || evolution.IsDeleted)
throw new BusinessException("进化阶段不存在", 404);
throw new BusinessException("进化阶段不存在", ResultCode.NOT_FOUND);
return BuildEvolutionOutput(evolution);
}
@ -861,16 +861,16 @@ public class PetService(
public async Task<PetSkinOutput> CreateSkinAsync(PetSkinInput input)
{
if (input.TemplateId <= 0)
throw new BusinessException("模板Id不能为空", 400);
throw new BusinessException("模板Id不能为空", ResultCode.BAD_REQUEST);
if (string.IsNullOrWhiteSpace(input.Name))
throw new BusinessException("皮肤名称不能为空", 400);
throw new BusinessException("皮肤名称不能为空", ResultCode.BAD_REQUEST);
// 校验模板是否存在
var templateExists = petTemplateRepository.Context.Queryable<PetTemplate>()
.Any(t => t.Id == input.TemplateId && !t.IsDeleted);
if (!templateExists)
throw new BusinessException("宠物模板不存在", 404);
throw new BusinessException("宠物模板不存在", ResultCode.NOT_FOUND);
var skin = new PetSkin
{
@ -890,7 +890,7 @@ public class PetService(
var result = await petSkinRepository.InsertAsync(skin);
if (!result)
throw new BusinessException("创建皮肤失败", 500);
throw new BusinessException("创建皮肤失败", ResultCode.GLOBAL_ERROR);
// 搬运封面图到正式目录
if (OssImageHelper.IsTempImage(skin.CoverImageUrl))
@ -911,10 +911,10 @@ public class PetService(
{
var skin = await petSkinRepository.GetByIdAsync(id);
if (skin == null || skin.IsDeleted)
throw new BusinessException("皮肤不存在", 404);
throw new BusinessException("皮肤不存在", ResultCode.NOT_FOUND);
if (string.IsNullOrWhiteSpace(input.Name))
throw new BusinessException("皮肤名称不能为空", 400);
throw new BusinessException("皮肤名称不能为空", ResultCode.BAD_REQUEST);
skin.TemplateId = input.TemplateId;
skin.Name = input.Name.Trim();
@ -937,7 +937,7 @@ public class PetService(
var result = await petSkinRepository.UpdateAsync(skin);
if (!result)
throw new BusinessException("更新皮肤失败", 500);
throw new BusinessException("更新皮肤失败", ResultCode.GLOBAL_ERROR);
logger.LogInformation("更新皮肤成功Id: {Id}", id);
@ -957,13 +957,13 @@ public class PetService(
{
var skin = await petSkinRepository.GetByIdAsync(id);
if (skin == null || skin.IsDeleted)
throw new BusinessException("皮肤不存在", 404);
throw new BusinessException("皮肤不存在", ResultCode.NOT_FOUND);
// 校验是否有用户宠物正在使用该皮肤
var inUse = petRepository.Context.Queryable<UserPet>()
.Any(p => p.CurrentSkinId == id && !p.IsDeleted);
if (inUse)
throw new BusinessException("有用户宠物正在使用该皮肤,无法删除", 400);
throw new BusinessException("有用户宠物正在使用该皮肤,无法删除", ResultCode.BAD_REQUEST);
skin.IsDeleted = true;
skin.UpdatedBy = "System";
@ -980,7 +980,7 @@ public class PetService(
{
var skin = await petSkinRepository.GetByIdAsync(id);
if (skin == null || skin.IsDeleted)
throw new BusinessException("皮肤不存在", 404);
throw new BusinessException("皮肤不存在", ResultCode.NOT_FOUND);
var images = await petSkinImageRepository.Queryable()
.Where(i => i.SkinId == id && !i.IsDeleted)
@ -1072,19 +1072,19 @@ public class PetService(
public async Task<PetSkinImageOutput> CreateSkinImageAsync(PetSkinImageInput input)
{
if (input.SkinId <= 0)
throw new BusinessException("皮肤Id不能为空", 400);
throw new BusinessException("皮肤Id不能为空", ResultCode.BAD_REQUEST);
if (input.EvolutionStageId <= 0)
throw new BusinessException("进化阶段Id不能为空", 400);
throw new BusinessException("进化阶段Id不能为空", ResultCode.BAD_REQUEST);
if (string.IsNullOrWhiteSpace(input.ImageUrl))
throw new BusinessException("图片地址不能为空", 400);
throw new BusinessException("图片地址不能为空", ResultCode.BAD_REQUEST);
// 校验皮肤是否存在
var skinExists = petSkinRepository.Context.Queryable<PetSkin>()
.Any(s => s.Id == input.SkinId && !s.IsDeleted);
if (!skinExists)
throw new BusinessException("皮肤不存在", 404);
throw new BusinessException("皮肤不存在", ResultCode.NOT_FOUND);
var image = new PetSkinImage
{
@ -1102,7 +1102,7 @@ public class PetService(
var result = await petSkinImageRepository.InsertAsync(image);
if (!result)
throw new BusinessException("创建皮肤图片失败", 500);
throw new BusinessException("创建皮肤图片失败", ResultCode.GLOBAL_ERROR);
// 搬运图片到正式目录
if (OssImageHelper.IsTempImage(image.ImageUrl))
@ -1123,10 +1123,10 @@ public class PetService(
{
var image = await petSkinImageRepository.GetByIdAsync(id);
if (image == null || image.IsDeleted)
throw new BusinessException("皮肤图片不存在", 404);
throw new BusinessException("皮肤图片不存在", ResultCode.NOT_FOUND);
if (string.IsNullOrWhiteSpace(input.ImageUrl))
throw new BusinessException("图片地址不能为空", 400);
throw new BusinessException("图片地址不能为空", ResultCode.BAD_REQUEST);
image.SkinId = input.SkinId;
image.EvolutionStageId = input.EvolutionStageId;
@ -1147,7 +1147,7 @@ public class PetService(
var result = await petSkinImageRepository.UpdateAsync(image);
if (!result)
throw new BusinessException("更新皮肤图片失败", 500);
throw new BusinessException("更新皮肤图片失败", ResultCode.GLOBAL_ERROR);
logger.LogInformation("更新皮肤图片成功Id: {Id}", id);
return BuildSkinImageOutput(image);
@ -1160,7 +1160,7 @@ public class PetService(
{
var image = await petSkinImageRepository.GetByIdAsync(id);
if (image == null || image.IsDeleted)
throw new BusinessException("皮肤图片不存在", 404);
throw new BusinessException("皮肤图片不存在", ResultCode.NOT_FOUND);
image.IsDeleted = true;
image.UpdatedBy = "System";
@ -1208,7 +1208,7 @@ public class PetService(
public async Task<List<PetEvolutionOutput>> GetEvolutionsByTemplateIdAsync(long templateId)
{
if (templateId <= 0)
throw new BusinessException("模板Id不能为空", 400);
throw new BusinessException("模板Id不能为空", ResultCode.BAD_REQUEST);
var list = await petEvolutionRepository.Queryable()
.Where(e => e.TemplateId == templateId && !e.IsDeleted)
@ -1224,7 +1224,7 @@ public class PetService(
public async Task<List<PetSkinOutput>> GetSkinsByTemplateIdAsync(long templateId)
{
if (templateId <= 0)
throw new BusinessException("模板Id不能为空", 400);
throw new BusinessException("模板Id不能为空", ResultCode.BAD_REQUEST);
var list = await petSkinRepository.Queryable()
.Where(s => s.TemplateId == templateId && !s.IsDeleted)
@ -1276,12 +1276,12 @@ public class PetService(
public async Task<List<SkinEvolutionStageOutput>> GetSkinImagesGroupedBySkinIdAsync(long skinId)
{
if (skinId <= 0)
throw new BusinessException("皮肤Id不能为空", 400);
throw new BusinessException("皮肤Id不能为空", ResultCode.BAD_REQUEST);
// 1. 获取皮肤信息
var skin = await petSkinRepository.GetByIdAsync(skinId);
if (skin == null || skin.IsDeleted)
throw new BusinessException("皮肤不存在", 404);
throw new BusinessException("皮肤不存在", ResultCode.NOT_FOUND);
// 2. 获取该模板的所有进化阶段(按阶段等级排序)
var evolutions = await petEvolutionRepository.Queryable()