diff --git a/src/Shentun.Peis.HttpApi.Host/Filter/CustomerExceptionFilterAttribute.cs b/src/Shentun.Peis.HttpApi.Host/Filter/CustomerExceptionFilterAttribute.cs index 4eb44fc9..61266e07 100644 --- a/src/Shentun.Peis.HttpApi.Host/Filter/CustomerExceptionFilterAttribute.cs +++ b/src/Shentun.Peis.HttpApi.Host/Filter/CustomerExceptionFilterAttribute.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; @@ -15,6 +16,7 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Auditing; +using Volo.Abp.Data; using Volo.Abp.Domain.Entities; namespace Shentun.Peis @@ -65,6 +67,69 @@ namespace Shentun.Peis { //errorMessage = "数据库更新错误:" + exceptionContext.Exception.Message; errorMessage = "数据库更新错误:" + exceptionContext.Exception.Message + "=>" + exceptionContext.Exception.InnerException.Message; + } + else if (exceptionContext.Exception is AbpDbConcurrencyException concurrencyEx) + { + var detailBuilder = new System.Text.StringBuilder(); + detailBuilder.AppendLine("并发冲突详细信息:"); + + // 从内部异常获取详细信息 + if (concurrencyEx.InnerException is DbUpdateConcurrencyException dbUpdateEx) + { + + // 获取涉及的所有实体 + foreach (var entry in dbUpdateEx.Entries) + { + var entity = entry.Entity; + var entityType = entity.GetType().Name; + var entityId = GetEntityId(entity); // 使用之前的辅助方法 + + detailBuilder.AppendLine($"\n▶ 冲突实体 [{entityType}]"); + detailBuilder.AppendLine($" 实体ID:{entityId}"); + detailBuilder.AppendLine($" 状态:{entry.State}"); + + // 获取数据库中的当前值 + var databaseValues = entry.GetDatabaseValues(); + if (databaseValues != null) + { + // 记录所有发生冲突的属性 + foreach (var property in entry.Properties) + { + var originalValue = property.OriginalValue; + var currentValue = property.CurrentValue; + var databaseValue = databaseValues[property.Metadata.Name]; + + if (!Equals(currentValue, databaseValue)) + { + detailBuilder.AppendLine($" ⚠ 字段 [{property.Metadata.Name}] 冲突:"); + detailBuilder.AppendLine($" 当前值:{currentValue}"); + detailBuilder.AppendLine($" 数据库值:{databaseValue}"); + detailBuilder.AppendLine($" 原始值:{originalValue}"); + } + } + } + else + { + detailBuilder.AppendLine($" 数据库记录可能已被删除"); + } + } + } + else + { + // 如果没有内部异常,只能记录基本信息 + detailBuilder.AppendLine($"异常类型:{concurrencyEx.GetType().Name}"); + detailBuilder.AppendLine($"消息:{concurrencyEx.Message}"); + } + + var detailInfo = detailBuilder.ToString(); + + // 记录详细日志 + _logger.LogWarning(concurrencyEx, "并发冲突详情:\n{Detail}", detailInfo); + + + errorMessage = "数据并发,稍后再试"; + + } else if (exceptionContext.Exception is BusinessException) { @@ -158,5 +223,45 @@ namespace Shentun.Peis ReadException(ex.InnerException, ref error); } } + + + private string GetEntityId(object entity) + { + try + { + // 方法1:通过实体类型获取 Id 属性 + var entityType = entity.GetType(); + + // 查找名为 "Id" 的属性(不区分大小写) + var idProperty = entityType.GetProperty("Id") ?? + entityType.GetProperty("id") ?? + entityType.GetProperty("ID"); + + if (idProperty != null) + { + var idValue = idProperty.GetValue(entity); + return idValue?.ToString() ?? "null"; + } + + // 如果没找到 Id,查找可能的主键属性(以 Id 结尾) + var possibleKeys = entityType.GetProperties() + .Where(p => p.Name.EndsWith("Id", StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (possibleKeys.Any()) + { + var keyValues = possibleKeys + .Select(p => $"{p.Name}:{p.GetValue(entity)}") + .ToList(); + return string.Join(", ", keyValues); + } + + return "未找到主键属性"; + } + catch (Exception ex) + { + return $"获取主键失败:{ex.Message}"; + } + } } }