- 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>
8.7 KiB
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— все типы и структуры, namespacehttp://nsd.ru/schemas/m2m/..., version2026-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в locationEurope/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):- Сериализуй в utf-8 через
encoding/xml. - Прогони через
charmap.Windows1251.NewEncoder(). - Замени первую строку на
<?xml version="1.0" encoding="windows-1251"?>.
- Сериализуй в utf-8 через
Unmarshal(data []byte, v any) error:- Создай
xml.DecoderсCharsetReader, в котором приcharset == "windows-1251"оборачиваем reader вcharmap.Windows1251.NewDecoder().Reader(...). - Декодируй в
v.
- Создай
- Проверь, что кириллица в эталонах (ФИО инвестора, наименования организаций) читается и пишется корректно.
6. internal/m2m/messages_test.go
Round-trip тесты на всех эталонах.
- Таблица «файл → ожидаемый тип сообщения» для всех XML в
DOC/Примеры/иDOC/Эталонные сообщения/. - Для каждого файла:
Unmarshal→ структураS1.S1.Validate()— без ошибок.Marshal(S1)→ байтыB2.Unmarshal(B2)→ структураS2.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 постфактум).
Проверка перед коммитом
make ci
Если зелёное — push. Если красное — фиксируй ошибки до зелёного, не коммить промежуточные правки.
После коммита
- Обнови
docs/tasks/README.md: PR-1 — «выполнено», sha коммита. - Обнови
internal/m2m/README.mdиinternal/nsdxml/README.md, убери пометку «реализация — задача M1» и добавь короткое описание что есть.