feat(ft546): adapt existing WMS contract to WCS core
This commit is contained in:
@@ -5,6 +5,8 @@ import com.ruoyi.api.domain.wcs2wms.JobParam;
|
||||
import com.ruoyi.api.domain.wcs2wms.ReceiveJob;
|
||||
import com.ruoyi.api.service.IApiService;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.project.ft546.config.Ft546WcsProperties;
|
||||
import com.ruoyi.project.ft546.wms.Ft546WmsAdapterService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -15,9 +17,18 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
@RequestMapping("/wcs2Wms/api")
|
||||
public class Wcs2WmsController {
|
||||
private final IApiService apiService;
|
||||
private final Ft546WmsAdapterService standardAdapter;
|
||||
private final Ft546WcsProperties properties;
|
||||
|
||||
@Autowired
|
||||
private IApiService apiService;
|
||||
public Wcs2WmsController(IApiService apiService,
|
||||
Ft546WmsAdapterService standardAdapter,
|
||||
Ft546WcsProperties properties) {
|
||||
this.apiService = apiService;
|
||||
this.standardAdapter = standardAdapter;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收出库任务
|
||||
@@ -27,7 +38,10 @@ public class Wcs2WmsController {
|
||||
*/
|
||||
@PostMapping("/receiveOutMydoJob")
|
||||
public AjaxResult receiveOutMydoJob(@RequestBody ReceiveJob receiveOutJob) {
|
||||
return apiService.receiveOutMydoJob(receiveOutJob);
|
||||
return isStandard()
|
||||
? AjaxResult.success(
|
||||
standardAdapter.receiveOutMydoJob(receiveOutJob))
|
||||
: apiService.receiveOutMydoJob(receiveOutJob);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +52,10 @@ public class Wcs2WmsController {
|
||||
*/
|
||||
@PostMapping("/receiveInMydoJob")
|
||||
public AjaxResult receiveInMydoJob(@RequestBody ReceiveJob receiveInJob) {
|
||||
return apiService.receiveInMydoJob(receiveInJob);
|
||||
return isStandard()
|
||||
? AjaxResult.success(
|
||||
standardAdapter.receiveInMydoJob(receiveInJob))
|
||||
: apiService.receiveInMydoJob(receiveInJob);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,13 +67,23 @@ public class Wcs2WmsController {
|
||||
*/
|
||||
@PostMapping("/cancelMydoJob")
|
||||
public AjaxResult cancelMydoJob(@RequestBody JobParam jobParam) {
|
||||
return apiService.cancelMydoJob(jobParam);
|
||||
return isStandard()
|
||||
? AjaxResult.success(standardAdapter.cancelMydoJob(
|
||||
jobParam == null ? null : jobParam.getTaskNo()))
|
||||
: apiService.cancelMydoJob(jobParam);
|
||||
}
|
||||
|
||||
/** Return a read-only snapshot of all S-RGV vehicles. */
|
||||
@PostMapping("/tidyVehicleSnapshot")
|
||||
public AjaxResult tidyVehicleSnapshot() {
|
||||
return apiService.tidyVehicleSnapshot();
|
||||
return isStandard()
|
||||
? AjaxResult.success(standardAdapter.tidyVehicleSnapshot())
|
||||
: apiService.tidyVehicleSnapshot();
|
||||
}
|
||||
|
||||
private boolean isStandard() {
|
||||
return properties.getRuntimeMode()
|
||||
== Ft546WcsProperties.RuntimeMode.STANDARD;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.ruoyi.job.domain.MydoJob;
|
||||
import com.ruoyi.job.service.IMydoJobService;
|
||||
import com.ruoyi.queue.agv.domain.AgvQueue;
|
||||
import com.ruoyi.queue.agv.service.IAgvQueueService;
|
||||
import com.ruoyi.project.ft546.wms.Ft546WmsAdapterService;
|
||||
import com.ruoyi.system.domain.AgvContainerTask;
|
||||
import com.ruoyi.system.service.IAgvContainerTaskService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -60,6 +61,8 @@ public class ApiServiceImpl implements IApiService {
|
||||
private IAgvContainerTaskService agvContainerTaskService;
|
||||
@Autowired
|
||||
private DeviceSrgvMapper deviceSrgvMapper;
|
||||
@Autowired
|
||||
private Ft546WmsAdapterService ft546WmsAdapterService;
|
||||
|
||||
|
||||
/**
|
||||
@@ -520,36 +523,8 @@ public class ApiServiceImpl implements IApiService {
|
||||
|
||||
@Override
|
||||
public AjaxResult tidyVehicleSnapshot() {
|
||||
List<DeviceSrgv> vehicles = deviceSrgvMapper.selectDeviceSrgvList(new DeviceSrgv());
|
||||
List<SrgvVehicleSnapshot> snapshots = new ArrayList<>();
|
||||
for (DeviceSrgv vehicle : vehicles) {
|
||||
boolean online = "1".equals(vehicle.getAgvStatus());
|
||||
boolean healthy = online && value(vehicle.getAlarm()) <= 1 && value(vehicle.getErrMsg()) == 0;
|
||||
boolean idle = healthy && value(vehicle.getTaskNo()) == 0
|
||||
&& value(vehicle.getLocked()) == 0 && value(vehicle.getChargeFlag()) == 0;
|
||||
SrgvVehicleSnapshot snapshot = new SrgvVehicleSnapshot();
|
||||
snapshot.setGuid(vehicle.getGuid());
|
||||
snapshot.setDeviceId(vehicle.getDeviceId());
|
||||
snapshot.setDeviceName(vehicle.getDeviceName());
|
||||
snapshot.setAgvStatus(vehicle.getAgvStatus());
|
||||
snapshot.setOnline(online);
|
||||
snapshot.setHealthy(healthy);
|
||||
snapshot.setIdle(idle);
|
||||
snapshot.setTaskNo(vehicle.getTaskNo());
|
||||
snapshot.setAlarm(vehicle.getAlarm());
|
||||
snapshot.setErrMsg(vehicle.getErrMsg());
|
||||
snapshot.setChargeFlag(vehicle.getChargeFlag());
|
||||
snapshot.setLocked(vehicle.getLocked());
|
||||
snapshot.setBattery(vehicle.getBattery());
|
||||
snapshot.setCurrSite(vehicle.getCurrSite());
|
||||
snapshot.setCurrFloor(vehicle.getCurrFloor());
|
||||
snapshots.add(snapshot);
|
||||
}
|
||||
return AjaxResult.success(snapshots);
|
||||
}
|
||||
|
||||
private int value(Integer number) {
|
||||
return number == null ? 0 : number;
|
||||
return AjaxResult.success(ft546WmsAdapterService
|
||||
.tidyVehicleSnapshot());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.ruoyi.project.ft546.wms;
|
||||
|
||||
import com.ruoyi.api.domain.wcs2wms.ReceiveJob;
|
||||
import com.ruoyi.api.domain.wcs2wms.SrgvVehicleSnapshot;
|
||||
import com.ruoyi.device.srgv.domain.DeviceSrgv;
|
||||
import com.ruoyi.device.srgv.mapper.DeviceSrgvMapper;
|
||||
import com.ruoyi.wcs.application.task.TransportOrderResult;
|
||||
import com.ruoyi.wcs.port.in.TransportOrderUseCase;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class Ft546WmsAdapterService {
|
||||
private final TransportOrderUseCase orderUseCase;
|
||||
private final VehicleSnapshotProvider vehicleSnapshotProvider;
|
||||
private final Ft546WmsOrderMapper mapper = new Ft546WmsOrderMapper();
|
||||
|
||||
@Autowired
|
||||
public Ft546WmsAdapterService(TransportOrderUseCase orderUseCase,
|
||||
DeviceSrgvMapper deviceMapper) {
|
||||
this(orderUseCase, () -> snapshots(deviceMapper));
|
||||
}
|
||||
|
||||
public Ft546WmsAdapterService(TransportOrderUseCase orderUseCase,
|
||||
VehicleSnapshotProvider snapshotProvider) {
|
||||
this.orderUseCase = Objects.requireNonNull(orderUseCase,
|
||||
"orderUseCase");
|
||||
this.vehicleSnapshotProvider = Objects.requireNonNull(
|
||||
snapshotProvider, "snapshotProvider");
|
||||
}
|
||||
|
||||
public TransportOrderResult receiveOutMydoJob(ReceiveJob job) {
|
||||
return orderUseCase.create(mapper.outbound(job));
|
||||
}
|
||||
|
||||
public TransportOrderResult receiveInMydoJob(ReceiveJob job) {
|
||||
return orderUseCase.create(mapper.inbound(job));
|
||||
}
|
||||
|
||||
public TransportOrderResult cancelMydoJob(String taskNo) {
|
||||
return orderUseCase.cancel(requireText(taskNo, "taskNo"));
|
||||
}
|
||||
|
||||
public List<?> tidyVehicleSnapshot() {
|
||||
List<?> snapshots = Objects.requireNonNull(
|
||||
vehicleSnapshotProvider.load(), "vehicle snapshots");
|
||||
return Collections.unmodifiableList(new ArrayList<Object>(snapshots));
|
||||
}
|
||||
|
||||
private static List<SrgvVehicleSnapshot> snapshots(
|
||||
DeviceSrgvMapper mapper) {
|
||||
List<DeviceSrgv> vehicles = Objects.requireNonNull(mapper,
|
||||
"deviceMapper").selectDeviceSrgvList(new DeviceSrgv());
|
||||
List<SrgvVehicleSnapshot> snapshots =
|
||||
new ArrayList<SrgvVehicleSnapshot>();
|
||||
for (DeviceSrgv vehicle : vehicles) {
|
||||
boolean online = "1".equals(vehicle.getAgvStatus());
|
||||
boolean healthy = online && value(vehicle.getAlarm()) <= 1
|
||||
&& value(vehicle.getErrMsg()) == 0;
|
||||
boolean idle = healthy && value(vehicle.getTaskNo()) == 0
|
||||
&& value(vehicle.getLocked()) == 0
|
||||
&& value(vehicle.getChargeFlag()) == 0;
|
||||
SrgvVehicleSnapshot snapshot = new SrgvVehicleSnapshot();
|
||||
snapshot.setGuid(vehicle.getGuid());
|
||||
snapshot.setDeviceId(vehicle.getDeviceId());
|
||||
snapshot.setDeviceName(vehicle.getDeviceName());
|
||||
snapshot.setAgvStatus(vehicle.getAgvStatus());
|
||||
snapshot.setOnline(online);
|
||||
snapshot.setHealthy(healthy);
|
||||
snapshot.setIdle(idle);
|
||||
snapshot.setTaskNo(vehicle.getTaskNo());
|
||||
snapshot.setAlarm(vehicle.getAlarm());
|
||||
snapshot.setErrMsg(vehicle.getErrMsg());
|
||||
snapshot.setChargeFlag(vehicle.getChargeFlag());
|
||||
snapshot.setLocked(vehicle.getLocked());
|
||||
snapshot.setBattery(vehicle.getBattery());
|
||||
snapshot.setCurrSite(vehicle.getCurrSite());
|
||||
snapshot.setCurrFloor(vehicle.getCurrFloor());
|
||||
snapshots.add(snapshot);
|
||||
}
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
private static int value(Integer number) {
|
||||
return number == null ? 0 : number;
|
||||
}
|
||||
|
||||
private static String requireText(String value, String field) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException(field + " must not be blank");
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface VehicleSnapshotProvider {
|
||||
List<?> load();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.ruoyi.project.ft546.wms;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.ruoyi.api.domain.RequestLog;
|
||||
import com.ruoyi.api.service.IRequestLogService;
|
||||
import com.ruoyi.wcs.port.out.WmsNotification;
|
||||
import com.ruoyi.wcs.port.out.WmsNotificationGateway;
|
||||
import com.ruoyi.wcs.port.out.WmsNotificationReceipt;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.ruoyi.common.Constant.JOB_FINISH_URL;
|
||||
|
||||
@Component
|
||||
public final class Ft546WmsNotificationGateway
|
||||
implements WmsNotificationGateway {
|
||||
private static final java.util.Set<String> SUPPORTED_EVENTS =
|
||||
new java.util.HashSet<String>(Arrays.asList("OrderCompleted",
|
||||
"OrderFailed", "OrderCancelled"));
|
||||
private final IRequestLogService requestLogService;
|
||||
|
||||
public Ft546WmsNotificationGateway(
|
||||
IRequestLogService requestLogService) {
|
||||
this.requestLogService = Objects.requireNonNull(requestLogService,
|
||||
"requestLogService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public WmsNotificationReceipt notify(WmsNotification notification) {
|
||||
Objects.requireNonNull(notification, "notification");
|
||||
if (!SUPPORTED_EVENTS.contains(notification.getEventType())) {
|
||||
return new WmsNotificationReceipt(
|
||||
WmsNotificationReceipt.Status.PERMANENT_FAILURE,
|
||||
"unsupported FT546 notification event");
|
||||
}
|
||||
Map<String, Object> payload = new LinkedHashMap<String, Object>();
|
||||
payload.put("eventId", notification.getEventId());
|
||||
payload.put("jobId", context(notification, "jobId",
|
||||
notification.getOrderId()));
|
||||
payload.put("taskNo", notification.getExternalRequestId());
|
||||
payload.put("wmsType", context(notification, "inOrOut", "UNKNOWN"));
|
||||
payload.put("eventType", notification.getEventType());
|
||||
payload.put("resultCode", notification.getResultCode());
|
||||
payload.put("resultMessage", notification.getResultMessage());
|
||||
RequestLog result = requestLogService.getRequestLog(JOB_FINISH_URL,
|
||||
"WCS标准任务结果回传", JSONUtil.toJsonStr(payload));
|
||||
if (result != null && "1".equals(result.getIsSuccessful())) {
|
||||
return new WmsNotificationReceipt(
|
||||
WmsNotificationReceipt.Status.DELIVERED,
|
||||
notification.getEventId());
|
||||
}
|
||||
return new WmsNotificationReceipt(
|
||||
WmsNotificationReceipt.Status.RETRYABLE_FAILURE,
|
||||
result == null ? "callback returned no result"
|
||||
: result.getErrorDetails());
|
||||
}
|
||||
|
||||
private static String context(WmsNotification notification, String key,
|
||||
String fallback) {
|
||||
String value = notification.getContext().get(key);
|
||||
return value == null || value.trim().isEmpty()
|
||||
? fallback : value.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.ruoyi.project.ft546.wms;
|
||||
|
||||
import com.ruoyi.api.domain.wcs2wms.ReceiveJob;
|
||||
import com.ruoyi.wcs.application.task.CreateTransportOrderCommand;
|
||||
import com.ruoyi.wcs.domain.task.TransportOrderType;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Ft546WmsOrderMapper {
|
||||
public CreateTransportOrderCommand inbound(ReceiveJob job) {
|
||||
ReceiveJob request = Objects.requireNonNull(job, "job");
|
||||
return command(request, TransportOrderType.INBOUND,
|
||||
firstText(request.getEntranceId(), request.getExitId()),
|
||||
requireText(request.getCargoSpaceId(), "cargoSpaceId"));
|
||||
}
|
||||
|
||||
public CreateTransportOrderCommand outbound(ReceiveJob job) {
|
||||
ReceiveJob request = Objects.requireNonNull(job, "job");
|
||||
return command(request, TransportOrderType.OUTBOUND,
|
||||
requireText(request.getCargoSpaceId(), "cargoSpaceId"),
|
||||
firstText(request.getExitId(),
|
||||
request.getTargetCargoSpaceId()));
|
||||
}
|
||||
|
||||
private static CreateTransportOrderCommand command(
|
||||
ReceiveJob job, TransportOrderType type, String source,
|
||||
String destination) {
|
||||
String taskNo = requireText(job.getTaskNo(), "taskNo");
|
||||
Map<String, String> attributes = new LinkedHashMap<String, String>();
|
||||
put(attributes, "legacyTaskNo", taskNo);
|
||||
put(attributes, "jobId", job.getJobId());
|
||||
put(attributes, "inOrOut", job.getInOrOut());
|
||||
put(attributes, "roadwayId", job.getRoadwayId());
|
||||
put(attributes, "classId", job.getClassId());
|
||||
put(attributes, "entranceId", job.getEntranceId());
|
||||
put(attributes, "exitId", job.getExitId());
|
||||
put(attributes, "cargoSpaceId", job.getCargoSpaceId());
|
||||
put(attributes, "targetCargoSpaceId",
|
||||
job.getTargetCargoSpaceId());
|
||||
return new CreateTransportOrderCommand(taskNo, type,
|
||||
textOrNull(job.getTrayCode()), source, destination,
|
||||
priority(job.getPriority()), null, attributes);
|
||||
}
|
||||
|
||||
private static int priority(Long value) {
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("priority is out of range");
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
private static void put(Map<String, String> target, String key,
|
||||
String value) {
|
||||
String text = textOrNull(value);
|
||||
if (text != null) {
|
||||
target.put(key, text);
|
||||
}
|
||||
}
|
||||
|
||||
private static String firstText(String first, String second) {
|
||||
String value = textOrNull(first);
|
||||
return value != null ? value : requireText(second, "location");
|
||||
}
|
||||
|
||||
private static String textOrNull(String value) {
|
||||
return value == null || value.trim().isEmpty()
|
||||
? null : value.trim();
|
||||
}
|
||||
|
||||
private static String requireText(String value, String field) {
|
||||
String text = textOrNull(value);
|
||||
if (text == null) {
|
||||
throw new IllegalArgumentException(field + " must not be blank");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package com.ruoyi.project.ft546.wms;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.ruoyi.api.controller.Wcs2WmsController;
|
||||
import com.ruoyi.api.domain.RequestLog;
|
||||
import com.ruoyi.api.domain.wcs2wms.JobParam;
|
||||
import com.ruoyi.api.domain.wcs2wms.ReceiveJob;
|
||||
import com.ruoyi.api.service.IApiService;
|
||||
import com.ruoyi.api.service.impl.RequestLogServiceImpl;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.project.ft546.config.Ft546WcsProperties;
|
||||
import com.ruoyi.wcs.application.task.CreateTransportOrderCommand;
|
||||
import com.ruoyi.wcs.application.task.TransportOrderResult;
|
||||
import com.ruoyi.wcs.domain.task.TransportOrder;
|
||||
import com.ruoyi.wcs.domain.task.TransportOrderType;
|
||||
import com.ruoyi.wcs.port.in.TransportOrderUseCase;
|
||||
import com.ruoyi.wcs.port.out.WmsNotification;
|
||||
import com.ruoyi.wcs.port.out.WmsNotificationReceipt;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class Ft546WmsAdapterServiceTest {
|
||||
private static final Instant NOW = Instant.parse("2026-08-11T06:00:00Z");
|
||||
|
||||
@Test
|
||||
void mapsExistingInboundRequestToCanonicalOrder() {
|
||||
Ft546WmsOrderMapper mapper = new Ft546WmsOrderMapper();
|
||||
ReceiveJob job = job("TASK-1");
|
||||
job.setEntranceId("PORT-1");
|
||||
job.setCargoSpaceId("SPACE-9");
|
||||
|
||||
CreateTransportOrderCommand command = mapper.inbound(job);
|
||||
|
||||
assertEquals("TASK-1", command.getExternalRequestId());
|
||||
assertEquals(TransportOrderType.INBOUND, command.getOrderType());
|
||||
assertEquals("PORT-1", command.getSourceCode());
|
||||
assertEquals("SPACE-9", command.getDestinationCode());
|
||||
assertEquals("TRAY-1", command.getLoadId());
|
||||
assertEquals("JOB-1", command.getAttributes().get("jobId"));
|
||||
assertEquals("TASK-1",
|
||||
command.getAttributes().get("legacyTaskNo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapsExistingOutboundRequestToCanonicalOrder() {
|
||||
Ft546WmsOrderMapper mapper = new Ft546WmsOrderMapper();
|
||||
ReceiveJob job = job("TASK-2");
|
||||
job.setCargoSpaceId("SPACE-9");
|
||||
job.setExitId("PORT-2");
|
||||
|
||||
CreateTransportOrderCommand command = mapper.outbound(job);
|
||||
|
||||
assertEquals(TransportOrderType.OUTBOUND, command.getOrderType());
|
||||
assertEquals("SPACE-9", command.getSourceCode());
|
||||
assertEquals("PORT-2", command.getDestinationCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void duplicateWmsRequestReturnsTheSameOrder() {
|
||||
FakeTransportOrderUseCase useCase = new FakeTransportOrderUseCase();
|
||||
Ft546WmsAdapterService service = new Ft546WmsAdapterService(useCase,
|
||||
() -> Collections.singletonList("SNAPSHOT"));
|
||||
ReceiveJob job = job("TASK-1");
|
||||
job.setEntranceId("PORT-1");
|
||||
job.setCargoSpaceId("SPACE-9");
|
||||
|
||||
TransportOrderResult first = service.receiveInMydoJob(job);
|
||||
TransportOrderResult duplicate = service.receiveInMydoJob(job);
|
||||
|
||||
assertEquals(first.getOrderId(), duplicate.getOrderId());
|
||||
assertEquals(2, useCase.createCommands.size());
|
||||
assertEquals("TASK-1", useCase.createCommands.get(1)
|
||||
.getExternalRequestId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void controllerUsesStandardAdapterOnlyInStandardMode() {
|
||||
IApiService legacy = mock(IApiService.class);
|
||||
AjaxResult legacyResult = AjaxResult.success("legacy");
|
||||
ReceiveJob job = job("TASK-1");
|
||||
job.setEntranceId("PORT-1");
|
||||
job.setCargoSpaceId("SPACE-9");
|
||||
when(legacy.receiveInMydoJob(job)).thenReturn(legacyResult);
|
||||
FakeTransportOrderUseCase useCase = new FakeTransportOrderUseCase();
|
||||
Ft546WmsAdapterService standard = new Ft546WmsAdapterService(useCase,
|
||||
() -> Collections.singletonList("SNAPSHOT"));
|
||||
Ft546WcsProperties properties = new Ft546WcsProperties();
|
||||
Wcs2WmsController controller = new Wcs2WmsController(legacy,
|
||||
standard, properties);
|
||||
|
||||
assertSame(legacyResult, controller.receiveInMydoJob(job));
|
||||
verify(legacy).receiveInMydoJob(job);
|
||||
|
||||
properties.setRuntimeMode(Ft546WcsProperties.RuntimeMode.STANDARD);
|
||||
AjaxResult standardResult = controller.receiveInMydoJob(job);
|
||||
|
||||
assertTrue(standardResult.isSuccess());
|
||||
assertTrue(standardResult.get(AjaxResult.DATA_TAG)
|
||||
instanceof TransportOrderResult);
|
||||
assertEquals(1, useCase.createCommands.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void controllerDelegatesCancelAndSnapshotWithoutLegacyWritesInStandard() {
|
||||
IApiService legacy = mock(IApiService.class);
|
||||
FakeTransportOrderUseCase useCase = new FakeTransportOrderUseCase();
|
||||
Ft546WmsAdapterService standard = new Ft546WmsAdapterService(useCase,
|
||||
() -> Arrays.asList("S1", "S2"));
|
||||
Ft546WcsProperties properties = new Ft546WcsProperties();
|
||||
properties.setRuntimeMode(Ft546WcsProperties.RuntimeMode.STANDARD);
|
||||
Wcs2WmsController controller = new Wcs2WmsController(legacy,
|
||||
standard, properties);
|
||||
JobParam cancel = new JobParam("JOB-1", "TRAY-1", "TASK-1");
|
||||
|
||||
AjaxResult cancelResult = controller.cancelMydoJob(cancel);
|
||||
AjaxResult snapshotResult = controller.tidyVehicleSnapshot();
|
||||
|
||||
assertTrue(cancelResult.isSuccess());
|
||||
assertEquals(Arrays.asList("S1", "S2"),
|
||||
snapshotResult.get(AjaxResult.DATA_TAG));
|
||||
assertEquals(Collections.singletonList("TASK-1"),
|
||||
useCase.cancelledExternalIds);
|
||||
verify(legacy, never()).cancelMydoJob(any(JobParam.class));
|
||||
verify(legacy, never()).tidyVehicleSnapshot();
|
||||
}
|
||||
|
||||
@Test
|
||||
void notificationUsesEventIdAsLegacyCallbackIdempotencyKey() {
|
||||
FakeRequestLogService requestLogs = new FakeRequestLogService();
|
||||
Ft546WmsNotificationGateway gateway =
|
||||
new Ft546WmsNotificationGateway(requestLogs);
|
||||
Map<String, String> context = new LinkedHashMap<String, String>();
|
||||
context.put("jobId", "JOB-1");
|
||||
context.put("inOrOut", "10");
|
||||
WmsNotification notification = new WmsNotification("EVENT-1",
|
||||
"TASK-1", "ORDER-1", NOW, "OrderCompleted", "OK",
|
||||
"completed", context);
|
||||
|
||||
WmsNotificationReceipt receipt = gateway.notify(notification);
|
||||
|
||||
assertEquals(WmsNotificationReceipt.Status.DELIVERED,
|
||||
receipt.getStatus());
|
||||
JSONObject payload = JSONUtil.parseObj(requestLogs.requestBody);
|
||||
assertEquals("EVENT-1", payload.getStr("eventId"));
|
||||
assertEquals("JOB-1", payload.getStr("jobId"));
|
||||
assertEquals("TASK-1", payload.getStr("taskNo"));
|
||||
assertEquals("10", payload.getStr("wmsType"));
|
||||
assertEquals("OrderCompleted", payload.getStr("eventType"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void notificationTransportFailureRemainsRetryable() {
|
||||
FakeRequestLogService requestLogs = new FakeRequestLogService();
|
||||
requestLogs.success = false;
|
||||
Ft546WmsNotificationGateway gateway =
|
||||
new Ft546WmsNotificationGateway(requestLogs);
|
||||
WmsNotification notification = new WmsNotification("EVENT-2",
|
||||
"TASK-2", "ORDER-2", NOW, "OrderFailed", "FAILED",
|
||||
"fault", Collections.<String, String>emptyMap());
|
||||
|
||||
WmsNotificationReceipt receipt = gateway.notify(notification);
|
||||
|
||||
assertEquals(WmsNotificationReceipt.Status.RETRYABLE_FAILURE,
|
||||
receipt.getStatus());
|
||||
}
|
||||
|
||||
private static ReceiveJob job(String taskNo) {
|
||||
ReceiveJob job = new ReceiveJob();
|
||||
job.setTaskNo(taskNo);
|
||||
job.setJobId("JOB-1");
|
||||
job.setTrayCode("TRAY-1");
|
||||
job.setInOrOut("10");
|
||||
job.setRoadwayId("R1");
|
||||
job.setClassId("7");
|
||||
job.setPriority(5L);
|
||||
return job;
|
||||
}
|
||||
|
||||
private static final class FakeTransportOrderUseCase
|
||||
implements TransportOrderUseCase {
|
||||
private final Map<String, TransportOrderResult> orders =
|
||||
new LinkedHashMap<String, TransportOrderResult>();
|
||||
private final List<CreateTransportOrderCommand> createCommands =
|
||||
new ArrayList<CreateTransportOrderCommand>();
|
||||
private final List<String> cancelledExternalIds =
|
||||
new ArrayList<String>();
|
||||
|
||||
@Override
|
||||
public TransportOrderResult create(CreateTransportOrderCommand command) {
|
||||
createCommands.add(command);
|
||||
TransportOrderResult existing = orders.get(
|
||||
command.getExternalRequestId());
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
TransportOrder order = TransportOrder.accept(
|
||||
"ORDER-" + command.getExternalRequestId(),
|
||||
command.getExternalRequestId(), command.getOrderType(),
|
||||
command.getLoadId(), command.getSourceCode(),
|
||||
command.getDestinationCode(), command.getPriority(), NOW);
|
||||
order.plan("WORKFLOW-" + command.getExternalRequestId());
|
||||
TransportOrderResult result = TransportOrderResult.from(order);
|
||||
orders.put(command.getExternalRequestId(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportOrderResult cancel(String externalRequestId) {
|
||||
cancelledExternalIds.add(externalRequestId);
|
||||
TransportOrderResult existing = orders.get(externalRequestId);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
CreateTransportOrderCommand command =
|
||||
new CreateTransportOrderCommand(externalRequestId,
|
||||
TransportOrderType.INBOUND, null, "UNKNOWN-SOURCE",
|
||||
"UNKNOWN-TARGET", 0, null,
|
||||
Collections.<String, String>emptyMap());
|
||||
return create(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportOrderResult suspend(String orderId, String reason) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportOrderResult resume(String orderId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FakeRequestLogService
|
||||
extends RequestLogServiceImpl {
|
||||
private boolean success = true;
|
||||
private String requestBody;
|
||||
|
||||
@Override
|
||||
public RequestLog getRequestLog(String url, String endpoint,
|
||||
String body) {
|
||||
this.requestBody = body;
|
||||
RequestLog log = new RequestLog();
|
||||
log.setIsSuccessful(success ? "1" : "0");
|
||||
log.setErrorDetails(success ? null : "injected failure");
|
||||
return log;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user