no message

main
HUOJIN\92525 2026-01-07 22:47:29 +08:00
parent d73d0d454f
commit d8b84477ad
21 changed files with 2746 additions and 120 deletions

View File

@ -181,7 +181,7 @@ public class ScanTrayProcessor {
*/
private String getAreaCode(Integer orderType) {
String areaCode = "";
if (Set.of(0, 1, 2, 3).contains(orderType)) {
if (Set.of(0, 1, 2, 3, 8).contains(orderType)) {
areaCode = AreaTypeEnum.CPCCQ.getValue();
} else {
areaCode = AreaTypeEnum.MJCCQ.getValue();
@ -199,51 +199,32 @@ public class ScanTrayProcessor {
* @return
*/
public Point allocatePoint(List<ItemKey> itemKeys, Point station, String areaCode, String type) {
//1.优先寻找同物料/同仓库/同项目号/同任务号/同批次/同外部库存状态的库位
List<Long> itemKeyIds = itemKeys.stream().map(ItemKey::getId).toList();
List<Point> availablePoints = pointService.findClusterPoint(itemKeyIds, areaCode);
// 1. 获取可用库位
List<Point> availablePoints = getAvailablePoints(itemKeys, areaCode, type);
if (CollectionUtils.isEmpty(availablePoints)) {
//2.获取所有可用库位
availablePoints = pointMapper.queryPoints(null, CommonStatusEnum.FREE.getValue(), areaCode);
throw new RuntimeException("系统无可用库位");
}
//根据巷道分组,得到每个巷道有多个库位,<巷道编号,库位个数>
Map<String, Long> colMap = getColMap(areaCode);
//根据layerNum层来分组
Map<String, List<Point>> layerMap = availablePoints.stream().collect(Collectors.groupingBy(Point::getLayerNum));
List<PointScore> firstScoredPoints = new ArrayList<>();
List<PointScore> firstScoredPoints;
List<PointScore> secondScoredPoints = new ArrayList<>();
//移位选择最优库位
if (type.equals(BusinessTypeEnum.MOVE.getValue())) {
String layerNum = station.getLayerNum();
Station currentStation = new Station(station.getPositionX(), station.getPositionY());
List<Point> firstLevelPoints = layerMap.get(layerNum);
if (CollectionUtils.isNotEmpty(firstLevelPoints)) {
firstScoredPoints = firstLevelPoints.stream()
.map(point -> clusterPointScore(point, currentStation, itemKeys, colMap))
.toList();
}
firstScoredPoints = selectBestPointForMove(station, itemKeys,colMap,layerMap);
} else {
//入库选择最优库位
//计算第一层的库位分数
Station firstStation = new Station(station.getPositionX(), station.getPositionY());
List<Point> firstLevelPoints = layerMap.get(String.valueOf(CommonStatusEnum.ONE.getValue()));
if (CollectionUtils.isNotEmpty(firstLevelPoints)) {
firstScoredPoints = firstLevelPoints.stream()
.map(point -> clusterPointScore(point, firstStation, itemKeys, colMap))
.toList();
}
firstScoredPoints = selectBestPointForStorage(firstStation, itemKeys, CommonStatusEnum.ONE.getValue(), colMap, layerMap);
//计算第二层的库位分数
Station secondStation = new Station(station.getPositionTwoX(), station.getPositionTwoY());
List<Point> secondLevelPoints = layerMap.get(String.valueOf(CommonStatusEnum.TWO.getValue()));
if (CollectionUtils.isNotEmpty(secondLevelPoints)) {
secondScoredPoints = secondLevelPoints.stream()
.map(point -> clusterPointScore(point, secondStation, itemKeys, colMap))
.toList();
}
secondScoredPoints= selectBestPointForStorage(secondStation, itemKeys, CommonStatusEnum.TWO.getValue(), colMap, layerMap);
}
//合并获取最优库位
@ -254,7 +235,7 @@ public class ScanTrayProcessor {
.sorted(Comparator.comparing(PointScore::getScore).reversed())
.toList();
if (!scoredPoints.isEmpty()) {
if (CollectionUtils.isNotEmpty(scoredPoints)) {
log.info("最优【{}】库位评分结果:{}", scoredPoints.get(0).getPoint().getPointCode(), scoredPoints.get(0).getScore());
return scoredPoints.get(0).getPoint();
}
@ -262,6 +243,60 @@ public class ScanTrayProcessor {
throw new RuntimeException("系统无可用库位");
}
/**
*
*/
private List<Point> getAvailablePoints(List<ItemKey> itemKeys, String areaCode, String type) {
//移位
if (BusinessTypeEnum.MOVE.getValue().equals(type)) {
return pointMapper.queryPoints(null, CommonStatusEnum.FREE.getValue(), areaCode);
}
//入库
List<Long> itemKeyIds = itemKeys.stream().map(ItemKey::getId).toList();
List<Point> availablePoints = pointService.findClusterPoint(itemKeyIds, areaCode);
if (CollectionUtils.isEmpty(availablePoints)) {
//2.获取所有可用库位
availablePoints = pointMapper.queryPoints(null, CommonStatusEnum.FREE.getValue(), areaCode);
}
return availablePoints;
}
/**
*
*/
private List<PointScore> selectBestPointForMove(Point station, List<ItemKey> itemKeys, Map<String, Long> colMap, Map<String, List<Point>> layerMap) {
//当前库位的层数
String layerNum = station.getLayerNum();
//当前库位的坐标
Station currentStation = new Station(station.getPositionX(), station.getPositionY());
//当前层的所有可用库位
List<Point> currentLayerPoints = layerMap.get(layerNum);
//过滤掉当前巷道的库位
currentLayerPoints = currentLayerPoints.stream().filter(point -> !point.getColNum().equals(station.getColNum())).toList();
if (CollectionUtils.isNotEmpty(currentLayerPoints)) {
return currentLayerPoints.stream()
.map(point -> clusterPointScore(point, currentStation, itemKeys, colMap))
.toList();
}
return new ArrayList<>();
}
/**
*
*/
private List<PointScore> selectBestPointForStorage(Station station, List<ItemKey> itemKeys, Integer layerNum, Map<String, Long> colMap, Map<String, List<Point>> layerMap) {
List<Point> currLayerPoints = layerMap.get(String.valueOf(layerNum));
if (CollectionUtils.isNotEmpty(currLayerPoints)) {
return currLayerPoints.stream()
.map(point -> clusterPointScore(point, station, itemKeys, colMap))
.toList();
}
return new ArrayList<>();
}
/**
*
*/

View File

@ -11,6 +11,7 @@ import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -41,16 +42,28 @@ public class AllocateJob implements Job {
// 缓存最大大小,防止内存溢出
private static final int MAX_CACHE_SIZE = 1000;
// 使用静态变量跟踪当前分配位置
private static volatile int currentIndex = 0;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
// 查询未分配或者部分分配的出库
List<Long> pickList = pickMapper.queryUnallocatedPick();
if (CollectionUtils.isNotEmpty(pickList)) {
// 分配出库
// 轮询分配每次只处理一个ID
if (currentIndex < pickList.size()) {
Long currentPickId = pickList.get(currentIndex);
List<Long> singlePickList = List.of(currentPickId);
long startTime = System.currentTimeMillis();
List<String> resultMsg = pickService.allocatePick(pickList);
List<String> resultMsg;
try {
resultMsg = pickService.allocatePick(singlePickList);
}catch (Exception e){
resultMsg=List.of(e.getMessage());
}
long endTime = System.currentTimeMillis();
log.info("分配出库明细耗时:{}ms", endTime - startTime);
log.info("分配出库明细耗时:{}ms处理ID{}", endTime - startTime, currentPickId);
if (CollectionUtils.isNotEmpty(resultMsg)) {
// 生成缓存键
String cacheKey = generateCacheKey(resultMsg);
@ -65,12 +78,16 @@ public class AllocateJob implements Job {
// 控制缓存大小
if (processedCache.size() > MAX_CACHE_SIZE) {
// 移除一部分旧缓存(简单实现,可以按需优化)
processedCache.clear();
}
}
}
// 更新索引,循环使用
currentIndex = (currentIndex + 1) % pickList.size();
}
}
//生成出库AGV出库任务
long startTime2 = System.currentTimeMillis();

View File

@ -64,6 +64,13 @@ public interface IAsnService extends IService<Asn> {
*/
void receiveAsn(Long asnId);
/**
*
*
* @param asn
*/
void cancelAsn(Asn asn);
}

View File

@ -108,9 +108,9 @@ public class AsnDetailServiceImpl extends ServiceImpl<AsnDetailMapper, AsnDetail
stockService.bindStock(stock);
//4.成品入库生成AGV任务
if (AsnOrderTypeEnum.PRODUCT.getValue().equals(inboundRequest.getType()) && station != null) {
/*if (AsnOrderTypeEnum.PRODUCT.getValue().equals(inboundRequest.getType()) && station != null) {
agvTaskService.createAgvTask(asn.getId(), stock.getStockCode(), srcPoint.getPointCode(), station.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), 0, AgvVendorEnum.HIK.getValue());
}
}*/
}
/**

View File

@ -1,12 +1,15 @@
package org.cpte.modules.receive.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.cpte.modules.agvTask.entity.AgvTask;
import org.cpte.modules.base.entity.Item;
import org.cpte.modules.base.entity.Point;
import org.cpte.modules.base.entity.Stock;
import org.cpte.modules.base.mapper.StockMapper;
import org.cpte.modules.constant.enums.AsnStatusEnum;
import org.cpte.modules.constant.enums.CommonStatusEnum;
import org.cpte.modules.receive.entity.Asn;
import org.cpte.modules.receive.entity.AsnDetail;
import org.cpte.modules.receive.mapper.AsnDetailMapper;
@ -36,6 +39,8 @@ import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnService {
@Autowired
private StockMapper stockMapper;
@Autowired
private AsnDetailMapper asnDetailMapper;
@Autowired
@ -119,6 +124,7 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
throw new RuntimeException("操作失败:【" + orderNoList + "】入库单,非创建状态不允许删除");
}
}
@Override
public void receiveAsn(Long asnId) {
String lockKey = "asn:" + asnId;
@ -138,4 +144,28 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelAsn(Asn asn) {
List<AsnDetail> details = asnDetailMapper.selectByMainId(asn.getId());
List<AsnDetail> updateToDetails = new ArrayList<>();
for (AsnDetail detail : details) {
detail.setStatus(AsnStatusEnum.CANCELED.getValue());
updateToDetails.add(detail);
}
if (CollectionUtils.isNotEmpty(updateToDetails)) {
asnDetailMapper.updateById(updateToDetails);
}
asn.setStatus(AsnStatusEnum.CANCELED.getValue());
this.baseMapper.updateById(asn);
if (CollectionUtils.isNotEmpty(details)) {
Stock stock = stockMapper.selectById(details.get(0).getStockId());
if (stock != null) {
stock.setStatus(CommonStatusEnum.FREE.getValue());
stockMapper.updateById(stock);
}
}
}
}

View File

@ -78,6 +78,10 @@ public class ReceiveProcessor {
// 1.数据准备
ReceiveData data = prepareReceiveData(asnId);
if(CollectionUtils.isEmpty(data.getAsnDetails())){
return;
}
//3.创建数据结构
List<AsnDetail> updateToAsnDetail = new ArrayList<>();
List<ReceiveRecord> createRecords = new ArrayList<>();

View File

@ -5,10 +5,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.cpte.modules.receive.service.IAsnService;
import org.cpte.modules.saiWms.request.CallAgvRequest;
import org.cpte.modules.saiWms.request.InboundRequest;
import org.cpte.modules.saiWms.request.OutboundRequest;
import org.cpte.modules.saiWms.request.SyncStockRequest;
import org.cpte.modules.saiWms.request.*;
import org.cpte.modules.saiWms.service.ISMOMService;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
@ -73,8 +70,21 @@ public class SaiWmsController {
@AutoLog(value = "出库任务下发")
@Operation(summary = "赛意WMS-出库任务下发")
@PostMapping(value = "/outBoundTask")
public Result<String> outBoundTask(@RequestBody @Valid OutboundRequest outboundRequest) {
public Result<String> outBoundTask(@RequestBody OutboundRequest outboundRequest) {
iSaiWmsService.outBoundTask(outboundRequest);
return Result.OK("操作成功!");
}
/**
*
*
* @param inBoundCancel
*/
@AutoLog(value = "入库任务取消")
@Operation(summary = "入库任务取消")
@PostMapping(value = "/inBoundCancel")
public Result<String> inBoundCancel(@RequestBody InBoundCancelRequest inBoundCancel) {
iSaiWmsService.inBoundCancel(inBoundCancel);
return Result.OK("操作成功!");
}
}

View File

@ -0,0 +1,11 @@
package org.cpte.modules.saiWms.request;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class InBoundCancelRequest {
// 任务号
@JsonProperty("No")
private String no;
}

View File

@ -15,17 +15,14 @@ import java.util.List;
@Data
public class OutboundRequest {
// 任务号
@NotBlank(message = "任务号不能为空")
@JsonProperty("No")
private String no;
// 单号
@NotBlank(message = "单号不能为空")
@JsonProperty("OrderNo")
private String orderNo;
// 仓库
@NotBlank(message = "仓库不能为空")
@JsonProperty("WhCode")
private String whCode;
@ -34,7 +31,6 @@ public class OutboundRequest {
private String customerCode;
// 单据类型:0.成品入库;1.配件入库;2.成品拆托入库;3.配件拆托入库;4.成品出库;5.配件出库;6.返工出库;7.检验出库;8.其他出库
@NotNull(message = "单据类型不能为空")
@JsonProperty("Type")
private Integer type;
@ -43,20 +39,16 @@ public class OutboundRequest {
private String enterprise;
// 出库明细列表
@NotNull(message = "入库明细不能为空")
@JsonProperty("details")
@Valid
private List<OutboundDetail> details;
@Data
public static class OutboundDetail {
// 行号
@NotBlank(message = "行号不能为空")
@JsonProperty("LineNo")
private String lineNo;
// 物料
@NotBlank(message = "物料不能为空")
@JsonProperty("Item")
private String item;
@ -66,10 +58,13 @@ public class OutboundRequest {
private String unit;
// 数量
@NotNull(message = "数量不能为空")
@JsonProperty("Qty")
private Double qty;
// 托盘号
@JsonProperty("Lpn")
private String lpn;
// 项目号
@JsonProperty("Project")
private String project;

View File

@ -1,9 +1,7 @@
package org.cpte.modules.saiWms.service;
import org.cpte.modules.saiWms.request.CallAgvRequest;
import org.cpte.modules.saiWms.request.InboundRequest;
import org.cpte.modules.saiWms.request.OutboundRequest;
import org.cpte.modules.saiWms.request.SyncStockRequest;
import jakarta.validation.Valid;
import org.cpte.modules.saiWms.request.*;
public interface ISMOMService {
@ -30,4 +28,9 @@ public interface ISMOMService {
* AGV
*/
void callAgv(CallAgvRequest callAgvRequest);
/**
*
*/
void inBoundCancel(InBoundCancelRequest inBoundCancel);
}

View File

@ -2,15 +2,9 @@ package org.cpte.modules.saiWms.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.cpte.modules.saiWms.request.CallAgvRequest;
import org.cpte.modules.saiWms.request.InboundRequest;
import org.cpte.modules.saiWms.request.OutboundRequest;
import org.cpte.modules.saiWms.request.SyncStockRequest;
import org.cpte.modules.saiWms.request.*;
import org.cpte.modules.saiWms.service.ISMOMService;
import org.cpte.modules.saiWms.service.processor.CallAgvProcessor;
import org.cpte.modules.saiWms.service.processor.InBoundTaskProcessor;
import org.cpte.modules.saiWms.service.processor.OutBoundTaskProcessor;
import org.cpte.modules.saiWms.service.processor.SyncStockProcessor;
import org.cpte.modules.saiWms.service.processor.*;
import org.cpte.modules.utils.RedisDistributedLockUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -31,6 +25,9 @@ public class ISMOMServiceImpl implements ISMOMService {
@Autowired
private CallAgvProcessor callAgvProcessor;
@Autowired
private InBoundCancelProcessor inBoundCancelProcessor;
@Autowired
private RedisDistributedLockUtil redissonLock;
@ -100,6 +97,26 @@ public class ISMOMServiceImpl implements ISMOMService {
}
}
}
@Override
public void inBoundCancel(InBoundCancelRequest inBoundCancel) {
String lockKey = "cancel:" + inBoundCancel.getNo();
String lockValue = null;
try {
lockValue = redissonLock.tryLock(lockKey, 10);
if (StringUtils.isEmpty(lockValue)) {
throw new RuntimeException("入库单取消中,请稍后重试");
}
inBoundCancelProcessor.inBoundCancel(inBoundCancel);
} catch (Exception e) {
log.error("入库单取消异常", e);
throw e;
} finally {
if (StringUtils.isNotEmpty(lockValue)) {
redissonLock.unlock(lockKey, lockValue);
}
}
}
}

View File

@ -0,0 +1,93 @@
package org.cpte.modules.saiWms.service.processor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.cpte.modules.base.entity.Item;
import org.cpte.modules.base.entity.Point;
import org.cpte.modules.base.entity.Stock;
import org.cpte.modules.base.service.IItemService;
import org.cpte.modules.base.service.IPointService;
import org.cpte.modules.base.service.IStockService;
import org.cpte.modules.constant.enums.AsnOrderTypeEnum;
import org.cpte.modules.constant.enums.AsnStatusEnum;
import org.cpte.modules.inventory.mapper.InventoryMapper;
import org.cpte.modules.receive.entity.Asn;
import org.cpte.modules.receive.entity.AsnDetail;
import org.cpte.modules.receive.mapper.AsnDetailMapper;
import org.cpte.modules.receive.mapper.AsnMapper;
import org.cpte.modules.receive.service.IAsnDetailService;
import org.cpte.modules.receive.service.IAsnService;
import org.cpte.modules.saiWms.request.InBoundCancelRequest;
import org.cpte.modules.saiWms.request.InboundRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
*
*/
@Service
@Slf4j
public class InBoundCancelProcessor {
@Autowired
private AsnMapper asnMapper;
@Autowired
private IAsnService asnService;
/**
*
*
* @param inBoundCancel
*/
public void inBoundCancel(InBoundCancelRequest inBoundCancel) {
// 1.参数校验
validateParams(inBoundCancel);
// 2.验证任务号
Asn asn = validateAsn(inBoundCancel.getNo());
if (AsnStatusEnum.CANCELED.getValue().equals(asn.getStatus())) {
throw new RuntimeException("【" + inBoundCancel.getNo() + "】任务号已取消,请勿重复操作");
}
if (AsnStatusEnum.CREATED.getValue().equals(asn.getStatus())) {
asnService.cancelAsn(asn);
} else {
throw new RuntimeException("【" + inBoundCancel.getNo() + "】任务号已入库,无法取消");
}
}
/**
*
*
* @param inboundRequest
*/
private void validateParams(InBoundCancelRequest inboundRequest) {
if (StringUtils.isBlank(inboundRequest.getNo())) {
throw new RuntimeException("任务号(No)必填");
}
}
/**
*
*
* @param no
*/
private Asn validateAsn(String no) {
Asn asn = asnMapper.queryByNo(no);
if (asn == null) {
throw new RuntimeException("【" + no + "】任务号不存在");
}
return asn;
}
}

View File

@ -100,6 +100,11 @@ public class InBoundTaskProcessor {
if (inboundRequest.getType() == null) {
throw new RuntimeException("任务类型(Type)必填");
}
if (!Set.of(0, 1, 2, 3, 8).contains(inboundRequest.getType())) {
throw new RuntimeException("【" + inboundRequest.getType() + "】任务类型错误");
}
if (AsnOrderTypeEnum.PRODUCT.getValue().equals(inboundRequest.getType())) {
if (StringUtils.isBlank(inboundRequest.getLocationFrom())) {
throw new RuntimeException("起点(LocationFrom)必填");

View File

@ -5,8 +5,10 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.cpte.modules.base.entity.Item;
import org.cpte.modules.base.entity.Point;
import org.cpte.modules.base.entity.Stock;
import org.cpte.modules.base.service.IItemService;
import org.cpte.modules.base.service.IPointService;
import org.cpte.modules.base.service.IStockService;
import org.cpte.modules.constant.GeneralConstant;
import org.cpte.modules.constant.enums.AreaTypeEnum;
import org.cpte.modules.constant.enums.AsnOrderTypeEnum;
@ -22,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
@ -36,6 +39,9 @@ public class OutBoundTaskProcessor {
@Autowired
private IItemService itemService;
@Autowired
private IStockService stockService;
@Autowired
private IPointService pointService;
@ -58,8 +64,11 @@ public class OutBoundTaskProcessor {
//3.验证物料
Map<String, Item> itemMap = validateItem(outboundRequest.getDetails());
//4.验证容器
Map<String, Stock> stockMap = validateStock(outboundRequest.getDetails());
//5.出库处理
pickDetailService.processOutBoundTask(outboundRequest, itemMap);
pickDetailService.processOutBoundTask(outboundRequest, itemMap,stockMap);
}
/**
@ -81,6 +90,9 @@ public class OutBoundTaskProcessor {
throw new RuntimeException("任务类型(Type)必填");
}
if (!Set.of(4, 5, 6, 7).contains(outboundRequest.getType())) {
throw new RuntimeException("【" + outboundRequest.getType() + "】任务类型错误");
}
if (CollectionUtils.isEmpty(outboundRequest.getDetails())) {
throw new RuntimeException("出库信息不能为空");
@ -140,6 +152,30 @@ public class OutBoundTaskProcessor {
return exitItemMap;
}
/**
*
*
* @param detail
*/
private Map<String, Stock> validateStock(List<OutboundRequest.OutboundDetail> detail) {
//获取明细中所有的容器
List<String> stockCodes = detail.stream().map(OutboundRequest.OutboundDetail::getLpn).toList();
if (CollectionUtils.isNotEmpty(stockCodes)) {
//获取数据库已存在容器
Map<String, Stock> exitStockMap = stockService.queryByStockCodesToMap(stockCodes);
//获取不存在的容器
List<String> notExitStockCodes = stockCodes.stream().filter(stockCode -> !exitStockMap.containsKey(stockCode)).toList();
if (CollectionUtils.isNotEmpty(notExitStockCodes)) {
throw new RuntimeException("系统无" + notExitStockCodes + "托盘,请维护");
}
return exitStockMap;
}
return null;
}
/**
*
*

View File

@ -61,6 +61,15 @@ public class PickDetail implements Serializable {
@Schema(description = "物料ID")
@JsonSerialize(using = ToStringSerializer.class)
private java.lang.Long itemId;
/**
*
*/
@Excel(name = "容器", width = 15)
@Schema(description = "容器")
@JsonSerialize(using = ToStringSerializer.class)
private java.lang.Long stockId;
/**
*
*/

View File

@ -1,14 +1,11 @@
package org.cpte.modules.shipping.service;
import org.cpte.modules.base.entity.Item;
import org.cpte.modules.base.entity.Point;
import org.cpte.modules.base.entity.Stock;
import org.cpte.modules.inventory.entity.Inventory;
import org.cpte.modules.saiWms.request.OutboundRequest;
import org.cpte.modules.shipping.entity.Pick;
import org.cpte.modules.shipping.entity.PickDetail;
import com.baomidou.mybatisplus.extension.service.IService;
import org.cpte.modules.shipping.entity.Task;
import java.util.List;
import java.util.Map;
@ -36,7 +33,7 @@ public interface IPickDetailService extends IService<PickDetail> {
* @param exitItemMap
* @return
*/
List<PickDetail> buildPickDetail(List<OutboundRequest.OutboundDetail> details, Map<String, Item> exitItemMap);
List<PickDetail> buildPickDetail(List<OutboundRequest.OutboundDetail> details, Map<String, Item> exitItemMap, Map<String, Stock> stockMap);
/**
@ -77,7 +74,7 @@ public interface IPickDetailService extends IService<PickDetail> {
* @param outboundRequest
* @param itemMap
*/
void processOutBoundTask(OutboundRequest outboundRequest, Map<String, Item> itemMap);
void processOutBoundTask(OutboundRequest outboundRequest, Map<String, Item> itemMap, Map<String, Stock> stockMap);
/**
*

View File

@ -3,6 +3,7 @@ package org.cpte.modules.shipping.service.impl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.cpte.modules.base.entity.Item;
import org.cpte.modules.base.entity.Stock;
import org.cpte.modules.constant.GeneralConstant;
import org.cpte.modules.constant.enums.PickStatusEnum;
import org.cpte.modules.saiWms.request.OutboundRequest;
@ -56,12 +57,13 @@ public class PickDetailServiceImpl extends ServiceImpl<PickDetailMapper, PickDet
}
@Override
public List<PickDetail> buildPickDetail(List<OutboundRequest.OutboundDetail> details, Map<String, Item> exitItemMap) {
public List<PickDetail> buildPickDetail(List<OutboundRequest.OutboundDetail> details, Map<String, Item> exitItemMap, Map<String, Stock> stockMap) {
List<PickDetail> newDetailList = new ArrayList<>();
for (OutboundRequest.OutboundDetail detail : details) {
PickDetail pickDetail = PickDetail.builder()
.lineNo(Integer.parseInt(detail.getLineNo()))
.itemId(exitItemMap.get(detail.getItem()).getId())
.stockId(stockMap == null ? null : stockMap.get(detail.getLpn()).getId())
.unit(detail.getUnit())
.orderQty(BigDecimal.valueOf(detail.getQty()))
.allocatedQty(BigDecimal.ZERO)
@ -146,10 +148,10 @@ public class PickDetailServiceImpl extends ServiceImpl<PickDetailMapper, PickDet
@Override
@Transactional(rollbackFor = Exception.class)
public void processOutBoundTask(OutboundRequest outboundRequest, Map<String, Item> itemMap) {
public void processOutBoundTask(OutboundRequest outboundRequest, Map<String, Item> itemMap, Map<String, Stock> stockMap) {
// 创建出库单和明细
Pick createPick = buildPick(outboundRequest);
List<PickDetail> pickDetails = buildPickDetail(outboundRequest.getDetails(), itemMap);
List<PickDetail> pickDetails = buildPickDetail(outboundRequest.getDetails(), itemMap, stockMap);
processorSaveMain(createPick, pickDetails);
}

View File

@ -23,7 +23,6 @@ import org.cpte.modules.shipping.entity.Pick;
import org.cpte.modules.shipping.entity.PickDetail;
import org.cpte.modules.shipping.entity.Task;
import org.cpte.modules.shipping.mapper.PickDetailMapper;
import org.cpte.modules.shipping.mapper.TaskMapper;
import org.cpte.modules.shipping.service.IPickDetailService;
import org.cpte.modules.shipping.service.ITaskService;
import org.cpte.modules.shipping.vo.AllocationData;
@ -57,8 +56,6 @@ public class AllocateProcessor {
@Autowired
private ItemKeyMapper itemKeyMapper;
@Autowired
private InventoryMapper inventoryMapper;
@ -347,6 +344,21 @@ public class AllocateProcessor {
// 智能排序库存
List<InventoryScore> scoredInventories = scoreInventories(matchedInventories);
//指定容器出库
if (pickDetail.getStockId() != null) {
//根据容器分组
Map<Long, List<InventoryScore>> inventoryScoreMap = scoredInventories.stream().collect(Collectors.groupingBy(InventoryScore::getStockId));
scoredInventories = inventoryScoreMap.get(pickDetail.getStockId());
}
if (CollectionUtils.isEmpty(scoredInventories)) {
Stock stock = stockService.getById(pickDetail.getStockId());
String message = String.format("任务号【%s】,容器【%s】无库存", pick.getNo(), stock.getStockCode());
errorMsgSet.add(message);
return;
}
//未分配数量
BigDecimal remainingQty = totalUnAllocatedQty;
for (InventoryScore inventoryScore : scoredInventories) {
@ -354,6 +366,7 @@ public class AllocateProcessor {
if (remainingQty.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
Inventory inventory = inventoryScore.getInventory();
// 库存可用数量
@ -459,7 +472,7 @@ public class AllocateProcessor {
log.info("【{}】库位距离评分: {}-移位评分: {}-总得分: {}-移位次数: {}",
currPoint.getPointCode(), distanceScore, moveScore, totalScore, movePoints.size());
return new InventoryScore(inventory, totalScore, bestPoint, movePoints);
return new InventoryScore(inventory, inventory.getStockId(), totalScore, bestPoint, movePoints);
}
/**
@ -716,6 +729,7 @@ public class AllocateProcessor {
Map<Long, Stock> stockMap = stockService.queryByStockIdsToMap(stockIds);
for (Inventory inv : moveInventoryList) {
try {
Item moveItem = moveItemMap.get(inv.getItemId());
Point fromPoint = fromPointMap.get(inv.getPointId());
Stock stock = stockMap.get(inv.getStockId());
@ -726,6 +740,9 @@ public class AllocateProcessor {
moveList.add(moveTask);
pointService.bindPoint(toPoint);
log.info("生成移位任务:{}- 容器:{} - 库位:{} - 库存数量:{}", taskNo, stock.getStockCode(), fromPoint.getPointCode(), inv.getQuantity());
} catch (Exception e) {
throw e;
}
}
return moveList;
}

View File

@ -12,6 +12,8 @@ import java.util.List;
public class InventoryScore {
// 库存
private Inventory inventory;
//容器
private Long stockId;
// 分数
private double score;
//最佳出库口

View File

@ -51,7 +51,7 @@ spring:
jdbc:
initialize-schema: embedded
#定时任务启动开关true-开 false-关
auto-startup: true
auto-startup: false
#延迟1秒启动定时任务
startup-delay: 1s
#启动时更新己存在的Job
@ -145,7 +145,7 @@ spring:
selectWhereAlwayTrueCheck: false
# 打开mergeSql功能慢SQL记录
stat:
log-slow-sql: true
#log-slow-sql: false
slow-sql-millis: 5000
merge-sql: true
datasource:

2336
hs_err_pid20204.log 100644

File diff suppressed because it is too large Load Diff