Tóm tắt luồng Xếp kệ

Luồng xếp kệ đến từ 2 nguồn của service No-diem:

Job xếp kệ (Rescan) API xếp kệ (Vận hành)
Entry point ReScanInsertPkgToPctMarginRouteService.rescanSaveRoute POST /api/v1/add-package-onto-shelf
Phạm vi Toàn bộ BC — phá kệ + xếp lại hàng loạt Một đơn tại thời điểm quét
Trigger Kafka / Async thread từ Xteam REST API đồng bộ được call từ các api xếp kệ từ Xteam-backend
Service xếp đơn lẻ AddPackageOntoShelfService.ontoShelf(..., false) AddAndMovePackageOntoShelfService.ontoShelfontoShelf(..., true)
Strategy IGNORE_MARGIN_CONDITION API_IGNORE_MARGIN_CONDITION
Response Không trả về client (chạy nền) PackageOntoShelfResponse (route + tên kệ)

Chi tiết từng bước + bảng tác động của luồng xếp kệ tại BC

*** Logic xếp kệ cho từng đơn nằm trong logic xếp kệ của cả BC(Ở bước 8) ***

Bước 1 — Lấy pool route hiện tại

Method: getAggregateRouteService.getAllByStationIdAndStatusIdAndTypeIn(...)

Thao tác Bảng
Đọc routes (status = WAITING_PULL, type SORT/CONNECTION)
Đọc route_packages (đơn trong route)

Bước 2 — Xác định route cần phá kệ

Method: filterRouteHasRescan(stationId, isChangeConfig)

Thao tác Bảng
Đọc routes (route mix qua routeSearchAdapter)
Đọc routes + config KBBC / khung giờ (getRouteIdsNeedRescanByKBBC hoặc getAllRouteIdNeedRescanByStationIdAndStatusId)

Chỉ đọc, chưa ghi DB.


Bước 3 — Phá kệ route không đủ điều kiện

Method: removePkgIntoRouteService.removePkgIntoRoute(...)
Bỏ qua nếu route thuộc tụ (checkRouteBelongToFocusPoint → đọc focus_point_packages).

Thao tác Bảng Chi tiết
Sửa route_packages status: WAITING_PULLROUTE_PACKAGE_REMOVE_FOR_RESCAN
Sửa temp_route_packages status tương tự (nếu là đơn tạm)
Thêm no_shelf_packages INSERT đơn với status NOT_YET_SAVE_ROUTE
Thêm tmp_shelf_packages INSERT (nếu là đơn tạm)
Sửa scan_shelf_packages Gán kệ xấu (assignBadShelfForPackageNotOntoStandardShelf)
Sửa routes status: WAITING_PULLROUTE_REMOVE_FOR_RESCAN (khi route rỗng, không còn route_bookings)
Xóa demand_config_routes deleteByRouteIdIn khi route bị xóa hoàn toàn

Đọc kiểm tra: route_bookings (nếu còn đơn booking thì không đổi status routes).


Bước 4 — Dọn bảng cung cầu

Method: demandStationConfigAdapter.removeNormalRouteInDemandTable(...)

Thao tác Bảng
Đọc demand_config_routes, demand_station_configs
Xóa demand_config_routes

Bước 5 — Lấy đơn chưa có route

Method: getAggregateShelfPackageService.getAllByStationIdAndStatusId(stationId, NOT_YET_SAVE_ROUTE)

Thao tác Bảng
Đọc no_shelf_packages
Đọc tmp_shelf_packages (đơn tạm)

Bước 6 — Sắp xếp & giới hạn batch

Method: sortPkgOrderByDistanceToStationcalRelateInfoLogCR.sortPkgOrderByDistanceToStation

Thao tác Bảng
Đọc packages (tọa độ đơn), stations (tọa độ BC)

Bước 7 — Nhóm đơn theo collection

Method: packageCollectionAdapter.getPackageCollectionMap(...)

Thao tác Bảng
Đọc Bảng collection/đối tác (partner_id) — chỉ đọc, không ghi

Bước 8 — Xếp kệ B1 (phần quan trọng nhất)

8A. Đơn lẻ — addPackageOntoShelfService.ontoShelf(...)

Luồng chính (BC dùng strategy IGNORE_MARGIN_CONDITION):

