Commit 31d2fb0196fcd6f84b32454d1b413943c04e38f0

Authored by 谭毅彬
1 parent da1c23a4

按库区下发任务到WCS,多库区互不干扰

Signed-off-by: TanYibin <5491541@qq.com>
ant-design-vue-jeecg/src/views/system/shipment/ShipmentHeaderList.vue
@@ -105,9 +105,9 @@ @@ -105,9 +105,9 @@
105 <!-- 操作按钮区域 --> 105 <!-- 操作按钮区域 -->
106 <div class="table-operator"> 106 <div class="table-operator">
107 <a-button v-has="'shipmentHeader:add'" @click="handleAdd" type="primary" icon="plus">新增</a-button> 107 <a-button v-has="'shipmentHeader:add'" @click="handleAdd" type="primary" icon="plus">新增</a-button>
  108 + <a-button @click="mergeShipment" type="primary" v-has="'shipmentHeader:autoShipmentCombine'">合并单据</a-button>
108 <a-button v-has="'shipmentHeader:export'" type="primary" icon="download" @click="handleExportXls('出库单')">导出</a-button> 109 <a-button v-has="'shipmentHeader:export'" type="primary" icon="download" @click="handleExportXls('出库单')">导出</a-button>
109 - <a-upload v-has="'shipmentHeader:import'" name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl"  
110 - @change="handleImportExcel"> 110 + <a-upload v-has="'shipmentHeader:import'" name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
111 <a-button type="primary" icon="import">导入</a-button> 111 <a-button type="primary" icon="import">导入</a-button>
112 <a-button v-has="'shipmentHeader:print'" @click="batchPrint()" type="primary">打印</a-button> 112 <a-button v-has="'shipmentHeader:print'" @click="batchPrint()" type="primary">打印</a-button>
113 </a-upload> 113 </a-upload>
@@ -135,7 +135,7 @@ @@ -135,7 +135,7 @@
135 :dataSource="dataSource" 135 :dataSource="dataSource"
136 :pagination="ipagination" 136 :pagination="ipagination"
137 :loading="loading" 137 :loading="loading"
138 - :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange, type:'radio'}" 138 + :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange, type:'checkbox'}"
139 :customRow="clickThenSelect" 139 :customRow="clickThenSelect"
140 @change="handleTableChange"> 140 @change="handleTableChange">
141 141
@@ -400,6 +400,25 @@ export default { @@ -400,6 +400,25 @@ export default {
400 } 400 }
401 }, 401 },
402 methods: { 402 methods: {
  403 + mergeShipment() {
  404 + debugger
  405 + if (this.selectedRowKeys.length <= 1) {
  406 + this.$message.warning('至少选择两条记录!')
  407 + return
  408 + }
  409 + var shipmentHeaderList = []
  410 + for (var a = 0; a < this.selectedRowKeys.length; a++) {
  411 + if (this.selectionRows[a] != null && this.selectionRows[a].firstStatus == 0 && this.selectionRows[a].lastStatus == 0) {
  412 + shipmentHeaderList.push(this.selectionRows[a])
  413 + }
  414 + }
  415 + if (shipmentHeaderList.length > 0) {
  416 + this.$refs.modalForm2.batchEdit(shipmentHeaderList)
  417 + this.$refs.modalForm2.title = '选择出库口'
  418 + } else {
  419 + this.$message.error("至少选择两条符合条件的记录!")
  420 + }
  421 + },
403 getDocumentAduitFlowStaus(){ 422 getDocumentAduitFlowStaus(){
404 getDocumentAduitFlow().then((res) => { 423 getDocumentAduitFlow().then((res) => {
405 this.flowOff=res.message; 424 this.flowOff=res.message;
@@ -435,7 +454,9 @@ export default { @@ -435,7 +454,9 @@ export default {
435 this.selectedMainId = '' 454 this.selectedMainId = ''
436 }, 455 },
437 onSelectChange(selectedRowKeys, selectionRows) { 456 onSelectChange(selectedRowKeys, selectionRows) {
438 - this.selectedMainId = selectedRowKeys[0].toString(); 457 + if (selectedRowKeys.length > 0) {
  458 + this.selectedMainId = selectedRowKeys[0].toString();
  459 + }
439 this.selectedRowKeys = selectedRowKeys; 460 this.selectedRowKeys = selectedRowKeys;
440 this.selectionRows = selectionRows; 461 this.selectionRows = selectionRows;
441 this.flowStatus = selectionRows[0].firstStatus.toString(); 462 this.flowStatus = selectionRows[0].firstStatus.toString();
huaheng-wms-core/src/main/java/org/jeecg/modules/wms/framework/controller/HuahengBaseController.java
@@ -6,6 +6,7 @@ import javax.annotation.Nonnull; @@ -6,6 +6,7 @@ import javax.annotation.Nonnull;
6 6
7 import org.jeecg.common.api.vo.Result; 7 import org.jeecg.common.api.vo.Result;
8 import org.jeecg.common.exception.JeecgBootException; 8 import org.jeecg.common.exception.JeecgBootException;
  9 +import org.jeecg.modules.wms.task.taskHeader.entity.TaskHeader;
9 import org.jeecg.utils.support.RedissonDistributedLocker; 10 import org.jeecg.utils.support.RedissonDistributedLocker;
10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Autowired;
11 12
@@ -59,14 +60,29 @@ public class HuahengBaseController { @@ -59,14 +60,29 @@ public class HuahengBaseController {
59 * @return 60 * @return
60 */ 61 */
61 public Result<?> handleMultiProcess(@Nonnull String taskName, @Nonnull String lockKey, MultiProcessListener multiProcessListener) { 62 public Result<?> handleMultiProcess(@Nonnull String taskName, @Nonnull String lockKey, MultiProcessListener multiProcessListener) {
  63 + return this.handleMultiProcess(taskName, lockKey, WAIT_TIME, LEASE_TIME, multiProcessListener);
  64 + }
  65 +
  66 + /**
  67 + * 获取分布式锁,自定义锁等待,释放时间</br>
  68 + * 注意锁与事务的顺序:获取分布式锁 -> 开启事务 -> 执行业务 -> 提交事务 -> 释放分布式锁
  69 + * @author TanYibin
  70 + * @createDate 2023年2月8日
  71 + * @param taskName
  72 + * @param lockKey
  73 + * @param multiProcessListener
  74 + * @return
  75 + */
  76 + public Result<?> handleMultiProcess(@Nonnull String taskName, @Nonnull String lockKey, @Nonnull Integer waitTime, @Nonnull Integer leaseTime,
  77 + MultiProcessListener multiProcessListener) {
62 Result<?> result = null; 78 Result<?> result = null;
63 final long startTime = SystemClock.now(); 79 final long startTime = SystemClock.now();
64 final String fullLockKey = "RedissonLock_" + taskName + "_" + lockKey; 80 final String fullLockKey = "RedissonLock_" + taskName + "_" + lockKey;
65 - final boolean tryLock = redissonDistributedLocker.tryLock(fullLockKey, TimeUnit.SECONDS, WAIT_TIME, LEASE_TIME); 81 + final boolean tryLock = redissonDistributedLocker.tryLock(fullLockKey, TimeUnit.SECONDS, waitTime, leaseTime);
66 final long endTime = SystemClock.now(); 82 final long endTime = SystemClock.now();
67 if (!tryLock) { 83 if (!tryLock) {
68 log.error("[{}] 获取分布式锁失败 lockKey = {},等待锁耗时:{}ms", taskName, fullLockKey, endTime - startTime); 84 log.error("[{}] 获取分布式锁失败 lockKey = {},等待锁耗时:{}ms", taskName, fullLockKey, endTime - startTime);
69 - throw new RuntimeException(StrUtil.format("[{}] 获取分布式锁失败 lockKey = {},等待时间超出10秒", taskName, fullLockKey)); 85 + throw new RuntimeException(StrUtil.format("[{}] 获取分布式锁失败 lockKey = {},超出设定等待时间:{}s", taskName, fullLockKey, waitTime));
70 } 86 }
71 // 注意:一定是获取锁成功后,才进行try{}finally{释放锁} 87 // 注意:一定是获取锁成功后,才进行try{}finally{释放锁}
72 try { 88 try {
huaheng-wms-core/src/main/java/org/jeecg/modules/wms/framework/service/IHuahengMultiHandlerService.java
1 package org.jeecg.modules.wms.framework.service; 1 package org.jeecg.modules.wms.framework.service;
2 2
  3 +import java.util.List;
  4 +
3 import org.jeecg.common.api.vo.Result; 5 import org.jeecg.common.api.vo.Result;
4 import org.jeecg.modules.wms.api.mobile.entity.CallBoxBean; 6 import org.jeecg.modules.wms.api.mobile.entity.CallBoxBean;
5 import org.jeecg.modules.wms.api.mobile.entity.QuickReceiptBean; 7 import org.jeecg.modules.wms.api.mobile.entity.QuickReceiptBean;
@@ -16,8 +18,6 @@ import org.jeecg.modules.wms.task.taskHeader.entity.TaskHeader; @@ -16,8 +18,6 @@ import org.jeecg.modules.wms.task.taskHeader.entity.TaskHeader;
16 */ 18 */
17 public interface IHuahengMultiHandlerService { 19 public interface IHuahengMultiHandlerService {
18 20
19 - Result sendTaskToWcs(TaskHeader taskHeader);  
20 -  
21 Result createReceiptTask(ReceiptContainerHeader receiptContainerHeader, String warehouseCode); 21 Result createReceiptTask(ReceiptContainerHeader receiptContainerHeader, String warehouseCode);
22 22
23 Result combination(CombinationModel combinationModel); 23 Result combination(CombinationModel combinationModel);
@@ -45,4 +45,9 @@ public interface IHuahengMultiHandlerService { @@ -45,4 +45,9 @@ public interface IHuahengMultiHandlerService {
45 Result backReceipt(ReceiptHeader receiptHeader); 45 Result backReceipt(ReceiptHeader receiptHeader);
46 46
47 Result backShipment(ShipmentHeader shipmentHeader); 47 Result backShipment(ShipmentHeader shipmentHeader);
  48 +
  49 + Result sendTaskToWcs(TaskHeader taskHeader, String zoneCode);
  50 +
  51 + void sendTaskToWcs(List<TaskHeader> taskHeaders, String warehouseCode, String zoneCode);
  52 +
48 } 53 }
huaheng-wms-core/src/main/java/org/jeecg/modules/wms/framework/service/impl/HuahengMultiHandlerServiceImpl.java
1 package org.jeecg.modules.wms.framework.service.impl; 1 package org.jeecg.modules.wms.framework.service.impl;
2 2
3 import java.math.BigDecimal; 3 import java.math.BigDecimal;
  4 +import java.util.List;
4 5
5 import javax.annotation.Resource; 6 import javax.annotation.Resource;
6 7
@@ -26,11 +27,15 @@ import org.jeecg.modules.wms.shipment.shipmentHeader.entity.ShipmentHeader; @@ -26,11 +27,15 @@ import org.jeecg.modules.wms.shipment.shipmentHeader.entity.ShipmentHeader;
26 import org.jeecg.modules.wms.shipment.shipmentHeader.service.IShipmentDetailService; 27 import org.jeecg.modules.wms.shipment.shipmentHeader.service.IShipmentDetailService;
27 import org.jeecg.modules.wms.task.taskHeader.entity.TaskHeader; 28 import org.jeecg.modules.wms.task.taskHeader.entity.TaskHeader;
28 import org.jeecg.modules.wms.task.taskHeader.service.ITaskHeaderService; 29 import org.jeecg.modules.wms.task.taskHeader.service.ITaskHeaderService;
  30 +import org.springframework.scheduling.annotation.Async;
29 import org.springframework.stereotype.Service; 31 import org.springframework.stereotype.Service;
30 32
  33 +import lombok.extern.slf4j.Slf4j;
  34 +
31 /** 35 /**
32 * @author 游杰 36 * @author 游杰
33 */ 37 */
  38 +@Slf4j
34 @Service 39 @Service
35 public class HuahengMultiHandlerServiceImpl extends HuahengBaseController implements IHuahengMultiHandlerService { 40 public class HuahengMultiHandlerServiceImpl extends HuahengBaseController implements IHuahengMultiHandlerService {
36 41
@@ -59,21 +64,41 @@ public class HuahengMultiHandlerServiceImpl extends HuahengBaseController implem @@ -59,21 +64,41 @@ public class HuahengMultiHandlerServiceImpl extends HuahengBaseController implem
59 private IErpService erpService; 64 private IErpService erpService;
60 65
61 @Override 66 @Override
62 - public Result sendTaskToWcs(TaskHeader taskHeader) { 67 + public Result sendTaskToWcs(TaskHeader taskHeader, String zoneCode) {
63 if (taskHeader == null) { 68 if (taskHeader == null) {
64 return Result.error("任务信息为空"); 69 return Result.error("任务信息为空");
65 } 70 }
66 - String lockKey = taskHeader.getWarehouseCode();  
67 - Result result = handleMultiProcess("sendTaskToWcs", lockKey, new MultiProcessListener() { 71 + String lockKey = taskHeader.getWarehouseCode() + "_" + zoneCode;
  72 + Result result = handleMultiProcess("sendTaskToWcs", lockKey, 10, 300, new MultiProcessListener() {
  73 +
68 @Override 74 @Override
69 public Result<?> doProcess() { 75 public Result<?> doProcess() {
70 - Result result = taskHeaderService.sendTaskToWcs(taskHeader.getId());  
71 - return result; 76 + return taskHeaderService.sendTaskToWcs(taskHeader.getId());
72 } 77 }
73 }); 78 });
74 return result; 79 return result;
75 } 80 }
76 81
  82 + @Async("baseExecutor")
  83 + @Override
  84 + public void sendTaskToWcs(List<TaskHeader> taskHeaders, String warehouseCode, String zoneCode) {
  85 + String lockKey = warehouseCode + "_" + zoneCode;
  86 + handleMultiProcess("sendTaskToWcs", lockKey, 10, 300, new MultiProcessListener() {
  87 +
  88 + @Override
  89 + public Result<?> doProcess() {
  90 + for (TaskHeader taskHeader : taskHeaders) {
  91 + try {
  92 + taskHeaderService.sendTaskToWcs(taskHeader.getId());
  93 + } catch (Exception e) {
  94 + log.error("sendTaskToWcs 执行异常,errorMessage: {}", e.getMessage(), e);
  95 + }
  96 + }
  97 + return Result.OK();
  98 + }
  99 + });
  100 + }
  101 +
77 @Override 102 @Override
78 public Result createReceiptTask(ReceiptContainerHeader receiptContainerHeader, String warehouseCode) { 103 public Result createReceiptTask(ReceiptContainerHeader receiptContainerHeader, String warehouseCode) {
79 Result result = handleMultiProcess("createReceiptTask", warehouseCode, new MultiProcessListener() { 104 Result result = handleMultiProcess("createReceiptTask", warehouseCode, new MultiProcessListener() {
huaheng-wms-core/src/main/java/org/jeecg/modules/wms/monitor/job/WcsTask.java
1 package org.jeecg.modules.wms.monitor.job; 1 package org.jeecg.modules.wms.monitor.job;
2 2
3 -import java.util.HashMap; 3 +import java.util.Date;
4 import java.util.List; 4 import java.util.List;
5 import java.util.Map; 5 import java.util.Map;
  6 +import java.util.stream.Collectors;
6 7
7 import javax.annotation.Resource; 8 import javax.annotation.Resource;
8 9
9 -import org.apache.commons.collections.MapUtils;  
10 import org.jeecg.common.util.DateUtils; 10 import org.jeecg.common.util.DateUtils;
  11 +import org.jeecg.modules.wms.config.container.entity.Container;
  12 +import org.jeecg.modules.wms.config.container.service.IContainerService;
11 import org.jeecg.modules.wms.framework.service.IHuahengMultiHandlerService; 13 import org.jeecg.modules.wms.framework.service.IHuahengMultiHandlerService;
12 import org.jeecg.modules.wms.task.taskHeader.entity.TaskHeader; 14 import org.jeecg.modules.wms.task.taskHeader.entity.TaskHeader;
13 import org.jeecg.modules.wms.task.taskHeader.service.ITaskHeaderService; 15 import org.jeecg.modules.wms.task.taskHeader.service.ITaskHeaderService;
@@ -17,10 +19,13 @@ import org.quartz.Job; @@ -17,10 +19,13 @@ import org.quartz.Job;
17 import org.quartz.JobExecutionContext; 19 import org.quartz.JobExecutionContext;
18 import org.quartz.JobExecutionException; 20 import org.quartz.JobExecutionException;
19 import org.quartz.PersistJobDataAfterExecution; 21 import org.quartz.PersistJobDataAfterExecution;
  22 +import org.springframework.util.CollectionUtils;
  23 +import org.springframework.util.StringUtils;
20 24
21 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 25 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
22 import com.baomidou.mybatisplus.core.toolkit.Wrappers; 26 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
23 27
  28 +import cn.hutool.core.date.DateUtil;
24 import lombok.extern.slf4j.Slf4j; 29 import lombok.extern.slf4j.Slf4j;
25 30
26 /** 31 /**
@@ -39,18 +44,29 @@ public class WcsTask implements Job { @@ -39,18 +44,29 @@ public class WcsTask implements Job {
39 @Resource 44 @Resource
40 private IHuahengMultiHandlerService huahengMultiHandlerService; 45 private IHuahengMultiHandlerService huahengMultiHandlerService;
41 46
  47 + @Resource
  48 + private IContainerService containerService;
  49 +
42 @Override 50 @Override
43 public void execute(JobExecutionContext context) throws JobExecutionException { 51 public void execute(JobExecutionContext context) throws JobExecutionException {
44 log.info(String.format(" WcsTask 执行任务! 时间:" + DateUtils.getTimestamp())); 52 log.info(String.format(" WcsTask 执行任务! 时间:" + DateUtils.getTimestamp()));
45 LambdaQueryWrapper<TaskHeader> taskHeaderLambdaQueryWrapper = Wrappers.lambdaQuery(); 53 LambdaQueryWrapper<TaskHeader> taskHeaderLambdaQueryWrapper = Wrappers.lambdaQuery();
46 - taskHeaderLambdaQueryWrapper.eq(TaskHeader::getStatus, QuantityConstant.TASK_STATUS_BUILD).eq(TaskHeader::getAgvTaskId, 0); 54 + taskHeaderLambdaQueryWrapper.eq(TaskHeader::getStatus, QuantityConstant.TASK_STATUS_BUILD).eq(TaskHeader::getAgvTaskId, 0)
  55 + .ge(TaskHeader::getCreateTime, DateUtil.offsetDay(new Date(), -180)).orderByAsc(TaskHeader::getCreateTime);
47 List<TaskHeader> taskHeaderList = taskHeaderService.list(taskHeaderLambdaQueryWrapper); 56 List<TaskHeader> taskHeaderList = taskHeaderService.list(taskHeaderLambdaQueryWrapper);
48 - for (TaskHeader taskHeader : taskHeaderList) {  
49 - try {  
50 - huahengMultiHandlerService.sendTaskToWcs(taskHeader);  
51 - } catch (Exception e) {  
52 - e.printStackTrace();  
53 - } 57 + if (CollectionUtils.isEmpty(taskHeaderList)) {
  58 + return;
54 } 59 }
  60 + LambdaQueryWrapper<Container> containerLambdaQueryWrapper = Wrappers.lambdaQuery();
  61 + containerLambdaQueryWrapper.in(Container::getCode, taskHeaderList.stream().map(TaskHeader::getContainerCode).collect(Collectors.toList()));
  62 + List<Container> containerList = containerService.list(containerLambdaQueryWrapper);
  63 + Map<String, String> containerZoneCodeMap = containerList.stream().collect(Collectors.toMap(Container::getCode, Container::getZoneCode));
  64 + Map<String, List<TaskHeader>> groupeTaskHeaders =
  65 + taskHeaderList.stream().collect(Collectors.groupingBy(taskHeader -> StringUtils.isEmpty(containerZoneCodeMap.get(taskHeader.getContainerCode()))
  66 + ? QuantityConstant.DEFAULT_ZONE_CODE : containerZoneCodeMap.get(taskHeader.getContainerCode())));
  67 + // 循环 groupedTaskHeaders 中的每个键值对
  68 + groupeTaskHeaders.forEach((zoneCode, taskHeaders) -> {
  69 + huahengMultiHandlerService.sendTaskToWcs(taskHeaders, taskHeaders.get(0).getWarehouseCode(), zoneCode);
  70 + });
55 } 71 }
56 } 72 }
huaheng-wms-core/src/main/java/org/jeecg/modules/wms/task/taskHeader/controller/TaskHeaderController.java
@@ -387,8 +387,8 @@ public class TaskHeaderController extends HuahengBaseController { @@ -387,8 +387,8 @@ public class TaskHeaderController extends HuahengBaseController {
387 if (taskHeader == null) { 387 if (taskHeader == null) {
388 return Result.error("taskHeader不能为空"); 388 return Result.error("taskHeader不能为空");
389 } 389 }
390 - Result result = huahengMultiHandlerService.sendTaskToWcs(taskHeader);  
391 - return result; 390 + Container container = containerService.getContainerByCode(taskHeader.getContainerCode(), taskHeader.getWarehouseCode());
  391 + return huahengMultiHandlerService.sendTaskToWcs(taskHeader, container == null ? QuantityConstant.DEFAULT_ZONE_CODE : container.getZoneCode());
392 } 392 }
393 393
394 /** 394 /**
huaheng-wms-core/src/main/java/org/jeecg/utils/constant/QuantityConstant.java
@@ -616,4 +616,7 @@ public class QuantityConstant { @@ -616,4 +616,7 @@ public class QuantityConstant {
616 * 受控,受控的库存详情不能出库 616 * 受控,受控的库存详情不能出库
617 */ 617 */
618 public static final int CONTROLLER_ENABLE = 1; 618 public static final int CONTROLLER_ENABLE = 1;
  619 +
  620 + /** 默认库区编码 */
  621 + public static final String DEFAULT_ZONE_CODE = "Default";
619 } 622 }