using System;
using Ban.Sdaid.Notation.Documents;
namespace Ban.Sdaid.Icd.Domain.Cards.Template
{
/// <summary>Вид типа значения — чем определяется допустимое значение атрибута.</summary>
public enum ValueKind
{
/// <summary>Строка. Ограничения — длина значения.</summary>
String,
/// <summary>Целое число. Ограничения — диапазон значения.</summary>
Integer,
/// <summary>Вещественное число. Ограничения — диапазон значения и число знаков после запятой.</summary>
Decimal,
/// <summary>Дата. Ограничения — диапазон значения.</summary>
Date,
/// <summary>Отметка в клетке: заполнена или нет. Ограничений не имеет.</summary>
Bool,
/// <summary>Выбор из перечисления, заданного в самом типе. Ограничения — количество выбранных значений.</summary>
Choice,
/// <summary>Выбор из справочника. Ограничения — количество выбранных значений.</summary>
Reference,
}
/// <summary>Тип значения атрибута: вид значения и ограничения, осмысленные для этого вида.</summary>
[DomainEntity(Origin = DomainDataOrigin.Reference)]
public class AttributeValueType
{
/// <summary>Идентификатор типа.</summary>
public Guid Id { get; init; }
/// <summary>Код типа, по которому на него ссылаются атрибуты.</summary>
public string Code { get; init; } = string.Empty;
/// <summary>Наименование типа.</summary>
public string Name { get; init; } = string.Empty;
/// <summary>Вид типа — отдельным полем, не внутри ограничений.</summary>
[Relation]
public ValueKind Kind { get; init; }
/// <summary>
/// Ограничения вида в виде JSON: границы, перечень вариантов, код справочника.
/// Состав зависит от вида типа.
/// </summary>
public string? Constraints { get; init; }
}
/// <summary>Описание доменной сущности <see cref="AttributeValueType"/>.</summary>
public class AttributeValueTypeSpec : IDomainEntityDocument
{
public Type Entity => typeof(AttributeValueType);
public string Name => "Тип значения";
public string Description =>
@"Правило, которому подчиняется значение атрибута: вид значения и ограничения — границы, перечень вариантов либо справочник.";
public string Version => "0.5";
public string Status => "draft";
public string[] Comments => new[]
{
"2026-08-27. После упразднения словаря атрибутов тип значения — единственное, что переиспользуется между видами документов.",
"2026-08-27. Числовой вид значения разделён на целое и вещественное: разница существенна для распознавания.",
"2026-08-27. Значения набора хранятся в ограничениях типа; вид «выбор из справочника» зарезервирован под большие наборы и пока не используется.",
};
public static string S1_Purpose = """
## Назначение
Тип значения отвечает на два вопроса сразу: **что можно вписать** в поле и **сколько**
значений допустимо. Второе не менее важно первого: на бумажной форме сосуществуют поля,
где отмечается ровно одна клетка, и поля, где отмечается сколько угодно, — и без явного
различия Сервис не отличит ошибку распознавания от верно прочитанной карты.
Тип **именован и переиспользуется**: «степень повреждения» — один тип на шесть полей
листа сохранности, «исполнитель» — на мониторинг и таблицу экспертов. После того как
общий словарь атрибутов упразднён, тип значения — единственное, что виды документов
делят между собой, и делят осознанно: его выбирают, описывая атрибут.
Отсюда обязанность интерфейса ведения: показывать, **где тип используется**. Правка
типа меняет поведение всех полей, которые на него ссылаются, — в этом его смысл,
но вслепую такую правку делать нельзя.
Вид значения (`kind`) — не справочник, а перечень возможностей системы: добавить
седьмой вид значит написать разбор ограничений, отрисовку поля, проверку ввода
и формат в запросе к модели. Администратор такого не заводит.
""";
public static string S2_Constraints = """
## Ограничения по видам типов
Ограничения есть у любого вида, но означают то, что осмысленно именно для него:
| Вид типа | Что означают ограничения | Откуда берутся значения | Пример |
|---|---|---|---|
| Строка | длина значения | ввод | заглавие: от 1 до 500 знаков |
| Целое | диапазон значения | ввод | количество страниц: от 1 до 5000 |
| Вещественное | диапазон значения, знаки после запятой | ввод | величина pH: от 0 до 14 |
| Дата | диапазон значения | ввод | время создания рукописи: от 900 до текущего года |
| Отметка | ограничений нет | ввод | «Реставрация задано» |
| Выбор из перечисления | количество выбранных вариантов | перечисление в самом типе | тиснение: слепое, золотое, другие виды; не более трёх |
| Выбор из справочника | количество выбранных значений | справочник | материалы основы: сколько угодно |
Через количество выражается кратность поля: не более одного значения — выбор одного
(степень повреждения); не менее и не более одного — обязательный выбор одного (тип
знаковой информации); более одного — множественный выбор (материалы основы, украшения).
Одиночная отметка вынесена в **свой вид**, а не выражена выбором из набора в одно
значение: таких полей на форме больше всего, и через перечисление они читались бы хуже.
Целое и вещественное разделены по той же причине, по которой отметка отделена от выбора:
разница видна распознавателю. У количества страниц дробной части не бывает, и `5,83`
в этом поле — ошибка чтения, а не значение; у величины pH, наоборот, запятая обязательна,
и прочитанное `583` разумно понимать как `5,83`. Одним числовым видом это не выразить.
""";
public static string S3_Storage = """
## Хранение ограничений
Вид типа хранится **отдельным полем**, ограничения — одним значением в формате JSON.
Отдельной таблицы на каждый вид значения не заводим: разложение на «тип строки», «тип
даты», «тип выбора» дало бы целостность ценой соединений и пяти таблиц вместо одной.
Вид при этом наружу вынесен намеренно: без него нельзя ответить на вопрос «покажи все
атрибуты-даты», не разбирая JSON в каждой записи, — а такие вопросы возникают при
настройке распознавания.
Цена решения: тип перестаёт переиспользоваться так, как переиспользовался бы справочник
типов. Ограничения строк и дат у каждого поля всё равно свои, а общие наборы значений
остаются справочниками, поэтому потеря невелика.
""";
public static string S4_ChoiceVsReference = """
## Где живут значения набора
Значения задаются **в ограничениях самого типа**, парами «код — значение», в порядке,
в каком они напечатаны на бланке:
```
вид = choice; значения = [a: «Кожа»; b: «Пергамент»; c: «Ткань»; d: «Бумага»;
e: «Другое»; 0: «Отсутствует»]; min = 0; max = 1
```
**Код — часть данных, а не порядковый номер.** У заказчика это буква подполя ИРБИС,
и в АБИС уходит именно она, поэтому код задаётся явно и не меняется. Один и тот же
`a` означает «Кожа» в одном типе и «Слепое» в другом — код уникален внутри своего типа,
и этого достаточно.
Прежний критерий — «набор нужен двум и более атрибутам, значит справочник» — отпал
вместе со словарём атрибутов. Атрибут теперь принадлежит виду и всегда один, так что
по этой букве справочником было бы всё. Переиспользование обеспечивает сам тип:
«степень повреждения» — один тип на шесть полей листа сохранности.
Остался вопрос **объёма и ведения**. Набор из четырёх значений живёт в ограничениях
без затей. Набор из пятисот — нет: его ищут, пополняют и правят по одному значению,
а ограничения переписываются целиком. Для таких наборов оставлен вид `ref`: значения
ведутся снаружи, тип на них ссылается. Пока он **не используется** — первым кандидатом
станет язык издания, 589 значений; хранилище значений появится тогда же, когда
понадобится.
""";
public static string S99_Related = $"""
## Связанные артефакты
- {nameof(Ban.Sdaid.Icd.Arch.Adr.ADR_006_CardDataStructure)} — решение о структуре данных информационной карты
""";
}
}