fix: 修复SKU软删除冲突导致唯一索引报错
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 50s
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 50s
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user