docs: план проекта + промпты задач PR-1..PR-6 для Claude Code на ВМ

- docs/architecture/plan.md — полный план проекта (архитектура,
  стек, SLA, регуляторика, Реестр ПО, roadmap M1–M5, открытые
  вопросы и решения).
- docs/tasks/README.md — индекс задач и инструкция запуска для
  Claude Code на dev-ВМ.
- docs/tasks/PR-1-go-models-m2m.md — Go-модели M2M, парсер
  windows-1251, NSDDateTime, round-trip тесты на эталонах.
- docs/tasks/PR-2-fansy-ddl.md — DDL принимающей БД для команды
  Fansy (контракт данных, ETL-требования, словарь полей,
  тестовые данные).
- docs/tasks/PR-3-lk-openapi.md — OpenAPI контракт lk-gateway по
  ESIA Finance API V1, для синхронизации с командой ЛК.
- docs/tasks/PR-4-m2m-core-skeleton.md — FSM сделки,
  репозиторий, идемпотентность по GUID, метрики SLA.
- docs/tasks/PR-5-nsd-adapter-skeleton.md — REST-клиент ИШ НРД,
  маршрутизация типов пакетов (M2MTR/M2MTD/M2MER/SUBBR/SUBER/SUB16).
- docs/tasks/PR-6-crypto-service-skeleton.md — gRPC-каркас
  Java-сервиса криптографии (КриптоПро JCP, UDS, Provider-абстракция).

