Разбить модули Deal.Modules.* по назначению
Application проектов Discovery, Kanban, Pipeline, Settings, Tenants разделён на Abstractions/Exceptions/Extensions/Models/Registrars/Services; namespace приведён к путям, using потребителей мигрированы и дедуплицированы (169 файлов), cref/FQN обновлены.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
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 L80–111, 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 (L97–110).
|
||||
/// </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 L108–110). Формат: {used}, {limit}, {plan}.
|
||||
/// </summary>
|
||||
public const string BudgetExceededFormat =
|
||||
"Бюджет авто-вступлений исчерпан: задачи уже занимают {0} из {1} в сутки, ещё {2} не влезает";
|
||||
|
||||
/// <summary>
|
||||
/// Верхняя граница plan_joins: суточный лимит авто-вступлений (python _plan_limit L80–82).
|
||||
/// </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 L94–100).
|
||||
/// </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 L103–111).
|
||||
/// </summary>
|
||||
/// <param name="planJoins">План создаваемой/итоговый план увеличиваемой задачи.</param>
|
||||
/// <param name="excludeTaskId">Id задачи, исключаемой из занятого бюджета (patch-рост плана); null — создание.</param>
|
||||
/// <param name="ct">Токен отмены.</param>
|
||||
/// <exception cref="DiscoveryValidationException">Бюджет исчерпан (текст python L108–110).</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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user