pcp-tgu: статусы плана — актуальная легенда с тултипами + док жизненного цикла
- STATUS_STYLES в порядке жизненного цикла: убран легаси ACCEPTED, добавлены LAID_IN/NOT_ACCEPTED/LAYIN_FAILED, у каждого статуса человеческое описание. - Легенда вкладки «Планы»: тултипы по наведению (title=description), убрана устаревшая приписка про стрелки. - TimelineStatusStyle: добавлено обязательное поле description. - HandlePlanDecisionUseCase: TODO(layin-confirmed) — обработчик приёма события «план заложен» (AWAITING_LAYIN → LAID_IN) ещё не реализован. - docs/architect/PCP_TGU_PLAN_STATUS_LIFECYCLE.md: справочник по каждому статусу (когда наступает / что значит / какой переход), диаграмма, зарезервированный ACCEPTED.
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# Жизненный цикл статусов плана ТГУ
|
||||
|
||||
Справочник по статусам business-plan в расписании ТГУ: когда наступает каждый статус, что он
|
||||
означает и какой переход его порождает. Источник правды — enum
|
||||
[`PlannedPlanStatus`](../../services/pcp-tgu-service/src/main/kotlin/space/nstart/pcp_tgu_service/entity/PlannedPlanStatus.kt);
|
||||
переходы — в `service/IssueDuePlanWorker`, `service/HandlePlanDecisionUseCase`,
|
||||
`service/CreateDraftUseCase`, `service/CreateChainPlanUseCase`, `dto/PlanResponse`.
|
||||
|
||||
Эти же статусы (кроме «зарезервированных», см. ниже) показаны в легенде вкладки «Планы»
|
||||
([`tguStatus.ts`](../../services/pcp-tgu-ops-ui/src/features/tgu-planning/tguStatus.ts)) и задают
|
||||
цвет плашек на таймлайне.
|
||||
|
||||
## Диаграмма переходов (упрощённо)
|
||||
|
||||
```
|
||||
создать черновик
|
||||
(нет плана) ───────────────────────────────► DRAFT
|
||||
│ promote («Применить») / авто-выдача
|
||||
▼
|
||||
проекция будущего ····(виртуально)····► PLANNED ──(подошёл срок, авто-выдача)──► ISSUING
|
||||
│ BPMN-процесс стартовал
|
||||
▼
|
||||
WAITING_DECISION
|
||||
ACCEPTED-решение │ │ REJECTED-решение
|
||||
▼ ▼
|
||||
AWAITING_LAYIN REJECTED (цепочка на паузе)
|
||||
│
|
||||
┌────────────────────────────────┼────────────────────────────────┐
|
||||
событие «заложен» событие «не заложен» событие «не принят»
|
||||
(пока не реализовано) ▼ ▼
|
||||
▼ LAYIN_FAILED NOT_ACCEPTED
|
||||
LAID_IN (срочная перевыдача) (срочная перевыдача)
|
||||
|
||||
при сбое старта BPMN из ISSUING ─────────────────────────────────────────► START_AMBIGUOUS
|
||||
```
|
||||
|
||||
## Статусы
|
||||
|
||||
### DRAFT — черновик
|
||||
- **Когда.** Оператор создаёт план через «Создать план» (`CreateChainPlanUseCase`) или «Добавить»/
|
||||
редактирование (`CreateDraftUseCase`).
|
||||
- **Что значит.** План в стадии правки. Персистится, но **не** является головой цепочки, **не**
|
||||
попадает в проекцию/read-путь как запланированный слот и **не** выдаётся авто-воркером.
|
||||
- **Дальше.** Становится активным только после promote («Применить») — переходит в `ISSUING`.
|
||||
Удаление черновика (abandon) убирает его и клон-миссию.
|
||||
- В UI помечен как «ЧЕРНОВИК», виден только при включённом режиме «Черновики».
|
||||
|
||||
### PLANNED — проекция будущего (виртуальный)
|
||||
- **Когда.** Вычисляется на лету `PlanProjectionService`/`PlanResponse.projected()` при чтении
|
||||
расписания. **Никогда не пишется в БД.**
|
||||
- **Что значит.** Предполагаемый будущий слот цепочки (следующие ЗРВ) — «как пойдёт цепочка, если
|
||||
ничего не менять». Не несёт `basePlanId`, стартом цепочки не считается.
|
||||
- **Дальше.** Когда подходит срок выдачи, по этому слоту авто-воркер материализует реальный план
|
||||
сразу в `ISSUING` (`materializeIssuingPlan` → `insertIssuingPlan`).
|
||||
- В UI виден только при включённом тумблере «Возможные будущие планы».
|
||||
|
||||
### ISSUING — идёт выдача
|
||||
- **Когда.** `IssueDuePlanWorker` начинает выдачу: promote черновика (`prepareDraftPromotion`),
|
||||
ручная (пере)выдача конкретного плана (`prepareSpecificPlan`) или авто-материализация головы
|
||||
проекции (`materializeIssuingPlan`).
|
||||
- **Что значит.** Запущен BPMN-процесс выдачи плана на борт; создан attempt со статусом `STARTING`.
|
||||
- **Дальше.** Успешный старт процесса → `WAITING_DECISION` (`markStarted`). Сбой старта BPMN →
|
||||
`START_AMBIGUOUS` (`markAmbiguous`).
|
||||
|
||||
### WAITING_DECISION — ожидает решения
|
||||
- **Когда.** BPMN-процесс выдачи успешно стартовал, attempt → `STARTED` (`markStarted`).
|
||||
- **Что значит.** План выдан, ждём решения оператора/борта: ACCEPTED либо REJECTED.
|
||||
- **Дальше.**
|
||||
- ACCEPTED → `AWAITING_LAYIN` (`acceptPlan`).
|
||||
- REJECTED → `REJECTED` (`rejectPlan`).
|
||||
- **Grace-окно.** Если решения нет дольше `waitingDecisionGraceMinutes` после `startTime`, цепочка
|
||||
«молчаливо» едет дальше: якорь сдвигается на этот план, блокировка снимается, но **статус
|
||||
остаётся `WAITING_DECISION`** (`tryAdvancePastUnconfirmedPlan`). Запоздалое решение запишется на
|
||||
attempt и ушедшую вперёд цепочку не тронет. Поэтому в UI кнопки решения гасятся после grace.
|
||||
|
||||
### AWAITING_LAYIN — принят, ждёт подтверждения закладки
|
||||
- **Когда.** Пришло решение ACCEPTED (`acceptPlan`).
|
||||
- **Что значит.** План принят и **оптимистично считается заложенным**; это текущий **базовый план
|
||||
цепочки** (`state.currentBasePlanId = plan.planId`, `chainVersion += 1`). Запускается джоб
|
||||
обновления слотов принятого плана (`acceptedPlanSlotsUpdateJobService.enqueue`).
|
||||
- **Дальше.** При подтверждении закладки → `LAID_IN` (терминальный успех; **событие пока не
|
||||
реализовано**, см. ниже). При негативе — `LAYIN_FAILED`/`NOT_ACCEPTED`.
|
||||
- В легенде это де-факто «принятый/заложенный» статус (зелёный). Перевыдача в свой слот ещё
|
||||
возможна, пока до старта больше `layinFreezeMinutes` (после — «точка невозврата», только
|
||||
fork-forward).
|
||||
|
||||
### REJECTED — отклонён
|
||||
- **Когда.** Пришло решение REJECTED (`rejectPlan`).
|
||||
- **Что значит.** План признан неправильным. Авто-перевыдача **не** взводится (повторять тот же план
|
||||
бессмысленно): активная выдача снимается, срочная перевыдача гасится, цепочка ставится **на паузу**
|
||||
(`pausedAfterReject = true`) — проекция становится пустой, воркер ничего не выдаёт. Базовый план не
|
||||
трогается.
|
||||
- **Дальше.** Оператор вручную создаёт исправленный план; его promote/ручная выдача снимают паузу.
|
||||
- В UI у КА с паузой рисуется пометка «⏸ на паузе».
|
||||
|
||||
### NOT_ACCEPTED — не принят (негативный фидбек)
|
||||
- **Когда.** Пришло внешнее событие «не принят» по текущему базовому плану в `AWAITING_LAYIN`
|
||||
(`handleNotAccepted` → `applyNegativeFeedback`).
|
||||
- **Что значит.** Терминальный негатив по базовому плану. В отличие от REJECTED — это **технический**
|
||||
негатив, поэтому запускается **срочная перевыдача** на ближайшую ЗРВ (`reissueOnNearestWindow`).
|
||||
- Событие, пришедшее не по текущему базовому плану (устаревшее/дубль), игнорируется.
|
||||
|
||||
### LAYIN_FAILED — не заложен (негативный фидбек)
|
||||
- **Когда.** Пришло внешнее событие «не заложен» по текущему базовому плану в `AWAITING_LAYIN`
|
||||
(`handleLayinFailed` → `applyNegativeFeedback`).
|
||||
- **Что значит.** Терминальный негатив: закладка не удалась. Поведение сейчас идентично
|
||||
`NOT_ACCEPTED` — **срочная перевыдача** на ближайшую ЗРВ.
|
||||
|
||||
### START_AMBIGUOUS — неопределённый старт
|
||||
- **Когда.** Из `ISSUING` не удалось достоверно стартовать BPMN-процесс выдачи (исключение в
|
||||
`startPreparedAttempt` → `markAmbiguous`); attempt → `AMBIGUOUS`.
|
||||
- **Что значит.** Неизвестно, ушёл план на борт или нет — требуется ручной разбор оператором.
|
||||
Опасный статус, в UI выделен пунктиром.
|
||||
|
||||
### LAID_IN — заложен (терминальный успех)
|
||||
- **Когда (целевое).** При подтверждении закладки базового плана из `AWAITING_LAYIN`.
|
||||
- **Что значит.** Подтверждённая закладка, терминальный успех; «точка невозврата» — дальше план
|
||||
меняется только через fork-forward (используется в гардах `CreateDraftUseCase`,
|
||||
`assertReissuableInSlot`).
|
||||
- **⚠️ TODO — не реализовано.** Потребителя события «план заложен» сейчас **нет**, поэтому план в
|
||||
`LAID_IN` не переходит и остаётся в `AWAITING_LAYIN`. Нужно: Kafka-consumer (по аналогии с
|
||||
`LayinFailedConsumer`/`PlanNotAcceptedConsumer`) + `handleLayinConfirmed` в
|
||||
`HandlePlanDecisionUseCase` (`AWAITING_LAYIN → LAID_IN` по текущему базовому плану). Маркер-TODO
|
||||
стоит в коде `HandlePlanDecisionUseCase` рядом с обработчиками негативного фидбека.
|
||||
- В легенде статус показан (зелёный, темнее `AWAITING_LAYIN`) — заранее, чтобы при появлении события
|
||||
цвет уже был.
|
||||
|
||||
## Зарезервированные / не возникающие сейчас статусы
|
||||
|
||||
- **ACCEPTED** — в enum присутствует, но приёмка (`acceptPlan`) переводит план **сразу** в
|
||||
`AWAITING_LAYIN`, минуя `ACCEPTED`. Фактически это легаси-значение, нигде не присваивается; в
|
||||
легенду вкладки «Планы» намеренно не вынесено.
|
||||
|
||||
## Связанные конфиги (`PlanningProperties`)
|
||||
- `waitingDecisionGraceMinutes` — сколько ждём решение после старта, прежде чем «молчаливо» ехать
|
||||
дальше (см. `WAITING_DECISION`).
|
||||
- `layinFreezeMinutes` — за сколько минут до старта окно выдачи замораживается («точка невозврата»):
|
||||
перевыдача в свой слот запрещена, только fork-forward.
|
||||
- `notificationBeforeStartMinutes` — за сколько до старта авто-воркер начинает выдачу головы цепочки.
|
||||
|
||||
## См. также
|
||||
- [PCP_ARCHITECTURE.md](PCP_ARCHITECTURE.md)
|
||||
- [PCP_TGU_OPS_UI_CAPABILITY_MAP.md](PCP_TGU_OPS_UI_CAPABILITY_MAP.md)
|
||||
Reference in New Issue
Block a user