feat(audit): полноценный writer в audit_log + admin UI (audit + outbox DLQ)
До этого audit_log entity была определена, но никто туда не писал — реальный аудит шёл только через Hibernate Envers (без user/IP/trace). Теперь: - AuditLogger service пишет в той же транзакции что и CRUD (DictionaryRecordService). Захватывает: user (JWT sub либо preferred_username), scope, IP (X-Forwarded-For), UA, trace_id (MDC), request_id. Не ломает основной flow при сбое — только громко логирует. - AuditLogRepository: гибкий search() с nullable фильтрами (dictionary, user, action, businessKey, from, to) + Pageable. - AuditAdminController: GET /admin/audit (paginated) + /admin/audit/export.csv (cap 10k строк против OOM). Frontend: - /audit route: фильтр-панель, таблица с цветными бейджами (CREATE/UPDATE/CLOSE), expand-row с JSON before/after diff, footer IP/UA/req_id, кнопка экспорта CSV. Pagination 50/100/200. - /outbox route: stat-cards pending/DLQ + DLQ-таблица с last_error. - Nav links + i18n (RU + EN).
This commit is contained in:
@@ -14,6 +14,51 @@ i18n
|
||||
translation: {
|
||||
'app.title': 'Ordinis НСИ ЦУОД',
|
||||
'nav.dictionaries': 'Справочники',
|
||||
'nav.audit': 'Аудит',
|
||||
'nav.outbox': 'Outbox',
|
||||
'audit.title': 'Журнал аудита',
|
||||
'audit.subtitle': 'Юридически значимый лог изменений НСИ',
|
||||
'audit.filter.dictionary': 'Справочник',
|
||||
'audit.filter.user': 'Пользователь',
|
||||
'audit.filter.action': 'Действие',
|
||||
'audit.filter.businessKey': 'Бизнес-ключ',
|
||||
'audit.filter.from': 'С',
|
||||
'audit.filter.to': 'По',
|
||||
'audit.filter.reset': 'Сбросить',
|
||||
'audit.filter.apply': 'Применить',
|
||||
'audit.col.time': 'Время',
|
||||
'audit.col.action': 'Действие',
|
||||
'audit.col.dictionary': 'Справочник',
|
||||
'audit.col.businessKey': 'Бизнес-ключ',
|
||||
'audit.col.user': 'Пользователь',
|
||||
'audit.col.scope': 'Scope',
|
||||
'audit.col.trace': 'Trace',
|
||||
'audit.col.diff': 'Изменения',
|
||||
'audit.action.expand': 'Раскрыть',
|
||||
'audit.action.export': 'Экспорт CSV',
|
||||
'audit.empty': 'Нет записей по выбранным фильтрам',
|
||||
'audit.action.CREATE': 'Создание',
|
||||
'audit.action.UPDATE': 'Изменение',
|
||||
'audit.action.CLOSE': 'Закрытие',
|
||||
'audit.diff.before': 'Было',
|
||||
'audit.diff.after': 'Стало',
|
||||
'audit.page.of': 'Стр. {{cur}} из {{total}}',
|
||||
'audit.page.size': 'На странице',
|
||||
'audit.page.prev': 'Назад',
|
||||
'audit.page.next': 'Вперёд',
|
||||
'outbox.title': 'Outbox events',
|
||||
'outbox.subtitle': 'Очередь публикации в Kafka и DLQ',
|
||||
'outbox.stats.pending': 'В очереди',
|
||||
'outbox.stats.dlq': 'В DLQ',
|
||||
'outbox.dlq.col.id': 'ID',
|
||||
'outbox.dlq.col.eventType': 'Тип',
|
||||
'outbox.dlq.col.dictionary': 'Справочник',
|
||||
'outbox.dlq.col.aggregateId': 'Aggregate ID',
|
||||
'outbox.dlq.col.topic': 'Topic',
|
||||
'outbox.dlq.col.attempts': 'Попыток',
|
||||
'outbox.dlq.col.lastError': 'Последняя ошибка',
|
||||
'outbox.dlq.col.dlqAt': 'В DLQ с',
|
||||
'outbox.dlq.empty': 'DLQ пуста — все события публикуются успешно',
|
||||
'header.scope': 'Доступный scope',
|
||||
'dict.list.subtitle': 'Каталоги НСИ ЦУОД ОДХ',
|
||||
'dict.empty': 'В этом справочнике пока нет записей',
|
||||
@@ -133,6 +178,51 @@ i18n
|
||||
translation: {
|
||||
'app.title': 'Ordinis MDM',
|
||||
'nav.dictionaries': 'Dictionaries',
|
||||
'nav.audit': 'Audit',
|
||||
'nav.outbox': 'Outbox',
|
||||
'audit.title': 'Audit log',
|
||||
'audit.subtitle': 'Legally significant log of NSI changes',
|
||||
'audit.filter.dictionary': 'Dictionary',
|
||||
'audit.filter.user': 'User',
|
||||
'audit.filter.action': 'Action',
|
||||
'audit.filter.businessKey': 'Business key',
|
||||
'audit.filter.from': 'From',
|
||||
'audit.filter.to': 'To',
|
||||
'audit.filter.reset': 'Reset',
|
||||
'audit.filter.apply': 'Apply',
|
||||
'audit.col.time': 'Time',
|
||||
'audit.col.action': 'Action',
|
||||
'audit.col.dictionary': 'Dictionary',
|
||||
'audit.col.businessKey': 'Business key',
|
||||
'audit.col.user': 'User',
|
||||
'audit.col.scope': 'Scope',
|
||||
'audit.col.trace': 'Trace',
|
||||
'audit.col.diff': 'Diff',
|
||||
'audit.action.expand': 'Expand',
|
||||
'audit.action.export': 'Export CSV',
|
||||
'audit.empty': 'No entries match the filters',
|
||||
'audit.action.CREATE': 'Create',
|
||||
'audit.action.UPDATE': 'Update',
|
||||
'audit.action.CLOSE': 'Close',
|
||||
'audit.diff.before': 'Before',
|
||||
'audit.diff.after': 'After',
|
||||
'audit.page.of': 'Page {{cur}} of {{total}}',
|
||||
'audit.page.size': 'Per page',
|
||||
'audit.page.prev': 'Prev',
|
||||
'audit.page.next': 'Next',
|
||||
'outbox.title': 'Outbox events',
|
||||
'outbox.subtitle': 'Publication queue and DLQ',
|
||||
'outbox.stats.pending': 'Pending',
|
||||
'outbox.stats.dlq': 'In DLQ',
|
||||
'outbox.dlq.col.id': 'ID',
|
||||
'outbox.dlq.col.eventType': 'Type',
|
||||
'outbox.dlq.col.dictionary': 'Dictionary',
|
||||
'outbox.dlq.col.aggregateId': 'Aggregate ID',
|
||||
'outbox.dlq.col.topic': 'Topic',
|
||||
'outbox.dlq.col.attempts': 'Attempts',
|
||||
'outbox.dlq.col.lastError': 'Last error',
|
||||
'outbox.dlq.col.dlqAt': 'DLQ since',
|
||||
'outbox.dlq.empty': 'DLQ empty — all events publish successfully',
|
||||
'header.scope': 'Allowed scope',
|
||||
'dict.list.subtitle': 'Reference data catalogues',
|
||||
'dict.empty': 'No records in this dictionary yet',
|
||||
|
||||
Reference in New Issue
Block a user