⟨/⟩ 50_Domain/Cards/InformationCard.cs

141 строк · в начало

using System;
using System.Collections.Generic;
using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd.Domain.Cards.Template;

namespace Ban.Sdaid.Icd.Domain.Cards
{
    /// <summary>Электронная ИК — документы своего вида плюс ядро и связи Сервиса.</summary>
    [DomainEntity]
    public class InformationCard
    {
        /// <summary>Идентификатор карты.</summary>
        public Guid Id { get; init; }

        /// <summary>Вид документа, по которому заполнена карта.</summary>
        [Relation]
        public DocumentKind Kind { get; init; } = null!;

        /// <summary>Дата карты: когда экземпляр передан в обработку.</summary>
        public DateOnly Date { get; init; }

        /// <summary>Документы карты: содержимое, заполненное по видам.</summary>
        [Relation(Kind = RelationKind.Composition, Min = 1)]
        public IReadOnlyList<CardDocument> Documents { get; init; } = new CardDocument[0];

        /// <summary>Пакет ввода, из которого создана карта.</summary>
        public Guid SourcePacketId { get; init; }

        /// <summary>Ядро: шифр хранения.</summary>
        public string Shelf { get; init; } = string.Empty;

        /// <summary>Ядро: инвентарный номер.</summary>
        public string InventoryNumber { get; init; } = string.Empty;

        /// <summary>Ядро: заглавие документа.</summary>
        public string Title { get; init; } = string.Empty;

        /// <summary>Ядро: автор.</summary>
        public string? Author { get; init; }

        /// <summary>Ядро: год издания.</summary>
        public int? Year { get; init; }

        /// <summary>Момент создания карты.</summary>
        public DateTime CreatedAt { get; init; }

        /// <summary>Пользователь, создавший карту.</summary>
        public Guid CreatedByUserId { get; init; }

        /// <summary>Момент последней выгрузки в АБИС; null — не выгружалась.</summary>
        public DateTime? ExportedAt { get; init; }
    }

    /// <summary>Описание доменной сущности <see cref="InformationCard"/>.</summary>
    public class InformationCardSpec : IDomainEntityDocument
    {
        public Type Entity => typeof(InformationCard);

        public string Name => "Информационная карта";

        public string Description =>
            @"Итог оцифровки одной бумажной формы: документы карты со значениями атрибутов, типизированное ядро для поиска, связь с пакетом-первоисточником и отметка о выгрузке в АБИС.";

        public string Version => "0.4";
        public string Status => "draft";

        public string[] Comments => new[]
        {
            "2026-08-19. Переписана по варианту 5: вид документа, дата как обязательный реквизит, дерево документов карты вместо одного корневого документа.",
            "2026-08-31. Убрана связь с экземпляром фонда: реестр экземпляров и привязка карты к экземпляру выведены из состава Сервиса.",
        };

        public static string S1_Purpose = """
            ## Назначение

            Итог оцифровки одной бумажной формы. Создаётся командой пользователя по итогам проверки
            и попадает в реестр информационных карт.
            """;

        public static string S2_WhySeparateEntity = """
            ## Почему карта — отдельная сущность, а не просто документ

            Содержимое карты хранится в документах карты, но карта ими не исчерпывается.
            У неё есть то, чего у документа как такового быть не может:

            - связь с пакетом-первоисточником;
            - дата карты: когда экземпляр передан в обработку;
            - факт выгрузки в АБИС;
            - собственный жизненный цикл: карта возникает командой пользователя по итогам проверки.

            Карта при этом **не является** документом дерева: она стоит над ним и содержит документы,
            а не наследует их устройство. Собственных атрибутов у неё нет — всё, что пишется
            на бланке, живёт в документах карты.

            Загнать это в атрибуты нельзя: «выгружена ли карта» не может быть строкой в общем
            хранилище значений наравне с заглавием и годом издания.
            """;

        public static string S3_Core = """
            ## Ядро

            Шифр, инвентарный номер, заглавие, автор, год вынесены в типизированные свойства.
            Дата в ядро не входит: это собственный реквизит карты, а не проекция значения.
            Критерий отбора функциональный: в ядро идёт то, **по чему Сервис работает сам** — ищет
            карту, строит реестр, отбирает выборку, формирует выгрузку.

            Структурный критерий «общее для всех бланков» здесь не работает: у ИК издания одно поле
            «Заглавие», а у ИК рукописи их три — унифицированное, полное и перевод.

            **Ядро — проекция, а не второй источник истины.** Источник истины — значения атрибутов
            документов карты; свойства ядра заполняются из них и пересчитываются при правке значений.
            Какой атрибут даёт какое свойство ядра, задаёт атрибут вида документа.

            Чем это оплачено: ядро можно рассинхронизировать со значениями, и ошибка будет тихой —
            реестр покажет одно, карта будет содержать другое.
            """;

        public static string S4_Invariants = """
            ## Инварианты

            - Карта создаётся только по пакету, у которого заполнены обязательные поля состава.
            - Один пакет порождает одну карту.
            - Карта не бывает без документов: собственных атрибутов у неё нет, и всё, что на бланке,
              живёт в документах. Карта без них пуста.
            - Карта не ссылается на экземпляр фонда: привязка карты к экземпляру в состав Сервиса
              не входит. Документ опознаётся на стороне АБИС по шифру и инвентарному номеру,
              которые в карте есть всегда — они обязательны.
            - У одного документа фонда может быть несколько карт — по числу событий обработки;
              различает их дата карты.
            - Свойства ядра совпадают со значениями атрибутов, из которых выведены.
            - Вид карты совпадает с видом документа, по которому собран пакет-первоисточник.
            """;

        public static string S99_Related = $"""
            ## Связанные артефакты

            - {nameof(Ban.Sdaid.Icd.Arch.Adr.ADR_006_CardDataStructure)} — решение о структуре данных информационной карты
            """;
    }
}