Files
CMS/src/CMSMicroservice.Infrastructure/Services/Commission/StoredProcedureCommissionCalculationStrategy.cs
T
masoodafar-web 4d6d77531d
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 15m3s
feat(commission): add per-package calculation support
- SP sp_CalculateWeeklyBalances: added @PackageId, @InputMaxBalancesPerLeg, @InputMaxNetworkLevel params; filters by PackageId
- SP sp_CalculateWeeklyCommissionPool: added @PackageId param; all queries filter by PackageId/WeeklyPoolId for isolation
- ICommissionCalculationStrategy: added optional PackageId param to both methods
- StoredProcedureCommissionCalculationStrategy: loops per-package for both Balance and Pool methods; filters by packageId if provided
- OrmCommissionCalculationStrategy: signature updated to match interface
- TriggerWeeklyCalculationCommand: added PackageId optional field
- TriggerWeeklyCalculationCommandHandler: passes PackageId to strategy
- GetWeeklyCommissionPoolQuery: added optional PackageId filter
- GetWeeklyCommissionPoolQueryHandler: filters pool by PackageId if provided
- GetAllWeeklyPoolsQuery/Handler/DTO: added PackageId filter + PackageTitle in response
- Proto commission.proto: added package_id to TriggerWeeklyCalculationRequest, GetWeeklyCommissionPoolRequest, WeeklyCommissionPoolModel, GetWeeklyCommissionPoolResponse, GetAllWeeklyPoolsRequest
- CommissionProfile: added explicit mappings for TriggerWeeklyCalculation, GetWeeklyCommissionPool, GetAllWeeklyPools

Fixes: SP picks wrong pool when multiple packages per week
Fixes: SP ignores PackageId on ForceRecalculate (now uses WeeklyPoolId)
Fixes: Zero-balance pools not fully recorded (now sets TotalBalances=0, ValuePerBalance=0)
2026-04-24 03:43:20 +03:30

183 lines
7.5 KiB
C#

