feat: 商户冻结/解冻功能及字典缓存重构

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
MSuMshk
2026-02-04 10:46:32 +08:00
parent 754dd788ea
commit f69904e195
54 changed files with 753 additions and 1385 deletions

View File

@@ -1,23 +1,27 @@
using MediatR;
using Microsoft.Extensions.Logging;
using System.Security.Cryptography;
using TakeoutSaaS.Application.App.Merchants.Commands;
using TakeoutSaaS.Application.App.Merchants.Dto;
using TakeoutSaaS.Domain.Merchants.Entities;
using TakeoutSaaS.Domain.Merchants.Enums;
using TakeoutSaaS.Domain.Merchants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Security;
namespace TakeoutSaaS.Application.App.Merchants.Handlers;
/// <summary>
/// 创建商户命令处理器。
/// </summary>
public sealed class CreateMerchantCommandHandler(IMerchantRepository merchantRepository, ILogger<CreateMerchantCommandHandler> logger)
public sealed class CreateMerchantCommandHandler(
IMerchantRepository merchantRepository,
ICurrentUserAccessor currentUserAccessor,
ILogger<CreateMerchantCommandHandler> logger)
: IRequestHandler<CreateMerchantCommand, MerchantDto>
{
/// <inheritdoc />
public async Task<MerchantDto> Handle(CreateMerchantCommand request, CancellationToken cancellationToken)
{
// 1. 构建商户实体
// 1. 构建商户实体RowVersion 由 PostgreSQL xmin 自动管理)
var merchant = new Merchant
{
TenantId = request.TenantId,
@@ -28,16 +32,39 @@ public sealed class CreateMerchantCommandHandler(IMerchantRepository merchantRep
ContactPhone = request.ContactPhone.Trim(),
ContactEmail = request.ContactEmail?.Trim(),
Status = request.Status,
RowVersion = RandomNumberGenerator.GetBytes(16),
JoinedAt = DateTime.UtcNow
};
// 2. 持久化
// 2. 如果状态为已通过,设置审核通过时间和审核人
if (request.Status == MerchantStatus.Approved)
{
merchant.ApprovedAt = DateTime.UtcNow;
merchant.ApprovedBy = currentUserAccessor.UserId;
}
// 3. 持久化商户
await merchantRepository.AddMerchantAsync(merchant, cancellationToken);
await merchantRepository.SaveChangesAsync(cancellationToken);
// 3. 记录日志
logger.LogInformation("创建商户 {MerchantId} - {BrandName}", merchant.Id, merchant.BrandName);
// 4. 如果状态为已通过,添加默认审核通过记录
if (request.Status == MerchantStatus.Approved)
{
var auditLog = new MerchantAuditLog
{
TenantId = merchant.TenantId,
MerchantId = merchant.Id,
Action = MerchantAuditAction.ReviewApproved,
Title = "商户创建时直接通过审核",
Description = "平台管理员创建商户时选择无需审核,系统自动通过",
OperatorId = currentUserAccessor.UserId == 0 ? null : currentUserAccessor.UserId,
OperatorName = currentUserAccessor.UserId == 0 ? "system" : $"user:{currentUserAccessor.UserId}"
};
await merchantRepository.AddAuditLogAsync(auditLog, cancellationToken);
await merchantRepository.SaveChangesAsync(cancellationToken);
}
// 5. 记录日志
logger.LogInformation("创建商户 {MerchantId} - {BrandName},状态:{Status}", merchant.Id, merchant.BrandName, merchant.Status);
return MapToDto(merchant);
}