UC-HND-001. Администратор поддерживает справочник (archive/restore)
Назначение
Описывает сценарий поддержки справочника администратором платформы: по жалобе на устаревшее значение или
по reconciliation-отчёту после полной пересинхронизации администратор проверяет состояние синка, выводит
запись из оборота (archive) — значение перестаёт предлагаться в lookup-выдачах, но все существующие ссылки
в сервисах остаются валидными (resolve отдаёт архивную запись), — и возвращает её (restore) при ошибке.
Связан с контекстом handbook.
Это единственный ручной write-путь справочника: создание и изменение реплика-полей — только синк источника (INV-HND-1, ../domain/country.md); физических удалений нет вообще — архив компенсирует delete-gap источника (ADR-0063 п.5).
Metadata
| Поле | Значение |
|---|---|
| ID | UC-HND-001 |
| Контекст | handbook (см. docs/06-services/handbook/) |
| Тип | основной |
| Статус | draft |
Актор
| Поле | Значение |
|---|---|
| Роль | Администратор платформы |
| Permissions / scope | handbook.entries.manage в scope «Платформа» |
| Tenant | не применяется — справочник не tenanted (платформенные данные) |
| Record-level | не применяется |
Предусловие
- Администратор аутентифицирован, имеет
handbook.entries.manageв scope платформы. - Реплика справочника наполнена синком (записи существуют); consumer группы
pin-handbook-talent-syncработает. - Для reconciliation-ветки: проведена полная пересинхронизация (Talent
POST sync-handbooks) и сформирован reconciliation-отчёт — строки реплики, не пришедшие в окно ресинка, помечены кандидатами в архив (../background/talent-initial-import.md); автоматического архива по отсутствию в окне нет — решение всегда за администратором.
Триггер
| Поле | Значение |
|---|---|
| Источник | Жалоба пользователя/поддержки («в фильтре городов предлагается несуществующий/дублирующий город») ИЛИ reconciliation-отчёт после пересинхронизации (delete-gap: запись удалена в источнике, но живёт в PIN) |
| Триггерящий элемент | Админ-раздел «Справочники» — карточка записи / список кандидатов в архив |
Основной Поток
- Сигнал. Администратор получает сигнал: жалоба на значение в lookup либо reconciliation-отчёт с кандидатами в архив.
- Проверка синка. Администратор открывает состояние синка —
GET /api/v1/admin/handbook/sync-state(../api/admin/get-sync-state.md): чекпоинтыHandbookSyncState(сводка по типам записей), лаг, DLQ-счётчики. Убеждается, что синк догнал источник — т.е. запись действительно больше не приходит из источника, а не просто отстаёт реплика (иначе — сначала разбор лага/DLQ, архив преждевременен). - Сверка с источником. Администратор находит запись (публичный lookup / resolve по id) и сверяет с источником: записи в Talent Handbook больше нет (delete-gap) либо значение ошибочно/задублировано и до правки в источнике его нужно скрыть от пользователей.
- Вызов архива. Администратор вызывает архив —
POST /api/v1/admin/handbook/{type}/{id}/archive(../api/admin/archive-restore-entry.md), где{type}∈ {countries, regions, cities, currencies, languages}. - Валидация и запись. Система проверяет существование записи (нет → 404
HANDBOOK_ENTRY_NOT_FOUND) и ставитIsArchived = true; повторный архив уже архивной записи — идемпотентный no-op (состояние не меняется, повторное событие не публикуется). Операция логируется с актором. - Публикация события. В одном согласованном изменении с архивом re-publish-ится событие
{x}-upsertedв соответствующий топикpin.handbook.*.v1(compacted, key = id, полный snapshot реплики сIsArchived = true). - Эффект для платформы (INV-HND-5 LookupVisibility):
- публичный lookup перестаёт предлагать значение — новые записи потребителей на него не ссылаются;
- существующие ссылки остаются валидными: internal resolve (../api/internal/resolve-references.md) отдаёт архивную запись по id, snapshot-модели строятся и для архивной записи — исторические документы и события полны (../domain/snapshots.md п.5);
chessboardполучает событие и помечает строку своей локальной реплики архивной — значение уходит из фильтров витрины (окно eventual-consistency — секунды).
- Проверка результата. Администратор проверяет результат: значения нет в lookup; точечный resolve по id по-прежнему возвращает запись (с признаком архива); в reconciliation-отчёте кандидат закрыт.
Альтернативные Потоки
A1. Ошибочный архив — restore
- Обнаружение ошибки. Администратор обнаруживает, что заархивировал не ту запись (жалоба «пропал
город из фильтра» / собственная проверка), и вызывает
POST /api/v1/admin/handbook/{type}/{id}/restore. - Проверка перед restore. Система проверяет: запись существует (нет → 404
HANDBOOK_ENTRY_NOT_FOUND); для записей с ISO-кодами (страны, валюты, языки) — коды свободны среди неархивных (INV-HND-2); конфликт → 409HANDBOOK_SYNC_ISO_CONFLICT, restore отклоняется (см. A3). - Восстановление.
IsArchived = false; повторный restore неархивной записи — идемпотентный no-op. Re-publish{x}-upsertedсIsArchived = false— запись снова в lookup, репликаchessboardдогоняет.
Завершается — состояние справочника возвращено.
A2. Запись снова приходит из источника — авто-restore
- Повторный upsert. После архива запись вновь приходит upsert-событием из Talent (запись не была удалена в источнике — архив был преждевременным, либо её завели заново).
- Авто-restore. Синк применяет upsert поверх архивной записи: реплика-поля перезаписываются
(snapshot-семантика),
IsArchivedавтоматически сбрасывается вfalse+ warn-метрика — источник главнее локального архива (правила ../domain/talent-replica.md). Re-publish события. - Сигнал администратору. Администратор видит warn на дашборде
handbook-sync-overview. Если скрытие значения всё же требуется — вопрос решается правкой данных в Talent Handbook (записи там больше не будет — и авто-restore не повторится), а не повторным архивом в PIN.
A3. Restore при ISO-конфликте
- За время архива источник завёл новую запись с теми же ISO-кодами — уникальность среди неархивных (INV-HND-2) теперь занята.
- Restore отклоняется: 409
HANDBOOK_SYNC_ISO_CONFLICT. Автопочинки на стороне PIN нет — разрешение конфликта (какая из записей легитимна) выполняется правкой данных в источнике; после прихода исправляющих событий restore повторяется либо становится ненужным.
A4. Синк не догнал источник
- На шаге 2 sync-state показывает лаг или непустой DLQ по нужному потоку — вывод «запись не приходит из источника» сделать нельзя.
- Администратор сначала разбирает лаг/DLQ (runbook ../consumers/talent-handbook-sync.md); архив откладывается. Возвращается к шагу 2 после того, как синк догнал.
Постусловие
- Запись переведена в целевое состояние:
IsArchived = true(архив) илиfalse(restore/авто-restore); физически ничего не удалено. - Соответствующее событие
{x}-upsertedопубликовано вpin.handbook.*.v1; локальная репликаchessboardсогласована асинхронно (секунды). - Публичный lookup отражает состояние (архивная запись скрыта / восстановленная видна); internal resolve
отдаёт запись по id в любом состоянии — все исторические ссылки (
CountryId/RegionId/CityId/CurrencyId/LanguageIdв документах и событиях сервисов) остаются валидными. - Операция администратора зафиксирована в аудите с актором и временем.
Возможные Ошибки
| Код | HTTP | Условие | Поведение клиента |
|---|---|---|---|
403 FORBIDDEN | 403 | Нет handbook.entries.manage в scope платформы | Скрыть admin-операции справочника |
HANDBOOK_ENTRY_NOT_FOUND | 404 | Archive/restore несуществующей записи (неверный id или тип справочника) | Проверить id и справочник, обновить список |
HANDBOOK_SYNC_ISO_CONFLICT | 409 | Restore: ISO-код записи занят другой неархивной записью (INV-HND-2, поток A3) | Показать конфликтующую запись; разрешение — правка данных в источнике |
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|---|---|---|
country-upserted / region-upserted / city-upserted / currency-upserted / language-upserted | pin.handbook.{countries|regions|cities|currencies|languages}.v1 (compacted, key = id, ContractVersion 1) | Archive/restore администратором (шаг 6, A1); авто-restore синком (A2) — payload = полный snapshot реплики с актуальным IsArchived | chessboard — локальная реплика (архив/возврат значения в фильтрах витрины) |
Acceptance Criteria
| # | Критерий | Проверка |
|---|---|---|
| AC1 | Given неархивная запись, when архив, then запись исчезает из публичного lookup, resolve отдаёт её с признаком архива, событие с IsArchived = true опубликовано | архив города → lookup городов без него; resolve по id → запись есть; контракт-тест события |
| AC2 | Given архивная запись, when повторный archive, then идемпотентный no-op — состояние и версия не меняются, повторное событие не публикуется | двойной archive → одно событие в топике |
| AC3 | Given архивная запись, when restore без ISO-конфликта, then запись снова в lookup, событие с IsArchived = false опубликовано | restore страны → lookup содержит её |
| AC4 | Given архивная запись, when restore при занятых ISO-кодах, then 409 HANDBOOK_SYNC_ISO_CONFLICT, состояние не меняется | архив валюты → синк завёл новую с тем же IsoCode → restore → 409 |
| AC5 | Given архивная запись, when из источника приходит её upsert, then авто-restore: IsArchived = false, warn-метрика, событие опубликовано | эмуляция upsert-события источника поверх архивной записи |
| AC6 | Существующие ссылки переживают архив: документ/событие потребителя с id архивной записи полны — snapshot строится, resolve резолвит | batch-resolve со смесью активных/архивных/несуществующих id: архивные в ответе, несуществующие отсутствуют |
| AC7 | Без handbook.entries.manage archive/restore недоступны; выполненная операция зафиксирована в аудите с актором | запрос без permission → 403; проверка записи аудита |
| AC8 | Sync-state показывает чекпоинты по типам записей, лаг и DLQ — администратор может отличить «запись не приходит» от «синк отстал» (поток A4) | остановка consumer → sync-state показывает лаг |
Обратные ссылки
Автоссылки: где используется этот документ.
- API (2): GET /api/v1/admin/handbook/sync-state — Состояние синхронизации справочников с Talent, POST /api/v1/admin/handbook/{type}/{id}/{archive,restore} — Архив и восстановление справочной записи
- Use Cases (2): UC-HND-002. Система синхронизирует реплику справочника из Talent, US-реестр контекста handbook (US-HND)
- index.md (1): Handbook Service
- registries (1): Реестр use cases и user stories PIN
Связанные Документы
- API методы: ../api/admin/archive-restore-entry.md, ../api/admin/get-sync-state.md, ../api/internal/resolve-references.md, ../api/public/lookup-references.md.
- Domain: ../domain/country.md (INV-HND-1/2/4/5), ../domain/city.md / ../domain/region.md (INV-HND-3), ../domain/snapshots.md (snapshot и архивные записи), ../domain/talent-replica.md (авто-restore, правила синка).
- Consumer/фоновые: ../consumers/talent-handbook-sync.md, ../background/talent-initial-import.md (пересинхронизация + reconciliation-отчёт).
- ADR-0063 п.5 (delete-gap, soft-архив, reconciliation).