修改提交
This commit is contained in:
+15
@@ -17,6 +17,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@@ -90,6 +92,19 @@ public class SmartDispatchController {
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/drafts/containers")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_SMART_DISPATCH + "')")
|
||||
public ApiResponse<com.baiyue.lastleg.dispatch.dto.DispatchContainerResponse> createContainer(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid @RequestBody DispatchRequests.DraftContainer request) {
|
||||
requirePlatform(user); return ApiResponse.success(service.createDraftContainer(operatorId(user), request));
|
||||
}
|
||||
|
||||
@DeleteMapping("/drafts/{draftToken}/containers/{containerId}")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_SMART_DISPATCH + "')")
|
||||
public ApiResponse<Void> deleteContainer(@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String draftToken, @PathVariable Long containerId) {
|
||||
requirePlatform(user); service.deleteDraftContainer(operatorId(user), draftToken, containerId); return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/draft-locks/unlock")
|
||||
@PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_SMART_DISPATCH + "')")
|
||||
@Operation(summary = "Unlock one pallet removed from the current intelligent dispatch draft")
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.baiyue.lastleg.dispatch.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record DispatchContainerResponse(String id, int capacity, List<String> palletIds) {}
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
package com.baiyue.lastleg.dispatch.dto;
|
||||
|
||||
public record DispatchDraftResponse(String draftToken, String planIdsJson, String containersJson) {
|
||||
import java.util.List;
|
||||
public record DispatchDraftResponse(String draftToken, String planIdsJson, String containersJson, List<DispatchContainerResponse> containers) {
|
||||
public DispatchDraftResponse(String draftToken, String planIdsJson, String containersJson) { this(draftToken, planIdsJson, containersJson, List.of()); }
|
||||
}
|
||||
|
||||
@@ -30,9 +30,12 @@ public final class DispatchRequests {
|
||||
}
|
||||
}
|
||||
|
||||
public record DraftPallet(@NotNull Long planPalletId, @NotBlank @Size(max = 64) String draftToken) {
|
||||
public record DraftPallet(@NotNull Long planPalletId, @NotBlank @Size(max = 64) String draftToken, Long draftContainerId) {
|
||||
public DraftPallet(Long planPalletId, String draftToken) { this(planPalletId, draftToken, null); }
|
||||
}
|
||||
|
||||
public record DraftContainer(@NotBlank @Size(max = 64) String draftToken, @NotNull @Min(1) @Max(500) Integer capacity) {}
|
||||
|
||||
public record DraftRelease(@NotBlank @Size(max = 64) String draftToken) {
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ package com.baiyue.lastleg.dispatch.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_dispatch_draft")
|
||||
public class DispatchDraft {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.baiyue.lastleg.dispatch.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_dispatch_draft_container")
|
||||
public class DispatchDraftContainer {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
private Long draftId;
|
||||
private String draftToken;
|
||||
private Integer sequenceNo;
|
||||
private Integer capacity;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
+1
@@ -24,6 +24,7 @@ public class DispatchPalletLock {
|
||||
private Long planPalletId;
|
||||
private Long warehousePalletId;
|
||||
private Long dispatchOrderId;
|
||||
private Long draftContainerId;
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String draftToken;
|
||||
private String operatorId;
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.baiyue.lastleg.dispatch.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baiyue.lastleg.dispatch.entity.DispatchDraftContainer;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DispatchDraftContainerMapper extends BaseMapper<DispatchDraftContainer> {
|
||||
@Select("SELECT * FROM ll_dispatch_draft_container WHERE draft_token=#{draftToken} ORDER BY sequence_no,id")
|
||||
List<DispatchDraftContainer> findByToken(@Param("draftToken") String draftToken);
|
||||
}
|
||||
+49
-1
@@ -10,11 +10,14 @@ import com.baiyue.lastleg.dispatch.entity.DispatchItem;
|
||||
import com.baiyue.lastleg.dispatch.entity.DispatchOrder;
|
||||
import com.baiyue.lastleg.dispatch.entity.DispatchPalletLock;
|
||||
import com.baiyue.lastleg.dispatch.entity.DispatchDraft;
|
||||
import com.baiyue.lastleg.dispatch.entity.DispatchDraftContainer;
|
||||
import com.baiyue.lastleg.dispatch.mapper.DispatchCandidateMapper;
|
||||
import com.baiyue.lastleg.dispatch.mapper.DispatchItemMapper;
|
||||
import com.baiyue.lastleg.dispatch.mapper.DispatchOrderMapper;
|
||||
import com.baiyue.lastleg.dispatch.mapper.DispatchPalletLockMapper;
|
||||
import com.baiyue.lastleg.dispatch.mapper.DispatchDraftMapper;
|
||||
import com.baiyue.lastleg.dispatch.mapper.DispatchDraftContainerMapper;
|
||||
import com.baiyue.lastleg.dispatch.dto.DispatchContainerResponse;
|
||||
import com.baiyue.lastleg.dispatch.dto.DispatchDraftResponse;
|
||||
import com.baiyue.lastleg.dispatch.model.DispatchPalletCandidate;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -43,6 +46,7 @@ public class DispatchService {
|
||||
@Resource private DispatchItemMapper items;
|
||||
@Resource private DispatchPalletLockMapper locks;
|
||||
@Resource private DispatchDraftMapper drafts;
|
||||
@Resource private DispatchDraftContainerMapper draftContainers;
|
||||
@Resource private DispatchCandidateMapper candidates;
|
||||
|
||||
public String resolveCustomerScopeForPlans(List<Long> planIds) {
|
||||
@@ -64,6 +68,10 @@ public class DispatchService {
|
||||
return candidates.available(customerId, scopedPlanIds, trimToNull(orderNo), trimToNull(fbaWarehouseCode),
|
||||
trimToNull(sourceStage), trimToNull(draftToken));
|
||||
}
|
||||
public List<DispatchPalletCandidate> candidates(String customerId, List<Long> planIds, String orderNo,
|
||||
String fbaWarehouseCode, String sourceStage) {
|
||||
return candidates(customerId, planIds, orderNo, fbaWarehouseCode, sourceStage, null);
|
||||
}
|
||||
|
||||
public List<DispatchPalletCandidate> platformCandidates(String orderNo, String fbaWarehouseCode,
|
||||
String sourceStage, String draftToken) {
|
||||
@@ -71,10 +79,32 @@ public class DispatchService {
|
||||
return candidates.available(null, null, trimToNull(orderNo), trimToNull(fbaWarehouseCode),
|
||||
trimToNull(sourceStage), trimToNull(draftToken));
|
||||
}
|
||||
public List<DispatchPalletCandidate> platformCandidates(String orderNo, String fbaWarehouseCode, String sourceStage) {
|
||||
return platformCandidates(orderNo, fbaWarehouseCode, sourceStage, null);
|
||||
}
|
||||
|
||||
public List<DispatchDraftResponse> activeDrafts(String operatorId) {
|
||||
return drafts.findActiveByOperator(trimToNull(operatorId)).stream()
|
||||
.map(item -> new DispatchDraftResponse(item.getDraftToken(), item.getPlanIdsJson(), item.getContainersJson())).toList();
|
||||
.map(item -> new DispatchDraftResponse(item.getDraftToken(), item.getPlanIdsJson(), item.getContainersJson(), containers(item.getDraftToken()))).toList();
|
||||
}
|
||||
|
||||
private List<DispatchContainerResponse> containers(String token) {
|
||||
return draftContainers.findByToken(token).stream().map(c -> new DispatchContainerResponse(String.valueOf(c.getId()), c.getCapacity(),
|
||||
locks.selectList(new LambdaQueryWrapper<DispatchPalletLock>().eq(DispatchPalletLock::getDraftContainerId, c.getId()).eq(DispatchPalletLock::getStatus, "ACTIVE")).stream().map(x -> String.valueOf(x.getPlanPalletId())).toList())).toList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DispatchContainerResponse createDraftContainer(String operatorId, DispatchRequests.DraftContainer request) {
|
||||
DispatchDraft draft = ensureDraft(operatorId, request.draftToken());
|
||||
int next = draftContainers.findByToken(request.draftToken()).stream().mapToInt(DispatchDraftContainer::getSequenceNo).max().orElse(0) + 1;
|
||||
DispatchDraftContainer c = new DispatchDraftContainer(); c.setId(IdWorker.getId()); c.setDraftId(draft.getId()); c.setDraftToken(request.draftToken()); c.setSequenceNo(next); c.setCapacity(request.capacity()); c.setCreatedAt(LocalDateTime.now()); c.setUpdatedAt(LocalDateTime.now()); draftContainers.insert(c);
|
||||
return new DispatchContainerResponse(String.valueOf(c.getId()), c.getCapacity(), List.of());
|
||||
}
|
||||
|
||||
private DispatchDraft ensureDraft(String operatorId, String token) {
|
||||
DispatchDraft draft = drafts.lockActive(token, trimToNull(operatorId));
|
||||
if (draft != null) return draft;
|
||||
LocalDateTime now = LocalDateTime.now(); draft = new DispatchDraft(); draft.setId(IdWorker.getId()); draft.setDraftToken(token); draft.setOperatorId(trimToNull(operatorId)); draft.setStatus("ACTIVE"); draft.setCreatedAt(now); draft.setUpdatedAt(now); draft.setExpiresAt(now.plusMinutes(30)); drafts.insert(draft); return draft;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -94,6 +124,12 @@ public class DispatchService {
|
||||
|
||||
@Transactional
|
||||
public void lockDraftPallet(String customerId, String operatorId, DispatchRequests.DraftPallet request) {
|
||||
DispatchDraft draft = ensureDraft(operatorId, request.draftToken());
|
||||
DispatchDraftContainer boundContainer = request.draftContainerId() == null ? null : draftContainers.selectById(request.draftContainerId());
|
||||
if (boundContainer == null && request.draftContainerId() == null) {
|
||||
boundContainer = new DispatchDraftContainer(); boundContainer.setId(IdWorker.getId()); boundContainer.setDraftId(draft.getId()); boundContainer.setDraftToken(request.draftToken()); boundContainer.setSequenceNo(1); boundContainer.setCapacity(500); boundContainer.setCreatedAt(LocalDateTime.now()); boundContainer.setUpdatedAt(LocalDateTime.now()); draftContainers.insert(boundContainer);
|
||||
}
|
||||
if (boundContainer == null || !request.draftToken().equals(boundContainer.getDraftToken()) || !Objects.equals(boundContainer.getDraftId(), draft.getId())) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "装车托盘不存在");
|
||||
DispatchPalletCandidate candidate = requireDraftCandidate(customerId, request.planPalletId());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
DispatchPalletLock active = activeLock(customerId, candidate);
|
||||
@@ -118,6 +154,7 @@ public class DispatchService {
|
||||
lock.setPlanPalletId(candidate.planPalletId());
|
||||
lock.setWarehousePalletId(candidate.warehousePalletId());
|
||||
lock.setDraftToken(request.draftToken());
|
||||
lock.setDraftContainerId(request.draftContainerId());
|
||||
lock.setOperatorId(trimToNull(operatorId));
|
||||
lock.setStatus("ACTIVE");
|
||||
lock.setLockedAt(now);
|
||||
@@ -149,10 +186,21 @@ public class DispatchService {
|
||||
.eq(DispatchPalletLock::getStatus, "ACTIVE"));
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
activeLocks.forEach(lock -> releaseLock(lock, now));
|
||||
draftContainers.delete(new LambdaQueryWrapper<DispatchDraftContainer>().eq(DispatchDraftContainer::getDraftToken, draftToken));
|
||||
DispatchDraft draft = drafts.lockActive(draftToken, trimToNull(operatorId));
|
||||
if (draft != null) { draft.setStatus("RELEASED"); draft.setReleasedAt(now); draft.setUpdatedAt(now); drafts.updateById(draft); }
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteDraftContainer(String operatorId, String token, Long containerId) {
|
||||
if (drafts.lockActive(token, trimToNull(operatorId)) == null) throw new ResponseStatusException(HttpStatus.CONFLICT, "草稿不存在或已失效");
|
||||
DispatchDraftContainer c = draftContainers.selectById(containerId);
|
||||
if (c == null || !token.equals(c.getDraftToken())) return;
|
||||
List<DispatchPalletLock> bound = locks.selectList(new LambdaQueryWrapper<DispatchPalletLock>().eq(DispatchPalletLock::getDraftContainerId, containerId).eq(DispatchPalletLock::getStatus, "ACTIVE"));
|
||||
bound.forEach(lock -> { requireDraftOwner(lock, operatorId, token); releaseLock(lock, LocalDateTime.now()); });
|
||||
draftContainers.deleteById(containerId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DispatchResponse preArrange(String customerId, String operatorId, DispatchRequests.PreArrange request) {
|
||||
if (!StringUtils.hasText(customerId)) {
|
||||
|
||||
@@ -820,7 +820,7 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_item (
|
||||
KEY idx_ll_dispatch_item_pallet (customer_id,plan_pallet_id,warehouse_pallet_id)
|
||||
) ENGINE=InnoDB COMMENT='派车货物冻结明细';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_dispatch_pallet_lock (
|
||||
CREATE TABLE IF NOT EXISTS ll_dispatch_pallet_lock (
|
||||
id BIGINT NOT NULL COMMENT '主键ID',
|
||||
customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID',
|
||||
source_pallet_key VARCHAR(160) NOT NULL COMMENT '来源托盘业务键',
|
||||
@@ -828,7 +828,8 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_pallet_lock (
|
||||
plan_id BIGINT NULL COMMENT '入仓计划ID',
|
||||
plan_pallet_id BIGINT NULL COMMENT '计划托盘ID',
|
||||
warehouse_pallet_id BIGINT NULL COMMENT '仓库实物托盘ID',
|
||||
dispatch_order_id BIGINT NULL COMMENT '正式派车单ID,草稿锁阶段为空',
|
||||
dispatch_order_id BIGINT NULL COMMENT '正式派车单ID,草稿锁阶段为空',
|
||||
draft_container_id BIGINT NULL COMMENT '智能排车草稿装车托盘ID',
|
||||
draft_token VARCHAR(64) NULL COMMENT '智能排车草稿随机标识',
|
||||
operator_id VARCHAR(128) NULL COMMENT '草稿锁操作人ID',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' COMMENT '业务状态',
|
||||
@@ -841,7 +842,25 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_pallet_lock (
|
||||
UNIQUE KEY uk_ll_dispatch_active_pallet (customer_id,active_key),
|
||||
KEY idx_ll_dispatch_pallet_lock_order (customer_id,dispatch_order_id,status),
|
||||
KEY idx_ll_dispatch_pallet_lock_source (customer_id,source_pallet_key,status)
|
||||
) ENGINE=InnoDB COMMENT='派车托盘排他锁';
|
||||
) ENGINE=InnoDB COMMENT='派车托盘排他锁';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_dispatch_draft (
|
||||
id BIGINT NOT NULL, draft_token VARCHAR(64) NOT NULL, customer_id VARCHAR(128) NULL,
|
||||
operator_id VARCHAR(128) NULL, plan_ids_json VARCHAR(4000) NULL, containers_json LONGTEXT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', expires_at DATETIME(3) NULL, released_at DATETIME(3) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY(id), UNIQUE KEY uk_ll_dispatch_draft_token(draft_token), KEY idx_ll_dispatch_draft_operator(operator_id,status)
|
||||
) ENGINE=InnoDB COMMENT='智能排车草稿';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ll_dispatch_draft_container (
|
||||
id BIGINT NOT NULL, draft_id BIGINT NOT NULL, draft_token VARCHAR(64) NOT NULL,
|
||||
sequence_no INT NOT NULL, capacity INT NOT NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY(id),
|
||||
KEY idx_ll_dispatch_draft_container_token(draft_token)
|
||||
) ENGINE=InnoDB COMMENT='智能排车草稿装车托盘';
|
||||
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_pallet_lock' AND column_name='draft_container_id'), 'SELECT 1', 'ALTER TABLE ll_dispatch_pallet_lock ADD COLUMN draft_container_id BIGINT NULL COMMENT ''智能排车草稿装车托盘ID'' AFTER dispatch_order_id'));
|
||||
PREPARE dispatch_draft_container_column_stmt FROM @ddl; EXECUTE dispatch_draft_container_column_stmt; DEALLOCATE PREPARE dispatch_draft_container_column_stmt;
|
||||
|
||||
SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_pallet_lock' AND column_name='draft_token'), 'SELECT 1', 'ALTER TABLE ll_dispatch_pallet_lock ADD COLUMN draft_token VARCHAR(64) NULL COMMENT ''智能排车草稿随机标识'' AFTER dispatch_order_id'));
|
||||
PREPARE dispatch_schema_stmt FROM @ddl; EXECUTE dispatch_schema_stmt; DEALLOCATE PREPARE dispatch_schema_stmt;
|
||||
|
||||
Reference in New Issue
Block a user