Перейти к основному содержимому

Руководство по реализации бизнес‑логики сортировки

Цель: предоставить разработчикам лаконичную, структурированную инструкцию по созданию всей серверной логики сортировки (state machine, REST API, слои сервисов, персистенцию) на базе существующих подходов WMS‑платформы Sigma.

1  Обзор процесса

Бизнес‑этапREST‑эндпоинтыКлючевые сервисы
Старт сортировкиGET /sorting/search, POST /sorting/{batch}/joinSortingSearchService, SortingDocService.start()
Выбор контейнеровPOST /sorting/{batch}/enter-barcode
POST /sorting/{batch}/confirm-temp-containers-prepared
SorterBindingService, TempContainerService
Скан контейнера / SKU / SNPOST /sorting/{batch}/enter-barcode, POST /sorting/{batch}/enter-serial-numberSortingWorkflow.processBarcode()
Ввод партии / количестваPOST /sorting/{batch}/enter-sku-batch, POST /sorting/{batch}/enter-quantitySortingWorkflow.setBatch(), SortingWorkflow.setQty()
Назначить проверкуPOST /sorting/{batch}/item-not-available, POST /sorting/{batch}/upload-issue-photoSortingIssueService.create()
Выход / отменаPOST /sorting/{batch}/leave, POST /sorting/{batch}/cancelSortingWorkflow.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_docid (UUID PK), batch_id, status, created_at, updated_at
sorting_doc_itemid, 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