fix(api): 修复商品保存事务重试冲突并兼容kind缺省
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 46s
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 46s
This commit is contained in:
@@ -80,7 +80,7 @@ public sealed class SaveProductRequest
|
||||
/// <summary>
|
||||
/// 商品类型(single/combo)。
|
||||
/// </summary>
|
||||
public string Kind { get; set; } = "single";
|
||||
public string? Kind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品名称。
|
||||
|
||||
@@ -126,12 +126,10 @@ public sealed class ProductController(
|
||||
await EnsureStoreAccessibleAsync(storeId, cancellationToken);
|
||||
|
||||
var categoryId = StoreApiHelpers.ParseRequiredSnowflake(request.CategoryId, nameof(request.CategoryId));
|
||||
var kind = ParseKind(request.Kind);
|
||||
var productId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id);
|
||||
var timedOnShelfAt = ParseTimedOnShelfAt(request.ShelfMode, request.TimedOnShelfAt);
|
||||
var normalizedStatus = ResolveStatusByShelfMode(request.ShelfMode, request.Status);
|
||||
var imageUrls = NormalizeImageUrls(request.ImageUrls);
|
||||
var comboGroups = NormalizeComboGroups(request.ComboGroups, kind);
|
||||
|
||||
ProductDto? existing = null;
|
||||
if (productId.HasValue)
|
||||
@@ -147,6 +145,9 @@ public sealed class ProductController(
|
||||
}
|
||||
}
|
||||
|
||||
var kind = ResolveKindForSave(request.Kind, existing);
|
||||
var comboGroups = NormalizeComboGroups(request.ComboGroups, kind);
|
||||
|
||||
var shouldReplaceSpecAddon = existing is null || request.SpecTemplateIds is not null || request.AddonGroupIds is not null;
|
||||
var shouldReplaceLabels = existing is null || request.LabelIds is not null;
|
||||
var shouldReplaceSkus = existing is null || request.Skus is not null;
|
||||
@@ -187,9 +188,16 @@ public sealed class ProductController(
|
||||
int? remainStock = soldoutMode.HasValue ? Math.Max(0, existing?.RemainStock ?? request.Stock) : null;
|
||||
var soldoutReason = soldoutMode.HasValue ? existing?.SoldoutReason : null;
|
||||
|
||||
ProductDto? saved;
|
||||
await using (var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken))
|
||||
ProductDto? saved = null;
|
||||
var notFoundInTransaction = false;
|
||||
var executionStrategy = dbContext.Database.CreateExecutionStrategy();
|
||||
await executionStrategy.ExecuteAsync(async () =>
|
||||
{
|
||||
saved = null;
|
||||
notFoundInTransaction = false;
|
||||
dbContext.ChangeTracker.Clear();
|
||||
|
||||
await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
|
||||
if (existing is null)
|
||||
{
|
||||
saved = await mediator.Send(new CreateProductCommand
|
||||
@@ -261,7 +269,8 @@ public sealed class ProductController(
|
||||
|
||||
if (saved is null)
|
||||
{
|
||||
return ApiResponse<ProductDetailResponse>.Error(ErrorCodes.NotFound, "商品不存在");
|
||||
notFoundInTransaction = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldReplaceSpecAddon)
|
||||
@@ -281,9 +290,14 @@ public sealed class ProductController(
|
||||
|
||||
await ReplaceComboGroupsAsync(saved.Id, storeId, kind, comboGroups, cancellationToken);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
});
|
||||
|
||||
if (notFoundInTransaction || saved is null)
|
||||
{
|
||||
return ApiResponse<ProductDetailResponse>.Error(ErrorCodes.NotFound, "商品不存在");
|
||||
}
|
||||
|
||||
var savedProduct = saved!;
|
||||
var savedProduct = saved;
|
||||
var detailComboGroups = await BuildComboGroupResponsesAsync(savedProduct.Id, storeId, cancellationToken);
|
||||
var categoryNameLookup = await BuildCategoryNameLookupAsync(storeId, cancellationToken);
|
||||
var savedRelationState = await LoadProductRelationStateAsync(savedProduct.Id, storeId, cancellationToken);
|
||||
@@ -727,6 +741,16 @@ public sealed class ProductController(
|
||||
};
|
||||
}
|
||||
|
||||
private static ProductKind ResolveKindForSave(string? kind, ProductDto? existing)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(kind))
|
||||
{
|
||||
return existing?.Kind ?? ProductKind.Single;
|
||||
}
|
||||
|
||||
return ParseKind(kind);
|
||||
}
|
||||
|
||||
private static ProductKind? ParseKindOrNull(string? kind)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(kind))
|
||||
|
||||
Reference in New Issue
Block a user