diff --git a/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs b/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs index 2356c0f..d7c3971 100644 --- a/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs +++ b/src/Api/TakeoutSaaS.TenantApi/Controllers/ProductController.cs @@ -396,7 +396,7 @@ public sealed class ProductController( { entity.Status = ProductSkuSaveJobStatus.Failed; entity.FailedCount = entity.ProgressTotal; - entity.ErrorMessage = Truncate(ex.Message, 2000); + entity.ErrorMessage = Truncate(BuildDetailedErrorMessage(ex), 2000); entity.FinishedAt = DateTime.UtcNow; await dbContext.SaveChangesAsync(cancellationToken); return ApiResponse.Error(ErrorCodes.InternalServerError, "SKU 保存任务创建失败"); @@ -1370,6 +1370,32 @@ public sealed class ProductController( return value.Length <= maxLength ? value : value[..maxLength]; } + private static string BuildDetailedErrorMessage(Exception ex) + { + var lines = new List(); + var current = ex; + var depth = 0; + const int maxDepth = 8; + + while (current is not null && depth < maxDepth) + { + var prefix = depth == 0 ? "Exception" : $"Inner[{depth}]"; + var message = string.IsNullOrWhiteSpace(current.Message) + ? "(no message)" + : current.Message.Trim(); + lines.Add($"{prefix} {current.GetType().Name}: {message}"); + current = current.InnerException; + depth++; + } + + if (current is not null) + { + lines.Add($"Inner[{depth}] ..."); + } + + return string.Join(Environment.NewLine, lines); + } + private async Task LoadProductRelationStateAsync( long productId, long storeId, diff --git a/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveJobRunner.cs b/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveJobRunner.cs index 81131c6..0d72884 100644 --- a/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveJobRunner.cs +++ b/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveJobRunner.cs @@ -44,7 +44,7 @@ public sealed class ProductSkuSaveJobRunner( catch (Exception ex) { logger.LogError(ex, "SKU 异步保存任务执行失败,JobId={JobId}", jobId); - await MarkFailedAsync(jobMeta.Id, ex.Message); + await MarkFailedAsync(jobMeta.Id, BuildDetailedErrorMessage(ex)); } } @@ -159,5 +159,31 @@ public sealed class ProductSkuSaveJobRunner( return value.Length <= maxLength ? value : value[..maxLength]; } + private static string BuildDetailedErrorMessage(Exception ex) + { + var lines = new List(); + var current = ex; + var depth = 0; + const int maxDepth = 8; + + while (current is not null && depth < maxDepth) + { + var prefix = depth == 0 ? "Exception" : $"Inner[{depth}]"; + var message = string.IsNullOrWhiteSpace(current.Message) + ? "(no message)" + : current.Message.Trim(); + lines.Add($"{prefix} {current.GetType().Name}: {message}"); + current = current.InnerException; + depth++; + } + + if (current is not null) + { + lines.Add($"Inner[{depth}] ..."); + } + + return string.Join(Environment.NewLine, lines); + } + private sealed record JobMeta(long Id, long TenantId); }