Показывает: async-модель генерации протокола — как клиент запускает задачу, получает taskId, и опрашивает статус до готовности.
Актуальные ADR: ADR-0010, ADR-0013.
sequenceDiagram
autonumber
participant Client as Client<br/>(frontend / agenda SM)
participant Ctl as ProtocolTaskController<br/>(protocol.api)
participant Svc as ProtocolGenerationTaskService<br/>(protocol.service)
participant DB as protocol_generation_task<br/>(DB)
participant Exec as protocolTaskExecutor<br/>(virtual threads + ContextPropagating)
participant Gen as ProtocolGenerator<br/>(protocol.service)
%% ── Submission ──
Client->>Ctl: POST /api/v2/protocol/merge/byAgenda/{id}/task
Ctl->>Svc: submit(agendaId)
Svc->>DB: save(task: PENDING, createdBy)
Svc->>Exec: execute(() -> runGeneration(taskId, agendaId))
Note over Exec: ContextSnapshot.captureAll()<br/>— SecurityContext + MDC auto-propagated
Svc-->>Ctl: taskId
Ctl-->>Client: 202 Accepted { taskId, status: PENDING }
%% ── Background generation ──
rect rgb(235, 245, 255)
Note over Exec,Gen: Background (virtual thread)
Exec->>Svc: runGeneration(taskId, agendaId)
Svc->>DB: update status = IN_PROGRESS, updatedAt
Svc->>Gen: generate(agendaId)
Note over Gen: 4-phase pipeline<br/>(см. protocol-generation-pipeline.md)
alt success
Gen-->>Svc: protocolId
Svc->>DB: update status = COMPLETED, protocolId, completedAt
else failure (DomainException или Integration error)
Gen--xSvc: throws
Svc->>DB: update status = FAILED, errorMessage, completedAt
Note over Svc: log.error(...)<br/>НЕ re-throw — fire-and-forget
end
end
%% ── Polling ──
rect rgb(255, 250, 235)
Note over Client,DB: Polling loop (клиент опрашивает с выбранным интервалом)
loop каждые 2-5 сек пока IN_PROGRESS
Client->>Ctl: GET /api/v2/protocol/task/{taskId}/status
Ctl->>Svc: getTask(taskId)
Svc->>DB: findById(taskId)
DB-->>Svc: task
Note over Svc: logOwnershipMismatch(task)<br/>// soft check, WARN only
Svc-->>Ctl: task entity
Ctl-->>Client: 200 OK TaskStatusResponse
end
end
%% ── Terminal states ──
alt status == COMPLETED
Client->>Ctl: (следующий запрос)
Ctl-->>Client: 200 OK { status: COMPLETED, protocolId, completedAt }
Note over Client: использует protocolId для скачивания
else status == FAILED
Ctl-->>Client: 200 OK { status: FAILED, errorMessage, completedAt }
Note over Client: показывает ошибку пользователю
else taskId не найден
Ctl--xClient: 404 ProblemDetail /problems/not-found
end
- Fire-and-forget: controller возвращает
202 сразу после постановки задачи. Клиент обязан polling'ом узнавать статус.
- Ownership — soft:
createdBy сохраняется, но GET /status не блокирует других пользователей. Hard check включится по бизнес-решению (см. questions-to-business).
- Task state transitions:
PENDING → IN_PROGRESS → COMPLETED|FAILED. Обратно не переходит.
- Exceptions в generator ловятся внутри
runGeneration — не пробрасываются наружу. Клиент узнаёт об ошибке только через статус.
- Context propagation — автоматическая через
ContextPropagatingTaskDecorator (ADR-0012).
TaskCleanupScheduler ежедневно в 03:00 удаляет COMPLETED/FAILED задачи старше protocol.task.retention-days (default 7). Независимый scheduled job в modules/app/scheduler/.