9.4 KiB
Dense WCS Pull and Warehouse Routing Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Keep warehouse 03 inbound jobs pending for WCS pull, prevent duplicate active tray jobs, route push-mode jobs to each warehouse's configured WCS, and remove inbound-point selection from PDA tray binding.
Architecture: Warehouse 03 is the dense warehouse. PDA binding creates a status-2/phase-0 job without an entrance or outbound HTTP call; POST /api/external/wcs/inbound/job atomically claims that job by tray, writes the WCS-provided entrance and height, changes it to status-3/phase-1, and returns the stored job. Other warehouses keep push mode, but WcsClient resolves a warehouse-specific base URL from configuration. A SQL Server unique filtered index is the final concurrency-safe guarantee that mydo_job.tray_code is unique.
Tech Stack: Java 17, Spring Boot, MyBatis XML, SQL Server, JUnit 5/Mockito/AssertJ, Vue 2 uni-app.
File map
- Modify
ruoyi-admin/src/main/java/com/ruoyi/api/config/ExternalProp.java: support WCS base URLs keyed by warehouse ID. - Modify
ruoyi-admin/src/main/java/com/ruoyi/api/client/BaseExternalClient.java: resolve an API URL against a supplied base URL. - Modify
ruoyi-admin/src/main/java/com/ruoyi/api/client/WcsClient.java: route job push calls byjob.warehouseId. - Create
ruoyi-admin/src/main/java/com/ruoyi/api/domain/WcsDenseInboundJobRequest.java: tray barcode, entrance number, and cargo height request. - Modify
ruoyi-admin/src/main/java/com/ruoyi/api/controller/ExternalApiController.java: expose the dense WCS job-pull endpoint. - Modify
ruoyi-admin/src/main/java/com/ruoyi/in/job/mapper/MydoJobMapper.javaandruoyi-admin/src/main/resources/mapper/in/MydoJobMapper.xml: atomic dense-job claim and tray-existence contracts. - Modify
ruoyi-admin/src/main/java/com/ruoyi/in/job/service/IMydoJobService.javaandruoyi-admin/src/main/java/com/ruoyi/in/job/service/impl/MydoJobServiceImpl.java: claim/validate the dense job and give duplicate trays a business error. - Modify
ruoyi-admin/src/main/java/com/ruoyi/pda/domain/BindTrayRequest.java,ruoyi-admin/src/main/java/com/ruoyi/pda/domain/AddInJobRequest.java, andruoyi-admin/src/main/java/com/ruoyi/pda/service/impl/PdaServiceImpl.java: make entrance optional for binding and branch dense pull mode from ordinary push mode. - Modify
pda/pages/warehouse/inbound/bindTray.vueandpda/api/warehouse/inbound.js: remove inbound-point loading, selection, validation, and submission. - Modify
ruoyi-admin/src/main/resources/application.yml: document independently configurable warehouse WCS base URLs. - Create
sql/20260714_mydo_job_tray_unique_and_wcs_pull.sql: fail clearly on existing duplicates and add the unique index. - Add/modify focused tests under
ruoyi-admin/src/test/java/com/ruoyi/api,ruoyi-admin/src/test/java/com/ruoyi/in/job, andruoyi-admin/src/test/java/com/ruoyi/pda.
Task 1: Prove dense pending and station-free PDA binding
- Add service tests with a warehouse-03 request whose
stationPointIdis null. Assert the generated job is initialized withstatus="2",executionPhase="0", blankentranceId, blank roadway/location, andwcsClient.sendPutawayTaskis never called. - Add the ordinary-warehouse counterpart. Stub
selectAvailableInboundRoadwayand assert the job is still pushed, while no station is occupied and no entrance is written. - Run
mvn -pl ruoyi-admin -am -Dtest=PdaServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test; expect the new assertions to fail before implementation. - Implement a station-free branch equivalent to:
if (DENSE_WAREHOUSE_ID.equals(request.getWarehouseId())) {
job.setStatus("2");
job.setExecutionPhase("0");
return resultMsg + ";密集库作业已生成,等待WCS获取";
}
job.setRoadwayId(String.valueOf(selectAvailableInboundRoadway(
request.getTrayCode(), request.getWarehouseId()).get("roadway_id")));
return dispatchInboundJob(job, resultMsg);
- Remove station-point controls and
stationPointIdfrom the PDA payload, then re-run the focused test.
Task 2: Add concurrency-safe tray uniqueness
- Add mapper/service tests asserting a nonblank tray already in
mydo_jobis rejected with托盘已存在未完成作业before insert/procedure execution. - Add an idempotent SQL migration test requiring a duplicate preflight query and a unique filtered index on
mydo_job(tray_code). - Implement
countMydoJobByTrayCode(String trayCode)and call it at all application job-insert entry points touched by this flow. - Create the migration with this invariant:
IF EXISTS (
SELECT tray_code FROM dbo.mydo_job
WHERE tray_code IS NOT NULL
GROUP BY tray_code HAVING COUNT_BIG(*) > 1
)
THROW 51000, N'mydo_job存在重复托盘条码,请清理后重试', 1;
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'dbo.mydo_job') AND name = N'UX_mydo_job_tray_code')
CREATE UNIQUE NONCLUSTERED INDEX UX_mydo_job_tray_code
ON dbo.mydo_job(tray_code) WHERE tray_code IS NOT NULL;
- Run focused mapper/service/migration tests and verify pass.
Task 3: Route push-mode jobs by warehouse WCS address
- Add
WcsClientTestcases with distinct01and02base URLs and assert each job posts to its own URL. - Extend configuration with
warehouseBaseUrlsand deterministic fallback to the existing WCSbaseUrlwhen a warehouse has no override. - Resolve job URLs as:
String url = config().getApiUrl(apiKey, job.getWarehouseId());
return post(url, body, interfaceName, this::parseWcsAck, MsgTypeEnum.JSON);
- Configure
01,02, and03with independent environment-variable overrides inapplication.yml, without committing credentials. - Run
mvn -pl ruoyi-admin -am -Dtest=WcsClientTest -Dsurefire.failIfNoSpecifiedTests=false testand verify pass.
Task 4: Add the dense WCS job-pull endpoint
- Add controller/service tests for missing tray, entrance, unsupported height, unknown tray, non-03 warehouse, non-inbound job, successful claim, and idempotent same-value retry.
- Define the request fields and aliases:
private String trayCode;
private String entranceId;
private BigDecimal height;
- Add mapper CAS SQL that updates only warehouse 03 inbound jobs in pending state:
UPDATE mydo_job
SET entrance_id = #{entranceId}, height = #{height}, status = '3',
execution_phase = '1', start_time = COALESCE(start_time, CONVERT(varchar(19), GETDATE(), 120))
WHERE job_id = #{jobId} AND warehouse_id = '03' AND in_or_out < 20
AND status = '2' AND execution_phase = '0';
- Expose
POST /api/external/wcs/inbound/job, invoke the transactional claim service, reload the persisted row, and return it in the established WCScode/message/dataresponse. - Run controller and service tests and verify pass.
Task 5: Full verification and completion audit
- Run
mvn -pl ruoyi-admin -am test; expectBUILD SUCCESS. - Run the PDA build command available in
pda/package.json; if no build script exists, run a static import/reference check proving the removed station API is no longer referenced by the bind page. - Run
cd ruoyi-ui && npm run build:prodif shared warehouse/config UI files were changed. - Inspect the final diff and map each of the five numbered requirements to direct code plus test/migration evidence; do not mark complete if any item lacks evidence.
Task 6: Correct point selection scope to warehouse 03 only
Files:
-
Modify:
pda/pages/warehouse/inbound/bindTray.vue -
Modify:
ruoyi-admin/src/main/java/com/ruoyi/pda/service/impl/PdaServiceImpl.java -
Test:
ruoyi-admin/src/test/java/com/ruoyi/pda/service/impl/PdaServiceImplTest.java -
Step 1: Add differentiated service tests
Assert warehouse 01/02 binding without stationPointId fails with 请选择入库点位, while warehouse 03 succeeds without a station and never calls station-point services.
- Step 2: Restore conditional PDA selection
Use the exact dense-warehouse predicate and conditional submit rule:
isDenseWarehouse() {
return this.warehouseId === '03'
}
if (!this.isDenseWarehouse && !this.stationPointId) {
this.$modal.msg('请选择入库点位')
return
}
Show/load/reset the station picker only when warehouseId !== '03'; submit stationPointId for ordinary warehouses and omit it for 03.
- Step 3: Restore backend enforcement
In bindTrayAndAddInJob, reject a missing point for non-03 warehouses, pass the selected point into AddInJobRequest, and explicitly clear it for warehouse 03:
if (!DENSE_WAREHOUSE_ID.equals(request.getWarehouseId())
&& !StringUtils.hasText(request.getStationPointId())) {
throw new RuntimeException("请选择入库点位");
}
addInJobRequest.setStationPointId(DENSE_WAREHOUSE_ID.equals(request.getWarehouseId())
? null : request.getStationPointId());
- Step 4: Verify corrected behavior
Run mvn -pl ruoyi-admin -am -Dtest=PdaServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test, compile the PDA SFC template/script, and run the full Maven test suite. Expected: warehouse 01/02 require points, warehouse 03 does not, and all tests pass.