docs: plan dense outbound relocation dispatch

This commit is contained in:
zgw
2026-07-16 09:30:01 +08:00
parent 94a48122b1
commit 3ac7305724

View File

@@ -0,0 +1,443 @@
# Dense Outbound Relocation And Dispatch 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:** Add durable WCS outbound retry compensation, dense-lane outermost enforcement, automatic/manual same-floor relocation, and dense-lane outbound workflows without changing existing inbound or non-dense outbound behavior.
**Architecture:** Keep `mydo_job` as the business record and add a one-to-one `wcs_job_dispatch` outbox record. Route every outbound push through a single dispatch service that atomically claims attempts and calls a dense-lane eligibility service before selecting a type-specific WCS endpoint. Build relocation and lane-outbound workflows on shared dense-lane queries so automatic allocation, scheduled retry, manual push, location outbound, KPI relocation, and lane outbound enforce the same rules.
**Tech Stack:** Java 17, Spring Boot, MyBatis XML, SQL Server, Quartz through RuoYi `sys_job`, JUnit 5/Mockito/AssertJ, Vue 2, Element UI, Maven 3.9.11, npm.
## Global Constraints
- Preserve current RuoYi controller/domain/mapper/service/service.impl layering and naming style.
- Do not alter the existing inbound WCS pull flow or non-03 warehouse outbound allocation semantics.
- Apply dense-lane rules only to warehouse `03` cargo spaces with non-empty `group_coding`.
- `in_or_out=23` is relocation and uses `util_serial_task '23'`.
- Automatic WCS retries run at most five times after the initial failed call.
- Relocation source and destination must be in the same warehouse and same floor, but different channel groups.
- All WCS dispatch entry points must enforce current outermost eligibility immediately before the HTTP call.
- Use `D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd` for Maven commands.
- Use repeatable SQL Server migration statements; do not delete existing business data.
---
### Task 1: Dispatch Outbox Schema And Persistence
**Files:**
- Create: `sql/20260716_dense_outbound_relocation_dispatch.sql`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dispatch/domain/WcsJobDispatch.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dispatch/mapper/WcsJobDispatchMapper.java`
- Create: `ruoyi-admin/src/main/resources/mapper/out/WcsJobDispatchMapper.xml`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/out/dispatch/mapper/WcsJobDispatchMapperSqlTest.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/in/job/domain/MydoJob.java`
- Modify: `ruoyi-admin/src/main/resources/mapper/in/MydoJobMapper.xml`
**Interfaces:**
- Produces: `WcsJobDispatchMapper.insertIfAbsent(String jobId)`, `selectByJobId(String jobId)`, `selectRetryCandidates(Date now, int limit)`, `claimForSend(Map<String,Object>)`, `markBlocked(Map)`, `markRetryWait(Map)`, `markSuccess(Map)`, and `markExhausted(Map)`.
- Produces: non-persistent `MydoJob.dispatchStatus`, `autoRetryCount`, `manualRetryCount`, and `lastDispatchError` populated by list/detail queries.
- [ ] **Step 1: Write the failing mapper structure test**
Create a JUnit test that reads the mapper XML and migration and asserts the six statuses, the retry-count predicate, atomic `SENDING` claim, dispatch join aliases, ID `23` seed, Quartz invocation, menu permissions, and repeatable `IF NOT EXISTS` guards.
```java
assertThat(mapperXml)
.contains("dispatch_status = 'SENDING'")
.contains("auto_retry_count &lt; 5")
.contains("last_dispatch_error");
assertThat(migration)
.contains("wcs_job_dispatch")
.contains("wcsOutboundRetryTask.retryFailedJobs")
.contains("out:denseLaneOutbound:generate");
```
- [ ] **Step 2: Run the focused test and verify RED**
Run:
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=WcsJobDispatchMapperSqlTest test
```
Expected: FAIL because the mapper and migration do not exist.
- [ ] **Step 3: Implement the migration, domain, mapper, XML, and job query join**
Use exact dispatch constants `PENDING`, `SENDING`, `BLOCKED`, `RETRY_WAIT`, `SUCCESS`, `EXHAUSTED`. The migration creates the table and indexes, inserts task type `23` only when absent, inserts the non-concurrent one-minute Quartz row, and inserts/updates the lane-outbound menu and three permissions. `claimForSend` must update exactly one eligible row and increment either `auto_retry_count` or `manual_retry_count` based on a parameter.
- [ ] **Step 4: Run the focused test and verify GREEN**
Run the command from Step 2. Expected: PASS.
- [ ] **Step 5: Commit**
```powershell
git add sql/20260716_dense_outbound_relocation_dispatch.sql ruoyi-admin/src/main/java/com/ruoyi/out/dispatch ruoyi-admin/src/main/resources/mapper/out/WcsJobDispatchMapper.xml ruoyi-admin/src/main/java/com/ruoyi/in/job/domain/MydoJob.java ruoyi-admin/src/main/resources/mapper/in/MydoJobMapper.xml ruoyi-admin/src/test/java/com/ruoyi/out/dispatch/mapper/WcsJobDispatchMapperSqlTest.java
git commit -m "feat: add WCS dispatch outbox"
```
### Task 2: WCS Routing And Durable Dispatch Service
**Files:**
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dispatch/service/IOutboundDispatchService.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dispatch/service/impl/OutboundDispatchServiceImpl.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dispatch/domain/DispatchTrigger.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dispatch/task/WcsOutboundRetryTask.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/domain/DispatchEligibility.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/mapper/DenseLaneMapper.java`
- Create: `ruoyi-admin/src/main/resources/mapper/out/DenseLaneMapper.xml`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/service/IDenseLaneService.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/service/impl/DenseLaneServiceImpl.java`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/out/dispatch/service/impl/OutboundDispatchServiceImplTest.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/api/client/WcsClient.java`
- Modify: `ruoyi-admin/src/main/resources/application.yml`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/in/job/service/IMydoJobService.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/in/job/service/impl/MydoJobServiceImpl.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/job/controller/OutMydoJobController.java`
**Interfaces:**
- Produces: `AjaxResult dispatch(String jobId, DispatchTrigger trigger)` where trigger is `INITIAL`, `AUTO_RETRY`, or `MANUAL`.
- Produces: `int retryFailedJobs()` for Quartz.
- Produces: `WcsClient.sendOutboundTask(MydoJob)`, `sendLocationOutboundTask(MydoJob)`, and `sendRelocationTask(MydoJob)`.
- [ ] **Step 1: Write failing service tests**
Cover initial failure with count 0, five auto retries then exhausted, manual send past exhaustion, successful status/phase update, atomic claim rejection, and route selection for types 20, 21, and 23. Mock only mapper and WCS boundaries; assert real service decisions.
```java
assertThat(service.dispatch("CK1", DispatchTrigger.INITIAL).isSuccess()).isTrue();
verify(dispatchMapper).markRetryWait(argThat(m -> Integer.valueOf(0).equals(m.get("autoRetryCount"))));
verify(wcsClient).sendRelocationTask(jobOfType(23L));
```
- [ ] **Step 2: Run the tests and verify RED**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=OutboundDispatchServiceImplTest test
```
Expected: FAIL because the dispatch API and WCS methods do not exist.
- [ ] **Step 3: Implement dispatch state transitions, the minimal dense eligibility guard, and WCS routes**
The service creates the outbox row if absent, delegates dense eligibility to `IDenseLaneService`, claims `SENDING`, calls the type route, records failures without throwing away the business job, and updates `mydo_job.status='3'`, `execution_phase='1'`, `start_time` only after success. The minimal dense mapper answers whether the job source is the current outermost occupied cargo position; non-03 or non-dense jobs are immediately eligible. Add `PUT /out/job/pushWcs/{jobId}` as the manual trigger backed by this service. Configure:
```yaml
sendRetrieveTask: ${WCS_OUTBOUND_PATH:/wcs/api/receiveWmsOutJob}
sendLocationOutboundTask: ${WCS_LOCATION_OUTBOUND_PATH:/wcs/api/receiveWmsLocationOutJob}
sendRelocationTask: ${WCS_RELOCATION_PATH:/wcs/api/receiveWmsMoveJob}
```
- [ ] **Step 4: Run the tests and verify GREEN**
Run Step 2. Expected: PASS.
- [ ] **Step 5: Commit**
```powershell
git add ruoyi-admin/src/main/java/com/ruoyi/out/dispatch ruoyi-admin/src/main/java/com/ruoyi/api/client/WcsClient.java ruoyi-admin/src/main/resources/application.yml ruoyi-admin/src/main/java/com/ruoyi/in/job ruoyi-admin/src/main/java/com/ruoyi/out/job/controller/OutMydoJobController.java ruoyi-admin/src/test/java/com/ruoyi/out/dispatch/service/impl/OutboundDispatchServiceImplTest.java
git commit -m "feat: add outbound WCS retry dispatch"
```
### Task 3: Dense Lane Geometry And Eligibility
**Files:**
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/domain/DenseLanePosition.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/domain/DenseRelocationCandidate.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/mapper/DenseLaneMapper.java`
- Modify: `ruoyi-admin/src/main/resources/mapper/out/DenseLaneMapper.xml`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/service/IDenseLaneService.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/service/impl/DenseLaneServiceImpl.java`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/out/dense/service/impl/DenseLaneServiceImplTest.java`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/out/dense/mapper/DenseLaneMapperSqlTest.java`
**Interfaces:**
- Produces: `DispatchEligibility checkDispatchEligibility(MydoJob job)`.
- Produces: `List<DenseLanePosition> selectBlockersForUpdate(String cargoSpaceId)` ordered outer-to-inner.
- Produces: `BasCargoSpace selectRelocationTargetForUpdate(RelocationTargetQuery query)`.
- Produces: lane summary, tray preview, and inventory detail queries consumed by Tasks 6 and 7.
- [ ] **Step 1: Write failing direction and SQL tests**
Assert direction 1 treats the maximum occupied column as outermost, direction 2 treats the minimum as outermost, direction 3 uses nearest boundary, non-03 jobs bypass the rule, blockers exclude trays already covered by active outbound jobs, and destination SQL enforces same floor and different `group_coding`.
- [ ] **Step 2: Run focused tests and verify RED**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=DenseLaneServiceImplTest,DenseLaneMapperSqlTest test
```
Expected: FAIL because dense-lane classes do not exist.
- [ ] **Step 3: Implement pure ordering helpers and locked mapper queries**
Centralize direction ordering in one service. SQL uses `UPDLOCK,HOLDLOCK` for blocker/destination selection, treats cargo statuses 2/3/4 with tray codes as occupied, excludes active jobs for destination reservations, and joins stock profiles used by current inbound rules.
- [ ] **Step 4: Run tests and verify GREEN**
Run Step 2. Expected: PASS.
- [ ] **Step 5: Commit**
```powershell
git add ruoyi-admin/src/main/java/com/ruoyi/out/dense ruoyi-admin/src/main/resources/mapper/out/DenseLaneMapper.xml ruoyi-admin/src/test/java/com/ruoyi/out/dense
git commit -m "feat: enforce dense lane outermost order"
```
### Task 4: Relocation Job Generation And Completion
**Files:**
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/relocation/domain/RelocationGenerateRequest.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/relocation/domain/RelocationPreview.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/relocation/mapper/RelocationJobMapper.java`
- Create: `ruoyi-admin/src/main/resources/mapper/out/RelocationJobMapper.xml`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/relocation/service/IRelocationJobService.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/relocation/service/impl/RelocationJobServiceImpl.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/relocation/controller/RelocationJobController.java`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/out/relocation/service/impl/RelocationJobServiceImplTest.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/api/controller/ExternalApiController.java`
**Interfaces:**
- Produces: `List<String> createBlockerRelocations(String blockedJobId)`.
- Produces: `RelocationPreview preview(RelocationGenerateRequest request)` and `List<String> generate(RelocationGenerateRequest request)`.
- Produces: `AjaxResult completeFromWcs(String jobId)`.
- [ ] **Step 1: Write failing relocation tests**
Cover type 23/task sequence, one main job per tray, one detail per stock detail, same-floor/different-lane rejection, source status 4/target status 3 reservation, all-or-nothing capacity validation, completion inventory move, cargo status swap, archive, duplicate callback, and dispatch wake-up after commit.
- [ ] **Step 2: Run test and verify RED**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=RelocationJobServiceImplTest test
```
Expected: FAIL because relocation service does not exist.
- [ ] **Step 3: Implement transactional generation and completion**
Use `@Transactional(rollbackFor = Exception.class)`. Generate blockers outer-to-inner, reserve all destinations before inserting jobs, copy stock trace fields into job details, and create dispatch rows. On completion, lock and validate active type 23, update every `bas_stock` row for the tray, switch cargo statuses, archive job/detail with explicit column lists, delete active rows, then dispatch the next eligible source-lane job after commit.
- [ ] **Step 4: Route WCS completion by type**
In `ExternalApiController.completeOutbound`, type 23 calls `relocationJobService.completeFromWcs`; all other outbound types keep current phase-5/PDA behavior.
- [ ] **Step 5: Run tests and verify GREEN**
Run Step 2. Expected: PASS.
- [ ] **Step 6: Commit**
```powershell
git add ruoyi-admin/src/main/java/com/ruoyi/out/relocation ruoyi-admin/src/main/resources/mapper/out/RelocationJobMapper.xml ruoyi-admin/src/main/java/com/ruoyi/api/controller/ExternalApiController.java ruoyi-admin/src/test/java/com/ruoyi/out/relocation
git commit -m "feat: add dense warehouse relocation jobs"
```
### Task 5: Integrate Existing Outbound Allocation And Location Outbound
**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/stockout/service/impl/OutStockoutServiceImpl.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/out/location/service/impl/LocationOutboundServiceImpl.java`
- Modify: `ruoyi-admin/src/test/java/com/ruoyi/out/stockout/service/impl/OutStockoutServiceImplTest.java`
- Modify: `ruoyi-admin/src/test/java/com/ruoyi/out/location/service/impl/LocationOutboundServiceImplTest.java`
**Interfaces:**
- Consumes: relocation blocker generation and unified dispatch from Tasks 2 and 4.
- Produces: allocation responses that distinguish dispatched, queued-for-retry, and blocked-by-relocation jobs.
- [ ] **Step 1: Add failing integration-unit tests**
Assert auto/manual allocation no longer calls `MydoJobService.pushOutJobToWcs`, specified stock allocation creates blocker relocations before dispatch, normal non-03 allocation dispatches directly, location outbound creates relocations for non-outbound full blockers, and WCS failure returns business success with compensation message.
- [ ] **Step 2: Run tests and verify RED**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=OutStockoutServiceImplTest,LocationOutboundServiceImplTest test
```
- [ ] **Step 3: Replace direct push calls with prepare-and-dispatch orchestration**
After each successful database allocation, call blocker generation for the selected job, create dispatch rows for every new job, and dispatch only currently eligible jobs. Keep existing non-dense procedure behavior and stock-status filters unchanged.
- [ ] **Step 4: Run tests and verify GREEN**
Run Step 2. Expected: PASS.
- [ ] **Step 5: Commit**
```powershell
git add ruoyi-admin/src/main/java/com/ruoyi/out/stockout/service/impl/OutStockoutServiceImpl.java ruoyi-admin/src/main/java/com/ruoyi/out/location/service/impl/LocationOutboundServiceImpl.java ruoyi-admin/src/test/java/com/ruoyi/out/stockout ruoyi-admin/src/test/java/com/ruoyi/out/location
git commit -m "feat: prepare dense blockers during outbound allocation"
```
### Task 6: KPI Dense Plan Manual Relocation
**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/kpi/controller/KpiController.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/kpi/service/KpiService.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/kpi/service/impl/KpiServiceImpl.java`
- Modify: `ruoyi-ui/src/api/kpi/kpi.js`
- Modify: `ruoyi-ui/src/views/kpi/plan/index.vue`
- Modify: `ruoyi-ui/src/views/kpi/plan/components/DenseCargoSpaceMap.vue`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/kpi/service/impl/KpiDenseRelocationServiceTest.java`
**Interfaces:**
- Produces: `GET /kpi/denseRelocation/options`, `POST /kpi/denseRelocation/preview`, and `POST /kpi/denseRelocation/generate`.
- [ ] **Step 1: Write failing controller/service tests**
Assert only warehouse 03 is accepted, target lane floor equals source/current floor, options require sufficient compatible capacity, preview returns exact outer trays and inventory totals, and generate revalidates state.
- [ ] **Step 2: Run Java tests and verify RED**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=KpiDenseRelocationServiceTest test
```
- [ ] **Step 3: Implement backend endpoints and existing-style UI**
Add a compact “指定移库” button to the dense toolbar. Use an Element UI dialog with source lane, numeric tray count, target lane, and a fixed-height preview table. Do not add decorative cards or alter map dimensions. Refresh map data after successful generation.
- [ ] **Step 4: Run Java tests and production UI build**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=KpiDenseRelocationServiceTest test
Set-Location ruoyi-ui
npm run build:prod
```
Expected: tests PASS and Vue build exits 0.
- [ ] **Step 5: Commit**
```powershell
git add ruoyi-admin/src/main/java/com/ruoyi/kpi ruoyi-admin/src/test/java/com/ruoyi/kpi ruoyi-ui/src/api/kpi/kpi.js ruoyi-ui/src/views/kpi/plan
git commit -m "feat: add KPI dense lane relocation"
```
### Task 7: Dense Lane Outbound Menu And Workflow
**Files:**
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/controller/DenseLaneOutboundController.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/domain/DenseLaneOutboundQuery.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/domain/DenseLaneOutboundRequest.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/domain/DenseLaneSummary.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/service/IDenseLaneOutboundService.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/out/dense/service/impl/DenseLaneOutboundServiceImpl.java`
- Create: `ruoyi-ui/src/api/out/denseLaneOutbound.js`
- Create: `ruoyi-ui/src/views/out/denseLaneOutbound/index.vue`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/out/dense/service/impl/DenseLaneOutboundServiceImplTest.java`
**Interfaces:**
- Produces: `GET /out/denseLaneOutbound/list`, `GET /out/denseLaneOutbound/preview`, and `POST /out/denseLaneOutbound/generate`.
- [ ] **Step 1: Write failing service tests**
Assert list groups only full 03 dense lanes, totals all stock details per tray, preview selects exactly outermost N unreserved trays, quantity over capacity fails, stale preview fails generation, exit compatibility is validated, and every generated type-20 job contains the selected tray's complete inventory details.
- [ ] **Step 2: Run tests and verify RED**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=DenseLaneOutboundServiceImplTest test
```
- [ ] **Step 3: Implement backend and menu page**
Use the migration permissions from Task 1. The page displays group code, area, row, floor, full/available tray counts, total stock, current outer tray, and expandable inventory details. Generation dialog uses an integer stepper, exit selector, and preview table showing exact trays and quantities.
- [ ] **Step 4: Verify tests and UI build**
Run Task 6 Step 4 with `DenseLaneOutboundServiceImplTest`. Expected: PASS and build exit 0.
- [ ] **Step 5: Commit**
```powershell
git add ruoyi-admin/src/main/java/com/ruoyi/out/dense ruoyi-admin/src/test/java/com/ruoyi/out/dense ruoyi-ui/src/api/out/denseLaneOutbound.js ruoyi-ui/src/views/out/denseLaneOutbound sql/20260716_dense_outbound_relocation_dispatch.sql
git commit -m "feat: add dense lane outbound workflow"
```
### Task 8: Outbound Job Management Dispatch UI
**Files:**
- Modify: `ruoyi-ui/src/api/out/job.js`
- Modify: `ruoyi-ui/src/views/out/job/index.vue`
- Modify: `sql/20260716_dense_outbound_relocation_dispatch.sql`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/out/job/controller/OutMydoJobDispatchControllerTest.java`
**Interfaces:**
- Consumes: dispatch fields and `PUT /out/job/pushWcs/{jobId}`.
- [ ] **Step 1: Write failing controller permission/state test**
Assert the endpoint requires `out:job:pushWcs`, delegates with `MANUAL`, rejects completed jobs, and returns blocked reason without calling WCS when a dense job is not outermost.
- [ ] **Step 2: Run test and verify RED**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -Dtest=OutMydoJobDispatchControllerTest test
```
- [ ] **Step 3: Implement UI columns and manual action**
Add WCS status, automatic retry count, and last-error tooltip columns. Use the existing send icon button with tooltip “手动下发 WCS”, permission `out:job:pushWcs`, and a confirmation message containing attempts and last error. Show it for pending, retry-wait, exhausted, and blocked active jobs; backend remains authoritative.
- [ ] **Step 4: Verify controller and UI build**
Run Step 2 and `npm run build:prod`. Expected: PASS and build exit 0.
- [ ] **Step 5: Commit**
```powershell
git add ruoyi-admin/src/test/java/com/ruoyi/out/job ruoyi-ui/src/api/out/job.js ruoyi-ui/src/views/out/job/index.vue sql/20260716_dense_outbound_relocation_dispatch.sql
git commit -m "feat: add manual WCS dispatch action"
```
### Task 9: Full Regression, Migration, And Runtime Scenarios
**Files:**
- Create: `docs/testing/2026-07-16-dense-outbound-relocation-dispatch-report.md`
**Interfaces:**
- Verifies all interfaces produced above as one workflow.
- [ ] **Step 1: Run all backend tests**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' -pl ruoyi-admin -am test
```
Expected: BUILD SUCCESS with zero test failures.
- [ ] **Step 2: Run backend package and frontend production build**
```powershell
& 'D:\BDevTools\maven\apache-maven-3.9.11\bin\mvn.cmd' clean package -DskipTests
Set-Location ruoyi-ui
npm run build:prod
```
Expected: both commands exit 0.
- [ ] **Step 3: Apply migration to the configured disposable/local database**
Execute `sql/20260716_dense_outbound_relocation_dispatch.sql` twice. Expected: both runs succeed, one task type 23 row, one Quartz task, one menu, three button permissions, and no duplicate indexes.
- [ ] **Step 4: Run runtime integration scenarios**
Verify with controlled WCS mock responses: initial failure plus five retries reaches exhausted; manual recovery succeeds; direction-1 and direction-2 lanes reject inner jobs; blocker relocation completes source/target inventory atomically; location outbound creates blockers; KPI manual relocation stays same-floor; lane outbound selects exactly outer N; non-03 outbound remains direct.
- [ ] **Step 5: Verify UI with Playwright screenshots**
Check outbound job management, KPI relocation dialog, and dense lane outbound page at desktop and mobile/narrow viewports. Verify no overlap, clipped button text, blank table, or broken dialog sizing.
- [ ] **Step 6: Write the report and run requirement audit**
Record commands, counts, screenshots, created job IDs, retry transitions, relocation source/target IDs, lane outbound selections, and residual risks. Map each of the six user requirements to direct evidence.
- [ ] **Step 7: Commit the verification report**
```powershell
git add docs/testing/2026-07-16-dense-outbound-relocation-dispatch-report.md
git commit -m "test: verify dense outbound dispatch workflows"
```