From 7ff66cd8e787f4638cdf9db4e1083c7ddda76d73 Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Mon, 15 Dec 2025 21:29:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A5=97=E9=A4=90=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E5=A2=9E=E5=8A=A0MRR=E4=B8=8EARR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Tenants/Dto/TenantPackageUsageDto.cs | 11 +++++- .../GetTenantPackageUsagesQueryHandler.cs | 37 +++++++++++++------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Dto/TenantPackageUsageDto.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Dto/TenantPackageUsageDto.cs index d1e3398..c613598 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Dto/TenantPackageUsageDto.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Dto/TenantPackageUsageDto.cs @@ -24,5 +24,14 @@ public sealed class TenantPackageUsageDto /// 历史总订阅记录数量(不含软删)。 /// public int TotalSubscriptionCount { get; init; } -} + /// + /// MRR(Monthly Recurring Revenue)粗看:按“当前有效订阅数 × 套餐月付等效价”估算。 + /// + public decimal Mrr { get; init; } + + /// + /// ARR(Annual Recurring Revenue)粗看:按“当前有效订阅数 × 套餐年付等效价”估算。 + /// + public decimal Arr { get; init; } +} diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageUsagesQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageUsagesQueryHandler.cs index e3aa0cb..0eb2750 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageUsagesQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/GetTenantPackageUsagesQueryHandler.cs @@ -45,7 +45,9 @@ public sealed class GetTenantPackageUsagesQueryHandler(IDapperExecutor dapperExe TenantPackageId = reader.GetInt64(0), ActiveSubscriptionCount = reader.GetInt32(1), ActiveTenantCount = reader.GetInt32(2), - TotalSubscriptionCount = reader.GetInt32(3) + TotalSubscriptionCount = reader.GetInt32(3), + Mrr = reader.IsDBNull(4) ? 0m : reader.GetDecimal(4), + Arr = reader.IsDBNull(5) ? 0m : reader.GetDecimal(5) }); } @@ -56,16 +58,17 @@ public sealed class GetTenantPackageUsagesQueryHandler(IDapperExecutor dapperExe private static string BuildSql(long[]? ids, out (string Name, object? Value)[] parameters, DateTime now) { - // 1. 基础查询 + // 1. 基础查询:先按订阅表聚合,再回连套餐表计算 MRR/ARR var builder = new System.Text.StringBuilder(); builder.AppendLine(""" - select - "TenantPackageId" as "TenantPackageId", - count(*) filter (where "Status" = 1 and "EffectiveFrom" <= @now and "EffectiveTo" >= @now) as "ActiveSubscriptionCount", - count(distinct "TenantId") filter (where "Status" = 1 and "EffectiveFrom" <= @now and "EffectiveTo" >= @now) as "ActiveTenantCount", - count(*) as "TotalSubscriptionCount" - from public.tenant_subscriptions - where "DeletedAt" is null + with stats as ( + select + "TenantPackageId" as "TenantPackageId", + count(*) filter (where "Status" = 1 and "EffectiveFrom" <= @now and "EffectiveTo" >= @now) as "ActiveSubscriptionCount", + count(distinct "TenantId") filter (where "Status" = 1 and "EffectiveFrom" <= @now and "EffectiveTo" >= @now) as "ActiveTenantCount", + count(*) as "TotalSubscriptionCount" + from public.tenant_subscriptions + where "DeletedAt" is null """); var list = new List<(string Name, object? Value)> @@ -92,8 +95,20 @@ public sealed class GetTenantPackageUsagesQueryHandler(IDapperExecutor dapperExe builder.AppendLine(")"); } - // 3. (空行后) 分组 - builder.AppendLine("group by \"TenantPackageId\";"); + // 3. (空行后) 分组与回连套餐表 + builder.AppendLine(""" + group by "TenantPackageId" + ) + select + s."TenantPackageId" as "TenantPackageId", + s."ActiveSubscriptionCount" as "ActiveSubscriptionCount", + s."ActiveTenantCount" as "ActiveTenantCount", + s."TotalSubscriptionCount" as "TotalSubscriptionCount", + (s."ActiveSubscriptionCount"::numeric * coalesce(p."MonthlyPrice", (p."YearlyPrice" / 12.0), 0))::numeric(18, 2) as "Mrr", + (s."ActiveSubscriptionCount"::numeric * coalesce(p."YearlyPrice", (p."MonthlyPrice" * 12), 0))::numeric(18, 2) as "Arr" + from stats s + left join public.tenant_packages p on p."Id" = s."TenantPackageId" and p."DeletedAt" is null; + """); parameters = list.ToArray(); return builder.ToString();