12 KiB
quality-metrics Specification
Purpose
Факты качества первички (приём «как распознано», исход, технический отказ), отнесение правок аналитика к источнику
ошибки, матрица причин и API /quality-metrics с отдельным правом. Правила подсчёта — 05-domain-rules.md §14,
модель — DBML v5.25, дизайн — design.md этого change.
Requirements
Requirement: Факты качества — отдельная схема, только добавление
Ядро SHALL хранить факты качества тремя плоскими таблицами quality_intake_fact, quality_outcome_fact,
quality_failure_fact в отдельной схеме PostgreSQL (QUALITY_METRICS_SCHEMA, по умолчанию dkb_metrics; значение MUST
отличаться от DB_SCHEMA). Таблицы MUST NOT иметь внешних ключей на рабочие таблицы: идентификаторы документов, файлов,
заданий, ЮЛ и ГК — просто uuid, имена ЮЛ и ГК копируются на момент факта. Записи SHALL только добавляться (UPDATE и
DELETE по фактам MUST NOT выполняться); payload — jsonb с payload_schema_version, форма по контракту
quality-facts.schema.json. Факты пишутся только для документов, принятых после выкладки.
Scenario: Факт переживает удаление файла
- WHEN файл с документом, у которого есть факт приёма, удаляется
- THEN строки документа и файла удалены, а
quality_intake_factостаётся нетронутым
Scenario: Схема метрик совпала со схемой модели
- WHEN
DB_SCHEMAиQUALITY_METRICS_SCHEMAзаданы одним значением - THEN приложение не стартует с ошибкой конфигурации
Requirement: Факт приёма «как распознано»
В транзакции маппинга результата, после map_file и до автоподтверждения, ядро SHALL записать по одному
quality_intake_fact на каждый созданный документ BALANCE, INCOME, OSV (DISCLOSURE и OTHER факта не
получают): измерения (ЮЛ и ГК с именами, тип документа, source_kind pdf/excel, is_scan для PDF, сервис и версии,
intake_at, intake_status), счётчики строк, значений, нарушений и предупреждений, ключи S3, и payload со снимком
«как распознано». Запись факта MUST NOT менять документы, статусы и результат маппинга.
Scenario: Приём ББ
- WHEN маппинг создал документ
BALANCEиз PDF - THEN есть ровно один
quality_intake_factсdocument_id,source_kind = pdf,intake_statusкак у документа после маппинга и снимком строк и значений вpayload
Scenario: Расшифровка не учитывается
- WHEN маппинг создал документ
DISCLOSURE - THEN факта приёма по нему нет
Requirement: Факт исхода документа
Ядро SHALL записать quality_outcome_fact в транзакции события, которое завершает документ: ручное подтверждение
(CONFIRMED), автоподтверждение (AUTO_CONFIRMED), удаление файла (DELETED), автозамена файла (SUPERSEDED). Если
документ на момент удаления или замены уже CONFIRMED, исход SHALL быть DELETED_AFTER_CONFIRM /
SUPERSEDED_AFTER_CONFIRM. Факт содержит primary_cause, счётчики правок по источнику (service / ours / client),
checks_accepted, число нарушений на момент исхода, подтверждённую отчётную дату. Повторное подтверждение ОСВ после
правки SHALL писать новый факт; метрики берут последний факт документа.
Scenario: Автоподтверждение
- WHEN документ без замечаний автоподтверждён
- THEN факт с
outcome = AUTO_CONFIRMED,primary_cause = WITHOUT_PARTICIPATION,confirmed_byпуст
Scenario: Удаление после подтверждения
- WHEN удаляется файл с автоподтверждённым документом
- THEN новый факт
DELETED_AFTER_CONFIRM; прежний фактAUTO_CONFIRMEDне изменён
Requirement: Факт технического отказа
Ядро SHALL записывать quality_failure_fact при переходе задания распознавания в error, timeout, dlq и при
неповторяемой ошибке маппинга (файл → ERROR). failure_kind — SERVICE_ERROR / TIMEOUT / NO_ACK / MAPPING;
error_code отказа маппинга — константа MAPPING_FAILED. Повторная обработка создаёт новое задание: отказ первой
попытки остаётся фактом.
Scenario: Таймаут задания
- WHEN вахтёр переводит задание в
timeout - THEN есть факт
failure_kind = TIMEOUTсjob_id,file_id,failed_at
Requirement: Отнесение правки к источнику ошибки
Правка аналитика (отличие подтверждённого результата от снимка приёма) SHALL относиться к источнику чистой функцией без
БД по правилу «верный ответ есть в сыром тексте сервиса — ошибка наша, иначе сервиса»: сумма, код строки, добавленная
строка, поля шапки (отчётная дата сравнивается как дата после разбора parse_date), период и счёт ОСВ. Для Excel любая
ошибка — «наш разбор». Спорные случаи и случаи без сырого текста SHALL относиться к «нашему разбору». Правки колонок дат
и набор кодов сырого ответа для добавленных строк PDF в этом change не учитываются (отложено).
Scenario: Сервис прочитал верно
- WHEN аналитик исправил сумму, цифры которой совпадают с цифрами сырого текста
- THEN правка относится к «нашему разбору»
Scenario: Сервис ошибся
- WHEN подтверждённый код строки не извлекается из сырого текста кода
- THEN правка относится к «ошибке сервиса»
Requirement: Причина документа и матрица
Каждый документ с исходом SHALL попадать ровно в одну ячейку по первому сработавшему правилу: удалён/заменён до
подтверждения; удалён/заменён после подтверждения; есть правки «сервис» → ошибка сервиса; есть правки «наш разбор» →
наш разбор; ручное подтверждение с акцептом жёстких нарушений → документ клиента; AUTO_CONFIRMED → без участия;
иначе → как есть. Документы без исхода — «в работе». Доли считаются от документов с исходом AUTO_CONFIRMED /
CONFIRMED; удалённые, заменённые и «в работе» в знаменатель не входят. Технические отказы считаются по файлам своей
долей. Расчёт — чистая функция от фактов, в БД не хранится; последний факт документа выбирается по времени исхода, при
равенстве — детерминированно.
Scenario: Документ с правкой сервиса и клиента
- WHEN у подтверждённого документа есть правка «сервис» и акцепт нарушений
- THEN документ в ячейке «ошибка сервиса», счётчики обеих сторон сохранены в факте
Scenario: Нулевой знаменатель
- WHEN за период нет документов с исходом
- THEN доли равны 0,
denominator = 0
Requirement: API метрик качества
Система SHALL предоставлять под правом QUALITY_METRICS_VIEW маршруты /api/v1/quality-metrics: GET /matrix
(матрица за период с фильтрами период по дате приёма, ГК, ЮЛ, тип, источник, скан/цифровой, отказы по файлам, счётчик
уникальных отчётов), GET /series (ряд по месяцам даты приёма для причины, пропущенные месяцы — нули), GET /cells/{cell}/documents (постраничный список документов ячейки со счётчиками правок, ссылкой на документ, если он
существует, и confirmed_by), GET /export (факты за период, CSV с BOM и JSON; значения, начинающиеся с символов
формулы, экранируются). Выгрузка SHALL писать audit_event(EXPORT) с object_type = quality_metrics и фильтрами. Агрегаты
MUST NOT содержать фамилий пользователей.
Scenario: Матрица без права
- WHEN пользователь без
QUALITY_METRICS_VIEWзапрашивает/quality-metrics/matrix - THEN 403
Scenario: Выгрузка оставляет след
- WHEN пользователь выгружает факты за период
- THEN записан
audit_event(EXPORT)с форматом, фильтрами и числом строк
Requirement: Право QUALITY_METRICS_VIEW
Объект QUALITY_METRICS в permission_object и право QUALITY_METRICS_VIEW SHALL быть в seed и выдаваться всем
ролям; позже права сужаются данными (role_permission) без изменения кода. Видимость по филиалу и ГК не применяется.
Scenario: Право у всех ролей
- WHEN выполнен
make seedна чистой БД - THEN
GET /meлюбой роли содержитQUALITY_METRICS_VIEW