From 010c2b7043a6be8597c50be011e7c786e7e00b3d Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Thu, 29 Jan 2026 14:04:20 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=95=86=E5=93=81=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E7=A7=9F=E6=88=B7=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Handlers/CreateProductCommandHandler.cs | 24 +++++++++++--- .../Handlers/DeleteProductCommandHandler.cs | 12 +++---- .../Handlers/GetStoreMenuQueryHandler.cs | 15 ++++++--- .../Handlers/PublishProductCommandHandler.cs | 12 +++---- .../ReplaceProductAddonsCommandHandler.cs | 14 ++++----- .../ReplaceProductAttributesCommandHandler.cs | 14 ++++----- .../ReplaceProductMediaCommandHandler.cs | 11 +++---- ...eplaceProductPricingRulesCommandHandler.cs | 11 +++---- .../ReplaceProductSkusCommandHandler.cs | 13 ++++---- .../UnpublishProductCommandHandler.cs | 9 ++---- .../Handlers/UpdateProductCommandHandler.cs | 31 +++++++++++++------ 11 files changed, 95 insertions(+), 71 deletions(-) diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs index 2bf1e33..96be047 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/CreateProductCommandHandler.cs @@ -4,24 +4,40 @@ using TakeoutSaaS.Application.App.Products.Commands; using TakeoutSaaS.Application.App.Products.Dto; using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Repositories; +using TakeoutSaaS.Domain.Stores.Repositories; +using TakeoutSaaS.Shared.Abstractions.Constants; +using TakeoutSaaS.Shared.Abstractions.Exceptions; namespace TakeoutSaaS.Application.App.Products.Handlers; /// /// 创建商品命令处理器。 /// -public sealed class CreateProductCommandHandler(IProductRepository productRepository, ILogger logger) +public sealed class CreateProductCommandHandler( + IProductRepository productRepository, + IStoreRepository storeRepository, + ILogger logger) : IRequestHandler { private readonly IProductRepository _productRepository = productRepository; + private readonly IStoreRepository _storeRepository = storeRepository; private readonly ILogger _logger = logger; /// public async Task Handle(CreateProductCommand request, CancellationToken cancellationToken) { - // 1. 构建实体 + // 1. 校验门店存在并解析租户 + var store = await _storeRepository.FindByIdAsync(request.StoreId, tenantId: null, cancellationToken); + if (store is null) + { + throw new BusinessException(ErrorCodes.NotFound, "门店不存在"); + } + var tenantId = store.TenantId; + + // 2. (空行后) 构建实体并写入租户 var product = new Product { + TenantId = tenantId, StoreId = request.StoreId, CategoryId = request.CategoryId, SpuCode = request.SpuCode.Trim(), @@ -42,12 +58,12 @@ public sealed class CreateProductCommandHandler(IProductRepository productReposi IsFeatured = request.IsFeatured }; - // 2. 持久化 + // 3. (空行后) 持久化 await _productRepository.AddProductAsync(product, cancellationToken); await _productRepository.SaveChangesAsync(cancellationToken); _logger.LogInformation("创建商品 {ProductId} - {ProductName}", product.Id, product.Name); - // 3. 返回 DTO + // 4. (空行后) 返回 DTO return MapToDto(product); } diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/DeleteProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/DeleteProductCommandHandler.cs index f06cfa1..8c11f07 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/DeleteProductCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/DeleteProductCommandHandler.cs @@ -2,7 +2,6 @@ using MediatR; using Microsoft.Extensions.Logging; using TakeoutSaaS.Application.App.Products.Commands; using TakeoutSaaS.Domain.Products.Repositories; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -11,27 +10,24 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class DeleteProductCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler { private readonly IProductRepository _productRepository = productRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ILogger _logger = logger; /// public async Task Handle(DeleteProductCommand request, CancellationToken cancellationToken) { - // 1. 校验存在性 - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 校验存在性(跨租户) + var existing = await _productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (existing == null) { return false; } - // 2. 删除 - await _productRepository.DeleteProductAsync(request.ProductId, tenantId, cancellationToken); + // 2. (空行后) 删除 + await _productRepository.DeleteProductAsync(request.ProductId, existing.TenantId, cancellationToken); await _productRepository.SaveChangesAsync(cancellationToken); _logger.LogInformation("删除商品 {ProductId}", request.ProductId); diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/GetStoreMenuQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/GetStoreMenuQueryHandler.cs index bde1465..65154a0 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/GetStoreMenuQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/GetStoreMenuQueryHandler.cs @@ -9,7 +9,9 @@ using TakeoutSaaS.Application.App.Products.Queries; using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Enums; using TakeoutSaaS.Domain.Products.Repositories; -using TakeoutSaaS.Shared.Abstractions.Tenancy; +using TakeoutSaaS.Domain.Stores.Repositories; +using TakeoutSaaS.Shared.Abstractions.Constants; +using TakeoutSaaS.Shared.Abstractions.Exceptions; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -18,15 +20,20 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class GetStoreMenuQueryHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, + IStoreRepository storeRepository, ILogger logger) : IRequestHandler { /// public async Task Handle(GetStoreMenuQuery request, CancellationToken cancellationToken) { - // 1. 准备上下文 - var tenantId = tenantProvider.GetCurrentTenantId(); + // 1. 校验门店存在并解析租户 + var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId: null, cancellationToken); + if (store is null) + { + throw new BusinessException(ErrorCodes.NotFound, "门店不存在"); + } + var tenantId = store.TenantId; var updatedAfterUtc = request.UpdatedAfter?.ToUniversalTime(); // 2. 获取分类 var categories = await productRepository.GetCategoriesByStoreAsync(tenantId, request.StoreId, true, cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/PublishProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/PublishProductCommandHandler.cs index fd71ada..4744c3e 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/PublishProductCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/PublishProductCommandHandler.cs @@ -7,7 +7,6 @@ using TakeoutSaaS.Domain.Products.Enums; using TakeoutSaaS.Domain.Products.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -16,29 +15,28 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class PublishProductCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler { /// public async Task Handle(PublishProductCommand request, CancellationToken cancellationToken) { - // 1. 读取商品 - var tenantId = tenantProvider.GetCurrentTenantId(); - var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 读取商品(跨租户) + var product = await productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (product is null) { return null; } + var tenantId = product.TenantId; - // 2. 校验 SKU 可售 + // 2. (空行后) 校验 SKU 可售 var skus = await productRepository.GetSkusAsync(product.Id, tenantId, cancellationToken); if (skus.Count == 0) { throw new BusinessException(ErrorCodes.Conflict, "请先配置可售 SKU 后再上架"); } - // 3. 上架 + // 3. (空行后) 上架 product.Status = ProductStatus.OnSale; await productRepository.UpdateProductAsync(product, cancellationToken); await productRepository.SaveChangesAsync(cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAddonsCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAddonsCommandHandler.cs index af61144..d214a99 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAddonsCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAddonsCommandHandler.cs @@ -6,7 +6,6 @@ using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -15,33 +14,33 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class ReplaceProductAddonsCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler> { /// public async Task> Handle(ReplaceProductAddonsCommand request, CancellationToken cancellationToken) { - // 1. 校验商品 - var tenantId = tenantProvider.GetCurrentTenantId(); - var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 校验商品(跨租户) + var product = await productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (product is null) { throw new BusinessException(ErrorCodes.NotFound, "商品不存在"); } + var tenantId = product.TenantId; - // 2. 校验组名唯一 + // 2. (空行后) 校验组名唯一 var names = request.AddonGroups.Select(x => x.Name.Trim()).ToList(); if (names.Count != names.Distinct(StringComparer.OrdinalIgnoreCase).Count()) { throw new BusinessException(ErrorCodes.Conflict, "加料组名称重复"); } - // 3. 替换 + // 3. (空行后) 替换 await productRepository.RemoveAddonGroupsAsync(request.ProductId, tenantId, cancellationToken); // 重新插入组 var groupEntities = request.AddonGroups.Select(g => new ProductAddonGroup { + TenantId = tenantId, ProductId = request.ProductId, Name = g.Name.Trim(), MinSelect = g.MinSelect, @@ -57,6 +56,7 @@ public sealed class ReplaceProductAddonsCommandHandler( var optionEntities = request.AddonGroups .SelectMany(dto => dto.Options.Select(o => new ProductAddonOption { + TenantId = tenantId, AddonGroupId = groupIdLookup[dto], Name = o.Name.Trim(), ExtraPrice = o.ExtraPrice, diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAttributesCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAttributesCommandHandler.cs index 9cef4d1..6e10322 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAttributesCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductAttributesCommandHandler.cs @@ -6,7 +6,6 @@ using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -15,33 +14,33 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class ReplaceProductAttributesCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler> { /// public async Task> Handle(ReplaceProductAttributesCommand request, CancellationToken cancellationToken) { - // 1. 校验商品 - var tenantId = tenantProvider.GetCurrentTenantId(); - var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 校验商品(跨租户) + var product = await productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (product is null) { throw new BusinessException(ErrorCodes.NotFound, "商品不存在"); } + var tenantId = product.TenantId; - // 2. 组名唯一 + // 2. (空行后) 组名唯一 var groupNames = request.AttributeGroups.Select(x => x.Name.Trim()).ToList(); if (groupNames.Count != groupNames.Distinct(StringComparer.OrdinalIgnoreCase).Count()) { throw new BusinessException(ErrorCodes.Conflict, "规格组名称重复"); } - // 3. 替换 + // 3. (空行后) 替换 await productRepository.RemoveAttributeGroupsAsync(request.ProductId, tenantId, cancellationToken); var groupEntities = request.AttributeGroups.Select(g => new ProductAttributeGroup { + TenantId = tenantId, ProductId = request.ProductId, Name = g.Name.Trim(), SelectionType = (Domain.Products.Enums.AttributeSelectionType)g.SelectionType, @@ -59,6 +58,7 @@ public sealed class ReplaceProductAttributesCommandHandler( var optionEntities = request.AttributeGroups .SelectMany(dto => dto.Options.Select(o => new ProductAttributeOption { + TenantId = tenantId, AttributeGroupId = groupIdLookup[dto], Name = o.Name.Trim(), SortOrder = o.SortOrder diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductMediaCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductMediaCommandHandler.cs index 4c704f4..a2d3918 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductMediaCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductMediaCommandHandler.cs @@ -6,7 +6,6 @@ using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -15,26 +14,26 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class ReplaceProductMediaCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler> { /// public async Task> Handle(ReplaceProductMediaCommand request, CancellationToken cancellationToken) { - // 1. 校验商品 - var tenantId = tenantProvider.GetCurrentTenantId(); - var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 校验商品(跨租户) + var product = await productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (product is null) { throw new BusinessException(ErrorCodes.NotFound, "商品不存在"); } + var tenantId = product.TenantId; - // 2. 替换 + // 2. (空行后) 替换 await productRepository.RemoveMediaAssetsAsync(request.ProductId, tenantId, cancellationToken); var assets = request.MediaAssets.Select(a => new ProductMediaAsset { + TenantId = tenantId, ProductId = request.ProductId, MediaType = a.MediaType, Url = a.Url.Trim(), diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductPricingRulesCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductPricingRulesCommandHandler.cs index 8c02739..551b17d 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductPricingRulesCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductPricingRulesCommandHandler.cs @@ -6,7 +6,6 @@ using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -15,26 +14,26 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class ReplaceProductPricingRulesCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler> { /// public async Task> Handle(ReplaceProductPricingRulesCommand request, CancellationToken cancellationToken) { - // 1. 校验商品 - var tenantId = tenantProvider.GetCurrentTenantId(); - var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 校验商品(跨租户) + var product = await productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (product is null) { throw new BusinessException(ErrorCodes.NotFound, "商品不存在"); } + var tenantId = product.TenantId; - // 2. 替换 + // 2. (空行后) 替换 await productRepository.RemovePricingRulesAsync(request.ProductId, tenantId, cancellationToken); var rules = request.PricingRules.Select(r => new ProductPricingRule { + TenantId = tenantId, ProductId = request.ProductId, RuleType = r.RuleType, ConditionsJson = r.ConditionsJson.Trim(), diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductSkusCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductSkusCommandHandler.cs index 9f62d45..7bf0ab4 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductSkusCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/ReplaceProductSkusCommandHandler.cs @@ -6,7 +6,6 @@ using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -15,32 +14,32 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class ReplaceProductSkusCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler> { /// public async Task> Handle(ReplaceProductSkusCommand request, CancellationToken cancellationToken) { - // 1. 校验商品存在 - var tenantId = tenantProvider.GetCurrentTenantId(); - var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 校验商品存在(跨租户) + var product = await productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (product is null) { throw new BusinessException(ErrorCodes.NotFound, "商品不存在"); } + var tenantId = product.TenantId; - // 2. 校验 SKU 唯一性 + // 2. (空行后) 校验 SKU 唯一性 var codes = request.Skus.Select(x => x.SkuCode.Trim()).ToList(); if (codes.Count != codes.Distinct(StringComparer.OrdinalIgnoreCase).Count()) { throw new BusinessException(ErrorCodes.Conflict, "SKU 编码重复"); } - // 3. 替换 + // 3. (空行后) 替换 await productRepository.RemoveSkusAsync(request.ProductId, tenantId, cancellationToken); var entities = request.Skus.Select(x => new ProductSku { + TenantId = tenantId, ProductId = request.ProductId, SkuCode = x.SkuCode.Trim(), Barcode = x.Barcode?.Trim(), diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UnpublishProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UnpublishProductCommandHandler.cs index 83fe4c8..a05e027 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UnpublishProductCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UnpublishProductCommandHandler.cs @@ -4,7 +4,6 @@ using TakeoutSaaS.Application.App.Products.Commands; using TakeoutSaaS.Application.App.Products.Dto; using TakeoutSaaS.Domain.Products.Enums; using TakeoutSaaS.Domain.Products.Repositories; -using TakeoutSaaS.Shared.Abstractions.Tenancy; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -13,22 +12,20 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class UnpublishProductCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, ILogger logger) : IRequestHandler { /// public async Task Handle(UnpublishProductCommand request, CancellationToken cancellationToken) { - // 1. 读取商品 - var tenantId = tenantProvider.GetCurrentTenantId(); - var product = await productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 读取商品(跨租户) + var product = await productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (product is null) { return null; } - // 2. 下架 + // 2. (空行后) 下架 product.Status = ProductStatus.OffShelf; await productRepository.UpdateProductAsync(product, cancellationToken); await productRepository.SaveChangesAsync(cancellationToken); diff --git a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs index e616788..4a181d3 100644 --- a/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Products/Handlers/UpdateProductCommandHandler.cs @@ -4,7 +4,9 @@ using TakeoutSaaS.Application.App.Products.Commands; using TakeoutSaaS.Application.App.Products.Dto; using TakeoutSaaS.Domain.Products.Entities; using TakeoutSaaS.Domain.Products.Repositories; -using TakeoutSaaS.Shared.Abstractions.Tenancy; +using TakeoutSaaS.Domain.Stores.Repositories; +using TakeoutSaaS.Shared.Abstractions.Constants; +using TakeoutSaaS.Shared.Abstractions.Exceptions; namespace TakeoutSaaS.Application.App.Products.Handlers; @@ -13,26 +15,37 @@ namespace TakeoutSaaS.Application.App.Products.Handlers; /// public sealed class UpdateProductCommandHandler( IProductRepository productRepository, - ITenantProvider tenantProvider, + IStoreRepository storeRepository, ILogger logger) : IRequestHandler { private readonly IProductRepository _productRepository = productRepository; - private readonly ITenantProvider _tenantProvider = tenantProvider; + private readonly IStoreRepository _storeRepository = storeRepository; private readonly ILogger _logger = logger; /// public async Task Handle(UpdateProductCommand request, CancellationToken cancellationToken) { - // 1. 读取商品 - var tenantId = _tenantProvider.GetCurrentTenantId(); - var existing = await _productRepository.FindByIdAsync(request.ProductId, tenantId, cancellationToken); + // 1. 读取商品(跨租户) + var existing = await _productRepository.FindByIdAsync(request.ProductId, cancellationToken); if (existing == null) { return null; } + var tenantId = existing.TenantId; - // 2. 更新字段 + // 2. (空行后) 校验门店存在且属于同租户 + var store = await _storeRepository.FindByIdAsync(request.StoreId, tenantId: null, cancellationToken); + if (store is null) + { + throw new BusinessException(ErrorCodes.NotFound, "门店不存在"); + } + if (store.TenantId != tenantId) + { + throw new BusinessException(ErrorCodes.ValidationFailed, "门店与商品不属于同一租户"); + } + + // 3. (空行后) 更新字段 existing.StoreId = request.StoreId; existing.CategoryId = request.CategoryId; existing.SpuCode = request.SpuCode.Trim(); @@ -52,12 +65,12 @@ public sealed class UpdateProductCommandHandler( existing.EnableDelivery = request.EnableDelivery; existing.IsFeatured = request.IsFeatured; - // 3. 持久化 + // 4. (空行后) 持久化 await _productRepository.UpdateProductAsync(existing, cancellationToken); await _productRepository.SaveChangesAsync(cancellationToken); _logger.LogInformation("更新商品 {ProductId} - {ProductName}", existing.Id, existing.Name); - // 4. 返回 DTO + // 5. (空行后) 返回 DTO return MapToDto(existing); }