fd3603a086
- 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.
13 KiB
13 KiB
Жизненный цикл статусов плана ТГУ
Справочник по статусам business-plan в расписании ТГУ: когда наступает каждый статус, что он
означает и какой переход его порождает. Источник правды — enum
PlannedPlanStatus;
переходы — в service/IssueDuePlanWorker, service/HandlePlanDecisionUseCase,
service/CreateDraftUseCase, service/CreateChainPlanUseCase, dto/PlanResponse.
Эти же статусы (кроме «зарезервированных», см. ниже) показаны в легенде вкладки «Планы»
(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.
- ACCEPTED →
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— за сколько до старта авто-воркер начинает выдачу головы цепочки.