Files
zuevav 3fdc526031 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>
2026-05-13 22:48:21 +03:00

186 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PR-1: Go-модели M2M + парсер windows-1251 + round-trip тесты
## Цель
Реализовать типизированную доменную модель сообщений M2M по XSD НРД,
парсер/сериализатор XML в windows-1251 и round-trip тесты на эталонах.
Это первый осмысленный код в проекте, на нём основываются все
последующие модули (`nsd-adapter`, `m2m-core`, `lk-gateway`,
`lk-emulator`).
## Источники правды
- `DOC/M2MSchemas_260408/*.xsd` — все типы и структуры, namespace
`http://nsd.ru/schemas/m2m/...`, version `2026-04-08`.
- `DOC/Примеры/*.xml` — примеры всех типов сообщений (windows-1251).
- `DOC/Эталонные сообщения/*.xml` — эталоны для приёмочной проверки.
## Состав PR
### 1. `internal/m2m/types.go`
Все simple-типы и enum'ы из XSD как Go-типы.
**Enum'ы (со списком допустимых значений):**
- `StatusCode``INFO | ERROR`.
- `IIAContractType`**`T12 | T03`** (именно так — T12 = обмен ИИС-1/ИИС-2,
T03 = ИИС-3; не T01/T02/T03).
- `SecurityClassification``BOND | SHAR | MFUN`.
- `SecurityCategory``ORDN | PREF | UKWN`.
- `IdentityDocumentCode``01 | 02 | 03 | 04 | 05 | 06 | 07 | 09 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 26 | 27 | 91`.
- `IsolationStatus` — только `SGDN`.
**Simple-типы как Go-обёртки над `string`:**
- `DeponentCode`, `ReferenceId`, `ISIN`, `OrganizationINN`, `UUID`,
`AccountId`, `SecurityCode`, `IdentityDocSerial`.
### 2. `internal/m2m/validators.go`
Метод `Validate() error` на каждом типе из пункта 1, проверяющий pattern
и длину из XSD:
| Тип | Pattern | Длина |
|-----|---------|-------|
| `ReferenceId` | `^M2M[A-Z0-9]{13}$` | ровно 16 |
| `ISIN` | `^[A-Z]{2}[A-Z0-9]{9}[0-9]$` | ровно 12 |
| `OrganizationINN` | `^[0-9]{10}$` | ровно 10 |
| `DeponentCode` | `^[A-Z0-9]+$` | до 12 |
| `UUID` | стандартный UUID v1-5 pattern | 36 |
| `SecurityCode` | `^[0-9A-Z_/-]+$` | ровно 12 |
| `IdentityDocSerial` | `^\S+$` | от 1 |
Enum'ы валидируются проверкой принадлежности к допустимому множеству.
### 3. `internal/m2m/messages.go`
Структуры всех 6 типов сообщений M2M:
- `M2MTransferRequest` — Header (UUID, NSDDateTime, SenderCode,
ReceiverCode, CostInfo, опц. IIAAgreementDetails) + Data (IsM2M=true,
InvestorInformation, TransferringDepository, ReceivingDepository,
TransferredSecurities) + опц. NSDInfo.
- `M2MTransferDecision` — Header (тот же набор минус IIAAgreementDetails)
+ Data (ReceivingDepository, Security[] с TransferDecision Choice).
- `M2MTransferResponse` — GUID + StatusCode + Response[]
(ReferenceId, Code, Text).
- `M2MTransferHandbook` + `M2MTransferHandbookRequest`.
- `M2MTransferParticipantForm`.
**Choice-типы.** Реализуй через указатели на взаимоисключающие поля:
- `CostInfo { Yes *Yes; No *No }` — ровно один не nil.
- `Quantity { Whole *uint64; Fractional *Decimal16 }`.
- `SecurityDetails { ISIN *ISIN; SecurityInfo *SecurityDescription }`.
- `IdentificationDetails { RegNumber *string; FundShares *FundShares }`.
- `DecisionTransfer { Confirmation *Confirmation; Rejection *Rejection }`.
В `Validate()` каждого choice — проверка «ровно одно поле задано».
**Фиксированные значения.** `IsM2M` всегда `true` — захардкодь
в `MarshalXML` (не выноси в поле, заполняй автоматически).
**XML-теги.** Для каждого поля укажи `xml:"..."` с правильным namespace
из XSD. Используй namespace-aliases:
- `rt` для `http://nsd.ru/schemas/m2m/request`,
- `dn` для `http://nsd.ru/schemas/m2m/decision`,
- `hk` для `http://nsd.ru/schemas/m2m/handbook`,
- `hr` для `http://nsd.ru/schemas/m2m/handbook/request`,
- `pf` для `http://nsd.ru/schemas/m2m/participant/form`,
- `m2m` для `http://nsd.ru/schemas/m2m/types`.
### 4. `internal/nsdxml/datetime.go`
Тип `NSDDateTime` для нестандартного формата НРД.
- Формат: `YYYY-MM-DDThh:mm:ss(МСК[+-N])`, пример: `2026-03-02T14:30:45(МСК+2)`.
- Regex из XSD:
`^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\(МСК([+-][0-9]{1,2})?\)$`.
- Внутреннее представление — `time.Time` в location `Europe/Moscow`
плюс смещение (если есть).
- Реализуй: `MarshalXML`, `UnmarshalXML`, `MarshalText`, `UnmarshalText`,
`String() string`, `Now() NSDDateTime`.
- Кейсы для тестов: `(МСК)`, `(МСК+2)`, `(МСК-1)`, `(МСК+12)`.
### 5. `internal/nsdxml/codec.go`
Marshal/Unmarshal XML в windows-1251.
- Зависимость: `golang.org/x/text/encoding/charmap`.
- `Marshal(v any) ([]byte, error)`:
1. Сериализуй в utf-8 через `encoding/xml`.
2. Прогони через `charmap.Windows1251.NewEncoder()`.
3. Замени первую строку на
`<?xml version="1.0" encoding="windows-1251"?>`.
- `Unmarshal(data []byte, v any) error`:
1. Создай `xml.Decoder` с `CharsetReader`, в котором при
`charset == "windows-1251"` оборачиваем reader в
`charmap.Windows1251.NewDecoder().Reader(...)`.
2. Декодируй в `v`.
- Проверь, что кириллица в эталонах (ФИО инвестора, наименования
организаций) читается и пишется корректно.
### 6. `internal/m2m/messages_test.go`
Round-trip тесты на всех эталонах.
- Таблица «файл → ожидаемый тип сообщения» для всех XML в
`DOC/Примеры/` и `DOC/Эталонные сообщения/`.
- Для каждого файла:
1. `Unmarshal` → структура `S1`.
2. `S1.Validate()` — без ошибок.
3. `Marshal(S1)` → байты `B2`.
4. `Unmarshal(B2)` → структура `S2`.
5. `reflect.DeepEqual(S1, S2)` — true.
- Юнит-тесты валидаторов на негативных кейсах (короткий ИНН, неверный
префикс ReferenceId, неизвестное значение enum).
- Юнит-тесты `NSDDateTime` на всех вариантах зоны.
## Требования к коду
- **Без эмодзи** в коде и комментариях.
- Комментарии в коде — на русском.
- Имена типов и полей — на английском, как в XSD (`SettlementAccount`,
`IsolationStatus`, `IIAAgreementDetails` и т. п.).
- `go mod tidy` после добавления зависимостей (понадобится
`golang.org/x/text/encoding/charmap`).
- `make ci` (`tidy + fmt + vet + lint + test + build`) — зелёный.
- Покрытие тестами `internal/m2m/` и `internal/nsdxml/` — не менее 70%.
## Коммит
Один commit, сообщение:
```
feat(m2m): доменная модель сообщений + парсер windows-1251 + round-trip тесты
- internal/m2m/types.go: enum'ы и simple-типы из XSD НРД (M2MSchemas_260408)
- internal/m2m/validators.go: pattern-валидаторы ReferenceId/ISIN/INN/...
- internal/m2m/messages.go: структуры 6 типов сообщений M2M
- internal/nsdxml/datetime.go: тип NSDDateTime (формат "YYYY-MM-DDThh:mm:ss(МСК+N)")
- internal/nsdxml/codec.go: Marshal/Unmarshal XML в windows-1251
- internal/m2m/messages_test.go: round-trip тесты на всех эталонах из DOC/
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
```
Push в main (история линейная, ревью через diff в Gitea постфактум).
## Проверка перед коммитом
```bash
make ci
```
Если зелёное — push. Если красное — фиксируй ошибки до зелёного,
не коммить промежуточные правки.
## После коммита
1. Обнови `docs/tasks/README.md`: PR-1 — «выполнено», sha коммита.
2. Обнови `internal/m2m/README.md` и `internal/nsdxml/README.md`,
убери пометку «реализация — задача M1» и добавь короткое описание
что есть.