feat: 实现租户管理及套餐流程
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
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 ChangeTenantSubscriptionPlanCommandHandler(
|
||||
ITenantRepository tenantRepository,
|
||||
IIdGenerator idGenerator)
|
||||
: IRequestHandler<ChangeTenantSubscriptionPlanCommand, TenantSubscriptionDto>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
private readonly IIdGenerator _idGenerator = idGenerator;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TenantSubscriptionDto> Handle(ChangeTenantSubscriptionPlanCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
_ = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
var subscription = await _tenantRepository.FindSubscriptionByIdAsync(request.TenantId, request.TenantSubscriptionId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "订阅不存在");
|
||||
|
||||
var previousPackage = subscription.TenantPackageId;
|
||||
|
||||
if (request.Immediate)
|
||||
{
|
||||
subscription.TenantPackageId = request.TargetPackageId;
|
||||
subscription.ScheduledPackageId = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
subscription.ScheduledPackageId = request.TargetPackageId;
|
||||
}
|
||||
|
||||
await _tenantRepository.UpdateSubscriptionAsync(subscription, cancellationToken);
|
||||
await _tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory
|
||||
{
|
||||
Id = _idGenerator.NextId(),
|
||||
TenantId = subscription.TenantId,
|
||||
TenantSubscriptionId = subscription.Id,
|
||||
FromPackageId = previousPackage,
|
||||
ToPackageId = request.TargetPackageId,
|
||||
ChangeType = SubscriptionChangeType.Upgrade,
|
||||
EffectiveFrom = subscription.EffectiveFrom,
|
||||
EffectiveTo = subscription.EffectiveTo,
|
||||
Notes = request.Notes
|
||||
}, cancellationToken);
|
||||
|
||||
await _tenantRepository.AddAuditLogAsync(new TenantAuditLog
|
||||
{
|
||||
TenantId = subscription.TenantId,
|
||||
Action = TenantAuditAction.SubscriptionPlanChanged,
|
||||
Title = request.Immediate ? "套餐立即变更" : "套餐排期变更",
|
||||
Description = request.Notes,
|
||||
PreviousStatus = null,
|
||||
CurrentStatus = null
|
||||
}, cancellationToken);
|
||||
|
||||
await _tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return subscription.ToSubscriptionDto()
|
||||
?? throw new BusinessException(ErrorCodes.InternalServerError, "订阅更新失败");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
private readonly IIdGenerator _idGenerator = idGenerator;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TenantSubscriptionDto> Handle(CreateTenantSubscriptionCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.DurationMonths <= 0)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "订阅时长必须大于 0");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
await _tenantRepository.AddAuditLogAsync(new TenantAuditLog
|
||||
{
|
||||
TenantId = tenant.Id,
|
||||
Action = TenantAuditAction.SubscriptionUpdated,
|
||||
Title = current == null ? "创建订阅" : "续费订阅",
|
||||
Description = $"套餐 {request.TenantPackageId} 时长 {request.DurationMonths} 月"
|
||||
}, cancellationToken);
|
||||
|
||||
await _tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return subscription.ToSubscriptionDto()
|
||||
?? throw new BusinessException(ErrorCodes.InternalServerError, "订阅生成失败");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Linq;
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.App.Tenants.Queries;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Results;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Tenants.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 审核日志查询。
|
||||
/// </summary>
|
||||
public sealed class GetTenantAuditLogsQueryHandler(ITenantRepository tenantRepository)
|
||||
: IRequestHandler<GetTenantAuditLogsQuery, PagedResult<TenantAuditLogDto>>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedResult<TenantAuditLogDto>> Handle(GetTenantAuditLogsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var logs = await _tenantRepository.GetAuditLogsAsync(request.TenantId, cancellationToken);
|
||||
var total = logs.Count;
|
||||
|
||||
var paged = logs
|
||||
.Skip((request.Page - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.Select(TenantMapping.ToDto)
|
||||
.ToList();
|
||||
|
||||
return new PagedResult<TenantAuditLogDto>(paged, request.Page, request.PageSize, total);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.App.Tenants.Queries;
|
||||
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 GetTenantByIdQueryHandler(ITenantRepository tenantRepository)
|
||||
: IRequestHandler<GetTenantByIdQuery, TenantDetailDto>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TenantDetailDto> Handle(GetTenantByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var tenant = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
var subscription = await _tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken);
|
||||
var verification = await _tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken);
|
||||
|
||||
return new TenantDetailDto
|
||||
{
|
||||
Tenant = TenantMapping.ToDto(tenant, subscription, verification),
|
||||
Verification = verification.ToVerificationDto(),
|
||||
Subscription = subscription.ToSubscriptionDto()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
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 RegisterTenantCommandHandler(
|
||||
ITenantRepository tenantRepository,
|
||||
IIdGenerator idGenerator,
|
||||
ILogger<RegisterTenantCommandHandler> logger)
|
||||
: IRequestHandler<RegisterTenantCommand, TenantDto>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
private readonly IIdGenerator _idGenerator = idGenerator;
|
||||
private readonly ILogger<RegisterTenantCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TenantDto> Handle(RegisterTenantCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.DurationMonths <= 0)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "订阅时长必须大于 0");
|
||||
}
|
||||
|
||||
if (await _tenantRepository.ExistsByCodeAsync(request.Code, cancellationToken))
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Conflict, $"租户编码 {request.Code} 已存在");
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var effectiveFrom = request.EffectiveFrom ?? now;
|
||||
var effectiveTo = effectiveFrom.AddMonths(request.DurationMonths);
|
||||
|
||||
var tenant = new Tenant
|
||||
{
|
||||
Id = _idGenerator.NextId(),
|
||||
Code = request.Code.Trim(),
|
||||
Name = request.Name,
|
||||
ShortName = request.ShortName,
|
||||
Industry = request.Industry,
|
||||
ContactName = request.ContactName,
|
||||
ContactPhone = request.ContactPhone,
|
||||
ContactEmail = request.ContactEmail,
|
||||
Status = TenantStatus.PendingReview,
|
||||
EffectiveFrom = effectiveFrom,
|
||||
EffectiveTo = effectiveTo
|
||||
};
|
||||
|
||||
var subscription = new TenantSubscription
|
||||
{
|
||||
Id = _idGenerator.NextId(),
|
||||
TenantId = tenant.Id,
|
||||
TenantPackageId = request.TenantPackageId,
|
||||
EffectiveFrom = effectiveFrom,
|
||||
EffectiveTo = effectiveTo,
|
||||
NextBillingDate = effectiveTo,
|
||||
Status = SubscriptionStatus.Pending,
|
||||
AutoRenew = request.AutoRenew,
|
||||
Notes = "Init subscription"
|
||||
};
|
||||
|
||||
await _tenantRepository.AddTenantAsync(tenant, cancellationToken);
|
||||
await _tenantRepository.AddSubscriptionAsync(subscription, cancellationToken);
|
||||
await _tenantRepository.AddAuditLogAsync(new TenantAuditLog
|
||||
{
|
||||
TenantId = tenant.Id,
|
||||
Action = TenantAuditAction.RegistrationSubmitted,
|
||||
Title = "租户注册",
|
||||
Description = $"提交套餐 {request.TenantPackageId},时长 {request.DurationMonths} 月"
|
||||
}, cancellationToken);
|
||||
|
||||
await _tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation("已注册租户 {TenantCode}", tenant.Code);
|
||||
|
||||
return TenantMapping.ToDto(tenant, subscription, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Tenants.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 租户审核处理器。
|
||||
/// </summary>
|
||||
public sealed class ReviewTenantCommandHandler(
|
||||
ITenantRepository tenantRepository,
|
||||
ICurrentUserAccessor currentUserAccessor)
|
||||
: IRequestHandler<ReviewTenantCommand, TenantDto>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
private readonly ICurrentUserAccessor _currentUserAccessor = currentUserAccessor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TenantDto> Handle(ReviewTenantCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var tenant = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
var verification = await _tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.BadRequest, "请先提交实名认证资料");
|
||||
|
||||
var subscription = await _tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken);
|
||||
|
||||
var actorName = _currentUserAccessor.IsAuthenticated
|
||||
? $"user:{_currentUserAccessor.UserId}"
|
||||
: "system";
|
||||
|
||||
verification.ReviewedAt = DateTime.UtcNow;
|
||||
verification.ReviewedBy = _currentUserAccessor.UserId == 0 ? null : _currentUserAccessor.UserId;
|
||||
verification.ReviewedByName = actorName;
|
||||
verification.ReviewRemarks = request.Reason;
|
||||
|
||||
var previousStatus = tenant.Status;
|
||||
|
||||
if (request.Approve)
|
||||
{
|
||||
verification.Status = TenantVerificationStatus.Approved;
|
||||
tenant.Status = TenantStatus.Active;
|
||||
if (subscription != null)
|
||||
{
|
||||
subscription.Status = SubscriptionStatus.Active;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verification.Status = TenantVerificationStatus.Rejected;
|
||||
tenant.Status = TenantStatus.PendingReview;
|
||||
if (subscription != null)
|
||||
{
|
||||
subscription.Status = SubscriptionStatus.Suspended;
|
||||
}
|
||||
}
|
||||
|
||||
await _tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||
await _tenantRepository.UpsertVerificationProfileAsync(verification, cancellationToken);
|
||||
if (subscription != null)
|
||||
{
|
||||
await _tenantRepository.UpdateSubscriptionAsync(subscription, cancellationToken);
|
||||
}
|
||||
|
||||
await _tenantRepository.AddAuditLogAsync(new Domain.Tenants.Entities.TenantAuditLog
|
||||
{
|
||||
TenantId = tenant.Id,
|
||||
Action = request.Approve ? TenantAuditAction.VerificationApproved : TenantAuditAction.VerificationRejected,
|
||||
Title = request.Approve ? "审核通过" : "审核驳回",
|
||||
Description = request.Reason,
|
||||
OperatorId = _currentUserAccessor.UserId == 0 ? null : _currentUserAccessor.UserId,
|
||||
OperatorName = actorName,
|
||||
PreviousStatus = previousStatus,
|
||||
CurrentStatus = tenant.Status
|
||||
}, cancellationToken);
|
||||
|
||||
await _tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return TenantMapping.ToDto(tenant, subscription, verification);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Linq;
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.App.Tenants.Queries;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Results;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Tenants.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 租户分页查询处理器。
|
||||
/// </summary>
|
||||
public sealed class SearchTenantsQueryHandler(ITenantRepository tenantRepository)
|
||||
: IRequestHandler<SearchTenantsQuery, PagedResult<TenantDto>>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedResult<TenantDto>> Handle(SearchTenantsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var tenants = await _tenantRepository.SearchAsync(request.Status, request.Keyword, cancellationToken);
|
||||
var total = tenants.Count;
|
||||
|
||||
var paged = tenants
|
||||
.Skip((request.Page - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.ToList();
|
||||
|
||||
var result = new List<TenantDto>(paged.Count);
|
||||
foreach (var tenant in paged)
|
||||
{
|
||||
var subscription = await _tenantRepository.GetActiveSubscriptionAsync(tenant.Id, cancellationToken);
|
||||
var verification = await _tenantRepository.GetVerificationProfileAsync(tenant.Id, cancellationToken);
|
||||
result.Add(TenantMapping.ToDto(tenant, subscription, verification));
|
||||
}
|
||||
|
||||
return new PagedResult<TenantDto>(result, request.Page, request.PageSize, total);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
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 SubmitTenantVerificationCommandHandler(
|
||||
ITenantRepository tenantRepository,
|
||||
IIdGenerator idGenerator)
|
||||
: IRequestHandler<SubmitTenantVerificationCommand, TenantVerificationDto>
|
||||
{
|
||||
private readonly ITenantRepository _tenantRepository = tenantRepository;
|
||||
private readonly IIdGenerator _idGenerator = idGenerator;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TenantVerificationDto> Handle(SubmitTenantVerificationCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var tenant = await _tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
var profile = await _tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken)
|
||||
?? new TenantVerificationProfile { Id = _idGenerator.NextId(), TenantId = tenant.Id };
|
||||
|
||||
profile.BusinessLicenseNumber = request.BusinessLicenseNumber;
|
||||
profile.BusinessLicenseUrl = request.BusinessLicenseUrl;
|
||||
profile.LegalPersonName = request.LegalPersonName;
|
||||
profile.LegalPersonIdNumber = request.LegalPersonIdNumber;
|
||||
profile.LegalPersonIdFrontUrl = request.LegalPersonIdFrontUrl;
|
||||
profile.LegalPersonIdBackUrl = request.LegalPersonIdBackUrl;
|
||||
profile.BankAccountName = request.BankAccountName;
|
||||
profile.BankAccountNumber = request.BankAccountNumber;
|
||||
profile.BankName = request.BankName;
|
||||
profile.AdditionalDataJson = request.AdditionalDataJson;
|
||||
profile.Status = TenantVerificationStatus.Pending;
|
||||
profile.SubmittedAt = DateTime.UtcNow;
|
||||
profile.ReviewedAt = null;
|
||||
profile.ReviewRemarks = null;
|
||||
profile.ReviewedBy = null;
|
||||
profile.ReviewedByName = null;
|
||||
|
||||
await _tenantRepository.UpsertVerificationProfileAsync(profile, cancellationToken);
|
||||
await _tenantRepository.AddAuditLogAsync(new TenantAuditLog
|
||||
{
|
||||
TenantId = tenant.Id,
|
||||
Action = TenantAuditAction.VerificationSubmitted,
|
||||
Title = "提交实名认证资料",
|
||||
Description = request.BusinessLicenseNumber
|
||||
}, cancellationToken);
|
||||
await _tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return profile.ToVerificationDto()
|
||||
?? throw new BusinessException(ErrorCodes.InternalServerError, "实名资料保存失败");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user