С этого коммита дальнейшая разработка идёт на dev-ВМ через
запущенный там Claude Code. Промпты PR-1..PR-3 готовы к параллельному
запуску; PR-4 после PR-1; PR-5 и PR-6 ждут поставку артефактов
(ИШ НРД, сертификаты, КриптоПро JCP).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-05-13 22:48:21 +03:00
parent d5b5597c67
commit 3fdc526031
8 changed files with 1651 additions and 0 deletions
+186
View File
@@ -0,0 +1,186 @@
# PR-4: Каркас `m2m-core` — FSM сделки, репозитории, идемпотентность
## Цель
Реализовать ядро бизнес-логики M2M-перевода: конечный автомат сделки,
типизированный доступ к БД, идемпотентность по GUID, метрики SLA по
этапам. Сетевые интеграции (НРД, ЛК, crypto-service) пока
**заглушками через интерфейсы** — реальные клиенты подключаются
в PR-5 и далее.
## Зависимости
- PR-1 (Go-модели M2M) — для типизированных сообщений.
- PR-2 (DDL Fansy) — на схему БД.
## Источники
- `docs/architecture/plan.md` — раздел «Поток одной заявки end-to-end»
и «SLA и регуляторные сроки».
- `internal/m2m/` — модели сообщений (после PR-1).
## Состав PR
### 1. Конечный автомат сделки
`internal/m2mcore/fsm.go`:
```
Draft → Validated → SubmittedToNSD → AwaitingDecision →
Confirmed → AwaitingSUB16 → Done
↘ Rejected
↘ TimedOut (после 10→5 мин без Decision)
↘ ManualApproval (опц. ветка)
```
Переходы — явные методы:
- `(d *Deal) Validate() error`,
- `(d *Deal) Submit(ctx) error`,
- `(d *Deal) ReceiveDecision(ctx, m2m.M2MTransferDecision) error`,
- `(d *Deal) Timeout(ctx) error`,
- `(d *Deal) SendToManualApproval(ctx, reason string) error`,
- `(d *Deal) ApproveManually(ctx, operator string) error`,
- `(d *Deal) RejectManually(ctx, operator, code, comment string) error`.
Каждый переход:
- проверяет текущее состояние (нельзя `Submit` если не `Validated`);
- пишет событие в `deal_events` (event sourcing для аудита);
- эмитит метрику `m2m_stage_duration_seconds{stage=...}`.
### 2. Доменная модель сделки
`internal/m2mcore/deal.go`:
```go
type Deal struct {
ID uuid.UUID
GUID m2m.UUID // тот, что в M2MTransferRequest.Header.GUID
State State
InvestorID uuid.UUID
SignedClaim []byte // подписанный XML от ЛК
Request *m2m.M2MTransferRequest
Response *m2m.M2MTransferResponse
Decision *m2m.M2MTransferDecision
CreatedAt time.Time
UpdatedAt time.Time
Stages []StageRecord // история состояний с timestamp
}
```
### 3. Репозиторий
`internal/m2mcore/repo.go`:
Интерфейс `Repository` с реализацией на pgx:
```go
type Repository interface {
Create(ctx, *Deal) error
GetByGUID(ctx, m2m.UUID) (*Deal, error)
Update(ctx, *Deal) error
List(ctx, Filter) ([]*Deal, error)
AppendEvent(ctx, *Deal, Event) error
}
```
Идемпотентность: `Create` использует `INSERT ... ON CONFLICT (guid) DO NOTHING RETURNING ...`
повторный запрос с тем же GUID не создаёт дубль, а возвращает существующий.
### 4. Внешние зависимости — интерфейсы (для PR-5+)
`internal/m2mcore/ports.go`:
```go
type NSDSender interface {
Send(ctx, *m2m.M2MTransferRequest) (*m2m.M2MTransferResponse, error)
SendDecision(ctx, *m2m.M2MTransferDecision) error
}
type LKCallbackClient interface {
UpdateStatus(ctx, claimID, status, reason string) error
}
type CryptoVerifier interface {
VerifyXMLDSig(ctx, payload []byte) (CertInfo, error)
}
type FansyStore interface {
GetClientByID(ctx, uuid.UUID) (*Client, error)
GetDepoAccount(ctx, deponentCode, accountID string) (*DepoAccount, error)
CheckBalance(ctx, depoAccountID uuid.UUID, isin string) (Quantity, error)
}
```
В PR-4 — заглушки `noopNSDSender`, `noopLKCallbackClient` и т. п.
Реальные клиенты подключаются в PR-5, PR-6.
### 5. Сборка enrichment Request из заявки
`internal/m2mcore/enrich.go`:
Функция «из заявки от ЛК + данные из `fansy-store``M2MTransferRequest`».
Шаги:
1. Распарсить подписанное заявление (XML от ЛК).
2. Поднять реквизиты сторон, депо-счета, остатки из FansyStore.
3. Проверить достаточность остатков по каждой ЦБ.
4. Сгенерировать `GUID` (uuid v4) и `ReferenceId` для каждой ЦБ
(`M2M` + дата `YYYYMMDD` + 5 случайных символов A-Z0-9).
5. Заполнить `Header.CreationTimestamp` = `nsdxml.NSDDateTime.Now()`.
6. Вернуть готовый `*m2m.M2MTransferRequest`, валидированный.
### 6. Метрики SLA
`internal/m2mcore/metrics.go`:
Prometheus метрики:
- `m2m_stage_duration_seconds{stage="..."}` — histogram длительности этапа,
- `m2m_deals_total{state="..."}` — counter сделок по итоговому состоянию,
- `m2m_sla_breaches_total{stage="...",budget="5m|2m"}` — counter
превышений 80% бюджета SLA.
Endpoint `/metrics` поднимается в `cmd/m2m-core/main.go`.
### 7. Заменить заглушку `cmd/m2m-core/main.go`
Минимальный рабочий сервер:
- читает конфиг (env: `BJ_DSN`, `BJ_LOG_LEVEL`, ...),
- открывает соединение с Postgres,
- инициализирует Repository,
- поднимает HTTP-эндпоинты `/healthz`, `/metrics`,
- логирует все запуски.
### 8. Тесты
- Юнит-тесты FSM (все переходы, в т. ч. недопустимые).
- Интеграционный тест с PostgreSQL через `testcontainers-go` (или
`dockertest`): создал сделку, обновил, прочитал, проверил
идемпотентность по GUID.
- Тест `enrich.go` на эталонной заявке (fixtures из примеров ESIA
Finance API).
## Требования
- Без эмодзи. Комментарии — на русском.
- `make ci` зелёный.
- Покрытие тестами `internal/m2mcore/` — не менее 60%.
## Коммит
```
feat(m2m-core): FSM сделки, репозиторий на pgx, идемпотентность по GUID, метрики SLA
- internal/m2mcore/fsm.go: конечный автомат с переходами и аудит-событиями
- internal/m2mcore/deal.go: доменная модель сделки
- internal/m2mcore/repo.go: репозиторий на pgx с идемпотентным Create
- internal/m2mcore/ports.go: интерфейсы для внешних зависимостей (заглушки)
- internal/m2mcore/enrich.go: сборка M2MTransferRequest из заявки + Fansy
- internal/m2mcore/metrics.go: Prometheus-метрики этапов и SLA
- cmd/m2m-core/main.go: минимальный сервер с /healthz и /metrics
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
```
## После коммита
Обновить `docs/tasks/README.md`: PR-4 — «выполнено».