AddPackageOntoShelfService.ontoShelf
  → IgnoreMarginConditionService.addPkgOntoShelf
    → RoutePlanner.planRoute          // tìm route phù hợp
      → PreCreateNewRouteObjService.addPkgToRoute   // TH: ghép vào route có sẵn
      → PreCreateNewRouteObjService.addNewRoute     // TH: không có route → tạo mới

Khi tìm được route phù hợp (ghép đơn vào route cũ)

Layer Method
Chọn route RoutePlanner.allocateRouteselectRouteByPriority
Ghi DB PreCreateNewRouteObjService.addPkgToRoute
Validate SL đơn RoutePackageService.addNewPkgIntoRoute
Transaction ghi SaveAggregateRoutePackageService.saveAll

Chuỗi ghi DB qua saveAll (theo thứ tự @Order):

# Service Bảng Thao tác
0 ValidationBeforeAddPkgIntoRouteService route_packages, routes, xteam_routes Đọc (validate)
1 AddPkgIntoRouteService route_packages INSERT đơn vào route
1 AddPkgIntoRouteService temp_route_packages INSERT (nếu đơn tạm)
2 UpdateRouteTypeConnectionPointService routes Sửa type → CONNECTION (đơn nối điểm)
3 UpdateStatusInShelfPackageService no_shelf_packages / tmp_shelf_packages Sửa status → SAVE_ROUTE_SUCCESS
4 AssignShelfForPackageService physical_shelf_packages INSERT gán kệ vật lý
4 AssignShelfForPackageService scan_shelf_packages INSERT/UPDATE system_shelf_id (tên kệ quét)
5 UpdateDemandConfigRouteService demand_config_routes Sửa total_delivery_time (route cung cầu)

Kết quả trả về: route_id (từ routes), shelf_id / system_shelf_id (tên kệ quét trên scan_shelf_packages).

Khi không có route phù hợp → tạo route mới

Layer Method
Tạo route PreCreateNewRouteObjService.addNewRouteRouteAndRoutePackageService.createNewRouteAndRoutePackage
Ghi route DefaultRouteShelfAdapter.createNewRouteAndRoutePackage
Bảng Thao tác
routes INSERT (status WAITING_PULL, gán shelf_id)
route_packages INSERT đơn đầu tiên
demand_config_routes INSERT (nếu route cung cầu)
no_shelf_packages Sửa status
physical_shelf_packages, scan_shelf_packages INSERT/UPDATE (qua saveAll như trên)

8B. Đơn gom (collection) — multiXPackageIntoRouteService.intoRoute(...)

MultiXPackageIntoRouteServiceImpl.intoRoute
  → CepMultiXPackageIntoRouteService.into        // route có đơn nội tỉnh
  → CepMultiXPackageIntoDRouteService.into     // route chỉ đơn D
  → multiPackageNewRouteService.createNewRoute // fallback: tạo mới

Khi tìm được route phù hợp

Layer Method
Lọc route CepRouteInputFilter.filterMultiPackage
Tính margin CepRouteDiffBuilder.build + RouteDistanceFilter
Chọn route tốt nhất ChooseBestRouteCollector.choose
Ghi DB MultiPackageRoutePackageService.addMultiPackageIntoRouteSaveAggregateRoutePackageService.saveAll

Bảng tác động: giống bảng ở mục 8A (route_packages INSERT, no_shelf_packages UPDATE, scan_shelf_packages UPDATE, demand_config_routes UPDATE...).

Khi tạo route mới cho nhóm đơn

Method Bảng
DefaultRouteShelfAdapter.createNewRouteMultiPackage INSERT routes + INSERT nhiều route_packages + chuỗi saveAll như trên

Bước 9 — Xếp kệ B3: Nối điểm

Method: rescanConsecutivePackageService.saveRoute(stationId) (nếu config bật)

Sub-step Method Bảng tác động
B3.1 Ghép đơn nối điểm vào route có sẵn RescanAdditionalConsecutivePackageServiceConsecutivePackageIntoRouteAlreadyExistsService INSERT route_packages, Sửa routes.type, Sửa no_shelf_packages, scan_shelf_packages
B3.2 Tạo route nối điểm mới RescanAdditionalConsecutiveRouteServiceConsecutivePackageIntoRouteService INSERT routes, route_packages

Bước 10 — Gán kệ xấu cho đơn còn sót

Method: saveShelfAssignToPackageService.assignBadShelfForPackage(stationId, pkgOrders)

