Модель характеристик

NOTE

Статус: Target design. Документ описывает целевую доменную модель. Соответствующий код реализован частично (см. backend/internal/core/) или пока не начат. Правила маркировки — в 50-processes/documentation-standard.md.

Как описываются атрибуты товаров, как нормализуются значения, как сравниваются.

Классификация

Characteristic

Определение атрибута в классификаторе:

  • key — машинное имя, уникально (voltage, ip_rating, cable_section).
  • display_name — локализованное ({"ru": "Напряжение", "en": "Voltage"}).
  • value_type — см. ниже.
  • default_unit_ref — ссылка на Unit (для числовых типов).
  • validation — правила (min, max, regex, допустимые enum-значения).
  • semantic_role — служебная роль: identity_key (резерв), object_type, none.

Одна и та же Characteristic может участвовать в разных Identity Profile, в некоторых как critical, в других как некритическая. Критичность не хранится на Characteristic, а на IdentityProfile.critical_attribute_keys.

Типы значений

  • number — одиночное число (voltage = 220).
  • range — диапазон (operating_temp = [-40, +85]).
  • tolerance — номинал с допуском (resistance = {nominal: 100, tolerance_percent: 1}).
  • dimension — составной размер (size = {length: 100, width: 50, height: 25, unit: mm}).
  • enum — значение из справочника значений характеристики.
  • string — свободный текст; редко critical.
  • boolean — признак.

Каждый тип имеет свой алгоритм:

  • нормализации (парсинг из строки);
  • сравнения на равенство;
  • сравнения “не хуже” (для подбора аналогов);
  • fuzzy-сравнения (для поиска).

Единицы измерения

Unit
├── key             — "mm", "kg", "kPa"
├── symbol          — "мм", "кг", "кПа"
├── aliases[]       — ["mm", "миллиметр", "millimetre", "мм."]
├── dimension       — "length", "mass", "pressure", ...
└── to_canonical    — коэффициент (или функция) в каноническую единицу

Каждая dimension имеет одну каноническую единицу (length → m, mass → kg, pressure → Pa, …). При нормализации значение приводится к канонической, но оригинальная форма сохраняется для отображения.

Пример: поставщик прислал “50 мм”. Нормализуется в {value: 0.05, unit: "m", original_value: 50, original_unit: "mm"}.

Парсинг значений

Модуль парсинга — одно из самых важных мест в нормализации. Основные правила:

Числа и единицы

  • "50мм", "50 mm", "50.0 мм", "50,0 mm" → 50 mm.
  • "0,05 м", "0.05 m", "5 cm", "50mm" → 0.05 m (в канонике).
  • Десятичный разделитель — и ., и ,, регион-нейтрально.

Диапазоны

  • "-40...+85", "от -40 до 85", "−40…+85 °C", "-40..+85".

Допуски

  • "100 ± 1%", "100 +/- 1%", "100 ±1 Ом".

Размеры

  • "100x50x25 мм", "100×50×25 мм", "100*50*25".

Enum

  • Алиасы значений как и у units: “IP54” = “ip 54” = “ИП54” = “IP-54”.

Отсутствующие значения (nullable / missing)

Если поставщик не предоставил значение характеристики:

  • Сохраняется null в слое supplier offer.
  • При попадании в canonical product — заполняется из другого источника (другой поставщик, AI-enrichment, ручной ввод).
  • Критичные характеристики не могут быть null у canonical product: если все источники молчат, canonical не создаётся, offer становится orphan.

Конфликты значений между поставщиками

Два поставщика сообщили разные значения одной критической характеристики у (по MPN) одного товара.

Правила разрешения (в порядке применения):

  1. Правило priority источников: производитель > дистрибьютор первого уровня > партнёр > маркетплейс.
  2. Если приоритеты равны — самое свежее.
  3. Если всё равно неоднозначно — помечаем конфликт, попадает в очередь модерации. Canonical получает значение null по этой характеристике до разрешения.

Обогащение

Если характеристика отсутствует у всех поставщиков, но:

  • её можно извлечь из описания через LLM — используется AI enrichment с флагом source = ai, confidence.
  • её можно посчитать из других (например, объём из габаритов) — используется правило расчёта.
  • AI-значения НЕ используются как critical в identity_signature без ручной верификации.

Версионирование

  • Изменение validation или value_type Characteristic = breaking change, требует ADR и пересчёта затронутых canonical products.
  • Добавление нового alias unit — не breaking.
  • Добавление новой Characteristic — не breaking.