124 lines
12 KiB
Markdown
124 lines
12 KiB
Markdown
# 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`
|