forked from gongxuegit/tiku-backend.net
feat(saas): implement marketplace and tenant onboarding
This commit is contained in:
@@ -9,88 +9,201 @@ using Tiku.Domain.Tenancy;
|
||||
|
||||
namespace Tiku.Api.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// 平台管理端查询参数。
|
||||
/// </summary>
|
||||
public sealed class PlatformAdminQueryDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
[StringLength(32)]
|
||||
public string? Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 搜索关键字。
|
||||
/// </summary>
|
||||
[StringLength(200)]
|
||||
public string? Search { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回数量上限。
|
||||
/// </summary>
|
||||
[Range(1, 200)]
|
||||
public int? Limit { get; set; }
|
||||
|
||||
public PlatformAdminQuery ToQuery() => new(Status, Search, Limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建平台租户请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class CreatePlatformTenantDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 短编码。
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string Slug { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 名称。
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(200)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 法定名称。
|
||||
/// </summary>
|
||||
[StringLength(300)]
|
||||
public string? LegalName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
public TenantStatus Status { get; set; } = TenantStatus.Active;
|
||||
/// <summary>
|
||||
/// 账务状态。
|
||||
/// </summary>
|
||||
public BillingStatus BillingStatus { get; set; } = BillingStatus.Trial;
|
||||
/// <summary>
|
||||
/// 扩展元数据。
|
||||
/// </summary>
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
|
||||
public CreatePlatformTenantCommand ToCommand() => new(Slug, Name, LegalName, Status, BillingStatus, Metadata);
|
||||
/// <summary>
|
||||
/// 租户负责人邮箱。
|
||||
/// </summary>
|
||||
[EmailAddress, StringLength(320)]
|
||||
public string? OwnerEmail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户负责人手机号。
|
||||
/// </summary>
|
||||
[Phone, StringLength(32)]
|
||||
public string? OwnerPhone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户负责人姓名。
|
||||
/// </summary>
|
||||
[Required, StringLength(100)]
|
||||
public string OwnerName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 临时密码。
|
||||
/// </summary>
|
||||
[Required, StringLength(200, MinimumLength = 12)]
|
||||
public string TemporaryPassword { get; set; } = string.Empty;
|
||||
|
||||
public CreatePlatformTenantCommand ToCommand() => new(
|
||||
Slug, Name, LegalName, Status, BillingStatus, Metadata,
|
||||
OwnerEmail, OwnerPhone, OwnerName, TemporaryPassword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新平台租户状态请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class UpdatePlatformTenantStatusDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
public TenantStatus Status { get; set; } = TenantStatus.Active;
|
||||
/// <summary>
|
||||
/// 账务状态。
|
||||
/// </summary>
|
||||
public BillingStatus BillingStatus { get; set; } = BillingStatus.Active;
|
||||
|
||||
/// <summary>
|
||||
/// 原因。
|
||||
/// </summary>
|
||||
[StringLength(1000)]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public UpdatePlatformTenantStatusCommand ToCommand() => new(TenantId, Status, BillingStatus, Reason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增或更新平台租户账务资料请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class UpsertPlatformTenantBillingProfileDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账务名称。
|
||||
/// </summary>
|
||||
[StringLength(300)]
|
||||
public string? BillingName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 税号。
|
||||
/// </summary>
|
||||
[StringLength(100)]
|
||||
public string? TaxId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系人姓名。
|
||||
/// </summary>
|
||||
[StringLength(100)]
|
||||
public string? ContactName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系人手机号。
|
||||
/// </summary>
|
||||
[StringLength(32)]
|
||||
public string? ContactPhone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 联系人邮箱。
|
||||
/// </summary>
|
||||
[StringLength(320)]
|
||||
public string? ContactEmail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账务地址。
|
||||
/// </summary>
|
||||
[StringLength(1000)]
|
||||
public string? BillingAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发票抬头。
|
||||
/// </summary>
|
||||
[StringLength(300)]
|
||||
public string? InvoiceTitle { get; set; }
|
||||
|
||||
public TenantInvoiceTitleType? InvoiceType { get; set; }
|
||||
/// <summary>
|
||||
/// 发票类型。
|
||||
/// </summary>
|
||||
public TenantBillingInvoiceTitleType? InvoiceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 开户银行。
|
||||
/// </summary>
|
||||
[StringLength(300)]
|
||||
public string? BankName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 脱敏银行账号。
|
||||
/// </summary>
|
||||
[StringLength(100)]
|
||||
public string? BankAccountMasked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扩展元数据。
|
||||
/// </summary>
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
|
||||
public UpsertPlatformTenantBillingProfileCommand ToCommand() => new(
|
||||
@@ -108,143 +221,180 @@ public sealed class UpsertPlatformTenantBillingProfileDto
|
||||
Metadata);
|
||||
}
|
||||
|
||||
public sealed class UpsertPlatformSubscriptionDto
|
||||
{
|
||||
[Required]
|
||||
public Guid TenantId { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string PlanCode { get; set; } = string.Empty;
|
||||
|
||||
public TenantSubscriptionStatus Status { get; set; } = TenantSubscriptionStatus.Active;
|
||||
public DateTimeOffset? StartsAt { get; set; }
|
||||
public DateTimeOffset? ExpiresAt { get; set; }
|
||||
|
||||
[StringLength(32)]
|
||||
public string? BillingCycle { get; set; }
|
||||
|
||||
[Range(0, int.MaxValue)]
|
||||
public int AmountCents { get; set; }
|
||||
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
|
||||
public UpsertPlatformSubscriptionCommand ToCommand() => new(
|
||||
TenantId,
|
||||
PlanCode,
|
||||
Status,
|
||||
StartsAt,
|
||||
ExpiresAt,
|
||||
BillingCycle,
|
||||
AmountCents,
|
||||
Metadata);
|
||||
}
|
||||
|
||||
public sealed class ReplacePlatformPlanModulesDto
|
||||
{
|
||||
[Required]
|
||||
public IReadOnlyCollection<string> ModuleCodes { get; set; } = [];
|
||||
|
||||
public ReplacePlatformPlanModulesCommand ToCommand(string planCode) => new(planCode, ModuleCodes);
|
||||
}
|
||||
|
||||
public sealed class UpsertPlatformTenantModuleOverrideDto
|
||||
{
|
||||
public TenantModuleOverrideMode Mode { get; set; }
|
||||
public DateTimeOffset? ExpiresAt { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(1000, MinimumLength = 1)]
|
||||
public string Reason { get; set; } = string.Empty;
|
||||
|
||||
public UpsertPlatformTenantModuleOverrideCommand ToCommand(Guid tenantId, string moduleCode) =>
|
||||
new(tenantId, moduleCode, Mode, ExpiresAt, Reason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增或更新平台Staff请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class UpsertPlatformStaffDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户 ID。
|
||||
/// </summary>
|
||||
public Guid? UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮箱。
|
||||
/// </summary>
|
||||
[StringLength(320)]
|
||||
public string? Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 手机号。
|
||||
/// </summary>
|
||||
[StringLength(32)]
|
||||
public string? Phone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称。
|
||||
/// </summary>
|
||||
[StringLength(200)]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
public UserStatus Status { get; set; } = UserStatus.Active;
|
||||
/// <summary>
|
||||
/// 角色 ID 列表。
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<Guid> RoleIds { get; set; } = [];
|
||||
|
||||
public UpsertPlatformStaffCommand ToCommand() => new(UserId, Email, Phone, Name, Status, RoleIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新平台Staff状态请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class UpdatePlatformStaffStatusDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户 ID。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
public UserStatus Status { get; set; } = UserStatus.Active;
|
||||
|
||||
/// <summary>
|
||||
/// 原因。
|
||||
/// </summary>
|
||||
[StringLength(1000)]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public UpdatePlatformStaffStatusCommand ToCommand() => new(UserId, Status, Reason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新平台审计Alert状态请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class UpdatePlatformAuditAlertStatusDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 告警 ID。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid AlertId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 状态。
|
||||
/// </summary>
|
||||
public PlatformAuditAlertStatus Status { get; set; } = PlatformAuditAlertStatus.Acknowledged;
|
||||
|
||||
/// <summary>
|
||||
/// 处理备注。
|
||||
/// </summary>
|
||||
[StringLength(1000)]
|
||||
public string? ResolutionNote { get; set; }
|
||||
|
||||
public UpdatePlatformAuditAlertStatusCommand ToCommand() => new(AlertId, Status, ResolutionNote);
|
||||
}
|
||||
|
||||
public sealed class UpsertPlatformDunningChannelDto
|
||||
/// <summary>
|
||||
/// 新增或更新平台DunningChannel请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class UpsertPlatformBillingDunningChannelDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知渠道 ID。
|
||||
/// </summary>
|
||||
public Guid? ChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 渠道编码。
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string ChannelCode { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 名称。
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(200)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 说明。
|
||||
/// </summary>
|
||||
[StringLength(1000)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用。
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = true;
|
||||
public PlatformDunningProvider Provider { get; set; } = PlatformDunningProvider.Generic;
|
||||
/// <summary>
|
||||
/// 服务提供方。
|
||||
/// </summary>
|
||||
public PlatformBillingDunningProvider Provider { get; set; } = PlatformBillingDunningProvider.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Webhook 地址。
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(2048)]
|
||||
public string WebhookUrl { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 密钥引用。
|
||||
/// </summary>
|
||||
[StringLength(300)]
|
||||
public string? SecretRef { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提醒类型列表。
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> ReminderTypes { get; set; } = ["overdue", "final_notice"];
|
||||
/// <summary>
|
||||
/// 提醒渠道列表。
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> ReminderChannels { get; set; } = ["internal"];
|
||||
|
||||
/// <summary>
|
||||
/// 最低提醒级别。
|
||||
/// </summary>
|
||||
[Range(1, 20)]
|
||||
public int MinReminderLevel { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 租户 ID 列表。
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<Guid> TenantIds { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 超时时长,单位为秒。
|
||||
/// </summary>
|
||||
[Range(1, 60)]
|
||||
public int TimeoutSeconds { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 扩展元数据。
|
||||
/// </summary>
|
||||
public JsonElement Metadata { get; set; } = JsonDefaults.Object();
|
||||
|
||||
public UpsertPlatformDunningChannelCommand ToCommand() => new(
|
||||
public UpsertPlatformBillingDunningChannelCommand ToCommand() => new(
|
||||
ChannelId,
|
||||
ChannelCode,
|
||||
Name,
|
||||
@@ -261,24 +411,42 @@ public sealed class UpsertPlatformDunningChannelDto
|
||||
Metadata);
|
||||
}
|
||||
|
||||
public sealed class DisablePlatformDunningChannelDto
|
||||
/// <summary>
|
||||
/// 禁用平台DunningChannel请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class DisablePlatformBillingDunningChannelDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知渠道 ID。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid ChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原因。
|
||||
/// </summary>
|
||||
[StringLength(1000)]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public DisablePlatformDunningChannelCommand ToCommand() => new(ChannelId, Reason);
|
||||
public DisablePlatformBillingDunningChannelCommand ToCommand() => new(ChannelId, Reason);
|
||||
}
|
||||
|
||||
public sealed class RetryPlatformDunningEventDto
|
||||
/// <summary>
|
||||
/// 重试平台Dunning事件请求 DTO。
|
||||
/// </summary>
|
||||
public sealed class RetryPlatformBillingDunningEventDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件 ID。
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Guid EventId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原因。
|
||||
/// </summary>
|
||||
[StringLength(1000)]
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public RetryPlatformDunningEventCommand ToCommand() => new(EventId, Reason);
|
||||
public RetryPlatformBillingDunningEventCommand ToCommand() => new(EventId, Reason);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user