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");