Показывает: детально Phase 3 — параллельная генерация regular SHORT-выписок через шаблон и параллельный strip PRKK-выписок без шаблонизации. Это ключевая ветка под бизнес-требование из ADR-0008.
Zoom in на блок Phase 3 из protocol-generation-pipeline.md.
Актуальные ADR: ADR-0008, ADR-0009.
sequenceDiagram
autonumber
participant Gen as ProtocolGenerator<br/>(protocol)
participant FF as FeatureFlagProvider<br/>(integration.common)
participant ES as ExtractSynthesizer<br/>(extract.adapter)
participant Core as ExtractService<br/>(extract.service)
participant PS as PrkkExtractStripper<br/>(document.prkk)
participant ECM as ECM<br/>⟶ external
participant XDB as extract<br/>(DB)
Note over Gen: Вход: AfterPhase2<br/>(simpleExtracts = regular + PRKK-placeholders)
%% ── Feature flag check ──
Gen->>FF: isEnabled("PRKK_EXTRACT_SKIP_FORMATTING")
FF-->>Gen: true (default, emergency safety-valve)
Note over Gen: если вернулось false — вся генерация<br/>идёт через regular pipeline<br/>(fallback режим, PRKK теряет форматирование)
%% ── Split ──
Note over Gen: разделение simpleExtracts:<br/>regularSimples = filter(!ctx.isPrkk)<br/>prkkSimples = filter(ctx.isPrkk)
rect rgb(245, 255, 245)
Note over Gen,XDB: PARALLEL TRACK A — Regular SHORT generation<br/>(обычные вопросы через шаблон)
Gen->>ES: synthesizeShortExtracts(regularSimples, ctx)
Note over ES: StructuredTaskScope.open(<br/>Joiner.allSuccessfulOrThrow())<br/>ContextSnapshot.captureAll()
rect rgb(235, 250, 235)
par для каждой regular SIMPLE-выписки
ES->>Core: generateShortFromSimple(simple, agenda, question, votes)
Core->>ECM: LOAD simple-file (из externalStorageKey)
ECM-->>Core: SIMPLE docx bytes
Note over Core: strategy.selectFileTemplate(committeeType)<br/>+ ShortExtractTemplateChain:<br/>трансформация SIMPLE → SHORT<br/>через шаблон
Core->>ECM: SAVE new SHORT file
ECM-->>Core: externalStorageKey
Core-->>ES: Extract (type=SHORT, fileId=...)
end
Note over ES: scope.join()
end
ES->>XDB: saveAll(shortExtracts)
Note over ES: на DuplicateException<br/>→ findActive (cache-hit recovery)
ES-->>Gen: List<Extract> regularShort
end
rect rgb(255, 245, 245)
Note over Gen,ECM: PARALLEL TRACK B — PRKK strip<br/>(готовые PRKK-файлы, без шаблонизации)
Note over Gen: participantsLine =<br/>agenda.participantsByRoles(", ")<br/>— вычисляется один раз
Gen->>PS: stripAll(prkkSimples, questionsMap, participantsLine)
rect rgb(255, 235, 235)
Note over PS,ECM: Внутри PrkkExtractStripper
loop для каждой PRKK-выписки
PS->>ECM: GET file bytes (externalStorageKey = prkkExtractFileId)
ECM-->>PS: original PRKK docx bytes
Note over PS: docx4j manipulation:<br/>— удалить header (service metadata)<br/>— удалить footer (page numbers, etc.)<br/>— inject participants line в тело<br/>⚠️ НЕ ТРОГАТЬ шрифты, стили, разметку<br/>(бизнес-требование: PRKK сохраняет форматирование)
Note over PS: bytes → Map<UUID, byte[]><br/>НЕ сохраняется как Extract entity
end
end
PS-->>Gen: Map<UUID, byte[]> prkkStrippedBytes
end
Note over Gen: AfterPhase3 = (shortExtracts, prkkStrippedBytes)<br/>→ передаётся в ProtocolAssembler (Phase 4)
Regular и PRKK обрабатываются параллельно на уровне Generator'а — это две независимые операции, нет data-dependency между ними. Можно тоже выполнять через StructuredTaskScope для этой диаграммы (но Generator может просто делать CompletableFuture.allOf(...) или аналог).
- Идентичный паттерн Phase 2:
StructuredTaskScope + duplicate-handling.
- Отличие входа: работает с уже-готовой SIMPLE-выпиской (файл существует), превращает её в SHORT через другую цепочку шаблонов.
- Итог: новый файл в ECM, новая
Extract.type=SHORT entity в БД.
- НЕТ шаблонизации. PRKK-файл трогается только на уровне docx4j для удаления header/footer.
- Шрифты и разметка — сохраняются as-is. Это юридически значимая часть документа (ADR-0008).
- Result — raw bytes, не сохраняются как Extract entity. Живут только в памяти в
AfterPhase3.prkkStrippedBytes, передаются в Phase 4 для selective merge.
- participantsLine — строка имён участников заседания (например "Иванов И.И., Петров П.П., ..."), инжектится в тело PRKK-выписки вместо их оригинальной шапки.
Если PRKK_EXTRACT_SKIP_FORMATTING = false (emergency disable) — ветка B не запускается. Все PRKK-вопросы идут через ветку A (обычная шаблонизация, теряя оригинальное форматирование). Это emergency safety-valve (ADR-0008), в steady-state production всегда true.
shortExtracts — файлы в ECM, нормализуются при merge (Arial 10pt, разметка шаблона).
prkkStrippedBytes — bytes в памяти, мержатся as-is через SelectiveMerger.merge(shorts, prkk).
- Итоговый документ — гибрид: наш шаблонный скелет + обработанные SHORT + оригинальные PRKK-части.