feat(m2m): сквозной поток с веб-интерфейсами — lk-gateway BFF + admin UI + lk-emulator + mock NSD

Реализован M2-шаг-1: первый рабочий сквозной поток M2M-заявки от ЛК
через нашу систему и обратно, с двумя видимыми веб-интерфейсами.

internal/nsdadapter/mock/:
- mock NSDSender с реалистичным синтетическим Response и асинхронной
  эмиссией Decision через настраиваемую задержку (Confirm/Reject/Timeout)
- использует собственный жизненный цикл, чтобы HTTP-контексты вызывающего
  не прерывали эмиссию Decision до истечения DecisionDelay

internal/lkgateway/:
- REST по контракту ESIA Finance V1 (POST/GET/PATCH/list claims)
- admin web UI (/admin/, /admin/claims, /admin/claims/{id}, /admin/status):
  - дашборд со статусом подсистем (postgres, crypto-service UDS,
    nsd-adapter, lk-emulator callback) и счётчиками сделок
  - журнал и карточка заявки с историей FSM, ответом НРД, решением
    принимающей стороны и последним callback'ом
- in-memory SeedStore с 5 тестовыми клиентами и счетами депо
- фоновый consumeDecisions: подписан на mock.Sender.Decisions(),
  применяет ApplyDecision и отправляет PATCH callback в ЛК

internal/lkemulator/:
- имитация ЛК клиента (порт 8083)
- веб-формы: журнал, форма «новая заявка», карточка заявки
- HTTP-клиент к lk-gateway (создание заявки + регистрация callback URL)
- приёмник PATCH callback'ов, локальное хранилище заявок,
  автообновление страницы каждые 3 сек

cmd/lk-gateway/main.go и cmd/lk-emulator/main.go — заглушки заменены
на полные сервисы с graceful shutdown.

Сквозной поток проверен smoke-test'ом: подача заявки через форму
эмулятора → создание сделки в lk-gateway → Send в mock NSD →
эмиссия Decision через 3 сек → ApplyDecision → PATCH callback в ЛК →
эмулятор показывает confirmed. Дашборд lk-gateway: Total=1, Подтверждено=1.

make ci зелёный.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
fontvielle
2026-05-14 11:17:11 +03:00
parent e2720c09f7
commit c5695bf0b6
30 changed files with 3496 additions and 19 deletions
+70
View File
@@ -0,0 +1,70 @@
# internal/lkgateway — BFF слой ЛК + admin web UI
Реализует REST-контракт ESIA Finance V1
(`docs/lk-contract/v1/openapi.yaml`) на стороне Bridge-and-Join-s и
admin-веб для оператора.
## REST API
- `POST /api/v1/back_office/claims/` — приём заявки от ЛК.
Валидирует, собирает `M2MTransferRequest` через
`m2mcore.EnrichRequest`, создаёт `m2mcore.Deal`, отправляет в НРД
через `m2mcore.NSDSender` (на M2 — mock).
- `GET /api/v1/back_office/claims/{id}` — карточка заявки.
- `GET /api/v1/back_office/claims` — список с фильтрами
(status/investor_id/created_from/created_to/limit/offset).
- `PATCH /api/v1/back_office/claims/{id}` — placeholder для callback
от внешней системы.
- `/healthz` — health.
## Admin web UI
- `/admin/` — дашборд: статус системы, счётчики (Total/Confirmed/
InProgress/Failed), последние 10 заявок.
- `/admin/claims` — журнал всех заявок.
- `/admin/claims/{id}` — детальная карточка: история FSM, ответ НРД
(`M2MTransferResponse`), решение принимающей стороны
(`M2MTransferDecision`), последний callback.
- `/admin/status` — детальные проверки: postgres, crypto-service (UDS),
nsd-adapter (REST), lk-emulator callback URL.
## Состав пакета
- `server.go``Server` обвязка: HTTP mux + сервис + фоновый
consumeDecisions (читает из `mock.Sender.Decisions()` и обновляет
сделки + шлёт callback в ЛК).
- `service.go` — бизнес-логика: DTO ↔ доменные сущности m2mcore,
оркестрация FSM, отправка callback'ов.
- `api.go` — REST endpoints.
- `admin.go` — HTML endpoints с шаблонами в `web/templates/`.
- `checks.go` — проверки готовности подсистем (postgres, crypto-service,
nsd-adapter, callback URL).
- `seedstore.go` — in-memory `m2mcore.FansyStore` с 5 тестовыми
клиентами и счетами депо (соответствует
`docs/fansy-contract/v1/examples/seed-data.sql`).
- `types.go` — DTO под OpenAPI.
- `http_util.go` — JSON-хелперы.
## Конфигурация (cmd/lk-gateway/main.go, ENV)
| Переменная | По умолчанию | Назначение |
|---|---|---|
| `BJ_HTTP_ADDR` | `:8080` | Адрес HTTP |
| `BJ_M2M_SENDER` | `MC0079200000` | DeponentCode отправителя в M2M Header |
| `BJ_M2M_RECEIVER` | `MC0010300000` | DeponentCode получателя |
| `BJ_DSN` | — | PostgreSQL DSN (M2-шаг-3, пока пусто = in-memory) |
| `BJ_CRYPTO_SOCKET` | `/run/bj/crypto.sock` | UDS для crypto-service |
| `BJ_NSD_ADAPTER_URL` | — | URL nsd-adapter HTTP (пусто = mock) |
| `BJ_LK_CALLBACK_URL` | — | URL ЛК для PATCH callback'ов (пусто = эмулятор регистрирует свой) |
| `BJ_NSD_PROFILE` | `demo (mock NSD)` | Имя профиля (отображается в admin) |
| `BJ_CRYPTO_PROVIDER` | `stub` | Провайдер криптографии в admin-статусе |
## Что подключается в следующих шагах
- **M2-шаг-3**: pgx-репозиторий → миграция
`migrations/m2m-core/001__deals.sql` уже готова.
- **M3**: реальный `nsd-adapter` вместо mock — выставить
`BJ_NSD_ADAPTER_URL` и реализовать в nsd-adapter поллер,
отправляющий Decision не через канал mock, а через шину.
- **M4**: `admin-ui` v2 на React + раздел «Сертификаты КриптоПро»
для обновления публичных сертификатов через UI.