Thao tác Bảng
Đọc route_packages (lọc đơn đã có route hợp lệ)
Sửa/Thêm scan_shelf_packages

Sơ đồ ghi DB khi tìm được route phù hợp (đơn lẻ)

[Diagram]

Bảng cốt lõi cần nhớ

Bảng Vai trò trong luồng
routes Tập route/kệ logic; INSERT khi tạo mới, UPDATE status khi phá kệ
route_packages Liên kết đơn ↔ route; INSERT khi ghép đơn, UPDATE status khi phá kệ
no_shelf_packages Hàng đợi đơn chưa xếp kệ; INSERT khi phá kệ, UPDATE khi xếp thành công
scan_shelf_packages Tên kệ quét đơn (system_shelf_id); INSERT/UPDATE khi gán kệ
physical_shelf_packages Kệ vật lý thực tế
demand_config_routes Route cung cầu; DELETE khi phá kệ, INSERT/UPDATE khi tạo/ghép
temp_route_packages / tmp_shelf_packages Đơn tạm (nếu có)

Nếu cần, tôi có thể vẽ tiếp sequence diagram chi tiết cho một case cụ thể (đơn lẻ ghép route cũ vs đơn gom tạo route mới).

[Xteam-backend] Call chain

Tất cả các luồng xếp kệ bên Xteam-backend đều đi qua một điểm gọi ND duy nhất:

ND endpoint: POST ${add-package-onto-shelf} → mặc định api/v1/add-package-onto-shelf

NoDiemRestClient.getAddPackageToShelf()
  ← DefaultNdService.addPkgOntoShelf()
    ← (2 nhánh chính bên dưới)

2 nhánh code gọi ND

Nhánh A — NewWrapCheckinUseCaseImpl

getAddPackageToShelfResponse()ndService.addPkgOntoShelf()

Gọi từ: handleForNewStationConfigs, handleForNotRunStation, addPkgOntoShelfHoldHUB

Nhánh B — PackageCheckInToShelfAbstract.callNdToFindShelf()

PackageCheckInToShelfUseCase.process() → strategy → updateShelf()chỉ gọi ND khi không tìm được kệ hệ thống sẵn (findGoodsSystemShelf empty)

Strategy typeId
DefaultDelayPackageCheckInToShelfStrategy IMPORT_PKG_CHECKIN, SCAN_PILLAR_TO_BAG, CONFIRM_DELAY_PACKAGE, CONFIRM_DELAY_CIC
DefaultYcckCheckInPackageOntoShelfStrategy YCCK_CHECKIN, APPROVED_YCCK_CHECKIN, CRON_APPROVED_YCCK_CHECKIN

API xếp kệ gọi ND tương ứng với từng luồng xếp kệ hiện tại đang có của Xteam

Có thể một số luồng outdate không còn chạy, có thể nhờ BA verify kết hợp check log Kibana

Nhánh A — NewWrapCheckinUseCase

Method Endpoint Use case Ghi chú
POST /api/v1/pk-bus/packages/checkin NewCheckInPkBusUseCaseImpl Đơn kệ xấu → addPkgOntoShelfSupportTempDataWhenNotRunConfig
POST /api/v1/pk-bus/packages/rescan-checkin ProcessRescanPackageUseCaseImpl wrapCheckinUseCase.checkin()
POST /api/v1/pk-bus/packages/scan-ycck ScanYCCKPackageUseCaseImpl.checkIn Luồng cũ (BC không trong config mới)
POST /api/v1/pk-bus/packages/scan-ycck-v2 ScanYCCKPackageUseCaseImpl.checkInV2 Phần BC không trong STATIONS_RUN_NEW_RESCAN_PACKAGE_CHECKIN_SHELF + luồng HOLD_HUB
POST /api/v1/pk-bus/packages/scan-pillar NewCheckInPkBusUseCaseImpl BC không trong config mới → processHandleOldScanPillar
POST /api/v1/pk-bus/packages/scan-import-packages ScanImportPackageUseCaseImpl BC không trong config mới; luồng consecutive cũng gọi checkin()
POST /api/v1/pk-bus/packages/scan-import-psc-point ScanImportPackageUseCaseImpl Tương tự scan-import
POST /api/v1/distributor/checkin-package-shelf CheckinPackagesNotOnShelf handleForNewStationConfigs() khi đơn có trong no_shelf_packages
POST /api/v1/service/distributor/packages/confirm-export-for-pk AddPkYcckOntoShelfRunNewScanShelfServiceImpl BC chạy luồng xếp kệ mới → handleForNewStationConfigs

