From b970cd4ba704ae97d9ce87a7fdaacde8243658c3 Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Wed, 25 Feb 2026 09:49:20 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DSKU=E8=BD=AF=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=86=B2=E7=AA=81=E5=AF=BC=E8=87=B4=E5=94=AF=E4=B8=80?= =?UTF-8?q?=E7=B4=A2=E5=BC=95=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ProductSkuSaveService.cs | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveService.cs b/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveService.cs index 16911b8..a5dc240 100644 --- a/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveService.cs +++ b/src/Api/TakeoutSaaS.TenantApi/Services/ProductSkuSaveService.cs @@ -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 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); }