Files
TakeoutSaaS.AdminApi/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/UpdateTenantCommandHandler.cs
MSuMshk 907c9938ae feat(admin-api): 实现 TD-001 和 TD-002 后端接口
TD-001 - PUT /api/admin/v1/tenants/{tenantId}:
- 新增 UpdateTenantCommand + UpdateTenantCommandHandler
- Controller 新增 Update 端点(tenant:update 权限)
- 校验:租户存在、name 非空、name/contactPhone 冲突返回 409
- 仓储扩展:ITenantRepository.ExistsByNameAsync

TD-002 - GET /api/admin/v1/tenants/{tenantId}/quota-usage-history:
- 新增 CQRS Query/Handler/DTO/Validator
- 支持分页(Page>=1, PageSize 1~100)
- 支持时间范围和 QuotaType 过滤
- 新增 tenant_quota_usage_histories 表(含迁移)
- 写入点:CheckTenantQuotaCommandHandler + PurchaseQuotaPackageCommandHandler

构建验证:dotnet build 通过
数据库迁移:已应用 20251218121053_AddTenantQuotaUsageHistories
2025-12-18 20:19:33 +08:00

73 lines
3.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Tenants.Commands;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
namespace TakeoutSaaS.Application.App.Tenants.Handlers;
/// <summary>
/// 更新租户基础信息处理器。
/// </summary>
public sealed class UpdateTenantCommandHandler(
ITenantRepository tenantRepository,
ILogger<UpdateTenantCommandHandler> logger)
: IRequestHandler<UpdateTenantCommand, Unit>
{
/// <inheritdoc />
public async Task<Unit> Handle(UpdateTenantCommand request, CancellationToken cancellationToken)
{
// 1. 参数校验
if (request.TenantId <= 0)
{
throw new BusinessException(ErrorCodes.BadRequest, "tenantId 不能为空");
}
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new BusinessException(ErrorCodes.BadRequest, "租户名称不能为空");
}
// 2. (空行后) 查询租户
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
// 3. (空行后) 校验租户名称唯一性(排除自身)
var normalizedName = request.Name.Trim();
if (await tenantRepository.ExistsByNameAsync(normalizedName, excludeTenantId: request.TenantId, cancellationToken))
{
throw new BusinessException(ErrorCodes.Conflict, $"租户名称 {normalizedName} 已存在");
}
// 4. (空行后) 校验联系人手机号唯一性(仅当填写时)
if (!string.IsNullOrWhiteSpace(request.ContactPhone))
{
var normalizedPhone = request.ContactPhone.Trim();
var existingTenantId = await tenantRepository.FindTenantIdByContactPhoneAsync(normalizedPhone, cancellationToken);
if (existingTenantId.HasValue && existingTenantId.Value != request.TenantId)
{
throw new BusinessException(ErrorCodes.Conflict, $"手机号 {normalizedPhone} 已注册");
}
}
// 5. (空行后) 更新基础信息(禁止修改 Code
tenant.Name = normalizedName;
tenant.ShortName = string.IsNullOrWhiteSpace(request.ShortName) ? null : request.ShortName.Trim();
tenant.Industry = string.IsNullOrWhiteSpace(request.Industry) ? null : request.Industry.Trim();
tenant.ContactName = string.IsNullOrWhiteSpace(request.ContactName) ? null : request.ContactName.Trim();
tenant.ContactPhone = string.IsNullOrWhiteSpace(request.ContactPhone) ? null : request.ContactPhone.Trim();
tenant.ContactEmail = string.IsNullOrWhiteSpace(request.ContactEmail) ? null : request.ContactEmail.Trim();
// 6. (空行后) 持久化更新
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
await tenantRepository.SaveChangesAsync(cancellationToken);
// 7. (空行后) 记录日志
logger.LogInformation("已更新租户基础信息 {TenantId}", tenant.Id);
return Unit.Value;
}
}