using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace CMSMicroservice.Infrastructure.Services.Commission;
/// <summary>
/// پیاده‌سازی محاسبه کمیسیون با استفاده از Stored Procedure
/// این روش برای داده‌های زیاد بهینه‌تر است و از CTE استفاده می‌کند
/// حلقه روی پکیج‌های فعال — هر پکیج با تنظیمات خودش
/// </summary>
public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculationStrategy
{
private readonly IApplicationDbContext _context;
public StoredProcedureCommissionCalculationStrategy(IApplicationDbContext context)
{
_context = context;
}
/// <inheritdoc />
public async Task<int> CalculateWeeklyBalancesAsync(
long weekDefinitionId,
bool forceRecalculate,
long? packageId = null,
CancellationToken cancellationToken = default)
{
var dbContext = _context as DbContext;
if (dbContext == null)
{
throw new InvalidOperationException("DbContext برای اجرای Stored Procedure در دسترس نیست");
}
// بارگذاری پکیج‌های فعال — اگر packageId داده شده فقط همان پکیج
var packagesQuery = _context.Packages.Where(p => !p.IsDeleted && p.IsActive);
if (packageId.HasValue)
packagesQuery = packagesQuery.Where(p => p.Id == packageId.Value);
var activePackages = await packagesQuery.ToListAsync(cancellationToken);
if (!activePackages.Any())
{
throw new InvalidOperationException("هیچ پکیج فعالی یافت نشد");
}
var totalRowCount = 0;
var connection = dbContext.Database.GetDbConnection();
await connection.OpenAsync(cancellationToken);
try
{
// حلقه روی پکیج‌ها — هر پکیج با تنظیمات خودش
foreach (var package in activePackages)
{
using var command = connection.CreateCommand();
command.CommandText = "CMS.sp_CalculateWeeklyBalances";
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandTimeout = 300; // 5 دقیقه timeout
// پارامتر WeekDefinitionId
var paramWeekDefinitionId = command.CreateParameter();
paramWeekDefinitionId.ParameterName = "@WeekDefinitionId";
paramWeekDefinitionId.Value = weekDefinitionId;
command.Parameters.Add(paramWeekDefinitionId);
// پارامتر ForceRecalculate
var paramForceRecalculate = command.CreateParameter();
paramForceRecalculate.ParameterName = "@ForceRecalculate";
paramForceRecalculate.Value = forceRecalculate;
command.Parameters.Add(paramForceRecalculate);
// پارامتر PackageId — per-package
var paramPackageId = command.CreateParameter();
paramPackageId.ParameterName = "@PackageId";
paramPackageId.Value = package.Id;
command.Parameters.Add(paramPackageId);
// پارامتر MaxBalancesPerLeg — per-package
var paramMaxBalances = command.CreateParameter();
paramMaxBalances.ParameterName = "@InputMaxBalancesPerLeg";
paramMaxBalances.Value = package.MaxBalancesPerLeg;
command.Parameters.Add(paramMaxBalances);
// پارامتر MaxNetworkLevel — per-package
var paramMaxLevel = command.CreateParameter();
paramMaxLevel.ParameterName = "@InputMaxNetworkLevel";
paramMaxLevel.Value = package.MaxNetworkLevel;
command.Parameters.Add(paramMaxLevel);
// پارامتر خروجی
var paramRowCount = command.CreateParameter();
paramRowCount.ParameterName = "@RowCount";
paramRowCount.Direction = System.Data.ParameterDirection.Output;
paramRowCount.DbType = System.Data.DbType.Int32;
command.Parameters.Add(paramRowCount);
await command.ExecuteNonQueryAsync(cancellationToken);
var rowCount = (int)(paramRowCount.Value ?? 0);
totalRowCount += rowCount;
}
return totalRowCount;
}
finally
{
await connection.CloseAsync();
}
}
/// <inheritdoc />
public async Task<long> CalculateWeeklyCommissionPoolAsync(
long weekDefinitionId,
bool forceRecalculate,
long? packageId = null,
CancellationToken cancellationToken = default)
{
var dbContext = _context as DbContext;
if (dbContext == null)
{
throw new InvalidOperationException("DbContext برای اجرای Stored Procedure در دسترس نیست");
}
// بارگذاری پکیج‌های فعال — اگر packageId داده شده فقط همان پکیج
var packagesQuery = _context.Packages.Where(p => !p.IsDeleted && p.IsActive);
if (packageId.HasValue)
packagesQuery = packagesQuery.Where(p => p.Id == packageId.Value);
var activePackages = await packagesQuery.ToListAsync(cancellationToken);
if (!activePackages.Any())
{
throw new InvalidOperationException("هیچ پکیج فعالی یافت نشد");
}
var connection = dbContext.Database.GetDbConnection();
await connection.OpenAsync(cancellationToken);
long lastPoolId = 0;
try
{
// حلقه روی پکیج‌ها — هر پکیج Pool مستقل دارد
foreach (var package in activePackages)
{
using var command = connection.CreateCommand();
command.CommandText = "CMS.sp_CalculateWeeklyCommissionPool";
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandTimeout = 300;
var paramWeekDefinitionId = command.CreateParameter();
paramWeekDefinitionId.ParameterName = "@WeekDefinitionId";
paramWeekDefinitionId.Value = weekDefinitionId;
command.Parameters.Add(paramWeekDefinitionId);
var paramPackageId = command.CreateParameter();
paramPackageId.ParameterName = "@PackageId";
paramPackageId.Value = package.Id;
command.Parameters.Add(paramPackageId);
var paramForceRecalculate = command.CreateParameter();
paramForceRecalculate.ParameterName = "@ForceRecalculate";
paramForceRecalculate.Value = forceRecalculate;
command.Parameters.Add(paramForceRecalculate);
var paramPoolId = command.CreateParameter();
paramPoolId.ParameterName = "@PoolId";
paramPoolId.Direction = System.Data.ParameterDirection.Output;
paramPoolId.DbType = System.Data.DbType.Int64;
command.Parameters.Add(paramPoolId);
await command.ExecuteNonQueryAsync(cancellationToken);
lastPoolId = (long)(paramPoolId.Value ?? 0L);
}
return lastPoolId;
}
finally
{
await connection.CloseAsync();
}
}
}