This commit is contained in:
2026-08-25 09:52:49 +08:00
parent b211121365
commit b67217332a
37 changed files with 1009 additions and 343 deletions
@@ -75,6 +75,15 @@ public class PlatformUsTruckOrderController {
return ApiResponse.success(service.changeStatus(requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/review")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
public ApiResponse<UsTruckOrderResponses.Detail> review(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam String customerId, @PathVariable String orderNo,
@Valid @RequestBody UsTruckOrderRequests.Review request) {
requirePermission(user, OverseasPermissions.TRUCK_STATUS);
return ApiResponse.success(service.review(requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/receive-pallets")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
@Operation(summary = "Receive pallets for a local truck order", description = "Required authority: " + OverseasPermissions.TRUCK_STATUS)
@@ -85,6 +94,66 @@ public class PlatformUsTruckOrderController {
return ApiResponse.success(service.receivePallets(requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/review-pallet-overage")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
public ApiResponse<UsTruckOrderResponses.Detail> reviewPalletOverage(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam String customerId,
@PathVariable String orderNo, @Valid @RequestBody UsTruckOrderRequests.ReviewPalletOverage request) {
requirePermission(user, OverseasPermissions.TRUCK_STATUS);
return ApiResponse.success(service.reviewPalletOverage(
requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/allocate-pallets")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
public ApiResponse<UsTruckOrderResponses.Detail> allocatePallets(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam String customerId,
@PathVariable String orderNo, @Valid @RequestBody UsTruckOrderRequests.AllocatePallets request) {
requirePermission(user, OverseasPermissions.TRUCK_STATUS);
return ApiResponse.success(service.allocatePallets(
requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/prepare-dispatch")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
public ApiResponse<UsTruckOrderResponses.Detail> prepareDispatch(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam String customerId,
@PathVariable String orderNo, @Valid @RequestBody UsTruckOrderRequests.Command request) {
requirePermission(user, OverseasPermissions.TRUCK_STATUS);
return ApiResponse.success(service.prepareDispatch(
requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/dispatch")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
public ApiResponse<UsTruckOrderResponses.Detail> dispatch(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam String customerId,
@PathVariable String orderNo, @Valid @RequestBody UsTruckOrderRequests.Command request) {
requirePermission(user, OverseasPermissions.TRUCK_STATUS);
return ApiResponse.success(service.dispatch(
requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/start-transit")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
public ApiResponse<UsTruckOrderResponses.Detail> startTransit(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam String customerId,
@PathVariable String orderNo, @Valid @RequestBody UsTruckOrderRequests.Command request) {
requirePermission(user, OverseasPermissions.TRUCK_STATUS);
return ApiResponse.success(service.startTransit(
requiredCustomer(customerId), orderNo, operator(user), request));
}
@PostMapping("/{orderNo}/complete")
@PreAuthorize("hasAuthority('" + OverseasPermissions.TRUCK_STATUS + "')")
public ApiResponse<UsTruckOrderResponses.Detail> complete(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam String customerId,
@PathVariable String orderNo, @Valid @RequestBody UsTruckOrderRequests.Command request) {
requirePermission(user, OverseasPermissions.TRUCK_STATUS);
return ApiResponse.success(service.complete(
requiredCustomer(customerId), orderNo, operator(user), request));
}
private void requirePlatform(AuthenticatedUser user) {
if (user == null || user.tenantId() == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
if (!"PLATFORM".equals(user.appCode()) || user.tenantId() != 0L) {
@@ -4,7 +4,10 @@ import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
@@ -71,7 +74,31 @@ public final class UsTruckOrderRequests {
public record ChangeStatus(
@NotNull @Min(0) Long version,
@NotBlank @Size(max = 32) String status,
@NotBlank @Pattern(regexp = "CANCELLED") String status,
@NotBlank @Size(max = 255) String idempotencyKey,
@Size(max = 1000) String remark) {}
public record Review(
@NotNull @Min(0) Long version,
@NotBlank @Pattern(regexp = "APPROVE|REJECT") String decision,
@NotBlank @Size(max = 255) String idempotencyKey,
@Size(max = 1000) String remark) {}
public record ReviewPalletOverage(
@NotNull @Min(0) Long version,
@NotBlank @Pattern(regexp = "APPROVE|REJECT") String decision,
@NotBlank @Size(max = 255) String idempotencyKey,
@Size(max = 1000) String remark) {}
public record AllocatePallets(
@NotNull @Min(0) Long version,
@NotEmpty @Size(max = 500) List<@NotNull @Positive Long> palletIds,
@NotBlank @Size(max = 255) String idempotencyKey,
@Size(max = 1000) String remark) {}
public record Command(
@NotNull @Min(0) Long version,
@NotBlank @Size(max = 255) String idempotencyKey,
@Size(max = 1000) String remark) {}
public record ReceivePallets(
@@ -11,13 +11,14 @@ public final class UsTruckOrderResponses {
public record Summary(
Long id, String customerId, String orderNo, String transportType,
String origin, String destination, String containerType, int palletCount, int packageCount,
int receivedPalletCount, int allocatedPalletCount, int shippedPalletCount,
int receivedPalletCount, int allocatedPalletCount,
BigDecimal totalWeightKg, BigDecimal totalVolumeCbm, BigDecimal budgetAmount,
String currency, String contactName, String contactInfo, LocalDate requestedDate,
String status, long version, LocalDateTime createdAt, LocalDateTime updatedAt) {}
public record History(
String fromStatus, String toStatus, String operatorId, String remark, LocalDateTime createdAt) {}
String operationType, String fromStatus, String toStatus, String operatorId,
String idempotencyKey, String remark, LocalDateTime createdAt) {}
public record Pallet(Long id, String palletNo, int sequenceNo, String source, String status,
Long warehousePalletId, Integer packageCount, LocalDateTime receivedAt) {}
@@ -25,7 +25,6 @@ public class UsTruckOrder {
private Integer packageCount;
private Integer receivedPalletCount;
private Integer allocatedPalletCount;
private Integer shippedPalletCount;
private BigDecimal totalWeightKg;
private BigDecimal totalVolumeCbm;
private BigDecimal budgetAmount;
@@ -37,8 +36,6 @@ public class UsTruckOrder {
private String remark;
private String idempotencyKey;
private String requestHash;
private String receiptIdempotencyKey;
private String receiptRequestHash;
private String createdBy;
private Long version;
private LocalDateTime createdAt;
@@ -0,0 +1,22 @@
package com.baiyue.lastleg.trucking.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("ll_us_truck_order_operation")
public class UsTruckOrderOperation {
@TableId(type = IdType.INPUT) private Long id;
private Long orderId;
private String customerId;
private String operationType;
private String idempotencyKey;
private String requestHash;
private Long resultVersion;
private String createdBy;
private LocalDateTime createdAt;
}
@@ -13,9 +13,11 @@ public class UsTruckOrderStatusHistory {
@TableId(type = IdType.INPUT) private Long id;
private Long orderId;
private String customerId;
private String operationType;
private String fromStatus;
private String toStatus;
private String operatorId;
private String idempotencyKey;
private String remark;
private LocalDateTime createdAt;
}
@@ -0,0 +1,15 @@
package com.baiyue.lastleg.trucking.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.lastleg.trucking.entity.UsTruckOrderOperation;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UsTruckOrderOperationMapper extends BaseMapper<UsTruckOrderOperation> {
@Select("SELECT * FROM ll_us_truck_order_operation WHERE customer_id=#{customerId} AND order_id=#{orderId} AND idempotency_key=#{key} LIMIT 1")
UsTruckOrderOperation findKey(@Param("customerId") String customerId,
@Param("orderId") Long orderId,
@Param("key") String key);
}
@@ -7,9 +7,11 @@ import com.baiyue.common.core.api.PageResult;
import com.baiyue.lastleg.trucking.dto.UsTruckOrderRequests;
import com.baiyue.lastleg.trucking.dto.UsTruckOrderResponses;
import com.baiyue.lastleg.trucking.entity.UsTruckOrder;
import com.baiyue.lastleg.trucking.entity.UsTruckOrderOperation;
import com.baiyue.lastleg.trucking.entity.UsTruckOrderPallet;
import com.baiyue.lastleg.trucking.entity.UsTruckOrderStatusHistory;
import com.baiyue.lastleg.trucking.mapper.UsTruckOrderMapper;
import com.baiyue.lastleg.trucking.mapper.UsTruckOrderOperationMapper;
import com.baiyue.lastleg.trucking.mapper.UsTruckOrderPalletMapper;
import com.baiyue.lastleg.trucking.mapper.UsTruckOrderStatusHistoryMapper;
import com.baiyue.lastleg.security.OverseasPermissions;
@@ -30,6 +32,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HexFormat;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -55,20 +58,12 @@ public class UsTruckOrderService {
private static final Set<String> STATUSES = Set.of(PENDING_REVIEW, WAITING_INBOUND, RECEIVING,
WAITING_CONSOLIDATION, PALLET_OVERAGE_REVIEW, PARTIALLY_ALLOCATED, ALLOCATED,
PENDING_DISPATCH, DISPATCHED, IN_TRANSIT, COMPLETED, REJECTED, CANCELLED);
private static final java.util.Map<String, Set<String>> TRANSITIONS = java.util.Map.ofEntries(
java.util.Map.entry(PENDING_REVIEW, Set.of(WAITING_INBOUND, REJECTED, CANCELLED)),
java.util.Map.entry(WAITING_INBOUND, Set.of(RECEIVING, CANCELLED)),
java.util.Map.entry(RECEIVING, Set.of(WAITING_CONSOLIDATION, PALLET_OVERAGE_REVIEW, CANCELLED)),
java.util.Map.entry(PALLET_OVERAGE_REVIEW, Set.of(WAITING_CONSOLIDATION, CANCELLED)),
java.util.Map.entry(WAITING_CONSOLIDATION, Set.of(PARTIALLY_ALLOCATED, ALLOCATED, CANCELLED)),
java.util.Map.entry(PARTIALLY_ALLOCATED, Set.of(ALLOCATED, CANCELLED)),
java.util.Map.entry(ALLOCATED, Set.of(PENDING_DISPATCH, CANCELLED)),
java.util.Map.entry(PENDING_DISPATCH, Set.of(DISPATCHED, CANCELLED)),
java.util.Map.entry(DISPATCHED, Set.of(IN_TRANSIT)),
java.util.Map.entry(IN_TRANSIT, Set.of(COMPLETED)));
private static final Set<String> CANCELLABLE = Set.of(PENDING_REVIEW, WAITING_INBOUND, RECEIVING,
PALLET_OVERAGE_REVIEW, WAITING_CONSOLIDATION, PARTIALLY_ALLOCATED, ALLOCATED, PENDING_DISPATCH);
private static final Set<String> TRANSPORT_TYPES = Set.of("PORT_PICKUP", "WAREHOUSE_TO_WAREHOUSE", "WAREHOUSE_TO_DOOR");
@Resource private UsTruckOrderMapper orders;
@Resource private UsTruckOrderOperationMapper operations;
@Resource private UsTruckOrderPalletMapper pallets;
@Resource private UsTruckOrderStatusHistoryMapper histories;
@Resource private ObjectMapper objectMapper;
@@ -90,7 +85,7 @@ public class UsTruckOrderService {
order.setDestination(trim(request.destination())); order.setDestinationAddress(trim(request.destinationAddress()));
order.setContainerType(StringUtils.hasText(request.containerType()) ? norm(request.containerType()) : null);
order.setPalletCount(request.palletCount()); order.setPackageCount(value(request.packageCount()));
order.setReceivedPalletCount(0); order.setAllocatedPalletCount(0); order.setShippedPalletCount(0);
order.setReceivedPalletCount(0); order.setAllocatedPalletCount(0);
order.setTotalWeightKg(request.totalWeightKg()); order.setTotalVolumeCbm(request.totalVolumeCbm());
order.setBudgetAmount(request.budgetAmount()); order.setCurrency(defaultCurrency(request.currency()));
order.setContactName(request.contactName().trim()); order.setContactInfo(request.contactInfo().trim());
@@ -103,7 +98,7 @@ public class UsTruckOrderService {
if (concurrent != null) return replay(customerId, concurrent, hash);
throw e;
}
history(order, null, PENDING_REVIEW, operatorId, request.remark(), now);
history(order, "CREATE", null, PENDING_REVIEW, operatorId, key, request.remark(), now);
return detail(customerId, order.getOrderNo());
}
@@ -150,7 +145,7 @@ public class UsTruckOrderService {
setText(request.contactName(), order::setContactName); setText(request.contactInfo(), order::setContactInfo);
if (request.requestedDate() != null) order.setRequestedDate(request.requestedDate()); setPresent(request.remark(), order::setRemark);
touch(order); orders.updateById(order);
history(order, order.getStatus(), order.getStatus(), operatorId, "UPDATE", LocalDateTime.now());
history(order, "UPDATE", order.getStatus(), order.getStatus(), operatorId, null, "UPDATE", LocalDateTime.now());
return detail(customerId, orderNo);
}
@@ -158,34 +153,37 @@ public class UsTruckOrderService {
public UsTruckOrderResponses.Detail changeStatus(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.ChangeStatus request) {
UsTruckOrder order = lock(customerId, orderNo);
if (!request.version().equals(value(order.getVersion()))) throw conflict("Order was changed; refresh and retry");
String next = norm(request.status()); if (!STATUSES.contains(next)) throw badRequest("Unsupported truck order status");
String from = order.getStatus();
if (!next.equals(from)) {
if (!TRANSITIONS.getOrDefault(from, Set.of()).contains(next)) {
throw conflict("Unsupported truck order transition: " + from + " -> " + next);
}
order.setStatus(next);
if (PENDING_REVIEW.equals(from) && WAITING_INBOUND.equals(next)) createPlannedPallets(order);
touch(order); orders.updateById(order);
history(order, from, next, operatorId, request.remark(), LocalDateTime.now());
UsTruckOrderResponses.Detail replay = replayOperation(order, "CANCEL", request.idempotencyKey(), request);
if (replay != null) return replay;
requireVersion(order, request.version());
if (!CANCELLED.equals(norm(request.status())) || !CANCELLABLE.contains(order.getStatus())) {
throw conflict("Only cancellable truck orders can use the generic status endpoint");
}
return detail(customerId, orderNo);
return transition(order, operatorId, "CANCEL", request.idempotencyKey(), request,
order.getStatus(), CANCELLED, request.remark());
}
@Transactional
public UsTruckOrderResponses.Detail review(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.Review request) {
UsTruckOrder order = lock(customerId, orderNo);
UsTruckOrderResponses.Detail replay = replayOperation(order, "REVIEW", request.idempotencyKey(), request);
if (replay != null) return replay;
requireVersion(order, request.version());
if (!PENDING_REVIEW.equals(order.getStatus())) throw conflict("Only pending-review truck orders can be reviewed");
String next = "APPROVE".equals(norm(request.decision())) ? WAITING_INBOUND : REJECTED;
if (WAITING_INBOUND.equals(next)) createPlannedPallets(order);
return transition(order, operatorId, "REVIEW", request.idempotencyKey(), request,
PENDING_REVIEW, next, request.remark());
}
@Transactional
public UsTruckOrderResponses.Detail receivePallets(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.ReceivePallets request) {
UsTruckOrder order = lock(customerId, orderNo);
String key = request.idempotencyKey().trim();
String requestHash = hash(request);
if (key.equals(order.getReceiptIdempotencyKey())) {
if (!requestHash.equals(order.getReceiptRequestHash())) {
throw conflict("Receipt idempotency key was reused with different pallet data");
}
return detail(customerId, orderNo);
}
if (!request.version().equals(value(order.getVersion()))) throw conflict("Order was changed; refresh and retry");
UsTruckOrderResponses.Detail replay = replayOperation(order, "RECEIVE_PALLETS", request.idempotencyKey(), request);
if (replay != null) return replay;
requireVersion(order, request.version());
if (!Set.of(WAITING_INBOUND, RECEIVING).contains(order.getStatus())) {
throw conflict("Only waiting or receiving orders can receive pallets");
}
@@ -220,18 +218,118 @@ public class UsTruckOrderService {
}
}
order.setReceivedPalletCount(actual);
order.setReceiptIdempotencyKey(key);
order.setReceiptRequestHash(requestHash);
String from = order.getStatus();
String next = actual > planned ? PALLET_OVERAGE_REVIEW
: actual == planned ? WAITING_CONSOLIDATION : RECEIVING;
order.setStatus(next);
touch(order);
orders.updateById(order);
history(order, from, next, operatorId, request.remark(), now);
recordOperation(order, "RECEIVE_PALLETS", request.idempotencyKey(), request, operatorId, now);
history(order, "RECEIVE_PALLETS", from, next, operatorId, request.idempotencyKey(), request.remark(), now);
return detail(customerId, orderNo);
}
@Transactional
public UsTruckOrderResponses.Detail reviewPalletOverage(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.ReviewPalletOverage request) {
UsTruckOrder order = lock(customerId, orderNo);
UsTruckOrderResponses.Detail replay = replayOperation(order, "REVIEW_PALLET_OVERAGE",
request.idempotencyKey(), request);
if (replay != null) return replay;
requireVersion(order, request.version());
if (!PALLET_OVERAGE_REVIEW.equals(order.getStatus())) {
throw conflict("Only pallet-overage-review orders can be reviewed");
}
boolean approve = "APPROVE".equals(norm(request.decision()));
List<UsTruckOrderPallet> rows = pallets.listOrder(customerId, order.getId());
LocalDateTime now = LocalDateTime.now();
int accepted = value(order.getPalletCount());
for (UsTruckOrderPallet pallet : rows) {
if (!"OVERAGE_PENDING_REVIEW".equals(pallet.getStatus())) continue;
pallet.setStatus(approve ? "RECEIVED" : "OVERAGE_REJECTED");
pallet.setUpdatedAt(now);
pallets.updateById(pallet);
}
if (approve) {
accepted = value(order.getReceivedPalletCount());
order.setPalletCount(accepted);
} else {
order.setReceivedPalletCount(accepted);
}
order.setStatus(WAITING_CONSOLIDATION);
touch(order);
orders.updateById(order);
recordOperation(order, "REVIEW_PALLET_OVERAGE", request.idempotencyKey(), request, operatorId, now);
history(order, "REVIEW_PALLET_OVERAGE", PALLET_OVERAGE_REVIEW, WAITING_CONSOLIDATION,
operatorId, request.idempotencyKey(), request.remark(), now);
return detail(customerId, orderNo);
}
@Transactional
public UsTruckOrderResponses.Detail allocatePallets(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.AllocatePallets request) {
UsTruckOrder order = lock(customerId, orderNo);
UsTruckOrderResponses.Detail replay = replayOperation(order, "ALLOCATE_PALLETS",
request.idempotencyKey(), request);
if (replay != null) return replay;
requireVersion(order, request.version());
if (!Set.of(WAITING_CONSOLIDATION, PARTIALLY_ALLOCATED).contains(order.getStatus())) {
throw conflict("Only waiting-consolidation or partially-allocated orders can allocate pallets");
}
Set<Long> requested = new HashSet<>(request.palletIds());
if (requested.size() != request.palletIds().size()) throw badRequest("palletIds must be unique");
List<UsTruckOrderPallet> rows = pallets.listOrder(customerId, order.getId());
List<UsTruckOrderPallet> selected = rows.stream().filter(row -> requested.contains(row.getId())).toList();
if (selected.size() != requested.size() || selected.stream().anyMatch(row -> !"RECEIVED".equals(row.getStatus()))) {
throw conflict("Selected pallets must belong to the order and be received but unallocated");
}
LocalDateTime now = LocalDateTime.now();
for (UsTruckOrderPallet pallet : selected) {
pallet.setStatus("ALLOCATED");
pallet.setUpdatedAt(now);
pallets.updateById(pallet);
}
int allocated = (int) rows.stream().filter(row -> "ALLOCATED".equals(row.getStatus())).count()
+ selected.size();
int accepted = value(order.getReceivedPalletCount());
if (accepted < 1 || allocated > accepted) throw conflict("Allocated pallet count exceeds accepted pallets");
String from = order.getStatus();
String next = allocated == accepted ? ALLOCATED : PARTIALLY_ALLOCATED;
order.setAllocatedPalletCount(allocated);
order.setStatus(next);
touch(order);
orders.updateById(order);
recordOperation(order, "ALLOCATE_PALLETS", request.idempotencyKey(), request, operatorId, now);
history(order, "ALLOCATE_PALLETS", from, next, operatorId,
request.idempotencyKey(), request.remark(), now);
return detail(customerId, orderNo);
}
@Transactional
public UsTruckOrderResponses.Detail prepareDispatch(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.Command request) {
return command(customerId, orderNo, operatorId, "PREPARE_DISPATCH", ALLOCATED,
PENDING_DISPATCH, request);
}
@Transactional
public UsTruckOrderResponses.Detail dispatch(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.Command request) {
return command(customerId, orderNo, operatorId, "DISPATCH", PENDING_DISPATCH, DISPATCHED, request);
}
@Transactional
public UsTruckOrderResponses.Detail startTransit(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.Command request) {
return command(customerId, orderNo, operatorId, "START_TRANSIT", DISPATCHED, IN_TRANSIT, request);
}
@Transactional
public UsTruckOrderResponses.Detail complete(String customerId, String orderNo, String operatorId,
UsTruckOrderRequests.Command request) {
return command(customerId, orderNo, operatorId, "COMPLETE", IN_TRANSIT, COMPLETED, request);
}
private UsTruckOrder lock(String customerId, String orderNo) { UsTruckOrder o = orders.lockNo(customerId, orderNo); if (o == null) throw notFound(); return o; }
private UsTruckOrderResponses.Detail replay(String customerId, UsTruckOrder order, String hash) { if (!hash.equals(order.getRequestHash())) throw conflict("Idempotency key was reused with different truck data"); return detail(customerId, order.getOrderNo()); }
private UsTruckOrderResponses.Detail detail(UsTruckOrder o, List<UsTruckOrderStatusHistory> hs) {