diff --git a/src/Api/TakeoutSaaS.AdminApi/Contracts/Requests/SearchTenantBillsRequest.cs b/src/Api/TakeoutSaaS.AdminApi/Contracts/Requests/SearchTenantBillsRequest.cs
deleted file mode 100644
index d99fcad..0000000
--- a/src/Api/TakeoutSaaS.AdminApi/Contracts/Requests/SearchTenantBillsRequest.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.AdminApi.Contracts.Requests;
-
-///
-/// 租户账单分页查询请求(QueryString 参数)。
-///
-public sealed record SearchTenantBillsRequest
-{
- ///
- /// 账单状态筛选。
- ///
- public TenantBillingStatus? Status { get; init; }
-
- ///
- /// 账单起始时间(UTC)筛选。
- ///
- public DateTime? From { get; init; }
-
- ///
- /// 账单结束时间(UTC)筛选。
- ///
- public DateTime? To { get; init; }
-
- ///
- /// 页码(从 1 开始)。
- ///
- public int Page { get; init; } = 1;
-
- ///
- /// 每页条数。
- ///
- public int PageSize { get; init; } = 20;
-}
diff --git a/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json b/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json
index 6256ff4..f74da85 100644
--- a/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json
+++ b/src/Api/TakeoutSaaS.AdminApi/appsettings.Development.json
@@ -75,14 +75,6 @@
"user_role"
]
},
- "Tenancy": {
- "TenantIdHeaderName": "X-Tenant-Id",
- "TenantCodeHeaderName": "X-Tenant-Code",
- "IgnoredPaths": [
- "/health"
- ],
- "RootDomain": ""
- },
"Storage": {
"Provider": "TencentCos",
"CdnBaseUrl": "https://image-admin.laosankeji.com",
@@ -192,4 +184,3 @@
"UseConsoleExporter": true
}
}
-
diff --git a/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json b/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json
index 3eb75e2..aa0b8af 100644
--- a/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json
+++ b/src/Api/TakeoutSaaS.AdminApi/appsettings.Production.json
@@ -75,14 +75,6 @@
"user_role"
]
},
- "Tenancy": {
- "TenantIdHeaderName": "X-Tenant-Id",
- "TenantCodeHeaderName": "X-Tenant-Code",
- "IgnoredPaths": [
- "/health"
- ],
- "RootDomain": ""
- },
"Storage": {
"Provider": "TencentCos",
"CdnBaseUrl": "https://saas2025-1388556178.cos.ap-beijing.myqcloud.com",
@@ -192,4 +184,3 @@
"UseConsoleExporter": true
}
}
-
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/BillingLineItem.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/BillingLineItem.cs
deleted file mode 100644
index a1811d0..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/BillingLineItem.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 账单明细项(值对象)。
-/// 用于记录账单中的单项费用明细,如套餐费用、配额包费用等。
-///
-public sealed class BillingLineItem
-{
- ///
- /// 明细项类型(如:套餐费、配额包、其他费用)。
- ///
- public string ItemType { get; set; } = string.Empty;
-
- ///
- /// 明细项描述。
- ///
- public string Description { get; set; } = string.Empty;
-
- ///
- /// 数量。
- ///
- public decimal Quantity { get; set; }
-
- ///
- /// 单价。
- ///
- public decimal UnitPrice { get; set; }
-
- ///
- /// 金额(数量 × 单价)。
- ///
- public decimal Amount { get; set; }
-
- ///
- /// 折扣率(0-1 之间,如 0.1 表示 10% 折扣)。
- ///
- public decimal DiscountRate { get; set; }
-
- ///
- /// 创建账单明细项。
- ///
- /// 明细项类型。
- /// 描述。
- /// 数量。
- /// 单价。
- /// 折扣率。
- /// 账单明细项实例。
- public static BillingLineItem Create(
- string itemType,
- string description,
- decimal quantity,
- decimal unitPrice,
- decimal discountRate = 0)
- {
- var amount = quantity * unitPrice * (1 - discountRate);
- return new BillingLineItem
- {
- ItemType = itemType,
- Description = description,
- Quantity = quantity,
- UnitPrice = unitPrice,
- Amount = amount,
- DiscountRate = discountRate
- };
- }
-
- ///
- /// 计算折扣后的金额。
- ///
- /// 折扣后金额。
- public decimal CalculateDiscountedAmount()
- {
- return Quantity * UnitPrice * (1 - DiscountRate);
- }
-
- ///
- /// 获取折扣金额。
- ///
- /// 折扣金额。
- public decimal GetDiscountAmount()
- {
- return Quantity * UnitPrice * DiscountRate;
- }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/QuotaPackage.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/QuotaPackage.cs
deleted file mode 100644
index c480561..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/QuotaPackage.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 配额包定义(平台提供的可购买配额包)。
-///
-public sealed class QuotaPackage : AuditableEntityBase
-{
- ///
- /// 配额包名称。
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// 配额类型。
- ///
- public TenantQuotaType QuotaType { get; set; }
-
- ///
- /// 配额数值。
- ///
- public decimal QuotaValue { get; set; }
-
- ///
- /// 价格。
- ///
- public decimal Price { get; set; }
-
- ///
- /// 是否上架。
- ///
- public bool IsActive { get; set; } = true;
-
- ///
- /// 排序。
- ///
- public int SortOrder { get; set; } = 0;
-
- ///
- /// 描述。
- ///
- public string? Description { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAnnouncement.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAnnouncement.cs
deleted file mode 100644
index f3c73ab..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAnnouncement.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户公告。
-///
-public sealed class TenantAnnouncement : MultiTenantEntityBase
-{
- ///
- /// 公告标题。
- ///
- public string Title { get; set; } = string.Empty;
-
- ///
- /// 公告正文(可为 Markdown/HTML,前端自行渲染)。
- ///
- public string Content { get; set; } = string.Empty;
-
- ///
- /// 公告类型。
- ///
- public TenantAnnouncementType AnnouncementType { get; set; } = TenantAnnouncementType.System;
-
- ///
- /// 展示优先级,数值越大越靠前。
- ///
- public int Priority { get; set; } = 0;
-
- ///
- /// 生效时间(UTC)。
- ///
- public DateTime EffectiveFrom { get; set; } = DateTime.UtcNow;
-
- ///
- /// 失效时间(UTC),为空表示长期有效。
- ///
- public DateTime? EffectiveTo { get; set; }
-
- ///
- /// 发布者范围。
- ///
- public PublisherScope PublisherScope { get; set; }
-
- ///
- /// 发布者用户 ID(平台或租户后台账号)。
- ///
- public long? PublisherUserId { get; set; }
-
- ///
- /// 公告状态。
- ///
- public AnnouncementStatus Status { get; set; } = AnnouncementStatus.Draft;
-
- ///
- /// 实际发布时间(UTC)。
- ///
- public DateTime? PublishedAt { get; set; }
-
- ///
- /// 撤销时间(UTC)。
- ///
- public DateTime? RevokedAt { get; set; }
-
- ///
- /// 预定发布时间(UTC)。
- ///
- public DateTime? ScheduledPublishAt { get; set; }
-
- ///
- /// 目标受众类型。
- ///
- public string TargetType { get; set; } = string.Empty;
-
- ///
- /// 目标受众参数(JSON)。
- ///
- public string? TargetParameters { get; set; }
-
- ///
- /// 并发控制字段。
- ///
- public byte[] RowVersion { get; set; } = Array.Empty();
-
- ///
- /// 是否启用(已弃用,迁移期保留)。
- ///
- [Obsolete("Use Status instead.")]
- public bool IsActive { get; set; } = true;
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAnnouncementRead.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAnnouncementRead.cs
deleted file mode 100644
index fa52716..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAnnouncementRead.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户公告已读记录。
-///
-public sealed class TenantAnnouncementRead : MultiTenantEntityBase
-{
- ///
- /// 公告 ID。
- ///
- public long AnnouncementId { get; set; }
-
- ///
- /// 已读用户 ID(后台账号),为空表示租户级已读。
- ///
- public long? UserId { get; set; }
-
- ///
- /// 已读时间。
- ///
- public DateTime ReadAt { get; set; } = DateTime.UtcNow;
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAuditLog.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAuditLog.cs
deleted file mode 100644
index 79858ad..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantAuditLog.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户运营审核日志。
-///
-public sealed class TenantAuditLog : AuditableEntityBase
-{
- ///
- /// 关联的租户标识。
- ///
- public long TenantId { get; set; }
-
- ///
- /// 操作类型。
- ///
- public TenantAuditAction Action { get; set; }
-
- ///
- /// 日志标题。
- ///
- public string Title { get; set; } = string.Empty;
-
- ///
- /// 详细描述。
- ///
- public string? Description { get; set; }
-
- ///
- /// 操作人 ID。
- ///
- public long? OperatorId { get; set; }
-
- ///
- /// 操作人名称。
- ///
- public string? OperatorName { get; set; }
-
- ///
- /// 原状态。
- ///
- public TenantStatus? PreviousStatus { get; set; }
-
- ///
- /// 新状态。
- ///
- public TenantStatus? CurrentStatus { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantBillingStatement.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantBillingStatement.cs
deleted file mode 100644
index e7d12de..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantBillingStatement.cs
+++ /dev/null
@@ -1,200 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户账单,用于呈现周期性收费。
-///
-public sealed class TenantBillingStatement : MultiTenantEntityBase
-{
- ///
- /// 账单编号,供对账查询。
- ///
- public string StatementNo { get; set; } = string.Empty;
-
- ///
- /// 账单类型(订阅账单/配额包账单/手动账单/续费账单)。
- ///
- public BillingType BillingType { get; set; } = BillingType.Subscription;
-
- ///
- /// 关联的订阅 ID(仅当 BillingType 为 Subscription 或 Renewal 时有值)。
- ///
- public long? SubscriptionId { get; set; }
-
- ///
- /// 账单周期开始时间。
- ///
- public DateTime PeriodStart { get; set; }
-
- ///
- /// 账单周期结束时间。
- ///
- public DateTime PeriodEnd { get; set; }
-
- ///
- /// 应付金额(原始金额)。
- ///
- public decimal AmountDue { get; set; }
-
- ///
- /// 折扣金额。
- ///
- public decimal DiscountAmount { get; set; }
-
- ///
- /// 税费金额。
- ///
- public decimal TaxAmount { get; set; }
-
- ///
- /// 实付金额。
- ///
- public decimal AmountPaid { get; set; }
-
- ///
- /// 货币类型(默认 CNY)。
- ///
- public string Currency { get; set; } = "CNY";
-
- ///
- /// 当前付款状态。
- ///
- public TenantBillingStatus Status { get; set; } = TenantBillingStatus.Pending;
-
- ///
- /// 到期日。
- ///
- public DateTime DueDate { get; set; }
-
- ///
- /// 提醒发送时间(续费提醒、逾期提醒等)。
- ///
- public DateTime? ReminderSentAt { get; set; }
-
- ///
- /// 逾期通知时间。
- ///
- public DateTime? OverdueNotifiedAt { get; set; }
-
- ///
- /// 账单明细 JSON,记录各项费用。
- ///
- public string? LineItemsJson { get; set; }
-
- ///
- /// 备注信息(如:人工备注、取消原因等)。
- ///
- public string? Notes { get; set; }
-
- ///
- /// 计算总金额(应付金额 - 折扣 + 税费)。
- ///
- /// 总金额。
- public decimal CalculateTotalAmount()
- {
- return AmountDue - DiscountAmount + TaxAmount;
- }
-
- ///
- /// 标记为已支付(直接结清)。
- ///
- public void MarkAsPaid()
- {
- // 1. 计算剩余应付金额
- var remainingAmount = CalculateTotalAmount() - AmountPaid;
-
- // 2. 若已结清则直接返回
- if (remainingAmount <= 0)
- {
- Status = TenantBillingStatus.Paid;
- return;
- }
-
- // 3. 补足剩余金额并标记为已支付
- MarkAsPaid(remainingAmount, string.Empty);
- }
-
- ///
- /// 标记为已支付。
- ///
- /// 支付金额。
- /// 交易号。
- public void MarkAsPaid(decimal amount, string transactionNo)
- {
- if (Status == TenantBillingStatus.Paid)
- {
- throw new InvalidOperationException("账单已经处于已支付状态,不能重复标记。");
- }
-
- if (Status == TenantBillingStatus.Cancelled)
- {
- throw new InvalidOperationException("已取消的账单不能标记为已支付。");
- }
-
- // 1. 累加支付金额
- AmountPaid += amount;
-
- // 2. 如果实付金额大于等于应付总额,则标记为已支付
- if (AmountPaid >= CalculateTotalAmount())
- {
- Status = TenantBillingStatus.Paid;
- }
- }
-
- ///
- /// 标记为逾期。
- ///
- public void MarkAsOverdue()
- {
- // 1. 仅待支付账单允许标记逾期
- if (Status != TenantBillingStatus.Pending)
- {
- return;
- }
-
- // 2. 未超过到期日则不处理
- if (DateTime.UtcNow <= DueDate)
- {
- return;
- }
-
- // 3. 标记为逾期(通知时间由外部流程在发送通知时写入)
- Status = TenantBillingStatus.Overdue;
- }
-
- ///
- /// 取消账单。
- ///
- public void Cancel()
- {
- Cancel(null);
- }
-
- ///
- /// 取消账单。
- ///
- /// 取消原因。
- public void Cancel(string? reason)
- {
- if (Status == TenantBillingStatus.Paid)
- {
- throw new InvalidOperationException("已支付的账单不能取消。");
- }
-
- if (Status == TenantBillingStatus.Cancelled)
- {
- throw new InvalidOperationException("账单已经处于取消状态。");
- }
-
- // 1. 变更状态
- Status = TenantBillingStatus.Cancelled;
-
- // 2. 记录取消原因(可选)
- if (!string.IsNullOrWhiteSpace(reason))
- {
- Notes = string.IsNullOrWhiteSpace(Notes) ? $"[取消原因] {reason}" : $"{Notes}\n[取消原因] {reason}";
- }
- }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantNotification.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantNotification.cs
deleted file mode 100644
index 8d2b881..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantNotification.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 面向租户的站内通知或消息推送。
-///
-public sealed class TenantNotification : MultiTenantEntityBase
-{
- ///
- /// 通知标题。
- ///
- public string Title { get; set; } = string.Empty;
-
- ///
- /// 通知正文。
- ///
- public string Message { get; set; } = string.Empty;
-
- ///
- /// 发布通道(站内、邮件、短信等)。
- ///
- public TenantNotificationChannel Channel { get; set; } = TenantNotificationChannel.InApp;
-
- ///
- /// 通知重要级别。
- ///
- public TenantNotificationSeverity Severity { get; set; } = TenantNotificationSeverity.Info;
-
- ///
- /// 推送时间。
- ///
- public DateTime SentAt { get; set; } = DateTime.UtcNow;
-
- ///
- /// 租户是否已阅读。
- ///
- public DateTime? ReadAt { get; set; }
-
- ///
- /// 附加元数据 JSON。
- ///
- public string? MetadataJson { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantPackage.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantPackage.cs
deleted file mode 100644
index 3a89b15..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantPackage.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 平台提供的租户套餐定义。
-///
-public sealed class TenantPackage : AuditableEntityBase
-{
- ///
- /// 套餐名称,展示给租户的简称。
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// 套餐描述,包含适用场景、权益等。
- ///
- public string? Description { get; set; }
-
- ///
- /// 套餐分类(试用、标准、旗舰等)。
- ///
- public TenantPackageType PackageType { get; set; } = TenantPackageType.Standard;
-
- ///
- /// 月付价格,单位:人民币元。
- ///
- public decimal? MonthlyPrice { get; set; }
-
- ///
- /// 年付价格,单位:人民币元。
- ///
- public decimal? YearlyPrice { get; set; }
-
- ///
- /// 允许的最大门店数。
- ///
- public int? MaxStoreCount { get; set; }
-
- ///
- /// 允许创建的最大账号数。
- ///
- public int? MaxAccountCount { get; set; }
-
- ///
- /// 存储容量上限(GB)。
- ///
- public int? MaxStorageGb { get; set; }
-
- ///
- /// 每月短信额度上限。
- ///
- public int? MaxSmsCredits { get; set; }
-
- ///
- /// 每月可调用的配送单数量上限。
- ///
- public int? MaxDeliveryOrders { get; set; }
-
- ///
- /// 权益明细 JSON,记录自定义特性开关。
- ///
- public string? FeaturePoliciesJson { get; set; }
-
- ///
- /// 是否仍启用(平台控制)。
- ///
- public bool IsActive { get; set; } = true;
-
- ///
- /// 是否对外可见(展示页/套餐列表可见性)。
- ///
- public bool IsPublicVisible { get; set; } = true;
-
- ///
- /// 是否允许新租户购买/选择(仅影响新购,不影响已订阅租户)。
- ///
- public bool IsAllowNewTenantPurchase { get; set; } = true;
-
- ///
- /// 发布状态(草稿/已发布)。
- ///
- public TenantPackagePublishStatus PublishStatus { get; set; } = TenantPackagePublishStatus.Draft;
-
- ///
- /// 是否推荐展示(运营推荐标识)。
- ///
- public bool IsRecommended { get; set; }
-
- ///
- /// 套餐标签(用于展示与对比页,如:推荐/性价比/旗舰)。
- ///
- public string[] Tags { get; set; } = [];
-
- ///
- /// 展示排序,数值越小越靠前。
- ///
- public int SortOrder { get; set; } = 0;
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantPayment.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantPayment.cs
deleted file mode 100644
index e81dc4e..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantPayment.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户支付记录。
-///
-public sealed class TenantPayment : MultiTenantEntityBase
-{
- ///
- /// 关联的账单 ID。
- ///
- public long BillingStatementId { get; set; }
-
- ///
- /// 支付金额。
- ///
- public decimal Amount { get; set; }
-
- ///
- /// 支付方式。
- ///
- public TenantPaymentMethod Method { get; set; }
-
- ///
- /// 支付状态。
- ///
- public TenantPaymentStatus Status { get; set; }
-
- ///
- /// 交易号。
- ///
- public string? TransactionNo { get; set; }
-
- ///
- /// 支付凭证 URL。
- ///
- public string? ProofUrl { get; set; }
-
- ///
- /// 支付时间。
- ///
- public DateTime? PaidAt { get; set; }
-
- ///
- /// 退款原因。
- ///
- public string? RefundReason { get; set; }
-
- ///
- /// 退款时间。
- ///
- public DateTime? RefundedAt { get; set; }
-
- ///
- /// 审核人 ID(管理员)。
- ///
- public long? VerifiedBy { get; set; }
-
- ///
- /// 审核时间。
- ///
- public DateTime? VerifiedAt { get; set; }
-
- ///
- /// 备注信息。
- ///
- public string? Notes { get; set; }
-
- ///
- /// 审核支付记录(确认支付有效性)。
- ///
- public void Verify()
- {
- if (Status != TenantPaymentStatus.Pending)
- {
- throw new InvalidOperationException("只有待审核的支付记录才能被审核。");
- }
-
- if (VerifiedAt.HasValue)
- {
- throw new InvalidOperationException("该支付记录已经被审核过。");
- }
-
- // 1. 标记为支付成功
- Status = TenantPaymentStatus.Success;
-
- // 2. 写入审核时间与支付时间
- VerifiedAt = DateTime.UtcNow;
- PaidAt ??= DateTime.UtcNow;
- }
-
- ///
- /// 审核支付记录(确认支付有效性)。
- ///
- /// 审核人 ID。
- public void Verify(long verifierId)
- {
- Verify();
- VerifiedBy = verifierId;
- }
-
- ///
- /// 退款。
- ///
- public void Refund()
- {
- if (string.IsNullOrWhiteSpace(RefundReason))
- {
- throw new InvalidOperationException("退款原因不能为空。");
- }
-
- Refund(RefundReason);
- }
-
- ///
- /// 退款。
- ///
- /// 退款原因。
- public void Refund(string reason)
- {
- if (Status == TenantPaymentStatus.Refunded)
- {
- throw new InvalidOperationException("该支付记录已经处于退款状态。");
- }
-
- if (Status != TenantPaymentStatus.Success)
- {
- throw new InvalidOperationException("只有支付成功的记录才能退款。");
- }
-
- // 1. 标记退款状态
- Status = TenantPaymentStatus.Refunded;
-
- // 2. 写入退款原因与退款时间
- RefundReason = reason;
- RefundedAt = DateTime.UtcNow;
- }
-
- ///
- /// 拒绝支付(审核不通过)。
- ///
- /// 审核人 ID。
- /// 拒绝原因。
- public void Reject(long verifierId, string reason)
- {
- if (Status != TenantPaymentStatus.Pending)
- {
- throw new InvalidOperationException("只有待审核的支付记录才能被拒绝。");
- }
-
- Status = TenantPaymentStatus.Failed;
- VerifiedBy = verifierId;
- VerifiedAt = DateTime.UtcNow;
- Notes = $"拒绝原因: {reason}";
- }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaPackagePurchase.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaPackagePurchase.cs
deleted file mode 100644
index bd3bc51..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaPackagePurchase.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户配额包购买记录。
-///
-public sealed class TenantQuotaPackagePurchase : MultiTenantEntityBase
-{
- ///
- /// 配额包 ID。
- ///
- public long QuotaPackageId { get; set; }
-
- ///
- /// 购买时的配额值。
- ///
- public decimal QuotaValue { get; set; }
-
- ///
- /// 购买价格。
- ///
- public decimal Price { get; set; }
-
- ///
- /// 购买时间。
- ///
- public DateTime PurchasedAt { get; set; }
-
- ///
- /// 过期时间(可选)。
- ///
- public DateTime? ExpiredAt { get; set; }
-
- ///
- /// 备注。
- ///
- public string? Notes { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaUsage.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaUsage.cs
deleted file mode 100644
index 5b2f4c5..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaUsage.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户配额使用情况快照。
-///
-public sealed class TenantQuotaUsage : MultiTenantEntityBase
-{
- ///
- /// 配额类型,例如门店数、短信条数等。
- ///
- public TenantQuotaType QuotaType { get; set; }
-
- ///
- /// 当前配额上限。
- ///
- public decimal LimitValue { get; set; }
-
- ///
- /// 已消耗的数量。
- ///
- public decimal UsedValue { get; set; }
-
- ///
- /// 配额刷新周期描述(如月、年)。
- ///
- public string? ResetCycle { get; set; }
-
- ///
- /// 最近一次重置时间。
- ///
- public DateTime? LastResetAt { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaUsageHistory.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaUsageHistory.cs
deleted file mode 100644
index 7556126..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantQuotaUsageHistory.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户配额使用历史记录(用于追踪配额上下限与使用量的时间序列变化)。
-///
-public sealed class TenantQuotaUsageHistory : MultiTenantEntityBase
-{
- ///
- /// 配额类型。
- ///
- public TenantQuotaType QuotaType { get; set; }
-
- ///
- /// 已使用值(记录时刻的快照)。
- ///
- public decimal UsedValue { get; set; }
-
- ///
- /// 限额值(记录时刻的快照)。
- ///
- public decimal LimitValue { get; set; }
-
- ///
- /// 记录时间(UTC)。
- ///
- public DateTime RecordedAt { get; set; }
-
- ///
- /// 变更类型。
- ///
- public TenantQuotaUsageHistoryChangeType ChangeType { get; set; } = TenantQuotaUsageHistoryChangeType.Snapshot;
-
- ///
- /// 变更量(可选)。
- ///
- public decimal? ChangeAmount { get; set; }
-
- ///
- /// 变更原因(可选)。
- ///
- public string? ChangeReason { get; set; }
-}
-
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantReviewClaim.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantReviewClaim.cs
deleted file mode 100644
index 33e2948..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantReviewClaim.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户入驻审核领取记录(防止多管理员并发审核)。
-///
-public sealed class TenantReviewClaim : AuditableEntityBase
-{
- ///
- /// 被领取的租户 ID。
- ///
- public long TenantId { get; set; }
-
- ///
- /// 领取人用户 ID。
- ///
- public long ClaimedBy { get; set; }
-
- ///
- /// 领取人名称(展示用快照)。
- ///
- public string ClaimedByName { get; set; } = string.Empty;
-
- ///
- /// 领取时间(UTC)。
- ///
- public DateTime ClaimedAt { get; set; }
-
- ///
- /// 释放时间(UTC),未释放时为 null。
- ///
- public DateTime? ReleasedAt { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantSubscription.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantSubscription.cs
deleted file mode 100644
index 3090be2..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantSubscription.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户套餐订阅记录。
-///
-public sealed class TenantSubscription : MultiTenantEntityBase
-{
- ///
- /// 当前订阅关联的套餐标识。
- ///
- public long TenantPackageId { get; set; }
-
- ///
- /// 订阅生效时间(UTC)。
- ///
- public DateTime EffectiveFrom { get; set; }
-
- ///
- /// 订阅到期时间(UTC)。
- ///
- public DateTime EffectiveTo { get; set; }
-
- ///
- /// 下一个计费时间,配合自动续费使用。
- ///
- public DateTime? NextBillingDate { get; set; }
-
- ///
- /// 订阅当前状态。
- ///
- public SubscriptionStatus Status { get; set; } = SubscriptionStatus.Pending;
-
- ///
- /// 是否开启自动续费。
- ///
- public bool AutoRenew { get; set; }
-
- ///
- /// 若已排期升降配,对应的新套餐 ID。
- ///
- public long? ScheduledPackageId { get; set; }
-
- ///
- /// 运营备注信息。
- ///
- public string? Notes { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantSubscriptionHistory.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantSubscriptionHistory.cs
deleted file mode 100644
index 9a47b77..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantSubscriptionHistory.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户套餐订阅变更记录。
-///
-public sealed class TenantSubscriptionHistory : AuditableEntityBase
-{
- ///
- /// 租户标识。
- ///
- public long TenantId { get; set; }
-
- ///
- /// 对应的订阅 ID。
- ///
- public long TenantSubscriptionId { get; set; }
-
- ///
- /// 原套餐 ID。
- ///
- public long FromPackageId { get; set; }
-
- ///
- /// 新套餐 ID。
- ///
- public long ToPackageId { get; set; }
-
- ///
- /// 变更类型。
- ///
- public SubscriptionChangeType ChangeType { get; set; }
-
- ///
- /// 生效时间。
- ///
- public DateTime EffectiveFrom { get; set; }
-
- ///
- /// 到期时间。
- ///
- public DateTime EffectiveTo { get; set; }
-
- ///
- /// 相关费用。
- ///
- public decimal? Amount { get; set; }
-
- ///
- /// 币种。
- ///
- public string? Currency { get; set; }
-
- ///
- /// 备注。
- ///
- public string? Notes { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantVerificationProfile.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantVerificationProfile.cs
deleted file mode 100644
index 4a49fe9..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Entities/TenantVerificationProfile.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Shared.Abstractions.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Entities;
-
-///
-/// 租户实名认证资料。
-///
-public sealed class TenantVerificationProfile : AuditableEntityBase
-{
- ///
- /// 对应的租户标识。
- ///
- public long TenantId { get; set; }
-
- ///
- /// 实名状态。
- ///
- public TenantVerificationStatus Status { get; set; } = TenantVerificationStatus.Draft;
-
- ///
- /// 营业执照编号。
- ///
- public string? BusinessLicenseNumber { get; set; }
-
- ///
- /// 营业执照文件地址。
- ///
- public string? BusinessLicenseUrl { get; set; }
-
- ///
- /// 法人姓名。
- ///
- public string? LegalPersonName { get; set; }
-
- ///
- /// 法人身份证号。
- ///
- public string? LegalPersonIdNumber { get; set; }
-
- ///
- /// 法人身份证正面。
- ///
- public string? LegalPersonIdFrontUrl { get; set; }
-
- ///
- /// 法人身份证反面。
- ///
- public string? LegalPersonIdBackUrl { get; set; }
-
- ///
- /// 开户名。
- ///
- public string? BankAccountName { get; set; }
-
- ///
- /// 银行账号。
- ///
- public string? BankAccountNumber { get; set; }
-
- ///
- /// 银行名称。
- ///
- public string? BankName { get; set; }
-
- ///
- /// 附加资料(JSON)。
- ///
- public string? AdditionalDataJson { get; set; }
-
- ///
- /// 提交时间。
- ///
- public DateTime? SubmittedAt { get; set; }
-
- ///
- /// 审核时间。
- ///
- public DateTime? ReviewedAt { get; set; }
-
- ///
- /// 审核人 ID。
- ///
- public long? ReviewedBy { get; set; }
-
- ///
- /// 审核人姓名。
- ///
- public string? ReviewedByName { get; set; }
-
- ///
- /// 审核备注。
- ///
- public string? ReviewRemarks { get; set; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/AnnouncementStatus.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/AnnouncementStatus.cs
deleted file mode 100644
index 3bc0bcb..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/AnnouncementStatus.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 公告状态。
-///
-public enum AnnouncementStatus
-{
- ///
- /// 草稿。
- ///
- Draft = 0,
-
- ///
- /// 已发布。
- ///
- Published = 1,
-
- ///
- /// 已撤销。
- ///
- Revoked = 2
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/BillingExportFormat.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/BillingExportFormat.cs
deleted file mode 100644
index 19765f2..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/BillingExportFormat.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 账单导出格式。
-///
-public enum BillingExportFormat
-{
- ///
- /// Excel 格式(.xlsx)。
- ///
- Excel = 0,
-
- ///
- /// PDF 格式(.pdf)。
- ///
- Pdf = 1,
-
- ///
- /// CSV 格式(.csv)。
- ///
- Csv = 2
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/BillingType.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/BillingType.cs
deleted file mode 100644
index 980da39..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/BillingType.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 账单类型。
-///
-public enum BillingType
-{
- ///
- /// 订阅账单(周期性订阅费用)。
- ///
- Subscription = 0,
-
- ///
- /// 配额包购买(一次性配额包购买)。
- ///
- QuotaPurchase = 1,
-
- ///
- /// 手动创建(管理员手动生成的账单)。
- ///
- Manual = 2,
-
- ///
- /// 续费账单(自动续费生成的账单)。
- ///
- Renewal = 3
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/PublisherScope.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/PublisherScope.cs
deleted file mode 100644
index b87ffd6..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/PublisherScope.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 发布者范围。
-///
-public enum PublisherScope
-{
- ///
- /// 平台发布。
- ///
- Platform = 0,
-
- ///
- /// 租户发布。
- ///
- Tenant = 1
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/SubscriptionChangeType.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/SubscriptionChangeType.cs
deleted file mode 100644
index 0eb9af5..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/SubscriptionChangeType.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 套餐订阅的操作类型。
-///
-public enum SubscriptionChangeType
-{
- ///
- /// 新订阅。
- ///
- New = 0,
-
- ///
- /// 续费。
- ///
- Renew = 1,
-
- ///
- /// 升级套餐。
- ///
- Upgrade = 2,
-
- ///
- /// 降级套餐。
- ///
- Downgrade = 3
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/SubscriptionStatus.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/SubscriptionStatus.cs
deleted file mode 100644
index d1d1a49..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/SubscriptionStatus.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 订阅状态。
-///
-public enum SubscriptionStatus
-{
- ///
- /// 尚未支付或等待审批。
- ///
- Pending = 0,
-
- ///
- /// 订阅已生效。
- ///
- Active = 1,
-
- ///
- /// 已到期但仍保留数据。
- ///
- GracePeriod = 2,
-
- ///
- /// 已取消。
- ///
- Cancelled = 3,
-
- ///
- /// 因欠费被暂停。
- ///
- Suspended = 4
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantAnnouncementType.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantAnnouncementType.cs
deleted file mode 100644
index 0672fee..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantAnnouncementType.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 租户公告类型。
-///
-public enum TenantAnnouncementType
-{
- ///
- /// 系统公告。
- ///
- System = 0,
-
- ///
- /// 账单/订阅相关提醒。
- ///
- Billing = 1,
-
- ///
- /// 运营通知。
- ///
- Operation = 2,
-
- ///
- /// 平台系统更新公告。
- ///
- SYSTEM_PLATFORM_UPDATE = 3,
-
- ///
- /// 系统安全公告。
- ///
- SYSTEM_SECURITY_NOTICE = 4,
-
- ///
- /// 系统合规公告。
- ///
- SYSTEM_COMPLIANCE = 5,
-
- ///
- /// 租户内部公告。
- ///
- TENANT_INTERNAL = 6,
-
- ///
- /// 租户财务公告。
- ///
- TENANT_FINANCE = 7,
-
- ///
- /// 租户运营公告。
- ///
- TENANT_OPERATION = 8
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantAuditAction.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantAuditAction.cs
deleted file mode 100644
index 6395a2a..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantAuditAction.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 租户运营审核动作。
-///
-public enum TenantAuditAction
-{
- ///
- /// 注册信息提交。
- ///
- RegistrationSubmitted = 1,
-
- ///
- /// 实名资料提交或更新。
- ///
- VerificationSubmitted = 2,
-
- ///
- /// 实名审核通过。
- ///
- VerificationApproved = 3,
-
- ///
- /// 实名审核驳回。
- ///
- VerificationRejected = 4,
-
- ///
- /// 订阅创建或续费。
- ///
- SubscriptionUpdated = 5,
-
- ///
- /// 套餐升降配。
- ///
- SubscriptionPlanChanged = 6,
-
- ///
- /// 租户状态变更(启用/停用/到期等)。
- ///
- StatusChanged = 7,
-
- ///
- /// 领取入驻审核。
- ///
- ReviewClaimed = 8,
-
- ///
- /// 强制接管入驻审核。
- ///
- ReviewForceClaimed = 9,
-
- ///
- /// 释放入驻审核(审核完成或手动释放)。
- ///
- ReviewClaimReleased = 10,
-
- ///
- /// 平台伪装登录租户。
- ///
- ImpersonatedLogin = 11,
-
- ///
- /// 生成主管理员重置链接。
- ///
- AdminResetLinkIssued = 12
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantBillingStatus.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantBillingStatus.cs
deleted file mode 100644
index 11671d2..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantBillingStatus.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 账单状态。
-///
-public enum TenantBillingStatus
-{
- ///
- /// 等待付款。
- ///
- Pending = 0,
-
- ///
- /// 已付款结清。
- ///
- Paid = 1,
-
- ///
- /// 已逾期。
- ///
- Overdue = 2,
-
- ///
- /// 已取消或作废。
- ///
- Cancelled = 3
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantNotificationChannel.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantNotificationChannel.cs
deleted file mode 100644
index 25a3a36..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantNotificationChannel.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 通知推送渠道。
-///
-public enum TenantNotificationChannel
-{
- ///
- /// 站内消息。
- ///
- InApp = 0,
-
- ///
- /// 邮件推送。
- ///
- Email = 1,
-
- ///
- /// 短信提醒。
- ///
- Sms = 2,
-
- ///
- /// 管理后台弹窗。
- ///
- Portal = 3
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantNotificationSeverity.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantNotificationSeverity.cs
deleted file mode 100644
index 7947059..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantNotificationSeverity.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 租户通知的重要程度。
-///
-public enum TenantNotificationSeverity
-{
- ///
- /// 普通提示。
- ///
- Info = 0,
-
- ///
- /// 需要关注的提醒。
- ///
- Warning = 1,
-
- ///
- /// 影响业务的严重事件。
- ///
- Critical = 2
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPackagePublishStatus.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPackagePublishStatus.cs
deleted file mode 100644
index 958cead..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPackagePublishStatus.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 套餐发布状态。
-///
-public enum TenantPackagePublishStatus
-{
- ///
- /// 草稿。
- ///
- Draft = 0,
-
- ///
- /// 已发布。
- ///
- Published = 1
-}
-
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPackageType.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPackageType.cs
deleted file mode 100644
index 111ae2c..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPackageType.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 套餐类型枚举。
-///
-public enum TenantPackageType
-{
- ///
- /// 免费试用套餐。
- ///
- Trial = 0,
-
- ///
- /// 标准商业套餐。
- ///
- Standard = 1,
-
- ///
- /// 面向成长型商户的高级套餐。
- ///
- Professional = 2,
-
- ///
- /// 提供完整能力的旗舰套餐。
- ///
- Enterprise = 3
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPaymentMethod.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPaymentMethod.cs
deleted file mode 100644
index 558fa00..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPaymentMethod.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 租户支付方式。
-///
-public enum TenantPaymentMethod
-{
- ///
- /// 线上支付。
- ///
- Online = 0,
-
- ///
- /// 银行转账。
- ///
- BankTransfer = 1,
-
- ///
- /// 其他方式。
- ///
- Other = 2
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPaymentStatus.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPaymentStatus.cs
deleted file mode 100644
index a1f758b..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantPaymentStatus.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 租户支付状态。
-///
-public enum TenantPaymentStatus
-{
- ///
- /// 待支付。
- ///
- Pending = 0,
-
- ///
- /// 支付成功。
- ///
- Success = 1,
-
- ///
- /// 支付失败。
- ///
- Failed = 2,
-
- ///
- /// 已退款。
- ///
- Refunded = 3
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantQuotaType.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantQuotaType.cs
deleted file mode 100644
index 5392bca..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantQuotaType.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 配额类型,覆盖容量及调用次数。
-///
-public enum TenantQuotaType
-{
- ///
- /// 门店数量限制。
- ///
- StoreCount = 0,
-
- ///
- /// 员工账号数量限制。
- ///
- AccountCount = 1,
-
- ///
- /// 存储空间限制。
- ///
- Storage = 2,
-
- ///
- /// 短信额度。
- ///
- SmsCredits = 3,
-
- ///
- /// 配送订单数量限制。
- ///
- DeliveryOrders = 4,
-
- ///
- /// 营销活动并发数量。
- ///
- PromotionSlots = 5
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantQuotaUsageHistoryChangeType.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantQuotaUsageHistoryChangeType.cs
deleted file mode 100644
index bb8d1a8..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantQuotaUsageHistoryChangeType.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 租户配额使用历史变更类型。
-///
-public enum TenantQuotaUsageHistoryChangeType
-{
- ///
- /// 初始化(首次建立配额基线)。
- ///
- Init = 0,
-
- ///
- /// 快照(定时或手动记录的当前状态)。
- ///
- Snapshot = 1,
-
- ///
- /// 增加(剩余可用额度增加,例如购买配额包、重置等)。
- ///
- Increase = 2,
-
- ///
- /// 减少(剩余可用额度减少,例如配额消耗等)。
- ///
- Decrease = 3
-}
-
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantVerificationStatus.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantVerificationStatus.cs
deleted file mode 100644
index 88dbea7..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Enums/TenantVerificationStatus.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Enums;
-
-///
-/// 租户实名认证状态。
-///
-public enum TenantVerificationStatus
-{
- ///
- /// 草稿,未提交审核。
- ///
- Draft = 0,
-
- ///
- /// 已提交审核,等待运营处理。
- ///
- Pending = 1,
-
- ///
- /// 审核通过。
- ///
- Approved = 2,
-
- ///
- /// 审核驳回。
- ///
- Rejected = 3
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Events/AnnouncementPublished.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Events/AnnouncementPublished.cs
deleted file mode 100644
index 59fe5b4..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Events/AnnouncementPublished.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Events;
-
-///
-/// 公告发布事件。
-///
-public sealed class AnnouncementPublished
-{
- ///
- /// 公告 ID。
- ///
- public long AnnouncementId { get; init; }
-
- ///
- /// 发布时间(UTC)。
- ///
- public DateTime PublishedAt { get; init; }
-
- ///
- /// 目标受众类型。
- ///
- public string TargetType { get; init; } = string.Empty;
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Events/AnnouncementRevoked.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Events/AnnouncementRevoked.cs
deleted file mode 100644
index a49fe09..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Events/AnnouncementRevoked.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace TakeoutSaaS.Domain.Tenants.Events;
-
-///
-/// 公告撤销事件。
-///
-public sealed class AnnouncementRevoked
-{
- ///
- /// 公告 ID。
- ///
- public long AnnouncementId { get; init; }
-
- ///
- /// 撤销时间(UTC)。
- ///
- public DateTime RevokedAt { get; init; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/IQuotaPackageRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/IQuotaPackageRepository.cs
deleted file mode 100644
index 49aa91d..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/IQuotaPackageRepository.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 配额包仓储。
-///
-public interface IQuotaPackageRepository
-{
- #region 配额包定义
-
- ///
- /// 按 ID 查找配额包。
- ///
- /// 配额包 ID(雪花算法)。
- /// 取消标记。
- /// 配额包实体,未找到返回 null。
- Task FindByIdAsync(long id, CancellationToken cancellationToken = default);
-
- ///
- /// 分页查询配额包。
- ///
- /// 配额类型,为空不按类型过滤。
- /// 启用状态,为空不按状态过滤。
- /// 页码(从 1 开始)。
- /// 每页大小。
- /// 取消标记。
- /// 分页数据与总数。
- Task<(IReadOnlyList Items, int Total)> SearchPagedAsync(
- TenantQuotaType? quotaType,
- bool? isActive,
- int page,
- int pageSize,
- CancellationToken cancellationToken = default);
-
- ///
- /// 新增配额包。
- ///
- /// 配额包实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAsync(QuotaPackage quotaPackage, CancellationToken cancellationToken = default);
-
- ///
- /// 更新配额包。
- ///
- /// 配额包实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateAsync(QuotaPackage quotaPackage, CancellationToken cancellationToken = default);
-
- ///
- /// 软删除配额包。
- ///
- /// 配额包 ID(雪花算法)。
- /// 取消标记。
- /// 删除成功返回 true,未找到返回 false。
- Task SoftDeleteAsync(long id, CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 配额包购买记录
-
- ///
- /// 分页查询租户配额购买记录(包含配额包信息)。
- ///
- /// 租户 ID(雪花算法)。
- /// 页码(从 1 开始)。
- /// 每页大小。
- /// 取消标记。
- /// 分页数据与总数。
- Task<(IReadOnlyList<(TenantQuotaPackagePurchase Purchase, QuotaPackage Package)> Items, int Total)> GetPurchasesPagedAsync(
- long tenantId,
- int page,
- int pageSize,
- CancellationToken cancellationToken = default);
-
- ///
- /// 新增配额购买记录。
- ///
- /// 购买记录实体。
- /// 取消标记。
- /// 异步任务。
- Task AddPurchaseAsync(TenantQuotaPackagePurchase purchase, CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 配额使用情况
-
- ///
- /// 查询租户配额使用情况。
- ///
- /// 租户 ID(雪花算法)。
- /// 配额类型,为空查询全部。
- /// 取消标记。
- /// 配额使用情况列表。
- Task> GetUsageByTenantAsync(
- long tenantId,
- TenantQuotaType? quotaType,
- CancellationToken cancellationToken = default);
-
- ///
- /// 查找特定配额使用记录。
- ///
- /// 租户 ID(雪花算法)。
- /// 配额类型。
- /// 取消标记。
- /// 配额使用记录,未找到返回 null。
- Task FindUsageAsync(
- long tenantId,
- TenantQuotaType quotaType,
- CancellationToken cancellationToken = default);
-
- ///
- /// 更新配额使用情况。
- ///
- /// 配额使用实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateUsageAsync(TenantQuotaUsage usage, CancellationToken cancellationToken = default);
-
- #endregion
-
- ///
- /// 持久化。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/IStatisticsRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/IStatisticsRepository.cs
deleted file mode 100644
index 806d440..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/IStatisticsRepository.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 统计数据仓储接口。
-///
-public interface IStatisticsRepository
-{
- #region 订阅统计
-
- ///
- /// 获取所有订阅(用于统计)。
- ///
- /// 取消标记。
- /// 所有订阅记录。
- Task> GetAllSubscriptionsAsync(CancellationToken cancellationToken = default);
-
- ///
- /// 获取即将到期的订阅(含租户和套餐信息)。
- ///
- /// 到期天数。
- /// 是否仅查询未开启自动续费的。
- /// 取消标记。
- /// 即将到期的订阅信息列表。
- Task> GetExpiringSubscriptionsAsync(
- int daysAhead,
- bool onlyWithoutAutoRenew,
- CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 收入统计
-
- ///
- /// 获取所有已付款账单(用于收入统计)。
- ///
- /// 取消标记。
- /// 已付款账单列表。
- Task> GetPaidBillsAsync(CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 配额使用排行
-
- ///
- /// 获取配额使用排行(含租户信息)。
- ///
- /// 配额类型。
- /// 前 N 名。
- /// 取消标记。
- /// 配额使用排行列表。
- Task> GetQuotaUsageRankingAsync(
- TenantQuotaType quotaType,
- int topN,
- CancellationToken cancellationToken = default);
-
- #endregion
-}
-
-///
-/// 即将到期的订阅信息(含关联数据)。
-///
-public record ExpiringSubscriptionInfo
-{
- ///
- /// 订阅实体。
- ///
- public required TenantSubscription Subscription { get; init; }
-
- ///
- /// 租户名称。
- ///
- public required string TenantName { get; init; }
-
- ///
- /// 套餐名称。
- ///
- public required string PackageName { get; init; }
-}
-
-///
-/// 配额使用排行信息(含租户名称)。
-///
-public record QuotaUsageRankInfo
-{
- ///
- /// 租户 ID。
- ///
- public long TenantId { get; init; }
-
- ///
- /// 租户名称。
- ///
- public required string TenantName { get; init; }
-
- ///
- /// 已使用值。
- ///
- public decimal UsedValue { get; init; }
-
- ///
- /// 限制值。
- ///
- public decimal LimitValue { get; init; }
-
- ///
- /// 使用百分比。
- ///
- public decimal UsagePercentage { get; init; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ISubscriptionRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ISubscriptionRepository.cs
deleted file mode 100644
index 68e550d..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ISubscriptionRepository.cs
+++ /dev/null
@@ -1,402 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 订阅管理仓储接口。
-///
-public interface ISubscriptionRepository
-{
- #region 订阅查询
-
- ///
- /// 按 ID 查询订阅。
- ///
- /// 订阅 ID。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 订阅实体,未找到返回 null。
- Task FindByIdAsync(
- long subscriptionId,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 按 ID 列表批量查询订阅。
- ///
- /// 订阅 ID 列表。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 订阅实体列表。
- Task> FindByIdsAsync(
- IEnumerable subscriptionIds,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 分页查询订阅列表(含关联信息)。
- ///
- /// 查询过滤条件。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 分页结果。
- Task<(IReadOnlyList Items, int Total)> SearchPagedAsync(
- SubscriptionSearchFilter filter,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 获取订阅详情(含关联信息)。
- ///
- /// 订阅 ID。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 订阅详情信息。
- Task GetDetailAsync(
- long subscriptionId,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 按 ID 列表批量查询订阅(含租户信息)。
- ///
- /// 订阅 ID 列表。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 订阅与租户信息列表。
- Task> FindByIdsWithTenantAsync(
- IEnumerable subscriptionIds,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 查询自动续费候选订阅(活跃 + 开启自动续费 + 即将到期)。
- ///
- /// 当前时间(UTC)。
- /// 续费阈值时间(UTC),到期时间小于等于该时间视为候选。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 候选订阅集合(含套餐信息)。
- Task> FindAutoRenewalCandidatesAsync(
- DateTime now,
- DateTime renewalThreshold,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 查询续费提醒候选订阅(活跃 + 未开启自动续费 + 到期时间落在指定日期范围)。
- ///
- /// 筛选开始时间(UTC,含)。
- /// 筛选结束时间(UTC,不含)。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 候选订阅集合(含租户与套餐信息)。
- Task> FindRenewalReminderCandidatesAsync(
- DateTime startOfDay,
- DateTime endOfDay,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 查询已到期仍处于 Active 的订阅(用于进入宽限期)。
- ///
- /// 当前时间(UTC)。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 到期订阅集合。
- Task> FindExpiredActiveSubscriptionsAsync(
- DateTime now,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- ///
- /// 查询宽限期已结束的订阅(用于自动暂停)。
- ///
- /// 当前时间(UTC)。
- /// 宽限期天数。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 宽限期到期订阅集合。
- Task> FindGracePeriodExpiredSubscriptionsAsync(
- DateTime now,
- int gracePeriodDays,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- #endregion
-
- #region 套餐查询
-
- ///
- /// 按 ID 查询套餐。
- ///
- /// 套餐 ID。
- /// 取消标记。
- /// 套餐实体,未找到返回 null。
- Task FindPackageByIdAsync(long packageId, CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 订阅更新
-
- ///
- /// 更新订阅。
- ///
- /// 订阅实体。
- /// 取消标记。
- Task UpdateAsync(TenantSubscription subscription, CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 订阅历史
-
- ///
- /// 添加订阅变更历史。
- ///
- /// 历史记录实体。
- /// 取消标记。
- Task AddHistoryAsync(TenantSubscriptionHistory history, CancellationToken cancellationToken = default);
-
- ///
- /// 获取订阅变更历史(含套餐名称)。
- ///
- /// 订阅 ID。
- /// 取消标记。
- /// 历史记录列表。
- Task> GetHistoryAsync(
- long subscriptionId,
- CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 配额使用
-
- ///
- /// 获取租户配额使用情况。
- ///
- /// 租户 ID。
- /// 取消标记。
- /// 是否包含已删除数据。
- /// 配额使用列表。
- Task> GetQuotaUsagesAsync(
- long tenantId,
- CancellationToken cancellationToken = default,
- bool includeDeleted = false);
-
- #endregion
-
- #region 通知
-
- ///
- /// 添加租户通知。
- ///
- /// 通知实体。
- /// 取消标记。
- Task AddNotificationAsync(TenantNotification notification, CancellationToken cancellationToken = default);
-
- #endregion
-
- #region 操作日志
-
- ///
- /// 添加操作日志。
- ///
- /// 日志实体。
- /// 取消标记。
- Task AddOperationLogAsync(OperationLog log, CancellationToken cancellationToken = default);
-
- #endregion
-
- ///
- /// 保存变更。
- ///
- /// 取消标记。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
-
-#region 查询过滤与结果类型
-
-///
-/// 订阅查询过滤条件。
-///
-public record SubscriptionSearchFilter
-{
- ///
- /// 订阅状态。
- ///
- public SubscriptionStatus? Status { get; init; }
-
- ///
- /// 套餐 ID。
- ///
- public long? TenantPackageId { get; init; }
-
- ///
- /// 租户 ID。
- ///
- public long? TenantId { get; init; }
-
- ///
- /// 租户关键词(名称或编码)。
- ///
- public string? TenantKeyword { get; init; }
-
- ///
- /// 即将到期天数。
- ///
- public int? ExpiringWithinDays { get; init; }
-
- ///
- /// 自动续费状态。
- ///
- public bool? AutoRenew { get; init; }
-
- ///
- /// 页码(从 1 开始)。
- ///
- public int Page { get; init; } = 1;
-
- ///
- /// 每页数量。
- ///
- public int PageSize { get; init; } = 20;
-}
-
-///
-/// 订阅及关联信息。
-///
-public record SubscriptionWithRelations
-{
- ///
- /// 订阅实体。
- ///
- public required TenantSubscription Subscription { get; init; }
-
- ///
- /// 租户名称。
- ///
- public required string TenantName { get; init; }
-
- ///
- /// 租户编码。
- ///
- public required string TenantCode { get; init; }
-
- ///
- /// 套餐名称。
- ///
- public required string PackageName { get; init; }
-
- ///
- /// 排期套餐名称(可选)。
- ///
- public string? ScheduledPackageName { get; init; }
-}
-
-///
-/// 订阅详情信息。
-///
-public record SubscriptionDetailInfo
-{
- ///
- /// 订阅实体。
- ///
- public required TenantSubscription Subscription { get; init; }
-
- ///
- /// 租户名称。
- ///
- public required string TenantName { get; init; }
-
- ///
- /// 租户编码。
- ///
- public required string TenantCode { get; init; }
-
- ///
- /// 当前套餐。
- ///
- public TenantPackage? Package { get; init; }
-
- ///
- /// 排期套餐。
- ///
- public TenantPackage? ScheduledPackage { get; init; }
-}
-
-///
-/// 订阅与租户信息。
-///
-public record SubscriptionWithTenant
-{
- ///
- /// 订阅实体。
- ///
- public required TenantSubscription Subscription { get; init; }
-
- ///
- /// 租户实体。
- ///
- public required Tenant Tenant { get; init; }
-}
-
-///
-/// 自动续费候选订阅信息。
-///
-public sealed record AutoRenewalCandidate
-{
- ///
- /// 订阅实体。
- ///
- public required TenantSubscription Subscription { get; init; }
-
- ///
- /// 当前套餐实体。
- ///
- public required TenantPackage Package { get; init; }
-}
-
-///
-/// 续费提醒候选订阅信息。
-///
-public sealed record RenewalReminderCandidate
-{
- ///
- /// 订阅实体。
- ///
- public required TenantSubscription Subscription { get; init; }
-
- ///
- /// 租户实体。
- ///
- public required Tenant Tenant { get; init; }
-
- ///
- /// 当前套餐实体。
- ///
- public required TenantPackage Package { get; init; }
-}
-
-///
-/// 订阅历史(含套餐名称)。
-///
-public record SubscriptionHistoryWithPackageNames
-{
- ///
- /// 历史记录实体。
- ///
- public required TenantSubscriptionHistory History { get; init; }
-
- ///
- /// 原套餐名称。
- ///
- public required string FromPackageName { get; init; }
-
- ///
- /// 目标套餐名称。
- ///
- public required string ToPackageName { get; init; }
-}
-
-#endregion
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementRepository.cs
deleted file mode 100644
index b01af46..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantAnnouncementRepository.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 租户公告仓储。
-///
-public interface ITenantAnnouncementRepository
-{
- ///
- /// 查询公告列表(包含平台公告 TenantId=0),按类型、状态与生效时间筛选。
- ///
- /// 租户 ID。
- /// 关键词(标题/内容)。
- /// 公告状态。
- /// 公告类型。
- /// 启用状态。
- /// 生效开始时间筛选。
- /// 生效结束时间筛选。
- /// 生效时间点,为空不限制。
- /// 是否按优先级降序和生效时间降序排序,默认 false。
- /// 限制返回数量,为空不限制。
- /// 取消标记。
- /// 公告集合。
- Task> SearchAsync(
- long tenantId,
- string? keyword,
- AnnouncementStatus? status,
- TenantAnnouncementType? type,
- bool? isActive,
- DateTime? effectiveFrom,
- DateTime? effectiveTo,
- DateTime? effectiveAt,
- bool orderByPriority = false,
- int? limit = null,
- CancellationToken cancellationToken = default);
-
- ///
- /// 按 ID 获取公告。
- ///
- /// 租户 ID。
- /// 公告 ID。
- /// 取消标记。
- /// 公告实体或 null。
- Task FindByIdAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default);
-
- ///
- /// 新增公告。
- ///
- /// 公告实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default);
-
- ///
- /// 更新公告。
- ///
- /// 公告实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default);
-
- ///
- /// 删除公告。
- ///
- /// 租户 ID。
- /// 公告 ID。
- /// 取消标记。
- /// 异步任务。
- Task DeleteAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default);
-
- ///
- /// 保存变更。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantBillingRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantBillingRepository.cs
deleted file mode 100644
index 7c88d2b..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantBillingRepository.cs
+++ /dev/null
@@ -1,247 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 租户账单仓储。
-///
-public interface ITenantBillingRepository
-{
- ///
- /// 查询账单列表,按状态与时间范围筛选。
- ///
- /// 租户 ID。
- /// 账单状态。
- /// 开始时间(UTC)。
- /// 结束时间(UTC)。
- /// 取消标记。
- /// 账单集合。
- Task> SearchAsync(
- long tenantId,
- TenantBillingStatus? status,
- DateTime? from,
- DateTime? to,
- CancellationToken cancellationToken = default);
-
- ///
- /// 按 ID 获取账单。
- ///
- /// 租户 ID。
- /// 账单 ID。
- /// 取消标记。
- /// 账单实体或 null。
- Task FindByIdAsync(long tenantId, long billingId, CancellationToken cancellationToken = default);
-
- ///
- /// 按账单编号获取账单。
- ///
- /// 租户 ID。
- /// 账单编号。
- /// 取消标记。
- /// 账单实体或 null。
- Task FindByStatementNoAsync(long tenantId, string statementNo, CancellationToken cancellationToken = default);
-
- ///
- /// 按账单编号获取账单(不限租户,管理员端使用)。
- ///
- /// 账单编号。
- /// 取消标记。
- /// 账单实体或 null。
- Task GetByStatementNoAsync(string statementNo, CancellationToken cancellationToken = default);
-
- ///
- /// 判断是否已存在指定周期开始时间的未取消账单(用于自动续费幂等)。
- ///
- /// 租户 ID。
- /// 账单周期开始时间(UTC)。
- /// 取消标记。
- /// 存在返回 true,否则 false。
- Task ExistsNotCancelledByPeriodStartAsync(
- long tenantId,
- DateTime periodStart,
- CancellationToken cancellationToken = default);
-
- ///
- /// 获取逾期账单列表(已过到期日且未支付)。
- ///
- /// 取消标记。
- /// 逾期账单集合。
- Task> GetOverdueBillingsAsync(CancellationToken cancellationToken = default);
-
- ///
- /// 获取即将到期的账单列表(未来 N 天内到期且未支付)。
- ///
- /// 提前天数。
- /// 取消标记。
- /// 即将到期的账单集合。
- Task> GetBillingsDueSoonAsync(int daysAhead, CancellationToken cancellationToken = default);
-
- ///
- /// 按租户 ID 获取账单列表。
- ///
- /// 租户 ID。
- /// 取消标记。
- /// 账单集合。
- Task> GetByTenantIdAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 按 ID 列表批量获取账单(管理员端/批量操作场景)。
- ///
- /// 账单 ID 列表。
- /// 取消标记。
- /// 账单实体列表。
- Task> GetByIdsAsync(
- IReadOnlyCollection billingIds,
- CancellationToken cancellationToken = default);
-
- ///
- /// 新增账单。
- ///
- /// 账单实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);
-
- ///
- /// 更新账单。
- ///
- /// 账单实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);
-
- ///
- /// 保存变更。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-
- ///
- /// 管理员端分页查询账单列表(跨租户)。
- ///
- /// 租户 ID 筛选(可选)。
- /// 账单状态筛选(可选)。
- /// 开始时间(UTC,可选)。
- /// 结束时间(UTC,可选)。
- /// 最小应付金额筛选(包含,可选)。
- /// 最大应付金额筛选(包含,可选)。
- /// 关键词搜索(账单号或租户名)。
- /// 页码(从 1 开始)。
- /// 页大小。
- /// 取消标记。
- /// 账单集合与总数。
- Task<(IReadOnlyList Items, int Total)> SearchPagedAsync(
- long? tenantId,
- TenantBillingStatus? status,
- DateTime? from,
- DateTime? to,
- decimal? minAmount,
- decimal? maxAmount,
- string? keyword,
- int pageNumber,
- int pageSize,
- CancellationToken cancellationToken = default);
-
- ///
- /// 获取账单统计数据(用于报表与仪表盘)。
- ///
- /// 租户 ID(可选,管理员可查询所有租户)。
- /// 统计开始时间(UTC)。
- /// 统计结束时间(UTC)。
- /// 分组方式(Day/Week/Month)。
- /// 取消标记。
- /// 统计结果。
- Task GetStatisticsAsync(
- long? tenantId,
- DateTime startDate,
- DateTime endDate,
- string groupBy,
- CancellationToken cancellationToken = default);
-
- ///
- /// 按 ID 获取账单(不限租户,管理员端使用)。
- ///
- /// 账单 ID。
- /// 取消标记。
- /// 账单实体或 null。
- Task FindByIdAsync(long billingId, CancellationToken cancellationToken = default);
-}
-
-///
-/// 账单统计结果。
-///
-public sealed record TenantBillingStatistics
-{
- ///
- /// 总账单金额(统计区间内)。
- ///
- public decimal TotalAmount { get; init; }
-
- ///
- /// 已支付金额(统计区间内)。
- ///
- public decimal PaidAmount { get; init; }
-
- ///
- /// 未支付金额(统计区间内)。
- ///
- public decimal UnpaidAmount { get; init; }
-
- ///
- /// 逾期金额(统计区间内)。
- ///
- public decimal OverdueAmount { get; init; }
-
- ///
- /// 总账单数量(统计区间内)。
- ///
- public int TotalCount { get; init; }
-
- ///
- /// 已支付账单数量(统计区间内)。
- ///
- public int PaidCount { get; init; }
-
- ///
- /// 未支付账单数量(统计区间内)。
- ///
- public int UnpaidCount { get; init; }
-
- ///
- /// 逾期账单数量(统计区间内)。
- ///
- public int OverdueCount { get; init; }
-
- ///
- /// 趋势数据(按 groupBy 聚合)。
- ///
- public IReadOnlyList TrendData { get; init; } = [];
-}
-
-///
-/// 账单趋势统计点。
-///
-public sealed record TenantBillingTrendDataPoint
-{
- ///
- /// 分组时间点(Day/Week/Month 的代表日期,UTC)。
- ///
- public DateTime Period { get; init; }
-
- ///
- /// 账单数量。
- ///
- public int Count { get; init; }
-
- ///
- /// 总金额。
- ///
- public decimal TotalAmount { get; init; }
-
- ///
- /// 已支付金额。
- ///
- public decimal PaidAmount { get; init; }
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantNotificationRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantNotificationRepository.cs
deleted file mode 100644
index 3f14c8b..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantNotificationRepository.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 租户通知仓储。
-///
-public interface ITenantNotificationRepository
-{
- ///
- /// 查询通知列表,按等级、未读状态与时间范围筛选。
- ///
- /// 租户 ID。
- /// 通知等级。
- /// 仅返回未读。
- /// 开始时间(UTC)。
- /// 结束时间(UTC)。
- /// 取消标记。
- /// 通知集合。
- Task> SearchAsync(
- long tenantId,
- TenantNotificationSeverity? severity,
- bool? unreadOnly,
- DateTime? from,
- DateTime? to,
- CancellationToken cancellationToken = default);
-
- ///
- /// 按 ID 获取通知。
- ///
- /// 租户 ID。
- /// 通知 ID。
- /// 取消标记。
- /// 通知实体或 null。
- Task FindByIdAsync(long tenantId, long notificationId, CancellationToken cancellationToken = default);
-
- ///
- /// 判断是否已发送过指定元数据的通知(用于幂等控制)。
- ///
- /// 租户 ID。
- /// 通知标题。
- /// 元数据 JSON(需与写入值完全一致)。
- /// 只在该时间之后发送的记录范围内判断(UTC)。
- /// 取消标记。
- /// 存在返回 true,否则 false。
- Task ExistsByMetadataAsync(
- long tenantId,
- string title,
- string metadataJson,
- DateTime sentAfter,
- CancellationToken cancellationToken = default);
-
- ///
- /// 新增通知。
- ///
- /// 通知实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAsync(TenantNotification notification, CancellationToken cancellationToken = default);
-
- ///
- /// 更新通知。
- ///
- /// 通知实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateAsync(TenantNotification notification, CancellationToken cancellationToken = default);
-
- ///
- /// 保存变更。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPackageRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPackageRepository.cs
deleted file mode 100644
index 5d051f7..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPackageRepository.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 租户套餐仓储。
-///
-public interface ITenantPackageRepository
-{
- ///
- /// 按套餐 ID 查询套餐。
- ///
- /// 套餐 ID(雪花算法)。
- /// 取消标记。
- /// 匹配的套餐实体,未找到返回 null。
- Task FindByIdAsync(long id, CancellationToken cancellationToken = default);
-
- ///
- /// 按关键词与启用状态搜索套餐。
- ///
- /// 名称或描述关键字,空则不按关键字过滤。
- /// 启用状态,空则不按状态过滤。
- /// 取消标记。
- /// 符合条件的套餐列表。
- Task> SearchAsync(string? keyword, bool? isActive, CancellationToken cancellationToken = default);
-
- ///
- /// 查询公共可选购套餐(仅返回:已发布 + 对外可见 + 允许新购 + 启用)。
- ///
- /// 取消标记。
- /// 公共可选购套餐列表。
- Task> SearchPublicPurchasableAsync(CancellationToken cancellationToken = default);
-
- ///
- /// 新增套餐。
- ///
- /// 套餐实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAsync(TenantPackage package, CancellationToken cancellationToken = default);
-
- ///
- /// 更新套餐。
- ///
- /// 套餐实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateAsync(TenantPackage package, CancellationToken cancellationToken = default);
-
- ///
- /// 删除套餐。
- ///
- /// 套餐 ID(雪花算法)。
- /// 取消标记。
- /// 异步任务。
- Task DeleteAsync(long id, CancellationToken cancellationToken = default);
-
- ///
- /// 持久化。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPaymentRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPaymentRepository.cs
deleted file mode 100644
index 2deaa91..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantPaymentRepository.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 租户支付记录仓储。
-///
-public interface ITenantPaymentRepository
-{
- ///
- /// 查询指定账单的支付记录列表。
- ///
- /// 账单 ID。
- /// 取消标记。
- /// 支付记录集合。
- Task> GetByBillingIdAsync(long billingStatementId, CancellationToken cancellationToken = default);
-
- ///
- /// 计算指定账单的累计已支付金额。
- ///
- /// 账单 ID。
- /// 取消标记。
- /// 累计已支付金额。
- Task GetTotalPaidAmountAsync(long billingStatementId, CancellationToken cancellationToken = default);
-
- ///
- /// 按 ID 获取支付记录。
- ///
- /// 支付记录 ID。
- /// 取消标记。
- /// 支付记录实体或 null。
- Task FindByIdAsync(long paymentId, CancellationToken cancellationToken = default);
-
- ///
- /// 按交易号获取支付记录。
- ///
- /// 交易号。
- /// 取消标记。
- /// 支付记录实体或 null。
- Task GetByTransactionNoAsync(string transactionNo, CancellationToken cancellationToken = default);
-
- ///
- /// 新增支付记录。
- ///
- /// 支付记录实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAsync(TenantPayment payment, CancellationToken cancellationToken = default);
-
- ///
- /// 更新支付记录。
- ///
- /// 支付记录实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateAsync(TenantPayment payment, CancellationToken cancellationToken = default);
-
- ///
- /// 保存变更。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageHistoryRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageHistoryRepository.cs
deleted file mode 100644
index 927f5d4..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageHistoryRepository.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 租户配额使用历史仓储。
-///
-public interface ITenantQuotaUsageHistoryRepository
-{
- ///
- /// 新增历史记录。
- ///
- /// 历史记录实体。
- /// 取消标记。
- Task AddAsync(TenantQuotaUsageHistory history, CancellationToken cancellationToken = default);
-
- ///
- /// 持久化。
- ///
- /// 取消标记。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
-
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageRepository.cs
deleted file mode 100644
index ff1b791..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantQuotaUsageRepository.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Domain.Tenants.Repositories;
-
-///
-/// 租户配额使用仓储。
-///
-public interface ITenantQuotaUsageRepository
-{
- ///
- /// 获取租户指定配额的使用情况。
- ///
- /// 租户 ID(雪花算法)。
- /// 配额类型。
- /// 取消标记。
- /// 配额使用记录,未初始化则返回 null。
- Task FindAsync(long tenantId, TenantQuotaType quotaType, CancellationToken cancellationToken = default);
-
- ///
- /// 按租户批量获取配额使用记录。
- ///
- /// 租户 ID(雪花算法)。
- /// 取消标记。
- /// 该租户的所有配额使用记录。
- Task> GetByTenantAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 新增配额使用记录。
- ///
- /// 配额使用实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAsync(TenantQuotaUsage usage, CancellationToken cancellationToken = default);
-
- ///
- /// 更新配额使用记录。
- ///
- /// 配额使用实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateAsync(TenantQuotaUsage usage, CancellationToken cancellationToken = default);
-
- ///
- /// 持久化。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs
index 74754f1..504da3a 100644
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs
+++ b/src/Domain/TakeoutSaaS.Domain/Tenants/Repositories/ITenantRepository.cs
@@ -1,10 +1,9 @@
using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
namespace TakeoutSaaS.Domain.Tenants.Repositories;
///
-/// 租户聚合仓储。
+/// 租户只读仓储(AdminApi 使用)。
///
public interface ITenantRepository
{
@@ -23,229 +22,4 @@ public interface ITenantRepository
/// 取消标记。
/// 租户列表(仅返回找到的租户)。
Task> FindByIdsAsync(IReadOnlyCollection tenantIds, CancellationToken cancellationToken = default);
-
- ///
- /// 按状态与关键词查询租户列表。
- ///
- /// 租户状态,为空不按状态过滤。
- /// 名称或编码关键字,为空不按关键字过滤。
- /// 取消标记。
- /// 符合条件的租户列表。
- Task> SearchAsync(
- TenantStatus? status,
- string? keyword,
- CancellationToken cancellationToken = default);
-
- ///
- /// 分页查询租户(支持多条件过滤)。
- ///
- /// 租户状态,为空不按状态过滤。
- /// 实名认证状态,为空不按认证状态过滤。
- /// 租户名称,为空不按名称过滤。
- /// 联系人姓名,为空不按联系人过滤。
- /// 联系电话,为空不按电话过滤。
- /// 兼容关键词:名称/编码/联系人/电话,为空不按关键字过滤。
- /// 页码(从 1 开始)。
- /// 每页大小。
- /// 取消标记。
- /// 分页数据与总数。
- Task<(IReadOnlyList Items, int Total)> SearchPagedAsync(
- TenantStatus? status,
- TenantVerificationStatus? verificationStatus,
- string? name,
- string? contactName,
- string? contactPhone,
- string? keyword,
- int page,
- int pageSize,
- CancellationToken cancellationToken = default);
-
- ///
- /// 新增租户。
- ///
- /// 租户实体。
- /// 取消标记。
- /// 异步任务。
- Task AddTenantAsync(Tenant tenant, CancellationToken cancellationToken = default);
-
- ///
- /// 更新租户。
- ///
- /// 租户实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateTenantAsync(Tenant tenant, CancellationToken cancellationToken = default);
-
- ///
- /// 判断编码是否存在。
- ///
- /// 租户编码。
- /// 取消标记。
- /// 存在返回 true,否则 false。
- Task ExistsByCodeAsync(string code, CancellationToken cancellationToken = default);
-
- ///
- /// 判断租户名称是否存在(支持排除指定租户)。
- ///
- /// 租户名称。
- /// 排除的租户 ID(用于更新场景)。
- /// 取消标记。
- /// 存在返回 true,否则 false。
- Task ExistsByNameAsync(string name, long? excludeTenantId = null, CancellationToken cancellationToken = default);
-
- ///
- /// 判断联系人手机号是否存在。
- ///
- /// 联系人手机号。
- /// 取消标记。
- /// 存在返回 true,否则 false。
- Task ExistsByContactPhoneAsync(string phone, CancellationToken cancellationToken = default);
-
- ///
- /// 依据联系人手机号查询租户 ID。
- ///
- /// 联系人手机号。
- /// 取消标记。
- /// 租户 ID,未找到返回 null。
- Task FindTenantIdByContactPhoneAsync(string phone, CancellationToken cancellationToken = default);
-
- ///
- /// 获取实名资料。
- ///
- /// 租户 ID(雪花算法)。
- /// 取消标记。
- /// 实名资料实体,未提交返回 null。
- Task GetVerificationProfileAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 批量获取实名资料。
- ///
- /// 租户 ID 列表。
- /// 取消标记。
- /// 实名资料列表(未提交的不返回)。
- Task> GetVerificationProfilesAsync(
- IReadOnlyCollection tenantIds,
- CancellationToken cancellationToken = default);
-
- ///
- /// 新增或更新实名资料。
- ///
- /// 实名资料实体。
- /// 取消标记。
- /// 异步任务。
- Task UpsertVerificationProfileAsync(TenantVerificationProfile profile, CancellationToken cancellationToken = default);
-
- ///
- /// 获取当前审核领取信息(仅返回未释放的记录)。
- ///
- /// 租户 ID(雪花算法)。
- /// 取消标记。
- /// 领取记录,未领取返回 null。
- Task GetActiveReviewClaimAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 查询当前审核领取信息(用于更新,返回可跟踪实体)。
- ///
- /// 租户 ID(雪花算法)。
- /// 取消标记。
- /// 领取记录,未领取返回 null。
- Task FindActiveReviewClaimAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 新增审核领取记录。
- ///
- /// 领取记录。
- /// 领取动作审计日志。
- /// 取消标记。
- /// 新增成功返回 true;若已被其他人领取导致冲突则返回 false。
- Task TryAddReviewClaimAsync(TenantReviewClaim claim, TenantAuditLog auditLog, CancellationToken cancellationToken = default);
-
- ///
- /// 更新审核领取记录。
- ///
- /// 领取记录。
- /// 取消标记。
- Task UpdateReviewClaimAsync(TenantReviewClaim claim, CancellationToken cancellationToken = default);
-
- ///
- /// 获取当前订阅。
- ///
- /// 租户 ID(雪花算法)。
- /// 取消标记。
- /// 当前有效订阅,若无则 null。
- Task GetActiveSubscriptionAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 批量获取当前订阅。
- ///
- /// 租户 ID 列表。
- /// 取消标记。
- /// 订阅列表(可能包含同一租户的多条订阅记录)。
- Task> GetSubscriptionsAsync(
- IReadOnlyCollection tenantIds,
- CancellationToken cancellationToken = default);
-
- ///
- /// 依据订阅 ID 查询。
- ///
- /// 租户 ID(雪花算法)。
- /// 订阅 ID(雪花算法)。
- /// 取消标记。
- /// 订阅实体,未找到返回 null。
- Task FindSubscriptionByIdAsync(long tenantId, long subscriptionId, CancellationToken cancellationToken = default);
-
- ///
- /// 新增订阅。
- ///
- /// 订阅实体。
- /// 取消标记。
- /// 异步任务。
- Task AddSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default);
-
- ///
- /// 更新订阅。
- ///
- /// 订阅实体。
- /// 取消标记。
- /// 异步任务。
- Task UpdateSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default);
-
- ///
- /// 记录订阅历史。
- ///
- /// 订阅历史实体。
- /// 取消标记。
- /// 异步任务。
- Task AddSubscriptionHistoryAsync(TenantSubscriptionHistory history, CancellationToken cancellationToken = default);
-
- ///
- /// 获取订阅历史。
- ///
- /// 租户 ID(雪花算法)。
- /// 取消标记。
- /// 订阅历史列表。
- Task> GetSubscriptionHistoryAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 新增审核日志。
- ///
- /// 审核日志实体。
- /// 取消标记。
- /// 异步任务。
- Task AddAuditLogAsync(TenantAuditLog log, CancellationToken cancellationToken = default);
-
- ///
- /// 查询审核日志。
- ///
- /// 租户 ID(雪花算法)。
- /// 取消标记。
- /// 审核日志列表。
- Task> GetAuditLogsAsync(long tenantId, CancellationToken cancellationToken = default);
-
- ///
- /// 持久化。
- ///
- /// 取消标记。
- /// 异步任务。
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Services/IBillingDomainService.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Services/IBillingDomainService.cs
deleted file mode 100644
index a1a0680..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Services/IBillingDomainService.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Services;
-
-///
-/// 账单领域服务接口。
-/// 负责处理账单生成、账单编号生成、逾期处理等跨实体的业务逻辑。
-///
-public interface IBillingDomainService
-{
- ///
- /// 根据订阅信息生成账单。
- ///
- /// 租户订阅信息。
- /// 取消标记。
- /// 生成的账单实体。
- Task GenerateSubscriptionBillingAsync(
- TenantSubscription subscription,
- CancellationToken cancellationToken = default);
-
- ///
- /// 根据配额包购买信息生成账单。
- ///
- /// 租户 ID。
- /// 配额包信息。
- /// 购买数量。
- /// 取消标记。
- /// 生成的账单实体。
- Task GenerateQuotaPurchaseBillingAsync(
- long tenantId,
- QuotaPackage quotaPackage,
- int quantity,
- CancellationToken cancellationToken = default);
-
- ///
- /// 生成唯一的账单编号。
- /// 格式示例:BIL-20251217-000001
- ///
- /// 账单编号。
- string GenerateStatementNo();
-
- ///
- /// 处理逾期账单(批量标记逾期状态)。
- ///
- /// 取消标记。
- /// 处理的账单数量。
- Task ProcessOverdueBillingsAsync(CancellationToken cancellationToken = default);
-
- ///
- /// 计算账单总金额(含折扣和税费)。
- ///
- /// 基础金额。
- /// 折扣金额。
- /// 税费金额。
- /// 总金额。
- decimal CalculateTotalAmount(decimal baseAmount, decimal discountAmount, decimal taxAmount);
-
- ///
- /// 验证账单状态是否可以进行支付操作。
- ///
- /// 账单实体。
- /// 是否可以支付。
- bool CanProcessPayment(TenantBillingStatement billing);
-}
diff --git a/src/Domain/TakeoutSaaS.Domain/Tenants/Services/IBillingExportService.cs b/src/Domain/TakeoutSaaS.Domain/Tenants/Services/IBillingExportService.cs
deleted file mode 100644
index a9882b1..0000000
--- a/src/Domain/TakeoutSaaS.Domain/Tenants/Services/IBillingExportService.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using TakeoutSaaS.Domain.Tenants.Entities;
-
-namespace TakeoutSaaS.Domain.Tenants.Services;
-
-///
-/// 账单导出服务接口。
-///
-public interface IBillingExportService
-{
- ///
- /// 导出为 Excel(XLSX)。
- ///
- /// 账单数据。
- /// 取消标记。
- /// 文件字节数组。
- Task ExportToExcelAsync(IReadOnlyList billings, CancellationToken cancellationToken = default);
-
- ///
- /// 导出为 PDF。
- ///
- /// 账单数据。
- /// 取消标记。
- /// 文件字节数组。
- Task ExportToPdfAsync(IReadOnlyList billings, CancellationToken cancellationToken = default);
-
- ///
- /// 导出为 CSV。
- ///
- /// 账单数据。
- /// 取消标记。
- /// 文件字节数组。
- Task ExportToCsvAsync(IReadOnlyList billings, CancellationToken cancellationToken = default);
-}
diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/Configurations/TenantBillingStatementConfiguration.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/Configurations/TenantBillingStatementConfiguration.cs
deleted file mode 100644
index d40970f..0000000
--- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/Configurations/TenantBillingStatementConfiguration.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Infrastructure.App.Persistence.Configurations;
-
-///
-/// EF Core 映射配置。
-///
-public sealed class TenantBillingStatementConfiguration : IEntityTypeConfiguration
-{
- ///
- public void Configure(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_billing_statements");
- builder.HasKey(x => x.Id);
-
- // 1. 字段约束
- builder.Property(x => x.StatementNo).HasMaxLength(64).IsRequired();
- builder.Property(x => x.BillingType).HasConversion();
- builder.Property(x => x.AmountDue).HasPrecision(18, 2);
- builder.Property(x => x.DiscountAmount).HasPrecision(18, 2);
- builder.Property(x => x.TaxAmount).HasPrecision(18, 2);
- builder.Property(x => x.AmountPaid).HasPrecision(18, 2);
- builder.Property(x => x.Currency).HasMaxLength(8).HasDefaultValue("CNY");
- builder.Property(x => x.Status).HasConversion();
-
- // 2. JSON 字段(当前以 text 存储 JSON 字符串,便于兼容历史迁移)
- builder.Property(x => x.LineItemsJson).HasColumnType("text");
-
- // 3. 备注字段
- builder.Property(x => x.Notes).HasMaxLength(512);
-
- // 4. 唯一约束与索引
- builder.HasIndex(x => new { x.TenantId, x.StatementNo }).IsUnique();
-
- // 5. 性能索引(高频查询:租户+状态+到期日)
- builder.HasIndex(x => new { x.TenantId, x.Status, x.DueDate })
- .HasDatabaseName("idx_billing_tenant_status_duedate");
-
- // 6. 逾期扫描索引(仅索引 Pending/Overdue)
- builder.HasIndex(x => new { x.Status, x.DueDate })
- .HasDatabaseName("idx_billing_status_duedate")
- .HasFilter($"\"Status\" IN ({(int)TenantBillingStatus.Pending}, {(int)TenantBillingStatus.Overdue})");
-
- // 7. 创建时间索引(支持列表倒序)
- builder.HasIndex(x => x.CreatedAt)
- .HasDatabaseName("idx_billing_created_at");
- }
-}
diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/Configurations/TenantPaymentConfiguration.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/Configurations/TenantPaymentConfiguration.cs
deleted file mode 100644
index 114a31c..0000000
--- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/Configurations/TenantPaymentConfiguration.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using TakeoutSaaS.Domain.Tenants.Entities;
-
-namespace TakeoutSaaS.Infrastructure.App.Persistence.Configurations;
-
-///
-/// EF Core 映射配置。
-///
-public sealed class TenantPaymentConfiguration : IEntityTypeConfiguration
-{
- ///
- public void Configure(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_payments");
- builder.HasKey(x => x.Id);
-
- // 1. 字段约束
- builder.Property(x => x.BillingStatementId).IsRequired();
- builder.Property(x => x.Amount).HasPrecision(18, 2).IsRequired();
- builder.Property(x => x.Method).HasConversion();
- builder.Property(x => x.Status).HasConversion();
- builder.Property(x => x.TransactionNo).HasMaxLength(64);
- builder.Property(x => x.ProofUrl).HasMaxLength(512);
- builder.Property(x => x.RefundReason).HasMaxLength(512);
- builder.Property(x => x.Notes).HasMaxLength(512);
-
- // 2. 复合索引:租户+账单
- builder.HasIndex(x => new { x.TenantId, x.BillingStatementId });
-
- // 3. 支付记录时间排序索引
- builder.HasIndex(x => new { x.BillingStatementId, x.PaidAt })
- .HasDatabaseName("idx_payment_billing_paidat");
-
- // 4. 交易号索引(部分索引:仅非空)
- builder.HasIndex(x => x.TransactionNo)
- .HasDatabaseName("idx_payment_transaction_no")
- .HasFilter("\"TransactionNo\" IS NOT NULL");
- }
-}
diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs
index 19e8506..bd04bc9 100644
--- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs
+++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Persistence/TakeoutAppDbContext.cs
@@ -20,11 +20,9 @@ using TakeoutSaaS.Domain.Reservations.Entities;
using TakeoutSaaS.Domain.Stores.Entities;
using TakeoutSaaS.Domain.Stores.Enums;
using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Infrastructure.Common.Persistence;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Security;
-using TakeoutSaaS.Infrastructure.App.Persistence.Configurations;
namespace TakeoutSaaS.Infrastructure.App.Persistence;
@@ -42,62 +40,6 @@ public class TakeoutAppDbContext(
///
public DbSet Tenants => Set();
///
- /// 租户套餐。
- ///
- public DbSet TenantPackages => Set();
- ///
- /// 租户订阅。
- ///
- public DbSet TenantSubscriptions => Set();
- ///
- /// 租户订阅历史。
- ///
- public DbSet TenantSubscriptionHistories => Set();
- ///
- /// 租户配额使用记录。
- ///
- public DbSet TenantQuotaUsages => Set();
- ///
- /// 租户配额使用历史记录。
- ///
- public DbSet TenantQuotaUsageHistories => Set();
- ///
- /// 租户账单。
- ///
- public DbSet TenantBillingStatements => Set();
- ///
- /// 租户支付记录。
- ///
- public DbSet TenantPayments => Set();
- ///
- /// 租户通知。
- ///
- public DbSet TenantNotifications => Set();
- ///
- /// 租户公告。
- ///
- public DbSet TenantAnnouncements => Set();
- ///
- /// 租户公告已读记录。
- ///
- public DbSet TenantAnnouncementReads => Set();
- ///
- /// 租户认证资料。
- ///
- public DbSet TenantVerificationProfiles => Set();
- ///
- /// 租户审核领取记录。
- ///
- public DbSet TenantReviewClaims => Set();
- ///
- /// 配额包定义。
- ///
- public DbSet QuotaPackages => Set();
- ///
- /// 租户配额包购买记录。
- ///
- public DbSet TenantQuotaPackagePurchases => Set();
- ///
/// 商户实体。
///
public DbSet Merchants => Set();
@@ -401,20 +343,6 @@ public class TakeoutAppDbContext(
ConfigureTenant(modelBuilder.Entity());
ConfigureMerchant(modelBuilder.Entity());
ConfigureStore(modelBuilder.Entity());
- ConfigureTenantPackage(modelBuilder.Entity());
- ConfigureTenantSubscription(modelBuilder.Entity());
- ConfigureTenantSubscriptionHistory(modelBuilder.Entity());
- ConfigureTenantQuotaUsage(modelBuilder.Entity());
- ConfigureTenantQuotaUsageHistory(modelBuilder.Entity());
- ConfigureTenantBilling(modelBuilder.Entity());
- ConfigureTenantPayment(modelBuilder.Entity());
- ConfigureTenantNotification(modelBuilder.Entity());
- ConfigureTenantAnnouncement(modelBuilder.Entity());
- ConfigureTenantAnnouncementRead(modelBuilder.Entity());
- ConfigureTenantVerificationProfile(modelBuilder.Entity());
- ConfigureTenantReviewClaim(modelBuilder.Entity());
- ConfigureQuotaPackage(modelBuilder.Entity());
- ConfigureTenantQuotaPackagePurchase(modelBuilder.Entity());
ConfigureMerchantDocument(modelBuilder.Entity());
ConfigureMerchantContract(modelBuilder.Entity());
ConfigureMerchantStaff(modelBuilder.Entity());
@@ -502,51 +430,6 @@ public class TakeoutAppDbContext(
builder.HasIndex(x => x.ContactPhone).IsUnique();
}
- private static void ConfigureTenantVerificationProfile(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_verification_profiles");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantId).IsRequired();
- builder.Property(x => x.BusinessLicenseNumber).HasMaxLength(64);
- builder.Property(x => x.BusinessLicenseUrl).HasMaxLength(512);
- builder.Property(x => x.LegalPersonName).HasMaxLength(64);
- builder.Property(x => x.LegalPersonIdNumber).HasMaxLength(32);
- builder.Property(x => x.LegalPersonIdFrontUrl).HasMaxLength(512);
- builder.Property(x => x.LegalPersonIdBackUrl).HasMaxLength(512);
- builder.Property(x => x.BankAccountName).HasMaxLength(128);
- builder.Property(x => x.BankAccountNumber).HasMaxLength(64);
- builder.Property(x => x.BankName).HasMaxLength(128);
- builder.Property(x => x.ReviewRemarks).HasMaxLength(512);
- builder.Property(x => x.ReviewedByName).HasMaxLength(64);
- builder.HasIndex(x => x.TenantId).IsUnique();
- }
-
- private static void ConfigureTenantReviewClaim(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_review_claims");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantId).IsRequired();
- builder.Property(x => x.ClaimedBy).IsRequired();
- builder.Property(x => x.ClaimedByName).HasMaxLength(64).IsRequired();
- builder.Property(x => x.ClaimedAt).IsRequired();
- builder.Property(x => x.ReleasedAt);
- builder.HasIndex(x => x.TenantId);
- builder.HasIndex(x => x.ClaimedBy);
- builder.HasIndex(x => x.TenantId).IsUnique().HasFilter("\"ReleasedAt\" IS NULL AND \"DeletedAt\" IS NULL");
- }
-
-
- private static void ConfigureTenantSubscriptionHistory(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_subscription_histories");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantId).IsRequired();
- builder.Property(x => x.TenantSubscriptionId).IsRequired();
- builder.Property(x => x.Notes).HasMaxLength(512);
- builder.Property(x => x.Currency).HasMaxLength(8);
- builder.HasIndex(x => new { x.TenantId, x.TenantSubscriptionId });
- }
-
private static void ConfigureMerchant(EntityTypeBuilder builder)
{
builder.ToTable("merchants");
@@ -774,141 +657,6 @@ public class TakeoutAppDbContext(
builder.HasIndex(x => new { x.TenantId, x.OrderId }).IsUnique();
}
- private static void ConfigureTenantPackage(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_packages");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
- builder.Property(x => x.Description).HasMaxLength(512);
- builder.Property(x => x.FeaturePoliciesJson).HasColumnType("text");
- builder.Property(x => x.PublishStatus)
- .HasConversion()
- .HasDefaultValue(TenantPackagePublishStatus.Draft)
- .HasSentinel((TenantPackagePublishStatus)(-1))
- .HasComment("发布状态:0=草稿,1=已发布。");
-
- // 1. 解决 EF Core 默认值哨兵问题:当我们希望插入 false/0 时,若数据库配置了 default 且 EF 认为该值是“未设置”,会导致 insert 省略列,最终落库为默认值。
- // 2. 发布状态使用 -1 作为哨兵,避免 Draft=0 被误判为“未设置”而触发数据库默认值(发布/草稿切换必须可控)。
- // 3. 将布尔开关哨兵值设置为数据库默认值:true 作为哨兵,false 才会被显式写入,从而保证“可见性/可售开关”在新增时可正确落库。
- builder.Property(x => x.IsPublicVisible)
- .HasDefaultValue(true)
- .HasSentinel(true)
- .HasComment("是否对外可见(展示页/套餐列表可见性)。");
- builder.Property(x => x.IsAllowNewTenantPurchase)
- .HasDefaultValue(true)
- .HasSentinel(true)
- .HasComment("是否允许新租户购买/选择(仅影响新购)。");
-
- // 4. 展示配置:推荐标识与标签(用于套餐展示页/对比页)
- builder.Property(x => x.IsRecommended)
- .HasDefaultValue(false)
- .HasComment("是否推荐展示(运营推荐标识)。");
- builder.Property(x => x.Tags)
- .HasColumnType("text[]")
- .HasComment("套餐标签(用于展示与对比页)。");
- builder.Property(x => x.SortOrder).HasDefaultValue(0).HasComment("展示排序,数值越小越靠前。");
- builder.HasIndex(x => new { x.IsActive, x.SortOrder });
- builder.HasIndex(x => new { x.PublishStatus, x.IsActive, x.IsPublicVisible, x.IsAllowNewTenantPurchase, x.SortOrder });
- }
-
- private static void ConfigureTenantSubscription(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_subscriptions");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantPackageId).IsRequired();
- builder.Property(x => x.Status).HasConversion();
- builder.HasIndex(x => new { x.TenantId, x.TenantPackageId });
- }
-
- private static void ConfigureTenantQuotaUsage(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_quota_usages");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.QuotaType).HasConversion();
- builder.HasIndex(x => new { x.TenantId, x.QuotaType }).IsUnique();
- }
-
- private static void ConfigureTenantQuotaUsageHistory(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_quota_usage_histories");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.QuotaType).HasConversion();
- builder.Property(x => x.ChangeType).HasConversion();
- builder.Property(x => x.ChangeReason).HasMaxLength(256);
-
- builder.HasIndex(x => new { x.TenantId, x.QuotaType, x.RecordedAt });
- builder.HasIndex(x => new { x.TenantId, x.RecordedAt });
- }
-
- private static void ConfigureTenantBilling(EntityTypeBuilder builder)
- {
- new TenantBillingStatementConfiguration().Configure(builder);
- }
-
- private static void ConfigureTenantPayment(EntityTypeBuilder builder)
- {
- new TenantPaymentConfiguration().Configure(builder);
- }
-
- private static void ConfigureTenantNotification(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_notifications");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
- builder.Property(x => x.Message).HasMaxLength(1024).IsRequired();
- builder.Property(x => x.Channel).HasConversion();
- builder.Property(x => x.Severity).HasConversion();
- builder.Property(x => x.MetadataJson).HasColumnType("text");
- builder.HasIndex(x => new { x.TenantId, x.Channel, x.SentAt });
- }
-
- private static void ConfigureTenantAnnouncement(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_announcements");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantId).IsRequired();
- builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
- builder.Property(x => x.Content).HasColumnType("text").IsRequired();
- builder.Property(x => x.AnnouncementType)
- .HasConversion();
- builder.Property(x => x.PublisherScope)
- .HasConversion();
- builder.Property(x => x.PublisherUserId);
- builder.Property(x => x.Status)
- .HasConversion();
- builder.Property(x => x.PublishedAt);
- builder.Property(x => x.RevokedAt);
- builder.Property(x => x.ScheduledPublishAt);
- builder.Property(x => x.TargetType).HasMaxLength(64).IsRequired();
- builder.Property(x => x.TargetParameters).HasColumnType("text");
- builder.Property(x => x.Priority).IsRequired();
- builder.Property("IsActive").IsRequired();
- builder.Property(x => x.RowVersion)
- .IsRowVersion()
- .IsConcurrencyToken()
- .HasColumnType("bytea");
- ConfigureAuditableEntity(builder);
- ConfigureSoftDeleteEntity(builder);
- builder.HasIndex("TenantId", "AnnouncementType", "IsActive");
- builder.HasIndex(x => new { x.TenantId, x.EffectiveFrom, x.EffectiveTo });
- builder.HasIndex(x => new { x.TenantId, x.Status, x.EffectiveFrom });
- builder.HasIndex(x => new { x.Status, x.EffectiveFrom })
- .HasFilter("\"TenantId\" = 0");
- }
-
- private static void ConfigureTenantAnnouncementRead(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_announcement_reads");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantId).IsRequired();
- builder.Property(x => x.AnnouncementId).IsRequired();
- builder.Property(x => x.UserId);
- builder.Property(x => x.ReadAt).IsRequired();
- ConfigureAuditableEntity(builder);
- ConfigureSoftDeleteEntity(builder);
- builder.HasIndex(x => new { x.TenantId, x.AnnouncementId, x.UserId }).IsUnique();
- }
-
private static void ConfigureMerchantDocument(EntityTypeBuilder builder)
{
builder.ToTable("merchant_documents");
@@ -1538,30 +1286,4 @@ public class TakeoutAppDbContext(
builder.HasIndex(x => new { x.TenantId, x.MetricDefinitionId, x.Severity });
}
- private static void ConfigureQuotaPackage(EntityTypeBuilder builder)
- {
- builder.ToTable("quota_packages");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
- builder.Property(x => x.QuotaType).HasConversion().IsRequired();
- builder.Property(x => x.QuotaValue).HasPrecision(18, 2).IsRequired();
- builder.Property(x => x.Price).HasPrecision(18, 2).IsRequired();
- builder.Property(x => x.IsActive).IsRequired();
- builder.Property(x => x.SortOrder).HasDefaultValue(0);
- builder.Property(x => x.Description).HasMaxLength(512);
- builder.HasIndex(x => new { x.QuotaType, x.IsActive, x.SortOrder });
- }
-
- private static void ConfigureTenantQuotaPackagePurchase(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_quota_package_purchases");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantId).IsRequired();
- builder.Property(x => x.QuotaPackageId).IsRequired();
- builder.Property(x => x.QuotaValue).HasPrecision(18, 2).IsRequired();
- builder.Property(x => x.Price).HasPrecision(18, 2).IsRequired();
- builder.Property(x => x.PurchasedAt).IsRequired();
- builder.Property(x => x.Notes).HasMaxLength(512);
- builder.HasIndex(x => new { x.TenantId, x.QuotaPackageId, x.PurchasedAt });
- }
}
diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs
index 307d80c..2c4f5a3 100644
--- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs
+++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantRepository.cs
@@ -1,21 +1,19 @@
using Microsoft.EntityFrameworkCore;
-using Npgsql;
using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Infrastructure.App.Persistence;
-using TakeoutSaaS.Infrastructure.Logs.Persistence;
namespace TakeoutSaaS.Infrastructure.App.Repositories;
///
-/// 租户聚合的 EF Core 仓储实现。
+/// 租户只读仓储实现(AdminApi 使用)。
///
-public sealed class EfTenantRepository(TakeoutAdminDbContext context, TakeoutLogsDbContext logsContext) : ITenantRepository
+public sealed class EfTenantRepository(TakeoutAdminDbContext context) : ITenantRepository
{
///
public Task FindByIdAsync(long tenantId, CancellationToken cancellationToken = default)
{
+ // 1. 只读查询租户(跨租户)
return context.Tenants
.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == tenantId, cancellationToken);
@@ -24,381 +22,16 @@ public sealed class EfTenantRepository(TakeoutAdminDbContext context, TakeoutLog
///
public async Task> FindByIdsAsync(IReadOnlyCollection tenantIds, CancellationToken cancellationToken = default)
{
+ // 1. tenantIds 为空直接返回
if (tenantIds.Count == 0)
{
return Array.Empty();
}
+ // 2. (空行后) 批量查询租户
return await context.Tenants
.AsNoTracking()
.Where(x => tenantIds.Contains(x.Id))
.ToListAsync(cancellationToken);
}
-
- ///
- public async Task> SearchAsync(
- TenantStatus? status,
- string? keyword,
- CancellationToken cancellationToken = default)
- {
- // 1. 构建基础查询
- var query = context.Tenants.AsNoTracking();
-
- // 2. 按状态过滤
- if (status.HasValue)
- {
- query = query.Where(x => x.Status == status.Value);
- }
-
- // 3. 按关键字过滤
- if (!string.IsNullOrWhiteSpace(keyword))
- {
- keyword = keyword.Trim();
- query = query.Where(x =>
- EF.Functions.ILike(x.Name, $"%{keyword}%") ||
- EF.Functions.ILike(x.Code, $"%{keyword}%") ||
- EF.Functions.ILike(x.ContactName ?? string.Empty, $"%{keyword}%") ||
- EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{keyword}%"));
- }
-
- // 4. 排序返回
- return await query
- .OrderByDescending(x => x.CreatedAt)
- .ToListAsync(cancellationToken);
- }
-
- ///
- public async Task<(IReadOnlyList Items, int Total)> SearchPagedAsync(
- TenantStatus? status,
- TenantVerificationStatus? verificationStatus,
- string? name,
- string? contactName,
- string? contactPhone,
- string? keyword,
- int page,
- int pageSize,
- CancellationToken cancellationToken = default)
- {
- var query = context.Tenants.AsNoTracking();
-
- // 1. 按租户状态过滤
- if (status.HasValue)
- {
- query = query.Where(x => x.Status == status.Value);
- }
-
- // 2. 按实名认证状态过滤(未提交视为 Draft)
- if (verificationStatus.HasValue)
- {
- query = from tenant in query
- join profile in context.TenantVerificationProfiles.AsNoTracking()
- on tenant.Id equals profile.TenantId into profiles
- from profile in profiles.DefaultIfEmpty()
- where (profile == null ? TenantVerificationStatus.Draft : profile.Status) == verificationStatus.Value
- select tenant;
- }
-
- // 3. 按名称/联系人/电话过滤(模糊匹配)
- if (!string.IsNullOrWhiteSpace(name))
- {
- var normalizedName = name.Trim();
- query = query.Where(x => EF.Functions.ILike(x.Name, $"%{normalizedName}%"));
- }
-
- // 4. 按联系人过滤(模糊匹配)
- if (!string.IsNullOrWhiteSpace(contactName))
- {
- var normalizedContactName = contactName.Trim();
- query = query.Where(x => EF.Functions.ILike(x.ContactName ?? string.Empty, $"%{normalizedContactName}%"));
- }
-
- // 5. 按联系电话过滤(模糊匹配)
- if (!string.IsNullOrWhiteSpace(contactPhone))
- {
- var normalizedContactPhone = contactPhone.Trim();
- query = query.Where(x => EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{normalizedContactPhone}%"));
- }
-
- // 6. 兼容关键字查询:名称/编码/联系人/电话
- if (!string.IsNullOrWhiteSpace(keyword))
- {
- var normalizedKeyword = keyword.Trim();
- query = query.Where(x =>
- EF.Functions.ILike(x.Name, $"%{normalizedKeyword}%") ||
- EF.Functions.ILike(x.Code, $"%{normalizedKeyword}%") ||
- EF.Functions.ILike(x.ContactName ?? string.Empty, $"%{normalizedKeyword}%") ||
- EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{normalizedKeyword}%"));
- }
-
- // 7. 先统计总数,再按创建时间倒序分页
- var total = await query.CountAsync(cancellationToken);
-
- // 8. 查询当前页数据
- var items = await query
- .OrderByDescending(x => x.CreatedAt)
- .Skip((page - 1) * pageSize)
- .Take(pageSize)
- .ToListAsync(cancellationToken);
-
- return (items, total);
- }
-
- ///
- public Task AddTenantAsync(Tenant tenant, CancellationToken cancellationToken = default)
- {
- return context.Tenants.AddAsync(tenant, cancellationToken).AsTask();
- }
-
- ///
- public Task UpdateTenantAsync(Tenant tenant, CancellationToken cancellationToken = default)
- {
- context.Tenants.Update(tenant);
- return Task.CompletedTask;
- }
-
- ///
- public Task ExistsByCodeAsync(string code, CancellationToken cancellationToken = default)
- {
- var normalized = code.Trim();
- return context.Tenants.AnyAsync(x => x.Code == normalized, cancellationToken);
- }
-
- ///
- public Task ExistsByNameAsync(string name, long? excludeTenantId = null, CancellationToken cancellationToken = default)
- {
- // 1. 标准化名称
- var normalized = name.Trim();
-
- // 2. 构建查询(名称使用 ILike 做不区分大小写的等值匹配)
- var query = context.Tenants
- .AsNoTracking()
- .Where(x => EF.Functions.ILike(x.Name, normalized));
-
- // 3. 更新场景排除自身
- if (excludeTenantId.HasValue)
- {
- query = query.Where(x => x.Id != excludeTenantId.Value);
- }
-
- // 4. 判断是否存在
- return query.AnyAsync(cancellationToken);
- }
-
- ///
- public Task ExistsByContactPhoneAsync(string phone, CancellationToken cancellationToken = default)
- {
- var normalized = phone.Trim();
- return context.Tenants.AnyAsync(x => x.ContactPhone == normalized, cancellationToken);
- }
-
- ///
- public Task FindTenantIdByContactPhoneAsync(string phone, CancellationToken cancellationToken = default)
- {
- // 1. 标准化手机号
- var normalized = phone.Trim();
- // 2. 查询租户 ID
- return context.Tenants.AsNoTracking()
- .Where(x => x.ContactPhone == normalized)
- .Select(x => (long?)x.Id)
- .FirstOrDefaultAsync(cancellationToken);
- }
-
- ///
- public Task GetVerificationProfileAsync(long tenantId, CancellationToken cancellationToken = default)
- {
- return context.TenantVerificationProfiles
- .IgnoreQueryFilters()
- .AsNoTracking()
- .FirstOrDefaultAsync(x => x.DeletedAt == null && x.TenantId == tenantId, cancellationToken);
- }
-
- ///
- public async Task> GetVerificationProfilesAsync(
- IReadOnlyCollection tenantIds,
- CancellationToken cancellationToken = default)
- {
- // 1. tenantIds 为空直接返回
- if (tenantIds.Count == 0)
- {
- return Array.Empty();
- }
-
- // 2. 批量查询实名资料
- return await context.TenantVerificationProfiles
- .IgnoreQueryFilters()
- .AsNoTracking()
- .Where(x => x.DeletedAt == null && tenantIds.Contains(x.TenantId))
- .ToListAsync(cancellationToken);
- }
-
- ///
- public async Task UpsertVerificationProfileAsync(TenantVerificationProfile profile, CancellationToken cancellationToken = default)
- {
- // 1. 查询现有实名资料
- var existing = await context.TenantVerificationProfiles
- .IgnoreQueryFilters()
- .FirstOrDefaultAsync(x => x.DeletedAt == null && x.TenantId == profile.TenantId, cancellationToken);
-
- if (existing == null)
- {
- // 2. 不存在则新增
- await context.TenantVerificationProfiles.AddAsync(profile, cancellationToken);
- return;
- }
-
- // 3. 存在则更新当前值
- profile.Id = existing.Id;
- context.Entry(existing).CurrentValues.SetValues(profile);
- }
-
- ///
- public Task GetActiveReviewClaimAsync(long tenantId, CancellationToken cancellationToken = default)
- {
- return context.TenantReviewClaims
- .AsNoTracking()
- .Where(x => x.TenantId == tenantId && x.ReleasedAt == null)
- .OrderByDescending(x => x.ClaimedAt)
- .FirstOrDefaultAsync(cancellationToken);
- }
-
- ///
- public Task FindActiveReviewClaimAsync(long tenantId, CancellationToken cancellationToken = default)
- {
- return context.TenantReviewClaims
- .Where(x => x.TenantId == tenantId && x.ReleasedAt == null)
- .OrderByDescending(x => x.ClaimedAt)
- .FirstOrDefaultAsync(cancellationToken);
- }
-
- ///
- public async Task TryAddReviewClaimAsync(
- TenantReviewClaim claim,
- TenantAuditLog auditLog,
- CancellationToken cancellationToken = default)
- {
- try
- {
- // 1. 写入领取记录
- await context.TenantReviewClaims.AddAsync(claim, cancellationToken);
- await context.SaveChangesAsync(cancellationToken);
-
- // 2. 写入审计日志
- await logsContext.TenantAuditLogs.AddAsync(auditLog, cancellationToken);
- await logsContext.SaveChangesAsync(cancellationToken);
- return true;
- }
- catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation)
- {
- // 1. 释放实体跟踪避免重复写入
- context.Entry(claim).State = EntityState.Detached;
-
- // 2. 返回抢占失败
- return false;
- }
- }
-
- ///
- public Task UpdateReviewClaimAsync(TenantReviewClaim claim, CancellationToken cancellationToken = default)
- {
- context.TenantReviewClaims.Update(claim);
- return Task.CompletedTask;
- }
-
- ///
- public Task GetActiveSubscriptionAsync(long tenantId, CancellationToken cancellationToken = default)
- {
- return context.TenantSubscriptions
- .IgnoreQueryFilters()
- .AsNoTracking()
- .Where(x => x.DeletedAt == null && x.TenantId == tenantId)
- .OrderByDescending(x => x.EffectiveTo)
- .FirstOrDefaultAsync(cancellationToken);
- }
-
- ///
- public async Task> GetSubscriptionsAsync(
- IReadOnlyCollection tenantIds,
- CancellationToken cancellationToken = default)
- {
- // 1. tenantIds 为空直接返回
- if (tenantIds.Count == 0)
- {
- return Array.Empty();
- }
-
- // 2. 批量查询订阅数据
- return await context.TenantSubscriptions
- .IgnoreQueryFilters()
- .AsNoTracking()
- .Where(x => x.DeletedAt == null && tenantIds.Contains(x.TenantId))
- .OrderByDescending(x => x.EffectiveTo)
- .ToListAsync(cancellationToken);
- }
-
- ///
- public Task FindSubscriptionByIdAsync(long tenantId, long subscriptionId, CancellationToken cancellationToken = default)
- {
- return context.TenantSubscriptions
- .IgnoreQueryFilters()
- .FirstOrDefaultAsync(
- x => x.DeletedAt == null && x.TenantId == tenantId && x.Id == subscriptionId,
- cancellationToken);
- }
-
- ///
- public Task AddSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default)
- {
- return context.TenantSubscriptions.AddAsync(subscription, cancellationToken).AsTask();
- }
-
- ///
- public Task UpdateSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default)
- {
- context.TenantSubscriptions.Update(subscription);
- return Task.CompletedTask;
- }
-
- ///
- public Task AddSubscriptionHistoryAsync(TenantSubscriptionHistory history, CancellationToken cancellationToken = default)
- {
- return context.TenantSubscriptionHistories.AddAsync(history, cancellationToken).AsTask();
- }
-
- ///
- public async Task> GetSubscriptionHistoryAsync(long tenantId, CancellationToken cancellationToken = default)
- {
- return await context.TenantSubscriptionHistories
- .IgnoreQueryFilters()
- .AsNoTracking()
- .Where(x => x.DeletedAt == null && x.TenantId == tenantId)
- .OrderByDescending(x => x.EffectiveFrom)
- .ToListAsync(cancellationToken);
- }
-
- ///
- public Task AddAuditLogAsync(TenantAuditLog log, CancellationToken cancellationToken = default)
- {
- return logsContext.TenantAuditLogs.AddAsync(log, cancellationToken).AsTask();
- }
-
- ///
- public async Task> GetAuditLogsAsync(long tenantId, CancellationToken cancellationToken = default)
- {
- return await logsContext.TenantAuditLogs
- .IgnoreQueryFilters()
- .AsNoTracking()
- .Where(x => x.DeletedAt == null && x.TenantId == tenantId)
- .OrderByDescending(x => x.CreatedAt)
- .ToListAsync(cancellationToken);
- }
-
- ///
- public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
- {
- // 1. 保存业务库变更
- await context.SaveChangesAsync(cancellationToken);
-
- // 2. 保存日志库变更
- await logsContext.SaveChangesAsync(cancellationToken);
- }
}
diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/Logs/Persistence/TakeoutLogsDbContext.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/Logs/Persistence/TakeoutLogsDbContext.cs
index 87909cd..59d13ab 100644
--- a/src/Infrastructure/TakeoutSaaS.Infrastructure/Logs/Persistence/TakeoutLogsDbContext.cs
+++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/Logs/Persistence/TakeoutLogsDbContext.cs
@@ -18,11 +18,6 @@ public sealed class TakeoutLogsDbContext(
IIdGenerator? idGenerator = null)
: AppDbContext(options, currentUserAccessor, idGenerator)
{
- ///
- /// 租户审计日志集合。
- ///
- public DbSet TenantAuditLogs => Set();
-
///
/// 商户审计日志集合。
///
@@ -55,7 +50,6 @@ public sealed class TakeoutLogsDbContext(
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
- ConfigureTenantAuditLog(modelBuilder.Entity());
ConfigureMerchantAuditLog(modelBuilder.Entity());
ConfigureMerchantChangeLog(modelBuilder.Entity());
ConfigureOperationLog(modelBuilder.Entity());
@@ -63,17 +57,6 @@ public sealed class TakeoutLogsDbContext(
ConfigureMemberGrowthLog(modelBuilder.Entity());
}
- private static void ConfigureTenantAuditLog(EntityTypeBuilder builder)
- {
- builder.ToTable("tenant_audit_logs");
- builder.HasKey(x => x.Id);
- builder.Property(x => x.TenantId).IsRequired();
- builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
- builder.Property(x => x.Description).HasMaxLength(1024);
- builder.Property(x => x.OperatorName).HasMaxLength(64);
- builder.HasIndex(x => x.TenantId);
- }
-
private static void ConfigureMerchantAuditLog(EntityTypeBuilder builder)
{
builder.ToTable("merchant_audit_logs");