Nhánh B — PackageCheckInToShelfUseCase

Method Endpoint Use case typeId
POST /api/v1/pk-bus/packages/scan-pillar NewCheckInPkBusUseCaseImpl SCAN_PILLAR_TO_BAG
POST /api/v1/pk-bus/packages/scan-import-packages ScanImportPackageUseCaseImpl IMPORT_PKG_CHECKIN
POST /api/v1/pk-bus/packages/scan-import-psc-point ScanImportPackageUseCaseImpl IMPORT_PKG_CHECKIN
POST /api/v1/pk-bus/packages/scan-ycck-v2 ScanYCCKPackageUseCaseImpl.checkInV2 YCCK_CHECKIN
POST /api/v1/service/packages/push-to-pool-package PushDelayPackageToPoolUseCaseImpl CONFIRM_DELAY_PACKAGE

API duyệt YCCK → gọi ND (gián tiếp)

Method Endpoint Luồng gọi ND
POST /api/v1/distributor/confirm-export/confirm-export-packages-of-cod ApproveXteamReportCustomerService.processPutOrderOnToShelf → nhánh B (APPROVED_YCCK_CHECKIN)
POST /api/v1/cod/pillar/confirm-export-package Tương tự qua approve()
POST /api/v1/cod/pillar/confirm-export-package-v2 Tương tự
POST /api/distributor/v1/nvbc/confirm-xteam-report-customer ConfirmKeepPkgUseCaseConfirmCustomerReportServiceapprove()
POST api/service/v1/xteam-nvbc/confirm-report-customer ConfirmXteamKeepPkgUseCase → tương tự

Lưu ý: processPutOrderOnToShelf chỉ chạy khi BC thuộc luồng mới (STATIONS_RUN_NEW_RESCAN_PACKAGE_CHECKIN_SHELF rỗng hoặc BC nằm trong config). Các BC còn lại dùng luồng kệ xấu local, không gọi ND tại bước duyệt.


Job / Cron gọi ND

Job Schedule Luồng typeId
CronOutboxEventConfirmYCCKEvent @Scheduled(fixedDelay=5000) Outbox CONFIRM_YCCK_EVENT (sau duyệt YCCK) → defaultPackageCheckInToShelfUseCase.process CRON_APPROVED_YCCK_CHECKIN
CronOutboxConfirmDelayPackageCloseShiftEvent @Scheduled(fixedDelay=10000) Outbox delay chốt ca → pushDelayPackageToPool → nhánh B CONFIRM_DELAY_PACKAGE / CONFIRM_DELAY_CIC
CronAddPackagesCloseShifts Handler chain AutoOpenToBagAfterConfirmedDelayHandlerpackageCheckInToShelfUseCase.process CONFIRM_DELAY_PACKAGE / CONFIRM_DELAY_CIC

Reference solution

Mô tả nhanh hiện trạng và cải tiến

1. Hiện trạng hệ thống

Hiện tại, logic tìm kệ cho đơn hàng(AddPackageOntoShelfService::ontoShelf) đang được xử lý đồng thời với logic định tuyến (routing) và chia thành 2 trường hợp:


2. Đề xuất cải tiến (To-Be)

Tách biệt hoàn toàn logic xếp kệ và logic định tuyến thành 2 bài toán độc lập bên trong AddPackageOntoShelfService::ontoShelf. Quy trình mới được quy định như sau:

2.1. Logic Tìm Kệ (shelf_id)

Hệ thống sẽ xác định kệ cho đơn hàng dựa trên địa chỉ giao hàng và phân loại thời gian giao, thay vì phụ thuộc vào trạng thái của Route:

Lưu ý: Tham khảo và tái sử dụng logic cũ đã triển khai trong hàm ShelfServiceImpl::buildMapPkgOrderWithWardName.

2.2. Logic Tìm Route Phù Hợp (Dịch vụ Routing mới)

Thiết kế và xây dựng một Service Routing mới độc lập. Yêu cầu kỹ thuật bao gồm:


Vị trí triển khai: Toàn bộ logic phân tách và điều hướng trên sẽ được quản lý và xử lý tập trung tại hàm AddPackageOntoShelfService::ontoShelf. Sau có nhu cầu refactoring code hoặc logic chọn và phá kệ có thể sửa trong job xếp kệ