fix: 修复SKU软删除冲突导致唯一索引报错
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 50s

This commit is contained in:
2026-02-25 09:49:20 +08:00
parent 21d7be5a4f
commit b970cd4ba7

View File

@@ -1,6 +1,7 @@
using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using TakeoutSaaS.Domain.Products.Entities;
using TakeoutSaaS.Domain.Products.Enums;
using TakeoutSaaS.Infrastructure.App.Persistence;
@@ -53,21 +54,30 @@ public sealed class ProductSkuSaveService(TakeoutAppDbContext dbContext)
if (explicitSkuCodes.Count > 0)
{
var codeConflict = await dbContext.ProductSkus
.AsNoTracking()
.Where(item => item.ProductId != productId && explicitSkuCodes.Contains(item.SkuCode))
.Select(item => item.SkuCode)
.FirstOrDefaultAsync(cancellationToken);
string? codeConflict;
using (dbContext.DisableSoftDeleteFilter())
{
codeConflict = await dbContext.ProductSkus
.AsNoTracking()
.Where(item => item.ProductId != productId && explicitSkuCodes.Contains(item.SkuCode))
.Select(item => item.SkuCode)
.FirstOrDefaultAsync(cancellationToken);
}
if (!string.IsNullOrWhiteSpace(codeConflict))
{
throw new BusinessException(ErrorCodes.BadRequest, $"SKU 编码已存在: {codeConflict}");
}
}
var existingSkus = await dbContext.ProductSkus
.Where(item => item.ProductId == productId)
.OrderBy(item => item.Id)
.ToListAsync(cancellationToken);
List<ProductSku> existingSkus;
using (dbContext.DisableSoftDeleteFilter())
{
existingSkus = await dbContext.ProductSkus
.Where(item => item.ProductId == productId)
.OrderBy(item => item.Id)
.ToListAsync(cancellationToken);
}
var existingBySkuCode = existingSkus
.Where(item => !string.IsNullOrWhiteSpace(item.SkuCode))
@@ -156,7 +166,14 @@ public sealed class ProductSkuSaveService(TakeoutAppDbContext dbContext)
await dbContext.ProductSkus.AddRangeAsync(createdSkus, cancellationToken);
}
await dbContext.SaveChangesAsync(cancellationToken);
try
{
await dbContext.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateException ex) when (IsSkuCodeUniqueViolation(ex))
{
throw new BusinessException(ErrorCodes.BadRequest, "SKU 编码冲突,请刷新后重试");
}
}
private async Task ValidateSkuTemplateRefsAsync(
@@ -344,6 +361,20 @@ public sealed class ProductSkuSaveService(TakeoutAppDbContext dbContext)
return new string(buffer[pos..]);
}
private static bool IsSkuCodeUniqueViolation(DbUpdateException exception)
{
if (exception.InnerException is not PostgresException postgresException)
{
return false;
}
return postgresException.SqlState == PostgresErrorCodes.UniqueViolation &&
string.Equals(
postgresException.ConstraintName,
"IX_product_skus_TenantId_SkuCode",
StringComparison.Ordinal);
}
private sealed record SkuAttributePayload(long TemplateId, long OptionId);
}