87 lines
3.5 KiB
C#
87 lines
3.5 KiB
C#
using MediatR;
|
|
using TakeoutSaaS.Application.App.Tenants.Commands;
|
|
using TakeoutSaaS.Application.App.Tenants.Dto;
|
|
using TakeoutSaaS.Domain.Tenants.Entities;
|
|
using TakeoutSaaS.Domain.Tenants.Enums;
|
|
using TakeoutSaaS.Domain.Tenants.Repositories;
|
|
using TakeoutSaaS.Shared.Abstractions.Constants;
|
|
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
|
using TakeoutSaaS.Shared.Abstractions.Ids;
|
|
|
|
namespace TakeoutSaaS.Application.App.Tenants.Handlers;
|
|
|
|
/// <summary>
|
|
/// 新建/续费订阅处理器。
|
|
/// </summary>
|
|
public sealed class CreateTenantSubscriptionCommandHandler(
|
|
ITenantRepository tenantRepository,
|
|
IIdGenerator idGenerator)
|
|
: IRequestHandler<CreateTenantSubscriptionCommand, TenantSubscriptionDto>
|
|
{
|
|
/// <inheritdoc />
|
|
public async Task<TenantSubscriptionDto> Handle(CreateTenantSubscriptionCommand request, CancellationToken cancellationToken)
|
|
{
|
|
// 1. 校验订阅时长
|
|
if (request.DurationMonths <= 0)
|
|
{
|
|
throw new BusinessException(ErrorCodes.BadRequest, "订阅时长必须大于 0");
|
|
}
|
|
|
|
// 2. 获取租户与当前订阅
|
|
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
|
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
|
|
|
var current = await tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken);
|
|
var from = current?.EffectiveTo ?? tenant.EffectiveTo ?? DateTime.UtcNow;
|
|
var effectiveFrom = from > DateTime.UtcNow ? from : DateTime.UtcNow;
|
|
var effectiveTo = effectiveFrom.AddMonths(request.DurationMonths);
|
|
|
|
// 3. 创建订阅实体
|
|
var subscription = new TenantSubscription
|
|
{
|
|
Id = idGenerator.NextId(),
|
|
TenantId = tenant.Id,
|
|
TenantPackageId = request.TenantPackageId,
|
|
EffectiveFrom = effectiveFrom,
|
|
EffectiveTo = effectiveTo,
|
|
NextBillingDate = effectiveTo,
|
|
Status = SubscriptionStatus.Active,
|
|
AutoRenew = request.AutoRenew,
|
|
Notes = request.Notes
|
|
};
|
|
|
|
// 4. 记录订阅与历史
|
|
await tenantRepository.AddSubscriptionAsync(subscription, cancellationToken);
|
|
await tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory
|
|
{
|
|
Id = idGenerator.NextId(),
|
|
TenantId = tenant.Id,
|
|
TenantSubscriptionId = subscription.Id,
|
|
FromPackageId = current?.TenantPackageId ?? request.TenantPackageId,
|
|
ToPackageId = request.TenantPackageId,
|
|
ChangeType = current == null ? SubscriptionChangeType.New : SubscriptionChangeType.Renew,
|
|
EffectiveFrom = effectiveFrom,
|
|
EffectiveTo = effectiveTo,
|
|
Amount = null,
|
|
Currency = null,
|
|
Notes = request.Notes
|
|
}, cancellationToken);
|
|
|
|
// 5. 记录审计
|
|
await tenantRepository.AddAuditLogAsync(new TenantAuditLog
|
|
{
|
|
TenantId = tenant.Id,
|
|
Action = TenantAuditAction.SubscriptionUpdated,
|
|
Title = current == null ? "创建订阅" : "续费订阅",
|
|
Description = $"套餐 {request.TenantPackageId} 时长 {request.DurationMonths} 月"
|
|
}, cancellationToken);
|
|
|
|
// 6. 保存变更
|
|
await tenantRepository.SaveChangesAsync(cancellationToken);
|
|
|
|
// 7. 返回 DTO
|
|
return subscription.ToSubscriptionDto()
|
|
?? throw new BusinessException(ErrorCodes.InternalServerError, "订阅生成失败");
|
|
}
|
|
}
|