Files
Deal/src/core/Deal.Modules.Pipeline/Application/PipelineWorkerService.Settings.cs
T
Rustam Khalimov 9e07568ddd Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы
ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ,
инструкция пользователя, техдокументация, код-стайл), бэклог,
скрипты развёртывания и архив прототипа LeadRadar.
2026-09-11 02:50:17 +03:00

97 lines
6.6 KiB
C#

using System.Globalization;
using System.Text.Json;
using Deal.Contracts.Integrations;
using Deal.Contracts.Integrations.Models;
using Deal.Modules.Kanban.Application;
using Deal.Modules.Kanban.Application.ColumnRules;
using Deal.Modules.Kanban.Application.Models;
using Deal.Modules.Pipeline.Application.Models;
using Deal.Modules.Pipeline.Application.Parse;
using Deal.Modules.Settings.Application;
using Deal.Modules.Settings.Application.Models;
namespace Deal.Modules.Pipeline.Application;
/// <summary>
/// Снимок настроек прохода — partial-часть <see cref="PipelineWorkerService"/> (C32: выделено из общего
/// файла, поведение не менялось): WorkerRunSettings + один GetAllAsync на pump и резолв дефолтов.
/// </summary>
public sealed partial class PipelineWorkerService
{
// ── Снимок настроек на проход pump (Ruling 8/C30: один GetAllAsync вместо чтений на каждое сообщение) ──
// Снимок настроек воркера на один проход pump: значения прочитаны ОДИН раз (LoadRunSettingsAsync)
// и передаются во все проверки сообщений батча и в LocalFieldsParser — раньше настройки читались на каждое
// сообщение (ReadBool/ReadInt + GetAllAsync парсера — до 16 чтений таблицы за pump). Значения — из
// типизированного снимка TenantSettingsSnapshot (C30).
// Settings: Типизированный снимок переопределений тенанта (передаётся LocalFieldsParser).
// AutoArchive: Флаг автоархива (срок актуальности сообщения).
// ArchiveAfterDays: Срок до автоархива, сутки (stale/причина отсева).
// MlEnabled: ML-слот включён (не false — прогноз/решения).
// AiEnabled: ИИ-слот включён (классификация/фильтр).
// AiFilterEnabled: ИИ-фильтр включён (этап 2).
// BudgetRequiredHire: Фильтр «без суммы» для найма.
// BudgetRequiredOrder: Фильтр «без суммы» для разовых заказов.
// WantedType: Тип заявок «что собираем» (both|vacancy|freelance).
// GlobalExclusions: Снимок глобальных исключений (стоп до ML/ИИ, §5.14).
private sealed record WorkerRunSettings(
TenantSettingsSnapshot Settings,
bool AutoArchive,
int ArchiveAfterDays,
bool MlEnabled,
bool AiEnabled,
bool AiFilterEnabled,
bool BudgetRequiredHire,
bool BudgetRequiredOrder,
string WantedType,
GlobalExcludeSettings GlobalExclusions);
// Читает настройки воркера одним запросом (GetAllAsync) и резолвит значения с дефолтами
// SettingsDefaults через типизированный снимок (C30).
// settingsStore: KV-хранилище настроек тенанта.
// ct: Токен отмены.
// Возвращает: Снимок на проход pump (один на PumpOnce, передаётся в проверки обоих проходов).
private static async Task<WorkerRunSettings> LoadRunSettingsAsync(ISettingsStore settingsStore, CancellationToken ct)
{
TenantSettingsSnapshot settings = await TenantSettingsSnapshot.LoadAsync(settingsStore, ct);
return new WorkerRunSettings(
settings,
settings.GetBool(SettingsKeys.AutoArchive, SettingsDefaults.AutoArchive),
settings.GetInt(SettingsKeys.ArchiveAfterDays, SettingsDefaults.ArchiveAfterDays),
settings.GetBool(SettingsKeys.MlEnabled, SettingsDefaults.MlEnabled),
settings.GetBool(SettingsKeys.AiEnabled, SettingsDefaults.AiEnabled),
settings.GetBool(SettingsKeys.AiFilterEnabled, SettingsDefaults.AiFilterEnabled),
settings.GetBool(SettingsKeys.BudgetRequiredHire, SettingsDefaults.BudgetRequiredHire),
settings.GetBool(SettingsKeys.BudgetRequiredOrder, SettingsDefaults.BudgetRequiredOrder),
NormalizeWantedType(settings.GetString(SettingsKeys.WantedType, SettingsDefaults.WantedType)),
LoadGlobalExclusions(settings));
}
// Собирает снимок глобальных исключений тенанта (§5.14): списки слов/локаций/типов и границы бюджета.
// Граница бюджета 0 в настройках трактуется как «не задана» (null); сравнение сумм — числовое,
// без конвертации валют (у настройки нет валюты).
// settings: Типизированный снимок настроек тенанта.
// Возвращает: Снимок исключений для чистой проверки сообщений батча.
private static GlobalExcludeSettings LoadGlobalExclusions(TenantSettingsSnapshot settings)
{
int from = settings.GetInt(SettingsKeys.ExcludeBudgetFrom, SettingsDefaults.ExcludeBudgetFrom);
int to = settings.GetInt(SettingsKeys.ExcludeBudgetTo, SettingsDefaults.ExcludeBudgetTo);
return new GlobalExcludeSettings(
Keywords: settings.GetStringList(SettingsKeys.ExcludeKeywords, SettingsDefaults.ExcludeKeywords),
Locations: settings.GetStringList(SettingsKeys.ExcludeLocations, SettingsDefaults.ExcludeLocations),
Types: settings.GetStringList(SettingsKeys.ExcludeTypes, SettingsDefaults.ExcludeTypes),
BudgetFrom: from > 0 ? from : null,
BudgetTo: to > 0 ? to : null);
}
// wantedType: тип заявок (both|vacancy|freelance) в нижнем регистре (python L1030); пустая/отсут-
// ствующая/повреждённая строка → дефолт «both» (даёт GetString с дефолтом + нормализация).
// value: Значение настройки wantedType (JSON-строка либо дефолт из SettingsDefaults).
// Возвращает: Значение в нижнем регистре (пустое хранимое значение остаётся пустым — как wantedType="").
private static string NormalizeWantedType(string value)
{
return value.Trim().ToLowerInvariant();
}
}