no message
parent
5cdc1e3021
commit
126414e875
|
|
@ -47,6 +47,6 @@ public interface PointMapper extends BaseMapper<Point> {
|
||||||
@Select("SELECT COUNT(id) FROM base_point WHERE col_num = #{colNum} AND layer_num = #{layerNum} AND status = 1")
|
@Select("SELECT COUNT(id) FROM base_point WHERE col_num = #{colNum} AND layer_num = #{layerNum} AND status = 1")
|
||||||
int countOccupiedInSameColumn(@Param("colNum") String colNum, @Param("layerNum") String layerNum);
|
int countOccupiedInSameColumn(@Param("colNum") String colNum, @Param("layerNum") String layerNum);
|
||||||
|
|
||||||
|
@Select("SELECT * FROM base_point WHERE col_num = #{colNum} AND layer_num = #{layerNum} ORDER BY row_num ASC")
|
||||||
|
List<Point> findByColAndLayer(@Param("colNum") String colNum, @Param("layerNum") String layerNum);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,24 @@
|
||||||
JOIN base_area ba ON bp1.area_id = ba.id
|
JOIN base_area ba ON bp1.area_id = ba.id
|
||||||
JOIN base_point bp2 ON bp1.col_num = bp2.col_num
|
JOIN base_point bp2 ON bp1.col_num = bp2.col_num
|
||||||
JOIN data_inventory inv ON bp2.id = inv.point_id
|
JOIN data_inventory inv ON bp2.id = inv.point_id
|
||||||
<where>
|
WHERE inv.item_id = #{itemId}
|
||||||
AND inv.item_id = #{itemId}
|
|
||||||
<if test="propC1 != null and propC1 != ''">
|
|
||||||
AND inv.prop_c1 = #{propC1}
|
|
||||||
</if>
|
|
||||||
<if test="whCode != null and whCode != ''">
|
|
||||||
AND inv.wh_code = #{whCode}
|
|
||||||
</if>
|
|
||||||
<if test="areaCode != null and areaCode != ''">
|
|
||||||
AND ba.area_code = #{areaCode}
|
|
||||||
</if>
|
|
||||||
AND bp1.status = 0
|
AND bp1.status = 0
|
||||||
</where>
|
AND ba.area_code = #{areaCode}
|
||||||
|
<choose>
|
||||||
|
<when test="propC1 != null and propC1 != ''">
|
||||||
|
AND inv.prop_c1 = #{propC1}
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
AND (inv.prop_c1 IS NULL OR inv.prop_c1 = '')
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
<choose>
|
||||||
|
<when test="whCode != null and whCode != ''">
|
||||||
|
AND inv.wh_code = #{whCode}
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
AND (inv.wh_code IS NULL OR inv.wh_code = '')
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
@ -80,5 +80,21 @@ public interface IPointService extends IService<Point> {
|
||||||
*/
|
*/
|
||||||
String getElevatorPoint(String pointCode, String key);
|
String getElevatorPoint(String pointCode, String key);
|
||||||
|
|
||||||
Point queryToPoint(String pointCode, Integer status, String areaCode);
|
/**
|
||||||
|
* 通过巷道编码、层数查询库位信息
|
||||||
|
*
|
||||||
|
* @param colNum 巷道编码
|
||||||
|
* @return layerNum 层数
|
||||||
|
*/
|
||||||
|
List<Point> findByColAndLayer(String colNum, String layerNum);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询库位信息
|
||||||
|
*
|
||||||
|
* @param pointCode 库位编码
|
||||||
|
* @param status 状态
|
||||||
|
* @param areaCode 库区编码
|
||||||
|
* @return List<Point>
|
||||||
|
*/
|
||||||
|
List<Point> queryPoints(String pointCode, Integer status, String areaCode);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,12 +144,13 @@ public class PointServiceImpl extends ServiceImpl<PointMapper, Point> implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Point queryToPoint(String pointCode, Integer status, String areaCode) {
|
public List<Point> findByColAndLayer(String colNum, String layerNum) {
|
||||||
Point dstPoint = null;
|
return pointMapper.findByColAndLayer(colNum, layerNum);
|
||||||
List<Point> dstPointList = pointMapper.queryPoints(pointCode, status, areaCode);
|
|
||||||
if (dstPointList.isEmpty()) {
|
|
||||||
throw new RuntimeException("【" + AreaTypeEnum.CPCCQ.getDesc() + "】无空闲库位");
|
|
||||||
}
|
}
|
||||||
return dstPoint = dstPointList.get(0);
|
|
||||||
|
@Override
|
||||||
|
public List<Point> queryPoints(String pointCode, Integer status, String areaCode) {
|
||||||
|
return pointMapper.queryPoints(pointCode, status, areaCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ public enum AreaTypeEnum {
|
||||||
|
|
||||||
MJCCQ("MJCCQ", "模具存储区"),
|
MJCCQ("MJCCQ", "模具存储区"),
|
||||||
|
|
||||||
RK_DOCK("RK_DOCK", "入库输送线接驳口"),
|
RK_DOCK("RK_DOCK", "入库工作站"),
|
||||||
|
|
||||||
CK_DOCK("CK_DOCK", "出库输送线接驳口"),
|
CK_DOCK("CK_DOCK", "出库工作站"),
|
||||||
;
|
;
|
||||||
/**
|
/**
|
||||||
* 值
|
* 值
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,10 @@ import lombok.Data;
|
||||||
@Data
|
@Data
|
||||||
public class ScanTrayRequest {
|
public class ScanTrayRequest {
|
||||||
//托盘码
|
//托盘码
|
||||||
@NotBlank(message = "托盘码不能为空")
|
|
||||||
@JsonProperty("stockCode")
|
@JsonProperty("stockCode")
|
||||||
private String stockCode;
|
private String stockCode;
|
||||||
|
|
||||||
//工作站
|
//工作站
|
||||||
@NotBlank(message = "工作站不能为空")
|
|
||||||
@JsonProperty("station")
|
@JsonProperty("station")
|
||||||
private String station;
|
private String station;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,21 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IAgvTaskService iAgvTaskService;
|
private IAgvTaskService iAgvTaskService;
|
||||||
|
|
||||||
|
private void validateParams(ScanTrayRequest scanTrayRequest) {
|
||||||
|
if (StringUtils.isBlank(scanTrayRequest.getStockCode())) {
|
||||||
|
throw new RuntimeException("托盘码不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(scanTrayRequest.getStation())) {
|
||||||
|
throw new RuntimeException("工作站不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void scanTray(ScanTrayRequest scanTrayRequest) {
|
public void scanTray(ScanTrayRequest scanTrayRequest) {
|
||||||
|
//验证参数
|
||||||
|
validateParams(scanTrayRequest);
|
||||||
|
|
||||||
//工作站
|
//工作站
|
||||||
Point srcPoint = pointMapper.queryByPointCode(scanTrayRequest.getStation());
|
Point srcPoint = pointMapper.queryByPointCode(scanTrayRequest.getStation());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public interface InventoryMapper extends BaseMapper<Inventory> {
|
||||||
* @param whCodeList 外部仓库
|
* @param whCodeList 外部仓库
|
||||||
* @return List<Inventory>
|
* @return List<Inventory>
|
||||||
*/
|
*/
|
||||||
List<Inventory> queryInventoryWithLock(@Param("itemIds") List<Long> itemIds, @Param("propC1List") List<String> propC1List, @Param("propC3List") List<String> propC3List, @Param("whCodeList") List<String> whCodeList);
|
List<Inventory> queryInventory(@Param("itemIds") List<Long> itemIds, @Param("propC1List") List<String> propC1List, @Param("propC3List") List<String> propC3List, @Param("whCodeList") List<String> whCodeList);
|
||||||
|
|
||||||
// 查询相邻库位(同一巷道,深度±3范围内)
|
// 查询相邻库位(同一巷道,深度±3范围内)
|
||||||
@Select("SELECT di.* " +
|
@Select("SELECT di.* " +
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="org.cpte.modules.inventory.mapper.InventoryMapper">
|
<mapper namespace="org.cpte.modules.inventory.mapper.InventoryMapper">
|
||||||
<select id="queryInventoryWithLock" resultType="org.cpte.modules.inventory.entity.Inventory">
|
<select id="queryInventory" resultType="org.cpte.modules.inventory.entity.Inventory">
|
||||||
SELECT * FROM data_inventory
|
SELECT * FROM data_inventory
|
||||||
WHERE status in (1,2)
|
WHERE status in (1,2)
|
||||||
AND quantity > 0
|
AND quantity > 0
|
||||||
|
|
@ -9,25 +9,41 @@
|
||||||
<foreach collection="itemIds" item="itemId" open="(" separator="," close=")">
|
<foreach collection="itemIds" item="itemId" open="(" separator="," close=")">
|
||||||
#{itemId}
|
#{itemId}
|
||||||
</foreach>
|
</foreach>
|
||||||
<if test="propC1List != null and !propC1List.isEmpty()">
|
<choose>
|
||||||
|
<when test="propC1List != null and !propC1List.isEmpty()">
|
||||||
AND prop_c1 IN
|
AND prop_c1 IN
|
||||||
<foreach collection="propC1List" item="propC1" open="(" separator="," close=")">
|
<foreach collection="propC1List" item="propC1" open="(" separator="," close=")">
|
||||||
#{propC1}
|
#{propC1}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</when>
|
||||||
<if test="propC3List != null and !propC3List.isEmpty()">
|
<otherwise>
|
||||||
|
AND (prop_c1 IS NULL OR prop_c1 = '')
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
|
||||||
|
<choose>
|
||||||
|
<when test="propC3List != null and !propC3List.isEmpty()">
|
||||||
AND prop_c3 IN
|
AND prop_c3 IN
|
||||||
<foreach collection="propC3List" item="propC3" open="(" separator="," close=")">
|
<foreach collection="propC3List" item="propC3" open="(" separator="," close=")">
|
||||||
#{propC3}
|
#{propC3}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</when>
|
||||||
<if test="whCodeList != null and !whCodeList.isEmpty()">
|
<otherwise>
|
||||||
|
AND (prop_c3 IS NULL OR prop_c3 = '')
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
|
||||||
|
<choose>
|
||||||
|
<when test="whCodeList != null and !whCodeList.isEmpty()">
|
||||||
AND wh_code IN
|
AND wh_code IN
|
||||||
<foreach collection="whCodeList" item="whCode" open="(" separator="," close=")">
|
<foreach collection="whCodeList" item="whCode" open="(" separator="," close=")">
|
||||||
#{whCode}
|
#{whCode}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
AND (wh_code IS NULL OR wh_code = '')
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
ORDER BY create_time
|
ORDER BY create_time
|
||||||
FOR UPDATE
|
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
@ -49,12 +49,13 @@ public interface IInventoryLogService extends IService<InventoryLog> {
|
||||||
* 添加分配库存日志
|
* 添加分配库存日志
|
||||||
*
|
*
|
||||||
* @param inventory 库存
|
* @param inventory 库存
|
||||||
|
* @param dstPointId 目标库位
|
||||||
* @param AllocatedQty 分配数量
|
* @param AllocatedQty 分配数量
|
||||||
* @param businessNo 业务单号
|
* @param businessNo 业务单号
|
||||||
* @param businessDetailId 业务明细ID
|
* @param businessDetailId 业务明细ID
|
||||||
* @param description 描述
|
* @param description 描述
|
||||||
*/
|
*/
|
||||||
void addAllocInventoryLog(Inventory inventory, BigDecimal AllocatedQty, String businessNo, Long businessDetailId, String description);
|
void addAllocInventoryLog(Inventory inventory, Long dstPointId, BigDecimal AllocatedQty, String businessNo, Long businessDetailId, String description);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,12 @@ public class InventoryLogServiceImpl extends ServiceImpl<InventoryLogMapper, Inv
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void addAllocInventoryLog(Inventory inventory, BigDecimal AllocatedQty, String businessNo, Long businessDetailId, String description) {
|
public void addAllocInventoryLog(Inventory inventory,Long dstPointId, BigDecimal AllocatedQty, String businessNo, Long businessDetailId, String description) {
|
||||||
InventoryLog inventoryLog = buildInventoryLog(inventory, BigDecimal.ZERO, businessNo, businessDetailId, description);
|
InventoryLog inventoryLog = buildInventoryLog(inventory, BigDecimal.ZERO, businessNo, businessDetailId, description);
|
||||||
//出库分配
|
//出库分配
|
||||||
inventoryLog.setLogType(InventoryLogEnum.ALLOC.getValue());
|
inventoryLog.setLogType(InventoryLogEnum.ALLOC.getValue());
|
||||||
|
inventoryLog.setFromPointId(inventory.getPointId());
|
||||||
|
inventoryLog.setToPointId(dstPointId);
|
||||||
//实际数量不变
|
//实际数量不变
|
||||||
inventoryLog.setChangeQty(BigDecimal.ZERO);
|
inventoryLog.setChangeQty(BigDecimal.ZERO);
|
||||||
inventoryLog.setBeforeAllocatedQty(BigDecimalUtil.subtract(inventory.getQueuedQty(), AllocatedQty, 0));
|
inventoryLog.setBeforeAllocatedQty(BigDecimalUtil.subtract(inventory.getQueuedQty(), AllocatedQty, 0));
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
||||||
Stock stock = iStockService.validateStock(stockCode);
|
Stock stock = iStockService.validateStock(stockCode);
|
||||||
|
|
||||||
//获取输送线工作台点位,均衡分配点位任务-轮询方式
|
//获取输送线工作台点位,均衡分配点位任务-轮询方式
|
||||||
Point dstPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.RK_DOCK.getValue(), GeneralConstant.RK_DOCK_TASK_INDEX);
|
Point dstPoint = iPointService.getWorkStationPoint(null, AreaTypeEnum.RK_DOCK.getValue(), GeneralConstant.RK_DOCK_TASK_INDEX);
|
||||||
|
|
||||||
// 创建入库单和明细
|
// 创建入库单和明细
|
||||||
Asn createAsn = buildAsn(inboundRequest);
|
Asn createAsn = buildAsn(inboundRequest);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import org.cpte.modules.base.service.IItemService;
|
||||||
import org.cpte.modules.base.service.IPointService;
|
import org.cpte.modules.base.service.IPointService;
|
||||||
import org.cpte.modules.base.service.IStockService;
|
import org.cpte.modules.base.service.IStockService;
|
||||||
import org.cpte.modules.constant.enums.*;
|
import org.cpte.modules.constant.enums.*;
|
||||||
|
import org.cpte.modules.conveyorLine.vo.PointScore;
|
||||||
import org.cpte.modules.inventory.entity.Inventory;
|
import org.cpte.modules.inventory.entity.Inventory;
|
||||||
import org.cpte.modules.inventory.mapper.InventoryMapper;
|
import org.cpte.modules.inventory.mapper.InventoryMapper;
|
||||||
import org.cpte.modules.inventory.service.IInventoryService;
|
import org.cpte.modules.inventory.service.IInventoryService;
|
||||||
|
|
@ -27,6 +28,7 @@ import org.cpte.modules.shipping.mapper.TaskMapper;
|
||||||
import org.cpte.modules.shipping.service.IPickService;
|
import org.cpte.modules.shipping.service.IPickService;
|
||||||
import org.cpte.modules.shipping.service.ITaskService;
|
import org.cpte.modules.shipping.service.ITaskService;
|
||||||
import org.cpte.modules.shipping.vo.InventoryGroupKey;
|
import org.cpte.modules.shipping.vo.InventoryGroupKey;
|
||||||
|
import org.cpte.modules.shipping.vo.InventoryScore;
|
||||||
import org.cpte.modules.utils.BatchUtil;
|
import org.cpte.modules.utils.BatchUtil;
|
||||||
import org.cpte.modules.utils.BigDecimalUtil;
|
import org.cpte.modules.utils.BigDecimalUtil;
|
||||||
import org.jeecg.common.constant.CommonConstant;
|
import org.jeecg.common.constant.CommonConstant;
|
||||||
|
|
@ -318,7 +320,7 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
||||||
List<String> whCodeList = pickMap.values().stream().map(Pick::getWhCode).filter(StringUtils::hasText).distinct().toList();
|
List<String> whCodeList = pickMap.values().stream().map(Pick::getWhCode).filter(StringUtils::hasText).distinct().toList();
|
||||||
|
|
||||||
//查询库存
|
//查询库存
|
||||||
List<Inventory> inventories = inventoryMapper.queryInventoryWithLock(itemIds, propC1List, propC3List, whCodeList);
|
List<Inventory> inventories = inventoryMapper.queryInventory(itemIds, propC1List, propC3List, whCodeList);
|
||||||
if (CollectionUtils.isEmpty(inventories)) {
|
if (CollectionUtils.isEmpty(inventories)) {
|
||||||
String itemCodes = itemMap.values().stream().map(Item::getItemCode).collect(Collectors.joining(","));
|
String itemCodes = itemMap.values().stream().map(Item::getItemCode).collect(Collectors.joining(","));
|
||||||
errorMsgSet.add("【" + itemCodes + "】物料库存不足");
|
errorMsgSet.add("【" + itemCodes + "】物料库存不足");
|
||||||
|
|
@ -374,14 +376,14 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取输送线工作台点位,均衡分配点位任务-轮询方式
|
//智能排序,优先分配移位最小的库位
|
||||||
Point toPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CK_DOCK.getValue(), GeneralConstant.CK_DOCK_TASK_INDEX);
|
List<InventoryScore> scoredInventory = scoreInventories(matchedInventories);
|
||||||
//记录当前容器
|
|
||||||
Long currStockId = 0L;
|
for (InventoryScore inventoryScore : scoredInventory) {
|
||||||
for (Inventory inventory : matchedInventories) {
|
|
||||||
if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) {
|
if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Inventory inventory = inventoryScore.getInventory();
|
||||||
// 库存可用数量
|
// 库存可用数量
|
||||||
BigDecimal availableQty = BigDecimalUtil.subtract(inventory.getQuantity(), inventory.getQueuedQty(), 0);
|
BigDecimal availableQty = BigDecimalUtil.subtract(inventory.getQuantity(), inventory.getQueuedQty(), 0);
|
||||||
if (availableQty.compareTo(BigDecimal.ZERO) <= 0) {
|
if (availableQty.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
|
@ -405,20 +407,26 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
||||||
Point fromPoint = pointMap.get(inventory.getPointId());
|
Point fromPoint = pointMap.get(inventory.getPointId());
|
||||||
//是否整托:0整托、1拆托
|
//是否整托:0整托、1拆托
|
||||||
Integer izAll = availableQty.compareTo(allocateQty) == 0 ? 0 : 1;
|
Integer izAll = availableQty.compareTo(allocateQty) == 0 ? 0 : 1;
|
||||||
//判断当前容器是否一致,同一个容器去同一个目标库位
|
//目标库位
|
||||||
if (!currStockId.equals(stock.getId())) {
|
Point toPoint = inventoryScore.getOutPoint();
|
||||||
toPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CK_DOCK.getValue(), GeneralConstant.CK_DOCK_TASK_INDEX);
|
|
||||||
}
|
|
||||||
//构建拣货任务存入集合批量新增
|
//构建拣货任务存入集合批量新增
|
||||||
Task task = iTaskService.bulidTask(pick.getNo() + "_" + pickDetail.getLineNo(), TaskTypeEnum.PICK.getValue(), item, fromPoint, toPoint, stock, pick.getId(), pickDetail.getId(), inventory.getId(), allocateQty, izAll);
|
Task task = iTaskService.bulidTask(pick.getNo() + "_" + pickDetail.getLineNo(), TaskTypeEnum.PICK.getValue(), item, fromPoint, toPoint, stock, pick.getId(), pickDetail.getId(), inventory.getId(), allocateQty, izAll);
|
||||||
createToTask.add(task);
|
createToTask.add(task);
|
||||||
log.info("生成拣货任务:{}- 容器:{} - 库位:{} - 类型:{}- 分配数量:{}", task.getTaskNo(), stock.getStockCode(), fromPoint.getPointCode(), izAll, allocateQty);
|
log.info("生成拣货任务:{}- 容器:{} - 库位:{} - 类型:{}- 分配数量:{}", task.getTaskNo(), stock.getStockCode(), fromPoint.getPointCode(), izAll, allocateQty);
|
||||||
|
|
||||||
|
//移位任务
|
||||||
|
if (CollectionUtils.isNotEmpty(inventoryScore.getMovePoints())) {
|
||||||
|
for (Point movePoint : inventoryScore.getMovePoints()) {
|
||||||
|
log.info("生成移位任务:原库位:{}", movePoint.getPointCode());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("无移位任务");
|
||||||
|
}
|
||||||
|
|
||||||
//分配库存日志
|
//分配库存日志
|
||||||
iInventoryLogService.addAllocInventoryLog(inventory, allocateQty, pick.getOrderNo(), pickDetail.getId(), pickDetail.getDescription());
|
iInventoryLogService.addAllocInventoryLog(inventory, toPoint.getId(), allocateQty, pick.getOrderNo(), pickDetail.getId(), pickDetail.getDescription());
|
||||||
//更新未分配数量
|
//更新未分配数量
|
||||||
unAllocatedQty = BigDecimalUtil.subtract(unAllocatedQty, allocateQty, 0);
|
unAllocatedQty = BigDecimalUtil.subtract(unAllocatedQty, allocateQty, 0);
|
||||||
//更新当前容器
|
|
||||||
currStockId = stock.getId();
|
|
||||||
}
|
}
|
||||||
//分配后仍有缺货,记录错误
|
//分配后仍有缺货,记录错误
|
||||||
if (unAllocatedQty.compareTo(BigDecimal.ZERO) > 0) {
|
if (unAllocatedQty.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
|
@ -453,6 +461,145 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
||||||
return new ArrayList<>(errorMsgSet);
|
return new ArrayList<>(errorMsgSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算库存得分
|
||||||
|
*
|
||||||
|
* @param matchedInventories 库存集合
|
||||||
|
* @return List<InventoryScore>
|
||||||
|
*/
|
||||||
|
private List<InventoryScore> scoreInventories(List<Inventory> matchedInventories) {
|
||||||
|
// 批量查询库位
|
||||||
|
List<Long> pointIds = matchedInventories.stream()
|
||||||
|
.map(Inventory::getPointId)
|
||||||
|
.toList();
|
||||||
|
Map<Long, Point> pointMap = iPointService.queryByPointIdsToMap(pointIds);
|
||||||
|
|
||||||
|
// 按巷道和层分组
|
||||||
|
Map<String, List<Point>> colLayerPointsMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (Point point : pointMap.values()) {
|
||||||
|
String key = point.getColNum() + "-" + point.getLayerNum();
|
||||||
|
if (colLayerPointsMap.containsKey(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Point> points = iPointService.findByColAndLayer(point.getColNum(), point.getLayerNum());
|
||||||
|
colLayerPointsMap.put(key, points);
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取所以出库口的库位
|
||||||
|
List<Point> outPoints = iPointService.queryPoints(null, null, AreaTypeEnum.CK_DOCK.getValue());
|
||||||
|
|
||||||
|
//获取优化后的库存
|
||||||
|
return matchedInventories.stream()
|
||||||
|
.map(inventory -> {
|
||||||
|
Point currPoint = pointMap.get(inventory.getPointId());
|
||||||
|
String key = currPoint.getColNum() + "-" + currPoint.getLayerNum();
|
||||||
|
List<Point> points = colLayerPointsMap.get(key);
|
||||||
|
return calculateMoveCount(inventory, currPoint, points, outPoints);
|
||||||
|
})
|
||||||
|
.sorted(Comparator.comparing(InventoryScore::getScore).reversed())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算位移次数核心算法
|
||||||
|
*
|
||||||
|
* @param inventory 库存
|
||||||
|
* @param currPoint 当前库位
|
||||||
|
* @param points 当前库位巷道的库位集合
|
||||||
|
* @return 库位位移次数
|
||||||
|
*/
|
||||||
|
private InventoryScore calculateMoveCount(Inventory inventory, Point currPoint, List<Point> points, List<Point> outPoints) {
|
||||||
|
double totalScore = 0.0;
|
||||||
|
|
||||||
|
// 计算距离分数(权重30%)
|
||||||
|
Point bestPoint = getBestOutboundPoint(currPoint, outPoints);
|
||||||
|
double distanceScore = calculateClusterDistanceCost(currPoint, bestPoint);
|
||||||
|
totalScore += distanceScore * 0.3;
|
||||||
|
|
||||||
|
// 目标库位的深度位转换为索引
|
||||||
|
int targetIndex = Integer.parseInt(currPoint.getRowNum()) - 1;
|
||||||
|
|
||||||
|
//双通道
|
||||||
|
if (currPoint.getIzDoubleLane().equals(1)) {
|
||||||
|
// 计算左侧占用数
|
||||||
|
int leftOccupied = 0;
|
||||||
|
List<Point> leftPoints = new ArrayList<>();
|
||||||
|
for (int i = 0; i < targetIndex; i++) {
|
||||||
|
Point point = points.get(i);
|
||||||
|
if (point != null && point.getStatus().equals(CommonStatusEnum.USED.getValue())) {
|
||||||
|
leftOccupied++;
|
||||||
|
leftPoints.add(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算右侧占用数
|
||||||
|
int rightOccupied = 0;
|
||||||
|
List<Point> rightPoints = new ArrayList<>();
|
||||||
|
for (int i = targetIndex + 1; i < points.size(); i++) {
|
||||||
|
Point point = points.get(i);
|
||||||
|
if (point != null && point.getStatus().equals(CommonStatusEnum.USED.getValue())) {
|
||||||
|
rightOccupied++;
|
||||||
|
rightPoints.add(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//取两个集合中,元素最少的那个集合
|
||||||
|
List<Point> movePoints = leftOccupied < rightOccupied ? leftPoints : rightPoints;
|
||||||
|
// 取最小值,移位越小分数越高
|
||||||
|
int minOccupied = Math.min(leftOccupied, rightOccupied);
|
||||||
|
if (minOccupied == 0) {
|
||||||
|
minOccupied = 1; // 设置默认值防止除以0
|
||||||
|
}
|
||||||
|
totalScore += 100.0 / minOccupied * 0.7;
|
||||||
|
return new InventoryScore(inventory, totalScore, bestPoint, movePoints);
|
||||||
|
} else {
|
||||||
|
// 单通道,计算目标位置左侧的占用数
|
||||||
|
int leftOccupied = 0;
|
||||||
|
List<Point> movePoints = new ArrayList<>();
|
||||||
|
for (int i = 0; i < targetIndex; i++) {
|
||||||
|
Point point = points.get(i);
|
||||||
|
if (point != null && point.getStatus().equals(CommonStatusEnum.USED.getValue())) {
|
||||||
|
leftOccupied++;
|
||||||
|
movePoints.add(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftOccupied == 0) {
|
||||||
|
leftOccupied = 1;
|
||||||
|
}
|
||||||
|
totalScore += 100.0 / leftOccupied * 0.7;
|
||||||
|
return new InventoryScore(inventory, totalScore, bestPoint, movePoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param currPoint 当前库位
|
||||||
|
* @param outPoints 出库口集合
|
||||||
|
* @return 最佳出库口
|
||||||
|
*/
|
||||||
|
private Point getBestOutboundPoint(Point currPoint, List<Point> outPoints) {
|
||||||
|
//获取距离最近的出库口
|
||||||
|
return outPoints.stream()
|
||||||
|
.min(Comparator.comparingDouble(point ->
|
||||||
|
Math.abs(currPoint.getPositionX() - point.getPositionX()) +
|
||||||
|
Math.abs(currPoint.getPositionY() - point.getPositionY())))
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算距离评分
|
||||||
|
* 基于出库口位置和库位坐标计算最短路径距离
|
||||||
|
*/
|
||||||
|
private double calculateClusterDistanceCost(Point point, Point station) {
|
||||||
|
// 计算曼哈顿距离
|
||||||
|
double distance = Math.abs(point.getPositionX() - station.getPositionX()) + Math.abs(point.getPositionY() - station.getPositionY());
|
||||||
|
|
||||||
|
// 距离越小分数越高
|
||||||
|
return Math.max(0, 100 - (distance / 100.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public List<String> cancelAllocate(List<Long> pickIds) {
|
public List<String> cancelAllocate(List<Long> pickIds) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.cpte.modules.shipping.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.cpte.modules.base.entity.Point;
|
||||||
|
import org.cpte.modules.inventory.entity.Inventory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class InventoryScore {
|
||||||
|
// 库存
|
||||||
|
private Inventory inventory;
|
||||||
|
// 分数
|
||||||
|
private double score;
|
||||||
|
//最佳出库口
|
||||||
|
private Point outPoint;
|
||||||
|
//移位的库位
|
||||||
|
private List<Point> movePoints;
|
||||||
|
}
|
||||||
|
|
@ -178,7 +178,7 @@ mybatis-plus:
|
||||||
table-underline: true
|
table-underline: true
|
||||||
configuration:
|
configuration:
|
||||||
# # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
|
# # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
|
||||||
#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
# 返回类型为Map,显示null对应的字段
|
# 返回类型为Map,显示null对应的字段
|
||||||
call-setters-on-nulls: true
|
call-setters-on-nulls: true
|
||||||
#jeecg专用配置
|
#jeecg专用配置
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue