Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ, инструкция пользователя, техдокументация, код-стайл), бэклог, скрипты развёртывания и архив прототипа LeadRadar.
97 lines
6.6 KiB
C#
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();
|
|
}
|
|
|
|
}
|