9e6e95f431
- internal/m2mcore/fsm.go: конечный автомат с переходами Draft → Validated → SubmittedToNSD → AwaitingDecision → Confirmed → AwaitingSUB16 → Done, ветки Rejected/TimedOut/ManualApproval - internal/m2mcore/deal.go: доменная модель Deal с методами Validate/Submit/ReceiveDecision/Timeout/SendToManualApproval/ApproveManually/RejectManually/CompleteSUB16, журнал событий - internal/m2mcore/uuid.go: генератор UUID v4 без внешних зависимостей (crypto/rand) - internal/m2mcore/repo.go: порт Repository + MemoryRepository с идемпотентным Create по GUID - internal/m2mcore/ports.go: порты NSDSender/LKCallbackClient/CryptoVerifier/FansyStore с no-op заглушками для M1 - internal/m2mcore/enrich.go: EnrichRequest — сборка M2MTransferRequest из ClaimInput + Fansy, генерация ReferenceID по каждой ЦБ - internal/m2mcore/metrics.go: порт Recorder + MemoryRecorder в Prometheus-text формате - cmd/m2m-core/main.go: HTTP-сервер с /healthz и /metrics, graceful shutdown - migrations/m2m-core/001__deals.sql: схема для PostgreSQL-Repository (для M2) Покрытие: 63.1%. make ci зелёный. Без внешних Go-зависимостей (pgx и prometheus подключим в M2, когда прокси zetit откроет Go-модули). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
41 lines
2.2 KiB
SQL
41 lines
2.2 KiB
SQL
-- 001__deals.sql
|
||
-- Таблицы для PostgreSQL-реализации Repository в M2 (когда pgx будет
|
||
-- доступен через прокси). В M1 m2m-core работает с MemoryRepository.
|
||
|
||
CREATE SCHEMA IF NOT EXISTS m2m_core;
|
||
COMMENT ON SCHEMA m2m_core IS 'Состояние сделок M2M, события и аудит.';
|
||
|
||
CREATE TABLE IF NOT EXISTS m2m_core.deals (
|
||
id uuid PRIMARY KEY,
|
||
guid uuid NOT NULL UNIQUE,
|
||
state varchar(32) NOT NULL,
|
||
investor_id uuid,
|
||
signed_claim bytea,
|
||
request_xml bytea,
|
||
response_xml bytea,
|
||
decision_xml bytea,
|
||
created_at timestamptz NOT NULL DEFAULT now(),
|
||
updated_at timestamptz NOT NULL DEFAULT now()
|
||
);
|
||
COMMENT ON TABLE m2m_core.deals IS 'Корневая запись о M2M-сделке. Уникальность по guid обеспечивает идемпотентность.';
|
||
COMMENT ON COLUMN m2m_core.deals.guid IS 'GUID, который ушёл в M2MTransferRequest.Header.GUID.';
|
||
COMMENT ON COLUMN m2m_core.deals.state IS 'Текущее состояние FSM: draft/validated/submitted_to_nsd/awaiting_decision/confirmed/awaiting_sub16/done/rejected/timed_out/manual_approval.';
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_deals_state ON m2m_core.deals(state);
|
||
CREATE INDEX IF NOT EXISTS idx_deals_investor ON m2m_core.deals(investor_id) WHERE investor_id IS NOT NULL;
|
||
CREATE INDEX IF NOT EXISTS idx_deals_created ON m2m_core.deals(created_at DESC);
|
||
|
||
CREATE TABLE IF NOT EXISTS m2m_core.deal_events (
|
||
id bigserial PRIMARY KEY,
|
||
deal_id uuid NOT NULL REFERENCES m2m_core.deals(id) ON DELETE CASCADE,
|
||
type varchar(64) NOT NULL,
|
||
payload jsonb,
|
||
actor text,
|
||
created_at timestamptz NOT NULL DEFAULT now()
|
||
);
|
||
COMMENT ON TABLE m2m_core.deal_events IS 'Журнал событий сделки (event-sourcing для аудита).';
|
||
COMMENT ON COLUMN m2m_core.deal_events.actor IS 'Кто инициировал событие: system, nsd, operator-<login>, lk-callback и т.д.';
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_deal_events_deal ON m2m_core.deal_events(deal_id, created_at);
|
||
CREATE INDEX IF NOT EXISTS idx_deal_events_type ON m2m_core.deal_events(type);
|