Руководство по реализации бизнес‑логики сортировки
Цель: предоставить разработчикам лаконичную, структурированную инструкцию по созданию всей серверной логики сортировки (state machine, REST API, слои сервисов, персистенцию) на базе существующих подходов WMS‑платформы Sigma.
1 Обзор процесса
Бизнес‑этап | REST‑эндпоинты | Ключевые сервисы |
---|---|---|
Старт сортировки | GET /sorting/search , POST /sorting/{batch}/join | SortingSearchService , SortingDocService.start() |
Выбор контейнеров | POST /sorting/{batch}/enter-barcode POST /sorting/{batch}/confirm-temp-containers-prepared | SorterBindingService , TempContainerService |
Скан контейнера / SKU / SN | POST /sorting/{batch}/enter-barcode , POST /sorting/{batch}/enter-serial-number | SortingWorkflow.processBarcode() |
Ввод партии / количества | POST /sorting/{batch}/enter-sku-batch , POST /sorting/{batch}/enter-quantity | SortingWorkflow.setBatch() , SortingWorkflow.setQty() |
Назначить проверку | POST /sorting/{batch}/item-not-available , POST /sorting/{batch}/upload-issue-photo | SortingIssueService.create() |
Выход / отмена | POST /sorting/{batch}/leave , POST /sorting/{batch}/cancel | SortingWorkflow.leave() , SortingWorkflow.cancelItem() |
Диаграмму последовательностей см. в Сортировка.md
; при необходимости синхронизировать шаги.
2 Модель state machine
2.1 Состояния и события
public enum SortingState implements BaseState {
INIT(null),
AWAITING_SORTER(null), // ожидание сканирования SORTER‑я
AWAITING_CONTAINER(null), // ожидание контейнера OTB
AWAITING_SKU(CommonState.AWAITING_SKU),
AWAITING_SKU_BATCH(CommonState.AWAITING_SKU_BATCH),
AWAITING_QUANTITY(CommonState.AWAITING_ITEM_QUANTITY),
AWAITING_SERIAL(CommonState.AWAITING_SERIAL_NUMBER),
REVIEW(null), // просмотр «Неотсортировано»
END(null);
private final CommonState value;
SortingState(CommonState value) { this.value = value; }
@Override public CommonState getValue() { return value; }
}
public enum SortingEvent {
JOIN, LEAVE, CANCEL,
ENTER_SORTER, ENTER_CONTAINER,
ENTER_SKU, ENTER_SKU_BATCH, ENTER_QUANTITY,
ENTER_SERIAL_NUMBER,
ITEM_NOT_AVAILABLE
}
2.2 Действия (Required / Available)
public enum SortingRequiredAction implements BaseRequiredAction {
ENTER_SORTER(null), ENTER_BARCODE(null), ENTER_SKU_BATCH(CommonRequiredAction.ENTER_SKU_BATCH),
ENTER_QUANTITY(CommonRequiredAction.ENTER_QUANTITY), ENTER_SERIAL_NUMBER(CommonRequiredAction.ENTER_SERIAL_NUMBER);
private final CommonRequiredAction value;
SortingRequiredAction(CommonRequiredAction value) { this.value = value; }
@Override public CommonRequiredAction getValue() { return value; }
}
Отдаём RequiredAction фронту → ждём Event → переходим далее.
2.3 Параметры (StateParam / EventParam)
Используем BaseStateParam
и BaseEventParam
максимально. Для новых временных значений вводим TMP_…
, например:
public enum SortingStateParam implements StateParam {
TMP_SELECTED_SORTER(true),
TMP_SELECTED_CONTAINER(true);
private final boolean temp;
SortingStateParam(boolean temp) { this.temp = temp; }
@Override public Boolean getValue() { return temp; }
}
3 Конфигурация машины состояний
@Configuration
@EnableStateMachineFactory(name = "sortingStateMachineFactory")
public class SortingStateMachineConfig
extends EnumStateMachineConfigurerAdapter<SortingState, SortingEvent> {
// guards / actions внедряем через DI
@Override
public void configure(StateMachineStateConfigurer<SortingState, SortingEvent> states) throws Exception {
states.withStates()
.initial(SortingState.INIT)
.state(SortingState.END)
.and().withStates()
.parent(SortingState.INIT)
.initial(SortingState.AWAITING_SORTER)
.state(SortingState.AWAITING_CONTAINER)
.state(SortingState.REVIEW);
}
@Override
public void configure(StateMachineTransitionConfigurer<SortingState, SortingEvent> t) throws Exception {
// === Вход / выход ===
t.withExternal().source(SortingState.AWAITING_SORTER).target(SortingState.AWAITING_CONTAINER)
.event(SortingEvent.ENTER_SORTER).action(actions.enterSorter());
// …другие переходы аналогично примерам Picking*
}
}
3.1 Типовые Guard / Action
- Guard:
enterValidSkuGuard()
– проверяет, что SKU найдена и разрешена к сортировке. - Action:
commitSortingItemAction()
– создаётUnsavedSortingDocFactItem
, проверяет/запрашивает количество, сохраняет.
Переиспользуем базовые классы в ai.sigmation.wms.service.api.employeeapi.state.common
(например, EnterRootSpaceAction
, ClearCurrentDocItemAction
).
4 Персистенция контекста
Создать SortingStateMachinePersist
:
@Component
public class SortingStateMachinePersist
extends BaseStateMachinePersist<SortingState, SortingEvent> {
@Override
protected StateMachineContext<SortingState, SortingEvent> restoreContext(StateKey key) {
var ext = new DefaultExtendedState();
ext.getVariables().put(BaseStateParam.ROOT_DOC_ID, key.getDocumentId());
ext.getVariables().put(BaseStateParam.WORKSPACE_ID, key.getWorkspaceId());
ext.getVariables().put(BaseStateParam.EMPLOYEE, key.getEmployee());
// …доп. загрузка SortingDoc, установка nested контекстов
return new DefaultStateMachineContext<>(SortingState.INIT, null, null, null, ext);
}
}
⚠️ Напоминание: во write()
чистим все TMP_
‐параметры перед сохранением.
5 REST‑контроллер
@RestController
@RequestMapping("/sorting")
@RequiredArgsConstructor
class SortingController {
private final SortingStateMachineFacade facade;
private final BatchEntityService batchEntityService;
@PostMapping("/{batch_id}/enter-barcode")
public ResponseEntity<SortingResponseDto> enterBarcode(@PathVariable("batch_id") UUID batchId,
@RequestBody EnterBarcodeRequestDto dto) throws Exception {
return facade.executeWithStateMachine(batchId, sm -> {
// пример: распознаём barcode и маппим на событие
Message<SortingEvent> msg = messageBuilderFromBarcode(dto, sm);
sm.sendEvent(msg);
return ResponseEntity.ok(buildResponse(sm));
});
}
}
Полный набор методов – см. таблицу API из раздела 1.
6 Документы и хранилище данных
Таблица | Ключевые поля |
---|---|
sorting_doc | id (UUID PK), batch_id , status , created_at , updated_at |
sorting_doc_item | id , sorting_doc_id (FK), sku_id , container_id , serial_number , quantity , state , created_at |
Миграции liquibase находятся в db/changelog/…/sorting/*.xml
.
8 Шаблоны и DTO
-
SortingDocDto
,SortingItemDto
– аналогично Picking. -
SortingResponseDto
← базовыйBaseOperationResponseDto
, содержит:sorting
(документ),items
,required_action
,available_actions
,containers
,skus
,sku_batches
.
9 Рекомендации по коду
- Single‑responsibility: каждый Action/Guard выполняет одну задачу, повторяемую логику выносим в базовые классы.
- Enum‑композиция: ссылки на
CommonState
,CommonRequiredAction
уменьшают дублирование. - Fail‑fast: в Action’ах проверяем входные данные и б росаем
IllegalStateException
, если условия нарушены.
Appendix A. Пример Action‑цепочки «скан SKU → партия → кол-во»
// 1. RequiredAction: ENTER_BARCODE
// 2. Event: ENTER_SKU -> SortingState.AWAITING_SKU_BATCH
// 3. RequiredAction: ENTER_SKU_BATCH
// 4. Event: ENTER_SKU_BATCH -> SortingState.AWAITING_QUANTITY
// 5. RequiredAction: ENTER_QUANTITY
// 6. Event: ENTER_QUANTITY -> commitSortingItemAction(), back to AWAITING_CONTAINER