- CreateTenantManuallyCommand 添加 IsSkipApproval 字段 - 根据 IsSkipApproval 自动设置租户状态和认证状态 - 新增 ApproveTenantCommand/Handler 审核通过逻辑 - 新增 RejectTenantCommand/Handler 审核驳回逻辑 - TenantsController 添加 PUT /approve 和 PUT /reject 接口 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
287 lines
7.1 KiB
C#
287 lines
7.1 KiB
C#
using MediatR;
|
||
using TakeoutSaaS.Application.App.Tenants.Contracts;
|
||
using TakeoutSaaS.Domain.Billings.Enums;
|
||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||
|
||
namespace TakeoutSaaS.Application.App.Tenants.Commands;
|
||
|
||
/// <summary>
|
||
/// 后台手动新增租户命令(直接入驻:创建租户 + 认证 + 订阅 + 管理员账号)。
|
||
/// </summary>
|
||
public sealed record CreateTenantManuallyCommand : IRequest<TenantDetailDto>
|
||
{
|
||
// 1. 租户基本信息(public.tenants)
|
||
|
||
/// <summary>
|
||
/// 租户编码(唯一)。
|
||
/// </summary>
|
||
public required string Code { get; init; }
|
||
|
||
/// <summary>
|
||
/// 租户名称。
|
||
/// </summary>
|
||
public required string Name { get; init; }
|
||
|
||
/// <summary>
|
||
/// 租户简称。
|
||
/// </summary>
|
||
public string? ShortName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 法人/公司主体名称。
|
||
/// </summary>
|
||
public string? LegalEntityName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 所属行业。
|
||
/// </summary>
|
||
public string? Industry { get; init; }
|
||
|
||
/// <summary>
|
||
/// LOGO 图片地址。
|
||
/// </summary>
|
||
public string? LogoUrl { get; init; }
|
||
|
||
/// <summary>
|
||
/// 封面图地址。
|
||
/// </summary>
|
||
public string? CoverImageUrl { get; init; }
|
||
|
||
/// <summary>
|
||
/// 官网地址。
|
||
/// </summary>
|
||
public string? Website { get; init; }
|
||
|
||
/// <summary>
|
||
/// 国家/地区。
|
||
/// </summary>
|
||
public string? Country { get; init; }
|
||
|
||
/// <summary>
|
||
/// 省份。
|
||
/// </summary>
|
||
public string? Province { get; init; }
|
||
|
||
/// <summary>
|
||
/// 城市。
|
||
/// </summary>
|
||
public string? City { get; init; }
|
||
|
||
/// <summary>
|
||
/// 详细地址。
|
||
/// </summary>
|
||
public string? Address { get; init; }
|
||
|
||
/// <summary>
|
||
/// 联系人姓名。
|
||
/// </summary>
|
||
public string? ContactName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 联系人电话。
|
||
/// </summary>
|
||
public string? ContactPhone { get; init; }
|
||
|
||
/// <summary>
|
||
/// 联系人邮箱。
|
||
/// </summary>
|
||
public string? ContactEmail { get; init; }
|
||
|
||
/// <summary>
|
||
/// 标签(逗号分隔)。
|
||
/// </summary>
|
||
public string? Tags { get; init; }
|
||
|
||
/// <summary>
|
||
/// 备注。
|
||
/// </summary>
|
||
public string? Remarks { get; init; }
|
||
|
||
/// <summary>
|
||
/// 暂停时间。
|
||
/// </summary>
|
||
public DateTime? SuspendedAt { get; init; }
|
||
|
||
/// <summary>
|
||
/// 暂停原因。
|
||
/// </summary>
|
||
public string? SuspensionReason { get; init; }
|
||
|
||
/// <summary>
|
||
/// 租户状态。
|
||
/// </summary>
|
||
public TenantStatus TenantStatus { get; init; } = TenantStatus.Active;
|
||
|
||
/// <summary>
|
||
/// 是否跳过审核(直接激活)。
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// true:租户状态设为 Active,认证状态设为 Approved(默认)。
|
||
/// false:租户状态设为 PendingReview,认证状态设为 Pending,需后续审核。
|
||
/// </remarks>
|
||
public bool IsSkipApproval { get; init; } = true;
|
||
|
||
// 2. 订阅信息(public.tenant_subscriptions)
|
||
|
||
/// <summary>
|
||
/// 套餐 ID(雪花算法,字符串传输)。
|
||
/// </summary>
|
||
public required string TenantPackageId { get; init; }
|
||
|
||
/// <summary>
|
||
/// 订阅时长(月)。
|
||
/// </summary>
|
||
public int DurationMonths { get; init; } = 12;
|
||
|
||
/// <summary>
|
||
/// 是否自动续费。
|
||
/// </summary>
|
||
public bool AutoRenew { get; init; }
|
||
|
||
/// <summary>
|
||
/// 订阅生效时间。
|
||
/// </summary>
|
||
public DateTime? SubscriptionEffectiveFrom { get; init; }
|
||
|
||
/// <summary>
|
||
/// 下次计费日期。
|
||
/// </summary>
|
||
public DateTime? NextBillingDate { get; init; }
|
||
|
||
/// <summary>
|
||
/// 订阅状态(0=待激活, 1=生效中, 2=宽限期, 3=已取消, 4=已暂停)。
|
||
/// </summary>
|
||
public SubscriptionStatus SubscriptionStatus { get; init; } = SubscriptionStatus.Active;
|
||
|
||
/// <summary>
|
||
/// 预约变更的套餐 ID。
|
||
/// </summary>
|
||
public string? ScheduledPackageId { get; init; }
|
||
|
||
/// <summary>
|
||
/// 订阅备注。
|
||
/// </summary>
|
||
public string? SubscriptionNotes { get; init; }
|
||
|
||
// 3. 认证信息(public.tenant_verification_profiles)
|
||
|
||
/// <summary>
|
||
/// 认证状态。
|
||
/// </summary>
|
||
public TenantVerificationStatus VerificationStatus { get; init; } = TenantVerificationStatus.Approved;
|
||
|
||
/// <summary>
|
||
/// 营业执照号。
|
||
/// </summary>
|
||
public string? BusinessLicenseNumber { get; init; }
|
||
|
||
/// <summary>
|
||
/// 营业执照图片 URL。
|
||
/// </summary>
|
||
public string? BusinessLicenseUrl { get; init; }
|
||
|
||
/// <summary>
|
||
/// 法人姓名。
|
||
/// </summary>
|
||
public string? LegalPersonName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 法人身份证号。
|
||
/// </summary>
|
||
public string? LegalPersonIdNumber { get; init; }
|
||
|
||
/// <summary>
|
||
/// 法人身份证正面 URL。
|
||
/// </summary>
|
||
public string? LegalPersonIdFrontUrl { get; init; }
|
||
|
||
/// <summary>
|
||
/// 法人身份证背面 URL。
|
||
/// </summary>
|
||
public string? LegalPersonIdBackUrl { get; init; }
|
||
|
||
/// <summary>
|
||
/// 银行账户名。
|
||
/// </summary>
|
||
public string? BankAccountName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 银行账号。
|
||
/// </summary>
|
||
public string? BankAccountNumber { get; init; }
|
||
|
||
/// <summary>
|
||
/// 开户银行。
|
||
/// </summary>
|
||
public string? BankName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 附加数据 JSON。
|
||
/// </summary>
|
||
public string? AdditionalDataJson { get; init; }
|
||
|
||
/// <summary>
|
||
/// 审核人姓名。
|
||
/// </summary>
|
||
public string? ReviewedByName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 审核备注。
|
||
/// </summary>
|
||
public string? ReviewRemarks { get; init; }
|
||
|
||
// 4. 管理员账号(identity.identity_users)
|
||
|
||
/// <summary>
|
||
/// 管理员登录账号。
|
||
/// </summary>
|
||
public required string AdminAccount { get; init; }
|
||
|
||
/// <summary>
|
||
/// 管理员显示名称。
|
||
/// </summary>
|
||
public required string AdminDisplayName { get; init; }
|
||
|
||
/// <summary>
|
||
/// 管理员密码(明文,后端哈希)。
|
||
/// </summary>
|
||
public required string AdminPassword { get; init; }
|
||
|
||
/// <summary>
|
||
/// 管理员头像。
|
||
/// </summary>
|
||
public string? AdminAvatar { get; init; }
|
||
|
||
/// <summary>
|
||
/// 管理员所属商户 ID。
|
||
/// </summary>
|
||
public string? AdminMerchantId { get; init; }
|
||
|
||
// 5. 账单信息(public.tenant_billing_statements)
|
||
|
||
/// <summary>
|
||
/// 是否创建账单。
|
||
/// </summary>
|
||
public bool CreateBilling { get; init; } = true;
|
||
|
||
/// <summary>
|
||
/// 账单金额(不填则根据套餐价格×月数计算)。
|
||
/// </summary>
|
||
public decimal? BillingAmount { get; init; }
|
||
|
||
/// <summary>
|
||
/// 折扣金额。
|
||
/// </summary>
|
||
public decimal? BillingDiscountAmount { get; init; }
|
||
|
||
/// <summary>
|
||
/// 账单状态(默认已支付)。
|
||
/// </summary>
|
||
public TenantBillingStatus BillingStatus { get; init; } = TenantBillingStatus.Paid;
|
||
|
||
/// <summary>
|
||
/// 账单备注。
|
||
/// </summary>
|
||
public string? BillingNotes { get; init; }
|
||
}
|