1 Commits

Author SHA1 Message Date
zgw
a53438ba7e fix: reject blocked outbound jobs at WCS intake 2026-09-15 11:23:09 +08:00
5 changed files with 177 additions and 0 deletions

View File

@@ -83,6 +83,12 @@
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- plc连接库 --> <!-- plc连接库 -->
<dependency> <dependency>
<groupId>org.apache.plc4x</groupId> <groupId>org.apache.plc4x</groupId>
@@ -106,6 +112,11 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>

View File

@@ -18,6 +18,7 @@ import com.ruoyi.api.domain.agv.genAgvSchedulingTask.PositionCodeData;
import com.ruoyi.api.domain.wcs2wms.JobParam; import com.ruoyi.api.domain.wcs2wms.JobParam;
import com.ruoyi.api.domain.wcs2wms.ReceiveJob; import com.ruoyi.api.domain.wcs2wms.ReceiveJob;
import com.ruoyi.api.service.IApiService; import com.ruoyi.api.service.IApiService;
import com.ruoyi.bas.cargospace.mapper.BasCargoSpaceMapper;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.job.domain.MydoJob; import com.ruoyi.job.domain.MydoJob;
@@ -54,6 +55,8 @@ public class ApiServiceImpl implements IApiService {
@Autowired @Autowired
private IMydoJobService mydoJobService; private IMydoJobService mydoJobService;
@Autowired @Autowired
private BasCargoSpaceMapper basCargoSpaceMapper;
@Autowired
private IAgvContainerTaskService agvContainerTaskService; private IAgvContainerTaskService agvContainerTaskService;
@@ -359,6 +362,20 @@ public class ApiServiceImpl implements IApiService {
@Override @Override
public AjaxResult receiveOutMydoJob(ReceiveJob receiveOutJob) { public AjaxResult receiveOutMydoJob(ReceiveJob receiveOutJob) {
Integer jobType = parseOutboundJobType(receiveOutJob);
if (jobType == null || jobType < 20) {
String rawType = receiveOutJob == null ? null : receiveOutJob.getInOrOut();
return AjaxResult.error("WCS 无法识别作业类型 " + rawType + ",任务未接收");
}
if (jobType != 30) {
String cargoSpaceId = receiveOutJob.getCargoSpaceId();
if (basCargoSpaceMapper.selectBasCargoSpaceByCargoSpaceId(cargoSpaceId) == null) {
return AjaxResult.error("WCS 未找到出库货位 " + cargoSpaceId + ",任务未接收");
}
if (basCargoSpaceMapper.countOutboundBlockingPositions(cargoSpaceId) > 0) {
return AjaxResult.error("同通道外侧货位尚未清空,等待前置移库完成");
}
}
MydoJob mydoJob = new MydoJob(); MydoJob mydoJob = new MydoJob();
mydoJob.setJobId(receiveOutJob.getJobId()); mydoJob.setJobId(receiveOutJob.getJobId());
mydoJob.setTaskNo(receiveOutJob.getTaskNo()); mydoJob.setTaskNo(receiveOutJob.getTaskNo());
@@ -382,6 +399,17 @@ public class ApiServiceImpl implements IApiService {
} }
private Integer parseOutboundJobType(ReceiveJob job) {
if (job == null || job.getInOrOut() == null) {
return null;
}
try {
return Integer.valueOf(job.getInOrOut());
} catch (NumberFormatException ignored) {
return null;
}
}
@Override @Override
public AjaxResult receiveInMydoJob(ReceiveJob receiveInJob) { public AjaxResult receiveInMydoJob(ReceiveJob receiveInJob) {
MydoJob mydoJob = new MydoJob(); MydoJob mydoJob = new MydoJob();

View File

@@ -20,6 +20,11 @@ public interface BasCargoSpaceMapper
*/ */
public BasCargoSpace selectBasCargoSpaceByCargoSpaceId(String cargoSpaceId); public BasCargoSpace selectBasCargoSpaceByCargoSpaceId(String cargoSpaceId);
/**
* Count occupied positions or unfinished outbound jobs outside the requested position.
*/
int countOutboundBlockingPositions(@Param("cargoSpaceId") String cargoSpaceId);
/** /**
* 查询库存信息列表 * 查询库存信息列表
* *

View File

@@ -60,6 +60,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectBasCargoSpaceVo"/> <include refid="selectBasCargoSpaceVo"/>
where cargo_space_id = #{cargoSpaceId} where cargo_space_id = #{cargoSpaceId}
</select> </select>
<select id="countOutboundBlockingPositions" resultType="int">
SELECT COUNT(1)
FROM bas_cargo_space outer_space
INNER JOIN bas_cargo_space requested
ON requested.cargo_space_id = #{cargoSpaceId}
AND outer_space.the_floor = requested.the_floor
AND outer_space.group_coding_no = requested.group_coding_no
AND outer_space.group_coding &gt; requested.group_coding
WHERE NULLIF(LTRIM(RTRIM(outer_space.tray_code)), '') IS NOT NULL
OR ISNULL(NULLIF(LTRIM(RTRIM(outer_space.status)), ''), '0') &lt;&gt; '0'
OR ISNULL(NULLIF(LTRIM(RTRIM(outer_space.locked)), ''), '0') &lt;&gt; '0'
OR EXISTS (
SELECT 1
FROM mydo_job_wcs pending
WHERE pending.cargo_space_id = outer_space.cargo_space_id
AND pending.in_or_out &gt;= 20
AND ISNULL(pending.status, '') &lt;&gt; '4'
)
</select>
<select id="selectBasCargoSpaceByWmsCargoSpaceId" <select id="selectBasCargoSpaceByWmsCargoSpaceId"
resultMap="BasCargoSpaceResult"> resultMap="BasCargoSpaceResult">
<include refid="selectBasCargoSpaceVo"/> <include refid="selectBasCargoSpaceVo"/>

View File

@@ -0,0 +1,113 @@
package com.ruoyi.api.service.impl;
import com.ruoyi.api.domain.wcs2wms.ReceiveJob;
import com.ruoyi.bas.cargospace.domain.BasCargoSpace;
import com.ruoyi.bas.cargospace.mapper.BasCargoSpaceMapper;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.job.service.IMydoJobService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ApiServiceImplTest {
@Mock
private IMydoJobService mydoJobService;
@Mock
private BasCargoSpaceMapper basCargoSpaceMapper;
private ApiServiceImpl service;
@BeforeEach
void setUp() {
service = new ApiServiceImpl();
ReflectionTestUtils.setField(service, "mydoJobService", mydoJobService);
ReflectionTestUtils.setField(service, "basCargoSpaceMapper", basCargoSpaceMapper);
}
@Test
void blockedOrdinaryOutboundIsNotInserted() {
ReceiveJob job = outbound("60979", "130803", "20");
when(basCargoSpaceMapper.selectBasCargoSpaceByCargoSpaceId("130803"))
.thenReturn(cargoSpace("130803"));
when(basCargoSpaceMapper.countOutboundBlockingPositions("130803")).thenReturn(1);
AjaxResult result = service.receiveOutMydoJob(job);
assertEquals(500, result.get(AjaxResult.CODE_TAG));
assertEquals("同通道外侧货位尚未清空,等待前置移库完成", result.get(AjaxResult.MSG_TAG));
verify(mydoJobService, never()).insertMydoJob(any());
}
@Test
void moveJobBypassesOutboundGuard() {
ReceiveJob job = outbound("80183", "120703", "30");
when(mydoJobService.insertMydoJob(any())).thenReturn(1);
AjaxResult result = service.receiveOutMydoJob(job);
assertEquals(200, result.get(AjaxResult.CODE_TAG));
verify(basCargoSpaceMapper, never()).countOutboundBlockingPositions(any());
verify(mydoJobService).insertMydoJob(any());
}
@Test
void unblockedOrdinaryOutboundIsInserted() {
ReceiveJob job = outbound("60979", "130803", "20");
when(basCargoSpaceMapper.selectBasCargoSpaceByCargoSpaceId("130803"))
.thenReturn(cargoSpace("130803"));
when(basCargoSpaceMapper.countOutboundBlockingPositions("130803")).thenReturn(0);
when(mydoJobService.insertMydoJob(any())).thenReturn(1);
AjaxResult result = service.receiveOutMydoJob(job);
assertEquals(200, result.get(AjaxResult.CODE_TAG));
verify(mydoJobService).insertMydoJob(any());
}
@Test
void unknownCargoSpaceIsRejectedInsteadOfAssumedSafe() {
ReceiveJob job = outbound("60979", "UNKNOWN", "20");
AjaxResult result = service.receiveOutMydoJob(job);
assertEquals(500, result.get(AjaxResult.CODE_TAG));
assertEquals("WCS 未找到出库货位 UNKNOWN,任务未接收", result.get(AjaxResult.MSG_TAG));
verify(mydoJobService, never()).insertMydoJob(any());
}
@Test
void invalidOutboundTypeIsRejectedInsteadOfBypassingGuard() {
ReceiveJob job = outbound("60979", "130803", "invalid");
AjaxResult result = service.receiveOutMydoJob(job);
assertEquals(500, result.get(AjaxResult.CODE_TAG));
assertEquals("WCS 无法识别作业类型 invalid,任务未接收", result.get(AjaxResult.MSG_TAG));
verify(mydoJobService, never()).insertMydoJob(any());
}
private ReceiveJob outbound(String jobId, String cargoSpaceId, String inOrOut) {
ReceiveJob job = new ReceiveJob();
job.setJobId(jobId);
job.setCargoSpaceId(cargoSpaceId);
job.setInOrOut(inOrOut);
job.setClassId("1");
return job;
}
private BasCargoSpace cargoSpace(String cargoSpaceId) {
BasCargoSpace cargoSpace = new BasCargoSpace();
cargoSpace.setCargoSpaceId(cargoSpaceId);
return cargoSpace;
}
}