3fdc526031
- 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>
186 lines
8.7 KiB
Markdown
186 lines
8.7 KiB
Markdown
# 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» и добавь короткое описание
|
||
что есть.
|