Files
Deal/src/core/Deal.Modules.Discovery/Application/Services/DiscoveryPlanGuard.cs
T
Rustam Khalimov 492950bdd0 Отформатировать списки параметров по код-стайлу
Больше двух параметров — каждый на отдельной строке (закрывающая
скобка в конце последнего); два и меньше — в одну строку. Правило
добавлено в docs/spec/Код-стайл-Дейл.md; применено к 628 сигнатурам
в 253 файлах.
2026-09-11 13:22:56 +03:00

93 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Deal.Modules.Settings.Application.Abstractions;
using Deal.Modules.Settings.Application.Models;
using Deal.Modules.Settings.Application.Registrars;
using Deal.Modules.Settings.Application.Services;
using Deal.Modules.Discovery.Application.Abstractions;
using Deal.Modules.Discovery.Application.Exceptions;
using Deal.Modules.Discovery.Application.Extensions;
using Deal.Modules.Discovery.Application.Models;
using Deal.Modules.Discovery.Application.Registrars;
namespace Deal.Modules.Discovery.Application.Services;
/// <summary>
/// План-бюджет авто-вступлений Discovery: лимит и правило суммы (python L80111, Ruling 9).
/// </summary>
/// <remarks>
/// Правило 1:1 с discovery.py: суточный лимит discJoinLimit (настройка, дефолт 50 — SettingsDefaults) — верхняя
/// граница plan_joins одной задачи (<see cref="AssertPlanAsync"/>); сумма plan_joins активных задач (status NOT IN
/// done/failed) плюс новая/увеличиваемая задача не должна превышать лимит (<see cref="AssertBudgetAsync"/>).
/// Итог семантики: задача на 50 занимает весь бюджет (другую создать нельзя); задача на 25 оставляет остаток
/// 25 (следующая может быть не больше 25). Тексты 400 — 1:1 с python (L97110).
/// </remarks>
public sealed class DiscoveryPlanGuard(IDiscoveryStore store, ISettingsStore settings)
{
/// <summary>
/// 400: план меньше 1 (python L97 «plan_joins должен быть не меньше 1»).
/// </summary>
public const string PlanTooSmallDetail = "plan_joins должен быть не меньше 1";
/// <summary>
/// 400: план больше суточного лимита (python L100). Формат: {plan}, {limit}.
/// </summary>
public const string PlanTooBigFormat = "plan_joins {0} больше суточного лимита авто-вступлений ({1})";
/// <summary>
/// 400: бюджет исчерпан (python L108110). Формат: {used}, {limit}, {plan}.
/// </summary>
public const string BudgetExceededFormat =
"Бюджет авто-вступлений исчерпан: задачи уже занимают {0} из {1} в сутки, ещё {2} не влезает";
/// <summary>
/// Верхняя граница plan_joins: суточный лимит авто-вступлений (python _plan_limit L8082).
/// </summary>
/// <param name="ct">Токен отмены.</param>
/// <returns>discJoinLimit настройки (дефолт 50) с нижней границей 1.</returns>
public async Task<int> PlanLimitAsync(CancellationToken ct)
{
int limit = (await TenantSettingsSnapshot.LoadAsync(settings, ct))
.GetInt(SettingsKeys.DiscJoinLimit, SettingsDefaults.DiscJoinLimit);
return Math.Max(1, limit);
}
/// <summary>
/// Проверяет план новой задачи: 1..discJoinLimit (python _assert_plan L94100).
/// </summary>
/// <param name="planJoins">Запрашиваемый план авто-вступлений.</param>
/// <param name="ct">Токен отмены.</param>
/// <exception cref="DiscoveryValidationException">План меньше 1 или больше суточного лимита (тексты python).</exception>
public async Task AssertPlanAsync(int planJoins, CancellationToken ct)
{
if (planJoins < 1)
{
throw new DiscoveryValidationException(PlanTooSmallDetail);
}
int limit = await PlanLimitAsync(ct).ConfigureAwait(false);
if (planJoins > limit)
{
throw new DiscoveryValidationException(string.Format(PlanTooBigFormat, planJoins, limit));
}
}
/// <summary>
/// Проверяет бюджет: занято активными задачами + новая ≤ discJoinLimit (python _assert_budget L103111).
/// </summary>
/// <param name="planJoins">План создаваемой/итоговый план увеличиваемой задачи.</param>
/// <param name="excludeTaskId">Id задачи, исключаемой из занятого бюджета (patch-рост плана); null — создание.</param>
/// <param name="ct">Токен отмены.</param>
/// <exception cref="DiscoveryValidationException">Бюджет исчерпан (текст python L108110).</exception>
public async Task AssertBudgetAsync(
int planJoins,
string? excludeTaskId,
CancellationToken ct)
{
int limit = await PlanLimitAsync(ct).ConfigureAwait(false);
int used = await store.SumActivePlanAsync(excludeTaskId, ct).ConfigureAwait(false);
if (used + planJoins > limit)
{
throw new DiscoveryValidationException(string.Format(BudgetExceededFormat, used, limit, planJoins));
}
}
}