Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
58 lines
4.6 KiB
Markdown
58 lines
4.6 KiB
Markdown
# audit-trail Specification
|
||
|
||
## Purpose
|
||
Все действия из enum `audit_action` пишутся в `audit_event` одним способом — через сервис аудита; журнал только
|
||
дополняется, записи неизменяемы (ТЗ 8.2).
|
||
|
||
## Requirements
|
||
|
||
### Requirement: Единая точка записи
|
||
Сервис аудита SHALL предоставлять метод записи события с полями `action` (из enum `audit_action`), `section`
|
||
(из `audit_section`), `object_type`, `object_id`, `user_id` (NULL для системных действий), `consolidation_version_id`,
|
||
`description`, `before_value`, `after_value` (jsonb), `comment`, `result`, `changed_field`, `ip_address`, `user_agent`;
|
||
`occurred_at` SHALL проставляться сервером (UTC). Запись SHALL выполняться в той же транзакции, что и изменение данных.
|
||
|
||
NOTE: В Срезе 0 обмен через recognition_job аудит не пишет (рулинг R-7, design #20); сервис готов к использованию Срезом 2+.
|
||
|
||
#### Scenario: Системное событие
|
||
- **WHEN** системный процесс (без пользователя) вызывает сервис аудита
|
||
- **THEN** в `audit_event` появляется строка с `user_id = NULL`, `before_value`/`after_value` содержат старый и новый статус
|
||
|
||
#### Scenario: Откат транзакции
|
||
- **WHEN** изменение данных и запись аудита выполняются в одной транзакции, и она откатывается
|
||
- **THEN** строка `audit_event` не сохраняется
|
||
|
||
### Requirement: Журнал только дополняется
|
||
ORM-модель `audit_event` MUST NOT использоваться для `UPDATE` или `DELETE` в коде ядра; тест SHALL проверять, что в
|
||
`src/` нет обращений, изменяющих или удаляющих `audit_event`, а сервис аудита не имеет методов изменения.
|
||
|
||
#### Scenario: Попытка изменить запись
|
||
- **WHEN** код вызывает обновление строки `audit_event` через сервис аудита
|
||
- **THEN** такого метода нет (ошибка на этапе типизации/теста), запись остаётся прежней
|
||
|
||
### Requirement: События справочников, импорта и администрирования
|
||
Каждая мутация справочников, импорт и каждое действие администратора SHALL писать `audit_event` через сервис аудита в
|
||
той же транзакции с `user_id` инициатора, `ip_address` и `user_agent` запроса:
|
||
|
||
| Действие | `action` | `section` | `object_type` |
|
||
|---|---|---|---|
|
||
| создание/изменение/архив ГК | `GROUP_CHANGE` | `DIRECTORIES` | `company_group` |
|
||
| смена филиала ГК | `ASSIGNMENT_CHANGE` | `DIRECTORIES` | `company_group` |
|
||
| создание/изменение/перевод/закрытие ЮЛ, алиасы | `LEGAL_ENTITY_CHANGE` | `DIRECTORIES` | `legal_entity` / `legal_entity_alias` |
|
||
| ГСК/ГСЗ и их состав | `MEMBERSHIP_CHANGE` | `DIRECTORIES` | `related_group` / `related_group_member` |
|
||
| импорт выгрузки | `DIRECTORY_IMPORT` | `DIRECTORIES` | `directory_import` |
|
||
| пользователь создан/изменён/деактивирован | `USER_CHANGE` | `USERS` | `app_user` |
|
||
| роли пользователя | `USER_ROLE_CHANGE` | `USERS` | `app_user` |
|
||
| права роли | `ROLE_PERMISSION_CHANGE` | `ACCESS` | `role` |
|
||
|
||
`before_value`/`after_value` SHALL содержать изменённые поля (для списков — весь набор до и после); `changed_field` —
|
||
имя поля при изменении одного поля.
|
||
|
||
#### Scenario: Изменение имени ГК
|
||
- **WHEN** имя ГК меняется с «А» на «Б»
|
||
- **THEN** в `audit_event` строка `GROUP_CHANGE` с `changed_field = "name"`, `before_value = {"name": "А"}`, `after_value = {"name": "Б"}`, `user_id` инициатора
|
||
|
||
#### Scenario: Откат мутации справочника
|
||
- **WHEN** транзакция перевода ЮЛ откатывается
|
||
- **THEN** строки `LEGAL_ENTITY_CHANGE` и `MEMBERSHIP_CHANGE` в `audit_event` отсутствуют
|