feat: 管理端核心实体CRUD补齐

This commit is contained in:
2025-12-02 10:19:35 +08:00
parent 1a01454266
commit 93141fbf0c
75 changed files with 4513 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
using MediatR;
namespace TakeoutSaaS.Application.App.Merchants.Commands;
/// <summary>
/// 删除商户命令。
/// </summary>
public sealed class DeleteMerchantCommand : IRequest<bool>
{
/// <summary>
/// 商户 ID。
/// </summary>
public long MerchantId { get; init; }
}

View File

@@ -0,0 +1,51 @@
using MediatR;
using TakeoutSaaS.Application.App.Merchants.Dto;
using TakeoutSaaS.Domain.Merchants.Enums;
namespace TakeoutSaaS.Application.App.Merchants.Commands;
/// <summary>
/// 更新商户命令。
/// </summary>
public sealed class UpdateMerchantCommand : IRequest<MerchantDto?>
{
/// <summary>
/// 商户 ID。
/// </summary>
public long MerchantId { get; set; }
/// <summary>
/// 品牌名称。
/// </summary>
public string BrandName { get; set; } = string.Empty;
/// <summary>
/// 品牌简称。
/// </summary>
public string? BrandAlias { get; set; }
/// <summary>
/// Logo 地址。
/// </summary>
public string? LogoUrl { get; set; }
/// <summary>
/// 品类。
/// </summary>
public string? Category { get; set; }
/// <summary>
/// 联系电话。
/// </summary>
public string ContactPhone { get; set; } = string.Empty;
/// <summary>
/// 联系邮箱。
/// </summary>
public string? ContactEmail { get; set; }
/// <summary>
/// 入驻状态。
/// </summary>
public MerchantStatus Status { get; set; }
}

View File

@@ -0,0 +1,40 @@
using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Merchants.Commands;
using TakeoutSaaS.Domain.Merchants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.App.Merchants.Handlers;
/// <summary>
/// 删除商户命令处理器。
/// </summary>
public sealed class DeleteMerchantCommandHandler(
IMerchantRepository merchantRepository,
ITenantProvider tenantProvider,
ILogger<DeleteMerchantCommandHandler> logger)
: IRequestHandler<DeleteMerchantCommand, bool>
{
private readonly IMerchantRepository _merchantRepository = merchantRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly ILogger<DeleteMerchantCommandHandler> _logger = logger;
/// <inheritdoc />
public async Task<bool> Handle(DeleteMerchantCommand request, CancellationToken cancellationToken)
{
// 1. 校验存在性
var tenantId = _tenantProvider.GetCurrentTenantId();
var existing = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken);
if (existing == null)
{
return false;
}
// 2. 删除
await _merchantRepository.DeleteMerchantAsync(request.MerchantId, tenantId, cancellationToken);
await _merchantRepository.SaveChangesAsync(cancellationToken);
_logger.LogInformation("删除商户 {MerchantId}", request.MerchantId);
return true;
}
}

View File

@@ -0,0 +1,65 @@
using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Merchants.Commands;
using TakeoutSaaS.Application.App.Merchants.Dto;
using TakeoutSaaS.Domain.Merchants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.App.Merchants.Handlers;
/// <summary>
/// 更新商户命令处理器。
/// </summary>
public sealed class UpdateMerchantCommandHandler(
IMerchantRepository merchantRepository,
ITenantProvider tenantProvider,
ILogger<UpdateMerchantCommandHandler> logger)
: IRequestHandler<UpdateMerchantCommand, MerchantDto?>
{
private readonly IMerchantRepository _merchantRepository = merchantRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly ILogger<UpdateMerchantCommandHandler> _logger = logger;
/// <inheritdoc />
public async Task<MerchantDto?> Handle(UpdateMerchantCommand request, CancellationToken cancellationToken)
{
// 1. 读取现有商户
var tenantId = _tenantProvider.GetCurrentTenantId();
var existing = await _merchantRepository.FindByIdAsync(request.MerchantId, tenantId, cancellationToken);
if (existing == null)
{
return null;
}
// 2. 更新字段
existing.BrandName = request.BrandName.Trim();
existing.BrandAlias = request.BrandAlias?.Trim();
existing.LogoUrl = request.LogoUrl?.Trim();
existing.Category = request.Category?.Trim();
existing.ContactPhone = request.ContactPhone.Trim();
existing.ContactEmail = request.ContactEmail?.Trim();
existing.Status = request.Status;
// 3. 持久化
await _merchantRepository.UpdateMerchantAsync(existing, cancellationToken);
await _merchantRepository.SaveChangesAsync(cancellationToken);
_logger.LogInformation("更新商户 {MerchantId} - {BrandName}", existing.Id, existing.BrandName);
// 4. 返回 DTO
return MapToDto(existing);
}
private static MerchantDto MapToDto(Domain.Merchants.Entities.Merchant merchant) => new()
{
Id = merchant.Id,
TenantId = merchant.TenantId,
BrandName = merchant.BrandName,
BrandAlias = merchant.BrandAlias,
LogoUrl = merchant.LogoUrl,
Category = merchant.Category,
ContactPhone = merchant.ContactPhone,
ContactEmail = merchant.ContactEmail,
Status = merchant.Status,
JoinedAt = merchant.JoinedAt
};
}