feat: 商品模块移除租户上下文依赖
This commit is contained in:
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 创建商品命令处理器。
|
||||
/// </summary>
|
||||
public sealed class CreateProductCommandHandler(IProductRepository productRepository, ILogger<CreateProductCommandHandler> logger)
|
||||
public sealed class CreateProductCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
IStoreRepository storeRepository,
|
||||
ILogger<CreateProductCommandHandler> logger)
|
||||
: IRequestHandler<CreateProductCommand, ProductDto>
|
||||
{
|
||||
private readonly IProductRepository _productRepository = productRepository;
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ILogger<CreateProductCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ProductDto> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class DeleteProductCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<DeleteProductCommandHandler> logger)
|
||||
: IRequestHandler<DeleteProductCommand, bool>
|
||||
{
|
||||
private readonly IProductRepository _productRepository = productRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly ILogger<DeleteProductCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> 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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class GetStoreMenuQueryHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IStoreRepository storeRepository,
|
||||
ILogger<GetStoreMenuQueryHandler> logger)
|
||||
: IRequestHandler<GetStoreMenuQuery, StoreMenuDto>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<StoreMenuDto> 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);
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class PublishProductCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<PublishProductCommandHandler> logger)
|
||||
: IRequestHandler<PublishProductCommand, ProductDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<ProductDto?> 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);
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class ReplaceProductAddonsCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<ReplaceProductAddonsCommandHandler> logger)
|
||||
: IRequestHandler<ReplaceProductAddonsCommand, IReadOnlyList<ProductAddonGroupDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductAddonGroupDto>> 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,
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class ReplaceProductAttributesCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<ReplaceProductAttributesCommandHandler> logger)
|
||||
: IRequestHandler<ReplaceProductAttributesCommand, IReadOnlyList<ProductAttributeGroupDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductAttributeGroupDto>> 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
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class ReplaceProductMediaCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<ReplaceProductMediaCommandHandler> logger)
|
||||
: IRequestHandler<ReplaceProductMediaCommand, IReadOnlyList<ProductMediaAssetDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductMediaAssetDto>> 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(),
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class ReplaceProductPricingRulesCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<ReplaceProductPricingRulesCommandHandler> logger)
|
||||
: IRequestHandler<ReplaceProductPricingRulesCommand, IReadOnlyList<ProductPricingRuleDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductPricingRuleDto>> 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(),
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class ReplaceProductSkusCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<ReplaceProductSkusCommandHandler> logger)
|
||||
: IRequestHandler<ReplaceProductSkusCommand, IReadOnlyList<ProductSkuDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductSkuDto>> 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(),
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class UnpublishProductCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
ILogger<UnpublishProductCommandHandler> logger)
|
||||
: IRequestHandler<UnpublishProductCommand, ProductDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<ProductDto?> 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);
|
||||
|
||||
@@ -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;
|
||||
/// </summary>
|
||||
public sealed class UpdateProductCommandHandler(
|
||||
IProductRepository productRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IStoreRepository storeRepository,
|
||||
ILogger<UpdateProductCommandHandler> logger)
|
||||
: IRequestHandler<UpdateProductCommand, ProductDto?>
|
||||
{
|
||||
private readonly IProductRepository _productRepository = productRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ILogger<UpdateProductCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ProductDto?> 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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user