feat: implement commission calculation strategy with ORM and SP options
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m43s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m43s
This commit is contained in:
+350
@@ -0,0 +1,350 @@
|
||||
-- =============================================
|
||||
-- Stored Procedure: sp_CalculateWeeklyBalances
|
||||
-- Description: محاسبه تعادلهای هفتگی شبکه باینری با استفاده از CTE
|
||||
-- Author: System
|
||||
-- Created: 2025-12-19
|
||||
-- =============================================
|
||||
|
||||
CREATE OR ALTER PROCEDURE [CMS].[sp_CalculateWeeklyBalances]
|
||||
@WeekDefinitionId BIGINT,
|
||||
@ForceRecalculate BIT = 0,
|
||||
@RowCount INT OUTPUT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
DECLARE @ErrorMessage NVARCHAR(4000);
|
||||
DECLARE @StartDate DATETIME;
|
||||
DECLARE @EndDate DATETIME;
|
||||
DECLARE @PreviousWeekDefinitionId BIGINT;
|
||||
DECLARE @MaxBalancesPerLeg INT = 300;
|
||||
DECLARE @MaxNetworkLevel INT = 15;
|
||||
DECLARE @CalculatedAt DATETIME = GETDATE();
|
||||
|
||||
BEGIN TRY
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
-- =============================================
|
||||
-- 1. اعتبارسنجی WeekDefinitionId
|
||||
-- =============================================
|
||||
IF NOT EXISTS (SELECT 1 FROM CMS.WeekDefinitions WHERE Id = @WeekDefinitionId)
|
||||
BEGIN
|
||||
SET @ErrorMessage = N'هفته ' + CAST(@WeekDefinitionId AS NVARCHAR(20)) + N' در سیستم تعریف نشده است';
|
||||
RAISERROR(@ErrorMessage, 16, 1);
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- دریافت بازه تاریخی هفته
|
||||
SELECT @StartDate = StartDate, @EndDate = EndDate
|
||||
FROM CMS.WeekDefinitions
|
||||
WHERE Id = @WeekDefinitionId;
|
||||
|
||||
-- =============================================
|
||||
-- 2. بررسی محاسبه قبلی
|
||||
-- =============================================
|
||||
IF EXISTS (SELECT 1 FROM CMS.NetworkWeeklyBalances WHERE WeekDefinitionId = @WeekDefinitionId)
|
||||
BEGIN
|
||||
IF @ForceRecalculate = 0
|
||||
BEGIN
|
||||
SET @ErrorMessage = N'تعادلهای هفته ' + CAST(@WeekDefinitionId AS NVARCHAR(20)) + N' قبلاً محاسبه شده است. برای محاسبه مجدد از ForceRecalculate استفاده کنید';
|
||||
RAISERROR(@ErrorMessage, 16, 1);
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- حذف محاسبات قبلی
|
||||
DELETE FROM CMS.NetworkWeeklyBalances WHERE WeekDefinitionId = @WeekDefinitionId;
|
||||
END
|
||||
|
||||
-- =============================================
|
||||
-- 3. پیدا کردن هفته قبل
|
||||
-- =============================================
|
||||
SELECT TOP 1 @PreviousWeekDefinitionId = Id
|
||||
FROM CMS.WeekDefinitions
|
||||
WHERE WeekOrder = (SELECT WeekOrder - 1 FROM CMS.WeekDefinitions WHERE Id = @WeekDefinitionId)
|
||||
AND IsActive = 1;
|
||||
|
||||
-- =============================================
|
||||
-- 4. خواندن Configuration ها
|
||||
-- =============================================
|
||||
SELECT @MaxBalancesPerLeg = CAST(Value AS INT)
|
||||
FROM CMS.SystemConfigurations
|
||||
WHERE [Key] = 'Commission.MaxWeeklyBalancesPerLeg' AND IsActive = 1;
|
||||
|
||||
SELECT @MaxNetworkLevel = CAST(Value AS INT)
|
||||
FROM CMS.SystemConfigurations
|
||||
WHERE [Key] = 'Commission.MaxNetworkLevel' AND IsActive = 1;
|
||||
|
||||
-- مقادیر پیشفرض
|
||||
SET @MaxBalancesPerLeg = ISNULL(@MaxBalancesPerLeg, 300);
|
||||
SET @MaxNetworkLevel = ISNULL(@MaxNetworkLevel, 15);
|
||||
|
||||
-- =============================================
|
||||
-- 5. ایجاد جدول موقت برای نتایج
|
||||
-- =============================================
|
||||
CREATE TABLE #Balances (
|
||||
UserId BIGINT PRIMARY KEY,
|
||||
LeftLegNewMembers INT DEFAULT 0,
|
||||
RightLegNewMembers INT DEFAULT 0,
|
||||
LeftLegCarryover INT DEFAULT 0,
|
||||
RightLegCarryover INT DEFAULT 0,
|
||||
LeftLegTotal INT DEFAULT 0,
|
||||
RightLegTotal INT DEFAULT 0,
|
||||
TotalBalances INT DEFAULT 0,
|
||||
LeftLegRemainder INT DEFAULT 0,
|
||||
RightLegRemainder INT DEFAULT 0,
|
||||
FlushedPerSide INT DEFAULT 0,
|
||||
TotalFlushed INT DEFAULT 0,
|
||||
SubordinateBalances INT DEFAULT 0
|
||||
);
|
||||
|
||||
-- =============================================
|
||||
-- 6. دریافت کاربران فعال باشگاه
|
||||
-- =============================================
|
||||
INSERT INTO #Balances (UserId)
|
||||
SELECT DISTINCT u.Id
|
||||
FROM CMS.Users u
|
||||
INNER JOIN CMS.ClubMemberships cm ON cm.UserId = u.Id AND cm.IsActive = 1;
|
||||
|
||||
-- =============================================
|
||||
-- 7. دریافت باقیمانده هفته قبل
|
||||
-- =============================================
|
||||
IF @PreviousWeekDefinitionId IS NOT NULL
|
||||
BEGIN
|
||||
UPDATE b
|
||||
SET b.LeftLegCarryover = ISNULL(nb.LeftLegRemainder, 0),
|
||||
b.RightLegCarryover = ISNULL(nb.RightLegRemainder, 0)
|
||||
FROM #Balances b
|
||||
LEFT JOIN CMS.NetworkWeeklyBalances nb ON nb.UserId = b.UserId AND nb.WeekDefinitionId = @PreviousWeekDefinitionId;
|
||||
END
|
||||
|
||||
-- =============================================
|
||||
-- 8. محاسبه اعضای جدید هر پا با CTE (بهینه!)
|
||||
-- =============================================
|
||||
|
||||
-- CTE برای شمارش اعضای جدید شاخه چپ
|
||||
;WITH LeftLegCTE AS (
|
||||
-- فرزند مستقیم چپ (لول 1)
|
||||
SELECT
|
||||
u.NetworkParentId AS ParentId,
|
||||
u.Id AS ChildId,
|
||||
1 AS Level
|
||||
FROM CMS.Users u
|
||||
WHERE u.LegPosition = 0 -- Left
|
||||
AND u.NetworkParentId IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- زیرمجموعههای بازگشتی
|
||||
SELECT
|
||||
cte.ParentId,
|
||||
u.Id AS ChildId,
|
||||
cte.Level + 1 AS Level
|
||||
FROM CMS.Users u
|
||||
INNER JOIN LeftLegCTE cte ON u.NetworkParentId = cte.ChildId
|
||||
WHERE cte.Level < @MaxNetworkLevel
|
||||
),
|
||||
LeftNewMembers AS (
|
||||
SELECT
|
||||
cte.ParentId AS UserId,
|
||||
COUNT(*) AS NewMembersCount
|
||||
FROM LeftLegCTE cte
|
||||
INNER JOIN CMS.ClubMemberships cm ON cm.UserId = cte.ChildId AND cm.IsActive = 1
|
||||
WHERE cm.ActivatedAt >= @StartDate AND cm.ActivatedAt <= @EndDate
|
||||
GROUP BY cte.ParentId
|
||||
)
|
||||
UPDATE b
|
||||
SET b.LeftLegNewMembers = ISNULL(lnm.NewMembersCount, 0)
|
||||
FROM #Balances b
|
||||
LEFT JOIN LeftNewMembers lnm ON lnm.UserId = b.UserId
|
||||
OPTION (MAXRECURSION 100);
|
||||
|
||||
-- CTE برای شمارش اعضای جدید شاخه راست
|
||||
;WITH RightLegCTE AS (
|
||||
-- فرزند مستقیم راست (لول 1)
|
||||
SELECT
|
||||
u.NetworkParentId AS ParentId,
|
||||
u.Id AS ChildId,
|
||||
1 AS Level
|
||||
FROM CMS.Users u
|
||||
WHERE u.LegPosition = 1 -- Right
|
||||
AND u.NetworkParentId IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- زیرمجموعههای بازگشتی
|
||||
SELECT
|
||||
cte.ParentId,
|
||||
u.Id AS ChildId,
|
||||
cte.Level + 1 AS Level
|
||||
FROM CMS.Users u
|
||||
INNER JOIN RightLegCTE cte ON u.NetworkParentId = cte.ChildId
|
||||
WHERE cte.Level < @MaxNetworkLevel
|
||||
),
|
||||
RightNewMembers AS (
|
||||
SELECT
|
||||
cte.ParentId AS UserId,
|
||||
COUNT(*) AS NewMembersCount
|
||||
FROM RightLegCTE cte
|
||||
INNER JOIN CMS.ClubMemberships cm ON cm.UserId = cte.ChildId AND cm.IsActive = 1
|
||||
WHERE cm.ActivatedAt >= @StartDate AND cm.ActivatedAt <= @EndDate
|
||||
GROUP BY cte.ParentId
|
||||
)
|
||||
UPDATE b
|
||||
SET b.RightLegNewMembers = ISNULL(rnm.NewMembersCount, 0)
|
||||
FROM #Balances b
|
||||
LEFT JOIN RightNewMembers rnm ON rnm.UserId = b.UserId
|
||||
OPTION (MAXRECURSION 100);
|
||||
|
||||
-- =============================================
|
||||
-- 9. محاسبه تعادلها
|
||||
-- =============================================
|
||||
UPDATE #Balances
|
||||
SET
|
||||
-- مجموع هر پا
|
||||
LeftLegTotal = LeftLegNewMembers + LeftLegCarryover,
|
||||
RightLegTotal = RightLegNewMembers + RightLegCarryover;
|
||||
|
||||
UPDATE #Balances
|
||||
SET
|
||||
-- تعادل = MIN(چپ، راست)
|
||||
TotalBalances = CASE
|
||||
WHEN LeftLegTotal <= RightLegTotal THEN LeftLegTotal
|
||||
ELSE RightLegTotal
|
||||
END;
|
||||
|
||||
UPDATE #Balances
|
||||
SET
|
||||
-- باقیمانده برای هفته بعد
|
||||
LeftLegRemainder = LeftLegTotal - TotalBalances,
|
||||
RightLegRemainder = RightLegTotal - TotalBalances,
|
||||
-- فلش (اگر تعادل > سقف)
|
||||
FlushedPerSide = CASE
|
||||
WHEN TotalBalances > @MaxBalancesPerLeg THEN TotalBalances - @MaxBalancesPerLeg
|
||||
ELSE 0
|
||||
END;
|
||||
|
||||
UPDATE #Balances
|
||||
SET
|
||||
-- اعمال سقف
|
||||
TotalBalances = CASE
|
||||
WHEN TotalBalances > @MaxBalancesPerLeg THEN @MaxBalancesPerLeg
|
||||
ELSE TotalBalances
|
||||
END,
|
||||
-- مجموع فلش از دو طرف
|
||||
TotalFlushed = FlushedPerSide * 2;
|
||||
|
||||
-- =============================================
|
||||
-- 10. محاسبه تعادل زیرمجموعه با CTE
|
||||
-- =============================================
|
||||
;WITH SubordinateCTE AS (
|
||||
-- فرزندان مستقیم (لول 1)
|
||||
SELECT
|
||||
u.NetworkParentId AS ParentId,
|
||||
u.Id AS ChildId,
|
||||
1 AS Level
|
||||
FROM CMS.Users u
|
||||
WHERE u.NetworkParentId IS NOT NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- زیرمجموعههای بازگشتی
|
||||
SELECT
|
||||
cte.ParentId,
|
||||
u.Id AS ChildId,
|
||||
cte.Level + 1 AS Level
|
||||
FROM CMS.Users u
|
||||
INNER JOIN SubordinateCTE cte ON u.NetworkParentId = cte.ChildId
|
||||
WHERE cte.Level < @MaxNetworkLevel
|
||||
),
|
||||
SubordinateBalances AS (
|
||||
SELECT
|
||||
cte.ParentId AS UserId,
|
||||
SUM(b.TotalBalances) AS TotalSubordinateBalances
|
||||
FROM SubordinateCTE cte
|
||||
INNER JOIN #Balances b ON b.UserId = cte.ChildId
|
||||
GROUP BY cte.ParentId
|
||||
)
|
||||
UPDATE b
|
||||
SET b.SubordinateBalances = ISNULL(sb.TotalSubordinateBalances, 0)
|
||||
FROM #Balances b
|
||||
LEFT JOIN SubordinateBalances sb ON sb.UserId = b.UserId
|
||||
OPTION (MAXRECURSION 100);
|
||||
|
||||
-- =============================================
|
||||
-- 11. Insert نتایج در جدول اصلی
|
||||
-- =============================================
|
||||
INSERT INTO CMS.NetworkWeeklyBalances (
|
||||
UserId,
|
||||
WeekDefinitionId,
|
||||
LeftLegNewMembers,
|
||||
RightLegNewMembers,
|
||||
LeftLegCarryover,
|
||||
RightLegCarryover,
|
||||
LeftLegTotal,
|
||||
RightLegTotal,
|
||||
TotalBalances,
|
||||
LeftLegRemainder,
|
||||
RightLegRemainder,
|
||||
FlushedPerSide,
|
||||
TotalFlushed,
|
||||
SubordinateBalances,
|
||||
LeftLegBalances,
|
||||
RightLegBalances,
|
||||
WeeklyPoolContribution,
|
||||
CalculatedAt,
|
||||
IsExpired,
|
||||
IsDeleted,
|
||||
Created,
|
||||
CreatedBy,
|
||||
LastModified,
|
||||
LastModifiedBy
|
||||
)
|
||||
SELECT
|
||||
UserId,
|
||||
@WeekDefinitionId,
|
||||
LeftLegNewMembers,
|
||||
RightLegNewMembers,
|
||||
LeftLegCarryover,
|
||||
RightLegCarryover,
|
||||
LeftLegTotal,
|
||||
RightLegTotal,
|
||||
TotalBalances,
|
||||
LeftLegRemainder,
|
||||
RightLegRemainder,
|
||||
FlushedPerSide,
|
||||
TotalFlushed,
|
||||
SubordinateBalances,
|
||||
LeftLegTotal, -- LeftLegBalances (deprecated)
|
||||
RightLegTotal, -- RightLegBalances (deprecated)
|
||||
0, -- WeeklyPoolContribution
|
||||
@CalculatedAt,
|
||||
0, -- IsExpired
|
||||
0, -- IsDeleted
|
||||
@CalculatedAt, -- Created
|
||||
'SP', -- CreatedBy
|
||||
@CalculatedAt, -- LastModified
|
||||
'SP' -- LastModifiedBy
|
||||
FROM #Balances;
|
||||
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
|
||||
-- پاکسازی
|
||||
DROP TABLE #Balances;
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0
|
||||
ROLLBACK TRANSACTION;
|
||||
|
||||
-- پاکسازی در صورت خطا
|
||||
IF OBJECT_ID('tempdb..#Balances') IS NOT NULL
|
||||
DROP TABLE #Balances;
|
||||
|
||||
SET @ErrorMessage = ERROR_MESSAGE();
|
||||
RAISERROR(@ErrorMessage, 16, 1);
|
||||
END CATCH
|
||||
END
|
||||
GO
|
||||
Reference in New Issue
Block a user