no message
parent
126414e875
commit
90a27ac851
|
|
@ -17,6 +17,9 @@ public class LogDTO implements Serializable {
|
|||
/**内容*/
|
||||
private String logContent;
|
||||
|
||||
/** 返回 */
|
||||
private String returnData;
|
||||
|
||||
/**日志类型(0:操作日志;1:登录日志;2:定时任务) */
|
||||
private Integer logType;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package org.jeecg.common.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.serializer.PropertyFilter;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
|
@ -28,6 +29,7 @@ import jakarta.annotation.Resource;
|
|||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
|
||||
|
|
@ -71,10 +73,10 @@ public class AutoLogAspect {
|
|||
|
||||
LogDTO dto = new LogDTO();
|
||||
AutoLog syslog = method.getAnnotation(AutoLog.class);
|
||||
if(syslog != null){
|
||||
if (syslog != null) {
|
||||
//update-begin-author:taoyan date:
|
||||
String content = syslog.value();
|
||||
if(syslog.module()== ModuleType.ONLINE){
|
||||
if (syslog.module() == ModuleType.ONLINE) {
|
||||
content = getOnlineLogContent(obj, content);
|
||||
}
|
||||
//注解上的描述,操作日志内容
|
||||
|
|
@ -95,13 +97,16 @@ public class AutoLogAspect {
|
|||
|
||||
//获取request
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
//请求的url
|
||||
dto.setRequestUrl(request.getRequestURL().toString());
|
||||
//请求的参数
|
||||
dto.setRequestParam(getReqestParams(request,joinPoint));
|
||||
dto.setRequestParam(getReqestParams(request, joinPoint));
|
||||
dto.setReturnData(handlerResult(obj));
|
||||
//设置IP地址
|
||||
dto.setIp(IpUtils.getIpAddr(request));
|
||||
//获取登录用户信息
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
if(sysUser!=null){
|
||||
if (sysUser != null) {
|
||||
dto.setUserid(sysUser.getUsername());
|
||||
dto.setUsername(sysUser.getRealname());
|
||||
}
|
||||
|
|
@ -116,7 +121,7 @@ public class AutoLogAspect {
|
|||
/**
|
||||
* 获取操作类型
|
||||
*/
|
||||
private int getOperateType(String methodName,int operateType) {
|
||||
private int getOperateType(String methodName, int operateType) {
|
||||
if (operateType > 0) {
|
||||
return operateType;
|
||||
}
|
||||
|
|
@ -126,11 +131,11 @@ public class AutoLogAspect {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param request: request
|
||||
* @param joinPoint: joinPoint
|
||||
* @Description: 获取请求参数
|
||||
* @author: scott
|
||||
* @date: 2020/4/16 0:10
|
||||
* @param request: request
|
||||
* @param joinPoint: joinPoint
|
||||
* @Return: java.lang.String
|
||||
*/
|
||||
private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
|
||||
|
|
@ -140,7 +145,7 @@ public class AutoLogAspect {
|
|||
Object[] paramsArray = joinPoint.getArgs();
|
||||
// java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
|
||||
// https://my.oschina.net/mengzhang6/blog/2395893
|
||||
Object[] arguments = new Object[paramsArray.length];
|
||||
Object[] arguments = new Object[paramsArray.length];
|
||||
for (int i = 0; i < paramsArray.length; i++) {
|
||||
if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
|
||||
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
|
||||
|
|
@ -154,7 +159,7 @@ public class AutoLogAspect {
|
|||
@Override
|
||||
public boolean apply(Object o, String name, Object value) {
|
||||
int length = 500;
|
||||
if(value!=null && value.toString().length()>length){
|
||||
if (value != null && value.toString().length() > length) {
|
||||
return false;
|
||||
}
|
||||
return !(value instanceof MultipartFile);
|
||||
|
|
@ -168,7 +173,7 @@ public class AutoLogAspect {
|
|||
// 请求的方法参数值
|
||||
Object[] args = joinPoint.getArgs();
|
||||
// 请求的方法参数名称
|
||||
StandardReflectionParameterNameDiscoverer u=new StandardReflectionParameterNameDiscoverer();
|
||||
StandardReflectionParameterNameDiscoverer u = new StandardReflectionParameterNameDiscoverer();
|
||||
String[] paramNames = u.getParameterNames(method);
|
||||
if (args != null && paramNames != null) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
|
|
@ -179,24 +184,46 @@ public class AutoLogAspect {
|
|||
return params;
|
||||
}
|
||||
|
||||
private String handlerResult(Object result) {
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
String resultStr;
|
||||
try {
|
||||
if (result instanceof String) {
|
||||
resultStr = (String) result;
|
||||
} else {
|
||||
resultStr = JSON.toJSONString(result);// 如果返回结果非String类型,转换成JSON格式的字符串
|
||||
}
|
||||
|
||||
if (resultStr.length() > 10000) {
|
||||
resultStr = resultStr.substring(0, 10000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
resultStr = result.toString();
|
||||
}
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* online日志内容拼接
|
||||
*
|
||||
* @param obj
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
private String getOnlineLogContent(Object obj, String content){
|
||||
if (obj instanceof Result){
|
||||
Result res = (Result)obj;
|
||||
private String getOnlineLogContent(Object obj, String content) {
|
||||
if (obj instanceof Result) {
|
||||
Result res = (Result) obj;
|
||||
String msg = res.getMessage();
|
||||
String tableName = res.getOnlTable();
|
||||
if(oConvertUtils.isNotEmpty(tableName)){
|
||||
content+=",表名:"+tableName;
|
||||
if (oConvertUtils.isNotEmpty(tableName)) {
|
||||
content += ",表名:" + tableName;
|
||||
}
|
||||
if(res.isSuccess()){
|
||||
content+= ","+(oConvertUtils.isEmpty(msg)?"操作成功":msg);
|
||||
}else{
|
||||
content+= ","+(oConvertUtils.isEmpty(msg)?"操作失败":msg);
|
||||
if (res.isSuccess()) {
|
||||
content += "," + (oConvertUtils.isEmpty(msg) ? "操作成功" : msg);
|
||||
} else {
|
||||
content += "," + (oConvertUtils.isEmpty(msg) ? "操作失败" : msg);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<!-- 保存日志11 -->
|
||||
<insert id="saveLog" parameterType="Object">
|
||||
insert into sys_log (id, log_type, log_content, method, operate_type, request_url, request_type, request_param, ip, userid, username, cost_time, create_time,create_by, tenant_id, client_type)
|
||||
insert into sys_log (id, log_type, log_content, method, operate_type, request_url, request_type, request_param,return_data, ip, userid, username, cost_time, create_time,create_by, tenant_id, client_type)
|
||||
values(
|
||||
#{dto.id,jdbcType=VARCHAR},
|
||||
#{dto.logType,jdbcType=INTEGER},
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#{dto.requestUrl,jdbcType=VARCHAR},
|
||||
#{dto.requestType,jdbcType=VARCHAR},
|
||||
#{dto.requestParam,jdbcType=VARCHAR},
|
||||
#{dto.returnData,jdbcType=VARCHAR},
|
||||
#{dto.ip,jdbcType=VARCHAR},
|
||||
#{dto.userid,jdbcType=VARCHAR},
|
||||
#{dto.username,jdbcType=VARCHAR},
|
||||
|
|
|
|||
|
|
@ -34,14 +34,24 @@ public interface PointMapper extends BaseMapper<Point> {
|
|||
List<Point> queryPoints(@Param("pointCode") String pointCode, @Param("status") Integer status, @Param("areaCode") String areaCode);
|
||||
|
||||
/**
|
||||
* 查询同SKU、批次、PO的库位
|
||||
* 查询库位信息
|
||||
*
|
||||
* @param itemId 物料ID
|
||||
* @param propC1 批次号
|
||||
* @param whCode 仓库编码
|
||||
* @param itemId 商品ID
|
||||
* @param whCode 仓库编号
|
||||
* @param projects 项目编号
|
||||
* @param taskNos 任务编号
|
||||
* @param propC1List 批次号
|
||||
* @param propC3List 外部库存状态
|
||||
* @param areaCode 库区编号
|
||||
* @return List<Point>
|
||||
*/
|
||||
List<Point> findPointsWithSkuBatchPo(@Param("itemId") Long itemId, @Param("propC1") String propC1, @Param("whCode") String whCode,@Param("areaCode") String areaCode);
|
||||
List<Point> findPointsWithSkuBatchPo(@Param("itemId") Long itemId,
|
||||
@Param("whCode") String whCode,
|
||||
@Param("projects") List<String> projects,
|
||||
@Param("taskNos") List<String> taskNos,
|
||||
@Param("propC1List") List<String> propC1List,
|
||||
@Param("propC3List") List<String> propC3List,
|
||||
@Param("areaCode") String areaCode);
|
||||
|
||||
// 查询同一巷道占用数量
|
||||
@Select("SELECT COUNT(id) FROM base_point WHERE col_num = #{colNum} AND layer_num = #{layerNum} AND status = 1")
|
||||
|
|
|
|||
|
|
@ -25,14 +25,6 @@
|
|||
WHERE inv.item_id = #{itemId}
|
||||
AND bp1.status = 0
|
||||
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}
|
||||
|
|
@ -41,5 +33,49 @@
|
|||
AND (inv.wh_code IS NULL OR inv.wh_code = '')
|
||||
</otherwise>
|
||||
</choose>
|
||||
<choose>
|
||||
<when test="projects != null and !projects.isEmpty()">
|
||||
AND inv.project IN
|
||||
<foreach collection="projects" item="project" open="(" separator="," close=")">
|
||||
#{project}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
AND (inv.project IS NULL OR inv.project = '')
|
||||
</otherwise>
|
||||
</choose>
|
||||
<choose>
|
||||
<when test="taskNos != null and !taskNos.isEmpty()">
|
||||
AND inv.task_no IN
|
||||
<foreach collection="taskNos" item="taskNo" open="(" separator="," close=")">
|
||||
#{taskNo}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
AND (inv.task_no IS NULL OR inv.task_no = '')
|
||||
</otherwise>
|
||||
</choose>
|
||||
<choose>
|
||||
<when test="propC1List != null and !propC1List.isEmpty()">
|
||||
AND inv.prop_c1 IN
|
||||
<foreach collection="propC1List" item="propC1" open="(" separator="," close=")">
|
||||
#{propC1}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
AND (inv.prop_c1 IS NULL OR inv.prop_c1 = '')
|
||||
</otherwise>
|
||||
</choose>
|
||||
<choose>
|
||||
<when test="propC3List != null and !propC3List.isEmpty()">
|
||||
AND inv.prop_c3 IN
|
||||
<foreach collection="propC3List" item="propC3" open="(" separator="," close=")">
|
||||
#{propC3}
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
AND (inv.prop_c3 IS NULL OR inv.prop_c3 = '')
|
||||
</otherwise>
|
||||
</choose>
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
@ -3,6 +3,8 @@ package org.cpte.modules.base.service;
|
|||
import org.apache.ibatis.annotations.Param;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.cpte.modules.receive.entity.Asn;
|
||||
import org.cpte.modules.receive.entity.AsnDetail;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -97,4 +99,19 @@ public interface IPointService extends IService<Point> {
|
|||
* @return List<Point>
|
||||
*/
|
||||
List<Point> queryPoints(String pointCode, Integer status, String areaCode);
|
||||
|
||||
/**
|
||||
* 查询物料聚集库位
|
||||
*
|
||||
* @param itemId 物料
|
||||
* @param whCode 仓库代码
|
||||
* @param projects 项目号
|
||||
* @param taskNos 任务号
|
||||
* @param propC1List 批次
|
||||
* @param propC3List 外部库存状态
|
||||
* @param areaCode 库区编码
|
||||
* @return List<Point>
|
||||
*/
|
||||
List<Point> findClusterPoint(Long itemId, String whCode, List<String> projects, List<String> taskNos, List<String> propC1List, List<String> propC3List, String areaCode);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,4 +153,9 @@ public class PointServiceImpl extends ServiceImpl<PointMapper, Point> implements
|
|||
return pointMapper.queryPoints(pointCode, status, areaCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Point> findClusterPoint(Long itemId, String whCode, List<String> projects, List<String> taskNos, List<String> propC1List, List<String> propC3List, String areaCode) {
|
||||
return pointMapper.findPointsWithSkuBatchPo(itemId, whCode,projects,taskNos, propC1List, propC3List, areaCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements
|
|||
|
||||
@Override
|
||||
public void bindStock(Stock stock, Point point) {
|
||||
stock.setPointId(point.getId());
|
||||
stock.setPointId(point == null ? null : point.getId());
|
||||
stock.setStatus(CommonStatusEnum.USED.getValue());
|
||||
stockMapper.updateById(stock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ public interface GeneralConstant {
|
|||
*/
|
||||
String PICK_ORDER_NO = "pick_order_no";
|
||||
|
||||
/**
|
||||
* 移位规则编码
|
||||
*/
|
||||
String MOVE_ORDER_NO = "move_order_no";
|
||||
|
||||
/**
|
||||
* TES任务下发接口
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import lombok.Getter;
|
|||
@Getter
|
||||
public enum TaskTypeEnum {
|
||||
PICK(1, "出库"),
|
||||
MOVE(2, "移位"),
|
||||
;
|
||||
|
||||
TaskTypeEnum(Integer value, String desc) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package org.cpte.modules.conveyorLine.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -24,6 +22,7 @@ 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.shipping.entity.PickDetail;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import org.cpte.modules.shipping.mapper.TaskMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -81,17 +80,18 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
|||
validateParams(scanTrayRequest);
|
||||
|
||||
//工作站
|
||||
Point srcPoint = pointMapper.queryByPointCode(scanTrayRequest.getStation());
|
||||
Point station = pointMapper.queryByPointCode(scanTrayRequest.getStation());
|
||||
|
||||
//容器
|
||||
Stock stock = stockMapper.queryByStockCode(scanTrayRequest.getStockCode());
|
||||
|
||||
//验证入库信息
|
||||
AsnDetail asnDetail = asnDetailMapper.queryByStockCode(stock.getId(), AsnStatusEnum.CREATED.getValue());
|
||||
if (asnDetail == null) {
|
||||
throw new RuntimeException("【" + scanTrayRequest.getStockCode() + "】托盘,无入库信息");
|
||||
List<AsnDetail> asnDetails = asnDetailMapper.queryByStockCode(stock.getId(), AsnStatusEnum.CREATED.getValue());
|
||||
if (CollectionUtils.isEmpty(asnDetails)) {
|
||||
throw new RuntimeException("【" + scanTrayRequest.getStockCode() + "】托盘,无入库任务");
|
||||
}
|
||||
Asn asn = asnMapper.selectById(asnDetail.getAsnId());
|
||||
Asn asn = asnMapper.selectById(asnDetails.get(0).getAsnId());
|
||||
AsnDetail asnDetail = asnDetails.get(0);
|
||||
//验证托盘是否有库存
|
||||
if (inventoryMapper.queryByStockId(stock.getId()) != null) {
|
||||
throw new RuntimeException("【" + scanTrayRequest.getStockCode() + "】托盘已入库");
|
||||
|
|
@ -102,39 +102,53 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
|||
throw new RuntimeException("【" + scanTrayRequest.getStockCode() + "】托盘已扫描,请勿重复扫描");
|
||||
}
|
||||
|
||||
//项目号、任务号、批次号、外部库存状态
|
||||
List<String> projects = asnDetails.stream().map(AsnDetail::getProject).filter(StringUtils::isNotBlank).distinct().toList();
|
||||
List<String> taskNos = asnDetails.stream().map(AsnDetail::getTaskNo).filter(StringUtils::isNotBlank).distinct().toList();
|
||||
List<String> propC1List = asnDetails.stream().map(AsnDetail::getPropC1).filter(StringUtils::isNotBlank).distinct().toList();
|
||||
List<String> propC3List = asnDetails.stream().map(AsnDetail::getPropC3).filter(StringUtils::isNotBlank).distinct().toList();
|
||||
|
||||
|
||||
//通过算法获取目标点位
|
||||
Point dstPoint = allocatePoint(asn, asnDetail, srcPoint);
|
||||
Point dstPoint = allocatePoint(asn.getOrderType(), asnDetail.getItemId(), asn.getWhCode(), projects, taskNos, propC1List, propC3List, station);
|
||||
|
||||
//锁定目标库位
|
||||
pointService.bindPoint(dstPoint);
|
||||
|
||||
//验证通过,生成Tes任务
|
||||
iAgvTaskService.createAgvTask(asnDetail.getId(), stock.getStockCode(), srcPoint.getPointCode(), dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), 0, AgvVendorEnum.TES.getValue());
|
||||
iAgvTaskService.createAgvTask(asn.getId(), stock.getStockCode(), station.getPointCode(), dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), 0, AgvVendorEnum.TES.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能库位分配库位
|
||||
*
|
||||
* @param asnDetail 入库明细
|
||||
* @param orderType 单据类型
|
||||
* @param itemId 物料
|
||||
* @param whCode 仓库代码
|
||||
* @param projects 项目号
|
||||
* @param taskNos 任务号
|
||||
* @param propC1List 批次号
|
||||
* @param propC3List 外部库存状态
|
||||
* @param station 工作站
|
||||
* @return 目标库位
|
||||
*/
|
||||
private Point allocatePoint(Asn asn, AsnDetail asnDetail, Point station) {
|
||||
private Point allocatePoint(Integer orderType, Long itemId, String whCode, List<String> projects, List<String> taskNos, List<String> propC1List, List<String> propC3List, Point station) {
|
||||
String areaCode = "";
|
||||
if (Set.of(0, 1, 2, 3).contains(asn.getOrderType())) {
|
||||
if (Set.of(0, 1, 2, 3).contains(orderType)) {
|
||||
areaCode = AreaTypeEnum.CPCCQ.getValue();
|
||||
} else {
|
||||
areaCode = AreaTypeEnum.MJCCQ.getValue();
|
||||
}
|
||||
|
||||
//1.优先寻找同物料/同批次/同PO聚集库位
|
||||
List<Point> availablePoints = findClusterPoint(asn, asnDetail, areaCode);
|
||||
//1.优先寻找同物料/同仓库/同项目号/同任务号/同批次/同外部库存状态
|
||||
List<Point> availablePoints = pointService.findClusterPoint(itemId, whCode, projects, taskNos, propC1List, propC3List, areaCode);
|
||||
if (CollectionUtils.isEmpty(availablePoints)) {
|
||||
//2.获取所有可用库位
|
||||
availablePoints = pointMapper.queryPoints(null, CommonStatusEnum.FREE.getValue(), areaCode);
|
||||
}
|
||||
|
||||
List<PointScore> scoredPoints = availablePoints.stream()
|
||||
.map(point -> calculateEnhancedLocationScore(point, station, asn, asnDetail))
|
||||
.map(point -> clusterPointScore(point, station, itemId, whCode, projects, taskNos, propC1List, propC3List))
|
||||
.sorted(Comparator.comparing(PointScore::getScore).reversed())
|
||||
.toList();
|
||||
|
||||
|
|
@ -149,42 +163,30 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
|||
* 寻找物料聚集库位
|
||||
*
|
||||
*/
|
||||
private List<Point> findClusterPoint(Asn asn, AsnDetail asnDetail, String areaCode) {
|
||||
return pointMapper.findPointsWithSkuBatchPo(asnDetail.getItemId(), asnDetail.getPropC1(), asn.getWhCode(), areaCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强版库位评分计算
|
||||
* 新增:通道类型深度策略 + 物料聚集加分
|
||||
* 库位评分计算
|
||||
*/
|
||||
private PointScore calculateEnhancedLocationScore(Point point, Point station, Asn asn, AsnDetail asnDetail) {
|
||||
double totalScore = 0.0;
|
||||
private PointScore clusterPointScore(Point point, Point station, Long itemId, String whCode, List<String> projects, List<String> taskNos, List<String> propC1List, List<String> propC3List) {
|
||||
double totalScore;
|
||||
|
||||
// 1. 距离评分 - 考虑从入库口到库位的距离
|
||||
double distanceScore = calculateClusterDistanceCost(point, station);
|
||||
totalScore += distanceScore * 0.3; // 权重从40%降到30%
|
||||
log.info("【{}】距离评分: {}", point.getPointCode(), totalScore);
|
||||
double distanceScore = calculateClusterDistanceCost(point, station) * 0.3;// 权重30%
|
||||
|
||||
// 2. 通道深度策略评分 - 新增核心策略
|
||||
double channelDepthScore = calculateChannelDepthScore(point);
|
||||
totalScore += channelDepthScore * 0.25; // 新增25%权重
|
||||
log.info("【{}】通道深度评分: {}", point.getPointCode(), totalScore);
|
||||
double channelDepthScore = calculateChannelDepthScore(point) * 0.25; // 权重25%
|
||||
|
||||
// 3. 通道类型评分 - 双通道优先
|
||||
double channelScore = calculateChannelScore(point);
|
||||
totalScore += channelScore * 0.15; // 权重从20%降到15%
|
||||
log.info("【{}】通道类型评分: {}", point.getPointCode(), totalScore);
|
||||
double channelScore = calculateChannelScore(point) * 0.15; // 权重15%
|
||||
|
||||
// 4. 均衡评分 - 避免热点区域
|
||||
double balanceScore = calculateBalanceScore(point);
|
||||
totalScore += balanceScore * 0.1; // 权重从15%降到10%
|
||||
log.info("【{}】均衡评分: {}", point.getPointCode(), totalScore);
|
||||
double balanceScore = calculateBalanceScore(point) * 0.1; // 权重10%
|
||||
|
||||
// 5. 物料聚集潜力评分 - 新增
|
||||
double clusterPotentialScore = calculateClusterPotentialScore(point, asn, asnDetail);
|
||||
totalScore += clusterPotentialScore * 0.2; // 新增20%权重
|
||||
log.info("【{}】物料聚集潜力评分: {}", point.getPointCode(), totalScore);
|
||||
double clusterPotentialScore = calculateClusterPotentialScore(point, itemId, whCode, projects, taskNos, propC1List, propC3List) * 0.2; // 权重20%
|
||||
|
||||
totalScore = distanceScore + channelDepthScore + channelScore + balanceScore + clusterPotentialScore;
|
||||
log.info("【{}】库位总分:{} - 距离评分: {} - 通道深度策略评分: {} - 通道类型评分: {} - 均衡评分: {} - 物料聚集评分: {}", point.getPointCode(), totalScore, distanceScore, channelDepthScore, channelScore, balanceScore, clusterPotentialScore);
|
||||
return new PointScore(point, totalScore);
|
||||
}
|
||||
|
||||
|
|
@ -250,38 +252,66 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
|||
/**
|
||||
* 计算物料聚集潜力评分
|
||||
* 检查周围库位是否有相同特征的货物
|
||||
* 新增货物特性评分:物料、仓库代码、项目号、任务号、批次号、外部库存状态
|
||||
*/
|
||||
private double calculateClusterPotentialScore(Point point, Asn asn, AsnDetail asnDetail) {
|
||||
private double calculateClusterPotentialScore(Point point, Long itemId, String whCode,
|
||||
List<String> projects, List<String> taskNos,
|
||||
List<String> propC1List,
|
||||
List<String> propC3List) {
|
||||
double score = 0.0;
|
||||
|
||||
// 检查同一巷道相邻库位
|
||||
int minDepth, maxDepth;
|
||||
if (point.getIzDoubleLane().equals(1)) {
|
||||
// 双通道:考虑整个巷道所有位置(1-7)
|
||||
// 因为双通道可以从两端操作,所有位置都算邻居
|
||||
minDepth = 1;
|
||||
maxDepth = 7;
|
||||
} else {
|
||||
// 单通道:只考虑同一侧的位置(从当前深度向前)
|
||||
// 因为只能从一端操作,后面的位置不算邻居
|
||||
minDepth = Integer.parseInt(point.getRowNum()); // 从当前位置开始
|
||||
maxDepth = 7; // 到最深位置
|
||||
minDepth = Integer.parseInt(point.getRowNum());
|
||||
maxDepth = 7;
|
||||
}
|
||||
List<Inventory> neighbors = inventoryMapper.findNeighborPoints(point.getColNum(), point.getLayerNum(), String.valueOf(minDepth), String.valueOf(maxDepth));
|
||||
|
||||
List<Inventory> neighbors = inventoryMapper.findNeighborPoints(
|
||||
point.getColNum(), point.getLayerNum(), String.valueOf(minDepth), String.valueOf(maxDepth));
|
||||
|
||||
for (Inventory neighbor : neighbors) {
|
||||
if (neighbor.getItemId() != null) {
|
||||
// 同SKU加分
|
||||
if (neighbor.getItemId().equals(asnDetail.getItemId())) {
|
||||
score += 15;
|
||||
// 同批次额外加分
|
||||
if (asnDetail.getPropC1() != null && asnDetail.getPropC1().equals(neighbor.getPropC1())) {
|
||||
if (neighbor.getItemId().equals(itemId)) {
|
||||
score += 20;
|
||||
|
||||
// 同仓库代码加分
|
||||
if (whCode != null && whCode.equals(neighbor.getWhCode())) {
|
||||
score += 10;
|
||||
// 同PO额外加分
|
||||
if (asn.getWhCode() != null && asn.getWhCode().equals(neighbor.getWhCode())) {
|
||||
score += 5;
|
||||
}
|
||||
}
|
||||
|
||||
// 同批次号加分
|
||||
if (CollectionUtils.isNotEmpty(propC1List) &&
|
||||
StringUtils.isNotBlank(neighbor.getPropC1()) &&
|
||||
propC1List.contains(neighbor.getPropC1())) {
|
||||
score += 15;
|
||||
}
|
||||
|
||||
// 同项目号加分
|
||||
if (CollectionUtils.isNotEmpty(projects) &&
|
||||
StringUtils.isNotBlank(neighbor.getProject()) &&
|
||||
projects.contains(neighbor.getProject())) {
|
||||
score += 10;
|
||||
}
|
||||
|
||||
// 同任务号加分
|
||||
if (CollectionUtils.isNotEmpty(taskNos) &&
|
||||
StringUtils.isNotBlank(neighbor.getTaskNo()) &&
|
||||
taskNos.contains(neighbor.getTaskNo())) {
|
||||
score += 10;
|
||||
}
|
||||
|
||||
// 同外部库存状态加分
|
||||
if (CollectionUtils.isNotEmpty(propC3List) &&
|
||||
StringUtils.isNotBlank(neighbor.getPropC3()) &&
|
||||
propC3List.contains(neighbor.getPropC3())) {
|
||||
score += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -291,6 +321,7 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
|||
return Math.min(score, 100);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算距离评分
|
||||
* 基于入库口位置和库位坐标计算最短路径距离
|
||||
|
|
|
|||
|
|
@ -86,30 +86,46 @@ public class Inventory implements Serializable {
|
|||
@Schema(description = "入库记录ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long receiveRecordId;
|
||||
|
||||
/**
|
||||
* 外部仓库
|
||||
*/
|
||||
@Excel(name = "外部仓库", width = 15)
|
||||
@Schema(description = "外部仓库")
|
||||
private java.lang.String whCode;
|
||||
|
||||
/**
|
||||
* 项目号
|
||||
*/
|
||||
@Schema(description = "项目号")
|
||||
private java.lang.String project;
|
||||
/**
|
||||
* 任务号
|
||||
*/
|
||||
@Schema(description = "任务号")
|
||||
private java.lang.String taskNo;
|
||||
|
||||
/**
|
||||
* 批次号
|
||||
*/
|
||||
@Excel(name = "批次号", width = 15)
|
||||
@Schema(description = "批次号")
|
||||
private java.lang.String propC1;
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
@Excel(name = "序列号", width = 15)
|
||||
@Schema(description = "序列号")
|
||||
private java.lang.String propC2;
|
||||
|
||||
/**
|
||||
* 外部库存状态
|
||||
*/
|
||||
@Excel(name = "外部库存状态", width = 15)
|
||||
@Schema(description = "外部库存状态")
|
||||
private java.lang.String propC3;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
@Excel(name = "序列号", width = 15)
|
||||
@Schema(description = "序列号")
|
||||
private java.lang.String propC2;
|
||||
|
||||
/**
|
||||
* 库存状态
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ public interface InventoryMapper extends BaseMapper<Inventory> {
|
|||
/**
|
||||
* 查询库存列表
|
||||
*
|
||||
* @param itemIds 物料ID
|
||||
* @param propC1List 批次号
|
||||
* @param propC3List 外部库存状态
|
||||
* @param whCodeList 外部仓库
|
||||
* @param itemIds 物料ID
|
||||
* @param propC1List 批次号
|
||||
* @param propC3List 外部库存状态
|
||||
* @param whCodeList 外部仓库
|
||||
* @return List<Inventory>
|
||||
*/
|
||||
List<Inventory> queryInventory(@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范围内)
|
||||
@Select("SELECT di.* " +
|
||||
|
|
@ -46,10 +46,16 @@ public interface InventoryMapper extends BaseMapper<Inventory> {
|
|||
" AND bp.row_num BETWEEN #{minDepth} AND #{maxDepth} " +
|
||||
" AND bp.status = 1")
|
||||
List<Inventory> findNeighborPoints(@Param("colNum") String colNum,
|
||||
@Param("layerNum") String layerNum,
|
||||
@Param("minDepth") String minDepth,
|
||||
@Param("maxDepth") String maxDepth);
|
||||
|
||||
@Param("layerNum") String layerNum,
|
||||
@Param("minDepth") String minDepth,
|
||||
@Param("maxDepth") String maxDepth);
|
||||
|
||||
|
||||
/**
|
||||
* 根据库位ID集合查询
|
||||
*
|
||||
* @param pointIds 库位ID集合
|
||||
* @return List<Inventory>
|
||||
*/
|
||||
List<Inventory> queryByPointIds(@Param("pointIds") List<Long> pointIds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,4 +46,13 @@
|
|||
</choose>
|
||||
ORDER BY create_time
|
||||
</select>
|
||||
|
||||
<select id="queryByPointIds" resultType="org.cpte.modules.inventory.entity.Inventory">
|
||||
SELECT * FROM data_inventory
|
||||
WHERE quantity > 0
|
||||
AND point_id IN
|
||||
<foreach collection="pointIds" item="pointId" open="(" separator="," close=")">
|
||||
#{pointId}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
@ -23,11 +23,10 @@ public interface IInventoryService extends IService<Inventory> {
|
|||
* @param stockId 容器
|
||||
* @param receivedQty 收货数量
|
||||
* @param asn 入库单
|
||||
* @param asnDetail 入库明细
|
||||
* @param receiveRecord 入库记录
|
||||
* @return Inventory
|
||||
*/
|
||||
Inventory createInventory(Long stockId, BigDecimal receivedQty, Asn asn, AsnDetail asnDetail, ReceiveRecord receiveRecord);
|
||||
Inventory createInventory(Long stockId, BigDecimal receivedQty, Asn asn, ReceiveRecord receiveRecord);
|
||||
|
||||
/**
|
||||
* 根据库存ID查询库存
|
||||
|
|
|
|||
|
|
@ -37,24 +37,26 @@ public class InventoryServiceImpl extends ServiceImpl<InventoryMapper, Inventory
|
|||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Inventory createInventory(Long stockId, BigDecimal receivedQty, Asn asn, AsnDetail asnDetail, ReceiveRecord receiveRecord) {
|
||||
public Inventory createInventory(Long stockId, BigDecimal receivedQty, Asn asn, ReceiveRecord receiveRecord) {
|
||||
Inventory inventory = Inventory.builder()
|
||||
.itemId(asnDetail.getItemId())
|
||||
.itemId(receiveRecord.getItemId())
|
||||
.pointId(receiveRecord.getToPointId())
|
||||
.stockId(asnDetail.getStockId())
|
||||
.stockId(receiveRecord.getStockId())
|
||||
.quantity(receivedQty)
|
||||
.queuedQty(BigDecimal.ZERO)
|
||||
.receiveRecordId(receiveRecord.getId())
|
||||
.project(receiveRecord.getProject())
|
||||
.taskNo(receiveRecord.getTaskNo())
|
||||
.whCode(asn.getWhCode())
|
||||
.propC1(asnDetail.getPropC1())
|
||||
.propC2(asnDetail.getPropC2())
|
||||
.propC3(asnDetail.getPropC3())
|
||||
.propC1(receiveRecord.getPropC1())
|
||||
.propC2(receiveRecord.getPropC2())
|
||||
.propC3(receiveRecord.getPropC3())
|
||||
.status(InventoryStatusEnum.AVAILABLE.getValue())
|
||||
.propD1(asnDetail.getPropD1())
|
||||
.description(asnDetail.getDescription())
|
||||
.tenantId(asnDetail.getTenantId())
|
||||
.sysOrgCode(asnDetail.getSysOrgCode())
|
||||
.createBy(asnDetail.getCreateBy())
|
||||
.propD1(receiveRecord.getPropD1())
|
||||
.description(receiveRecord.getDescription())
|
||||
.tenantId(receiveRecord.getTenantId())
|
||||
.sysOrgCode(receiveRecord.getSysOrgCode())
|
||||
.createBy(receiveRecord.getCreateBy())
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
return this.save(inventory) ? inventory : null;
|
||||
|
|
|
|||
|
|
@ -97,12 +97,38 @@ public class InventoryLog implements Serializable {
|
|||
@Schema(description = "容器ID")
|
||||
@Dict(dictTable = "base_stock", dicCode = "id", dicText = "stock_code")
|
||||
private java.lang.Long stockId;
|
||||
/**
|
||||
* 外部仓库
|
||||
*/
|
||||
@Excel(name = "外部仓库", width = 15)
|
||||
@Schema(description = "外部仓库")
|
||||
private java.lang.String whCode;
|
||||
|
||||
/**
|
||||
* 项目号
|
||||
*/
|
||||
@Schema(description = "项目号")
|
||||
private java.lang.String project;
|
||||
/**
|
||||
* 任务号
|
||||
*/
|
||||
@Schema(description = "任务号")
|
||||
private java.lang.String taskNo;
|
||||
|
||||
/**
|
||||
* 批次号
|
||||
*/
|
||||
@Excel(name = "批次号", width = 15)
|
||||
@Schema(description = "批次号")
|
||||
private java.lang.String propC1;
|
||||
|
||||
/**
|
||||
* 外部库存状态
|
||||
*/
|
||||
@Excel(name = "外部库存状态", width = 15)
|
||||
@Schema(description = "外部库存状态")
|
||||
private java.lang.String propC3;
|
||||
|
||||
/**
|
||||
* 变动数量
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -169,7 +169,10 @@ public class InventoryLogServiceImpl extends ServiceImpl<InventoryLogMapper, Inv
|
|||
.stockId(inventory.getStockId())
|
||||
.businessNo(businessNo)
|
||||
.businessDetailId(businessDetailId)
|
||||
.project(inventory.getProject())
|
||||
.taskNo(inventory.getTaskNo())
|
||||
.propC1(inventory.getPropC1())
|
||||
.propC3(inventory.getPropC3())
|
||||
.description(description)
|
||||
//计算变动前后数量: 变动前数量 = 库存数量 - 变动数量
|
||||
.beforeQty(BigDecimalUtil.subtract(inventory.getQuantity(), changeQty, 0))
|
||||
|
|
|
|||
|
|
@ -91,18 +91,7 @@ public class AsnDetail implements Serializable {
|
|||
@Excel(name = "单位", width = 15)
|
||||
@Schema(description = "单位")
|
||||
private java.lang.String unit;
|
||||
/**
|
||||
* 项目号
|
||||
*/
|
||||
@Excel(name = "项目号", width = 15)
|
||||
@Schema(description = "项目号")
|
||||
private java.lang.String project;
|
||||
/**
|
||||
* 任务号
|
||||
*/
|
||||
@Excel(name = "任务号", width = 15)
|
||||
@Schema(description = "任务号")
|
||||
private java.lang.String taskNo;
|
||||
|
||||
/**
|
||||
* 需求数量
|
||||
*/
|
||||
|
|
@ -123,6 +112,18 @@ public class AsnDetail implements Serializable {
|
|||
@Schema(description = "状态")
|
||||
@Dict(dicCode = "asn_status")
|
||||
private java.lang.Integer status;
|
||||
/**
|
||||
* 项目号
|
||||
*/
|
||||
@Excel(name = "项目号", width = 15)
|
||||
@Schema(description = "项目号")
|
||||
private java.lang.String project;
|
||||
/**
|
||||
* 任务号
|
||||
*/
|
||||
@Excel(name = "任务号", width = 15)
|
||||
@Schema(description = "任务号")
|
||||
private java.lang.String taskNo;
|
||||
/**
|
||||
* 批次号
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import lombok.AllArgsConstructor;
|
|||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -68,21 +69,41 @@ public class ReceiveRecord implements Serializable {
|
|||
*/
|
||||
@Schema(description = "收货数量")
|
||||
private java.math.BigDecimal receivedQty;
|
||||
|
||||
/**
|
||||
* 外部仓库
|
||||
*/
|
||||
@Schema(description = "外部仓库")
|
||||
private java.lang.String whCode;
|
||||
|
||||
/**
|
||||
* 项目号
|
||||
*/
|
||||
@Schema(description = "项目号")
|
||||
private java.lang.String project;
|
||||
/**
|
||||
* 任务号
|
||||
*/
|
||||
@Schema(description = "任务号")
|
||||
private java.lang.String taskNo;
|
||||
|
||||
/**
|
||||
* 批次号
|
||||
*/
|
||||
@Schema(description = "批次号")
|
||||
private java.lang.String propC1;
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
@Schema(description = "序列号")
|
||||
private java.lang.String propC2;
|
||||
|
||||
/**
|
||||
* 外部库存状态
|
||||
*/
|
||||
@Schema(description = "外部库存状态")
|
||||
private java.lang.String propC3;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
@Schema(description = "序列号")
|
||||
private java.lang.String propC2;
|
||||
/**
|
||||
* 生产日期
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public interface AsnDetailMapper extends BaseMapper<AsnDetail> {
|
|||
* @return AsnDetail
|
||||
*/
|
||||
@Select("select * from data_asn_detail where stock_id = #{stockId} and status = #{status} for update")
|
||||
AsnDetail queryByStockCode(@Param("stockId") Long stockId, @Param("status") Integer status);
|
||||
List<AsnDetail> queryByStockCode(@Param("stockId") Long stockId, @Param("status") Integer status);
|
||||
|
||||
@Select("select MAX(line_no) from data_asn_detail where asn_id = #{asnId} ")
|
||||
Integer queryMaxLineNoByAsnId(@Param("asnId") Long asnId);
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ public interface IAsnService extends IService<Asn> {
|
|||
/**
|
||||
* 收货操作
|
||||
*
|
||||
* @param asnDetailId 入库明细ID
|
||||
* @param asnId 入库单ID
|
||||
* @param pointCode 目标库位
|
||||
*/
|
||||
void receiveGoods(Long asnDetailId, String pointCode);
|
||||
void receiveGoods(Long asnId, String pointCode);
|
||||
|
||||
/**
|
||||
* 入库任务回传
|
||||
|
|
|
|||
|
|
@ -93,10 +93,6 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
throw new RuntimeException("请新增入库明细");
|
||||
}
|
||||
|
||||
if (asnDetailList.size() > 1) {
|
||||
throw new RuntimeException("入库明细只允许新增一条");
|
||||
}
|
||||
|
||||
AtomicInteger lineNoCounter = new AtomicInteger(1);
|
||||
for (AsnDetail entity : asnDetailList) {
|
||||
if (entity.getLineNo() == null || entity.getLineNo() == 0) {
|
||||
|
|
@ -207,57 +203,65 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void receiveGoods(Long asnDetailId, String pointCode) {
|
||||
//入库明细任务
|
||||
AsnDetail asnDetail = asnDetailMapper.selectById(asnDetailId);
|
||||
if (asnDetail == null) {
|
||||
throw new RuntimeException("未匹配到入库任务【" + asnDetailId + "】");
|
||||
public void receiveGoods(Long asnId, String pointCode) {
|
||||
//入库任务
|
||||
Asn asn = asnMapper.selectById(asnId);
|
||||
if (asn == null) {
|
||||
throw new RuntimeException("未匹配到入库任务【" + asnId + "】");
|
||||
}
|
||||
|
||||
if (asn.getStatus() != 1) {
|
||||
throw new RuntimeException("入库任务【" + asnId + "】已收货");
|
||||
}
|
||||
List<AsnDetail> asnDetails = asnDetailMapper.selectByMainId(asnId);
|
||||
//验证容器是否入库
|
||||
if (inventoryMapper.queryByStockId(asnDetail.getStockId()) != null) {
|
||||
throw new RuntimeException("【" + asnDetail.getStockId() + "】容器已入库");
|
||||
Stock stock = stockMapper.selectById(asnDetails.get(0).getStockId());
|
||||
if (inventoryMapper.queryByStockId(stock.getId()) != null) {
|
||||
throw new RuntimeException("【" + stock.getStockCode() + "】容器已入库");
|
||||
}
|
||||
|
||||
//入库单
|
||||
Asn asn = this.getById(asnDetail.getAsnId());
|
||||
|
||||
//实际的存储位置
|
||||
Point dstPoint = pointMapper.queryByPointCode(pointCode);
|
||||
|
||||
//容器
|
||||
Stock stock = stockMapper.selectById(asnDetail.getStockId());
|
||||
|
||||
//更新收货数量
|
||||
BigDecimal receivedQty = BigDecimalUtil.add(asnDetail.getReceivedQty(), asnDetail.getOrderQty(), 0);
|
||||
asnDetail.setReceivedQty(receivedQty);
|
||||
for (AsnDetail ad : asnDetails) {
|
||||
//已收货直接跳过
|
||||
if (AsnStatusEnum.RECEIVED.getValue().equals(ad.getStatus())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BigDecimal receivedQty = BigDecimalUtil.add(ad.getReceivedQty(), ad.getOrderQty(), 0);
|
||||
ad.setReceivedQty(receivedQty);
|
||||
|
||||
//更新明细状态
|
||||
if (receivedQty.compareTo(ad.getOrderQty()) >= 0) {
|
||||
ad.setStatus(AsnStatusEnum.RECEIVED.getValue());
|
||||
} else {
|
||||
ad.setStatus(AsnStatusEnum.RECEIVING.getValue());
|
||||
}
|
||||
asnDetailMapper.updateById(ad);
|
||||
|
||||
//生成入库记录
|
||||
ReceiveRecord receiveRecord = createReceiveRecord(ad, receivedQty, dstPoint.getId());
|
||||
|
||||
// 生成库存
|
||||
Inventory inventory = iInventoryService.createInventory(stock.getId(), receivedQty, asn, receiveRecord);
|
||||
|
||||
//添加库存日志
|
||||
iInventoryLogService.addInboundInventoryLog(inventory, ad.getToPointId(), receivedQty, asn.getOrderNo(), ad.getId(), ad.getDescription());
|
||||
|
||||
//更新明细状态
|
||||
if (receivedQty.compareTo(asnDetail.getOrderQty()) >= 0) {
|
||||
asnDetail.setStatus(AsnStatusEnum.RECEIVED.getValue());
|
||||
} else {
|
||||
asnDetail.setStatus(AsnStatusEnum.RECEIVING.getValue());
|
||||
}
|
||||
asnDetailMapper.updateById(asnDetail);
|
||||
|
||||
//生成入库记录
|
||||
ReceiveRecord receiveRecord = createReceiveRecord(asnDetail, receivedQty, dstPoint.getId());
|
||||
|
||||
// 生成库存
|
||||
Inventory inventory = iInventoryService.createInventory(stock.getId(), receivedQty, asn, asnDetail, receiveRecord);
|
||||
|
||||
//更新入库单
|
||||
refreshAsn(asn, asnDetailMapper.selectByMainId(asn.getId()));
|
||||
refreshAsn(asn, asnDetails);
|
||||
|
||||
//更新容器状态和位置
|
||||
iStockService.bindStock(stock, dstPoint);
|
||||
iPointService.bindPoint(dstPoint);
|
||||
|
||||
//添加库存日志
|
||||
iInventoryLogService.addInboundInventoryLog(inventory, asnDetail.getToPointId(), receivedQty, asn.getOrderNo(), asnDetail.getId(), asnDetail.getDescription());
|
||||
|
||||
//回传
|
||||
receiveCallback(asn, asnDetail, stock);
|
||||
// receiveCallback(asn, asnDetail, stock);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -366,6 +370,8 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
.toPointId(dstPointId)
|
||||
.itemId(asnDetail.getItemId())
|
||||
.receivedQty(receivedQty)
|
||||
.project(asnDetail.getProject())
|
||||
.taskNo(asnDetail.getTaskNo())
|
||||
.propC1(asnDetail.getPropC1())
|
||||
.propC2(asnDetail.getPropC2())
|
||||
.propC3(asnDetail.getPropC3())
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
if (inboundRequest.getType() == null) {
|
||||
throw new RuntimeException("任务类型(Type)必填");
|
||||
}
|
||||
if (inboundRequest.getType().equals(0)) {
|
||||
if (AsnOrderTypeEnum.PRODUCT.getValue().equals(inboundRequest.getType())) {
|
||||
if (StringUtils.isBlank(inboundRequest.getLocationFrom())) {
|
||||
throw new RuntimeException("成品入库,起点(LocationFrom)必填");
|
||||
throw new RuntimeException("起点(LocationFrom)必填");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +120,18 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
throw new RuntimeException("托盘号(Lpn)必填");
|
||||
}
|
||||
}
|
||||
//验证明细中托盘只能是同一个,如果明细中托盘有不一样的则提示
|
||||
Set<String> lpns = inboundRequest.getDetails().stream().map(InboundRequest.InboundDetail::getLpn).collect(Collectors.toSet());
|
||||
if (lpns.size() > 1) {
|
||||
throw new RuntimeException("明细中托盘只能是同一个");
|
||||
}
|
||||
|
||||
//验证明细中物料只能是同一种,如果明细中物料有不一样的则提示
|
||||
Set<String> itemCodes = inboundRequest.getDetails().stream().map(InboundRequest.InboundDetail::getItem).collect(Collectors.toSet());
|
||||
if (itemCodes.size() > 1) {
|
||||
throw new RuntimeException("明细中物料只能是同一种");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -136,16 +148,30 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
throw new RuntimeException("【" + no + "】任务号已接收,请勿重复下发");
|
||||
}
|
||||
|
||||
// 获取唯一的明细
|
||||
InboundRequest.InboundDetail detail = inboundRequest.getDetails().get(0);
|
||||
// 获取明细
|
||||
List<InboundRequest.InboundDetail> detail = inboundRequest.getDetails();
|
||||
|
||||
// 验证基础数据
|
||||
String srcPointCode = inboundRequest.getLocationFrom();
|
||||
String itemCode = detail.getItem();
|
||||
String stockCode = detail.getLpn();
|
||||
//获取明细中所有的物料
|
||||
List<String> itemCodes = detail.stream().map(InboundRequest.InboundDetail::getItem).toList();
|
||||
|
||||
Point srcPoint = iPointService.validatePoint(srcPointCode);
|
||||
Item item = itemService.validateItem(itemCode);
|
||||
//获取存在的物料
|
||||
Map<String, Item> itemMap = itemService.queryByItemCodesToMap(itemCodes);
|
||||
|
||||
//获取数据库不存在的物料集合且去重
|
||||
List<String> notExistItemCodes = itemCodes.stream().filter(itemCode -> !itemMap.containsKey(itemCode)).distinct().toList();
|
||||
if (CollectionUtils.isNotEmpty(notExistItemCodes)) {
|
||||
throw new RuntimeException("【" + notExistItemCodes + "】物料不存在");
|
||||
}
|
||||
|
||||
//起点
|
||||
Point srcPoint = null;
|
||||
if (AsnOrderTypeEnum.PRODUCT.getValue().equals(inboundRequest.getType())) {
|
||||
String srcPointCode = inboundRequest.getLocationFrom();
|
||||
srcPoint = iPointService.validatePoint(srcPointCode);
|
||||
}
|
||||
|
||||
//托盘
|
||||
String stockCode = detail.get(0).getLpn();
|
||||
Stock stock = iStockService.validateStock(stockCode);
|
||||
|
||||
//获取输送线工作台点位,均衡分配点位任务-轮询方式
|
||||
|
|
@ -153,10 +179,15 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
|
||||
// 创建入库单和明细
|
||||
Asn createAsn = buildAsn(inboundRequest);
|
||||
AsnDetail asnDetail = buildAsnDetail(detail, srcPoint, dstPoint, item, stock);
|
||||
List<AsnDetail> createAsnDetails = new ArrayList<>();
|
||||
for (InboundRequest.InboundDetail inboundDetail : inboundRequest.getDetails()) {
|
||||
Item item = itemMap.get(inboundDetail.getItem());
|
||||
AsnDetail asnDetail = buildAsnDetail(inboundDetail, srcPoint, dstPoint, item, stock);
|
||||
createAsnDetails.add(asnDetail);
|
||||
}
|
||||
|
||||
// 保存入库单和入库明细
|
||||
asnService.saveMain(createAsn, Collections.singletonList(asnDetail));
|
||||
asnService.saveMain(createAsn, createAsnDetails);
|
||||
|
||||
//绑定容器和起点
|
||||
iStockService.bindStock(stock, srcPoint);
|
||||
|
|
@ -164,7 +195,8 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
//成品入库需要生成AGV
|
||||
if (AsnOrderTypeEnum.PRODUCT.getValue().equals(createAsn.getOrderType())) {
|
||||
//创建AGV任务
|
||||
iAgvTaskService.createAgvTask(asnDetail.getId(), stock.getStockCode(), srcPoint.getPointCode(), dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), 0, AgvVendorEnum.HIK.getValue());
|
||||
String srcPointCode = srcPoint == null ? null : srcPoint.getPointCode();
|
||||
iAgvTaskService.createAgvTask(createAsn.getId(), stock.getStockCode(), srcPointCode, dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), 0, AgvVendorEnum.HIK.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +352,7 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
.orderQty(BigDecimal.valueOf(detail.getQty()))
|
||||
.receivedQty(BigDecimal.ZERO)
|
||||
.stockId(stock.getId())
|
||||
.fromPointId(srcPoint.getId())
|
||||
.fromPointId(srcPoint == null ? null : srcPoint.getId())
|
||||
.toPointId(dstPoint.getId())
|
||||
.status(AsnStatusEnum.CREATED.getValue())
|
||||
.project(detail.getProject())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package org.cpte.modules.serialNumber;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.cpte.modules.utils.CodeGeneratorUtil;
|
||||
import org.jeecg.common.handler.IFillRuleHandler;
|
||||
import org.jeecg.modules.system.mapper.SysFillRuleMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MoveSerialNumberRule implements IFillRuleHandler {
|
||||
|
||||
@Autowired
|
||||
private CodeGeneratorUtil codeGeneratorUtil;
|
||||
|
||||
@Autowired
|
||||
private SysFillRuleMapper sysFillRuleMapper;
|
||||
|
||||
|
||||
@Override
|
||||
public Object execute(JSONObject params, JSONObject formData) {
|
||||
String prefix = params.getString("code");
|
||||
String code = codeGeneratorUtil.generateSerialNumber(prefix);
|
||||
log.info("生成业务编号:{}", code);
|
||||
return code;
|
||||
}
|
||||
|
||||
public String generateSerialNumber(String ruleCode) {
|
||||
String ruleParams = sysFillRuleMapper.queryByRuleCode(ruleCode);
|
||||
JSONObject jsonObject = JSONObject.parseObject(ruleParams);
|
||||
String prefix = null;
|
||||
if (jsonObject != null) {
|
||||
prefix = jsonObject.getString("code");
|
||||
} else {
|
||||
prefix = "MK";
|
||||
}
|
||||
return codeGeneratorUtil.generateSerialNumber(prefix);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,11 +10,6 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cpte.modules.constant.GeneralConstant;
|
||||
import org.cpte.modules.serialNumber.PickSerialNumberRule;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import org.cpte.modules.shipping.mapper.PickMapper;
|
||||
import org.cpte.modules.shipping.mapper.TaskMapper;
|
||||
import org.cpte.modules.shipping.service.ITaskService;
|
||||
import org.jeecg.config.shiro.IgnoreAuth;
|
||||
import org.jeecgframework.poi.excel.ExcelImportUtil;
|
||||
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
|
||||
import org.jeecgframework.poi.excel.entity.ExportParams;
|
||||
|
|
|
|||
|
|
@ -57,4 +57,12 @@ public interface ITaskService extends IService<Task> {
|
|||
*/
|
||||
void generateAgvTask();
|
||||
|
||||
/**
|
||||
* 根据需要移位的库位生成Task任务
|
||||
*
|
||||
* @param movePointIds 库位
|
||||
* @return Task
|
||||
*/
|
||||
List<Task> bulidMoveTask(List<Long> movePointIds);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import com.alibaba.fastjson.JSON;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.lang.util.StringUtils;
|
||||
import org.cpte.modules.constant.GeneralConstant;
|
||||
import org.cpte.modules.base.entity.Item;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
|
|
@ -14,7 +14,6 @@ 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.*;
|
||||
import org.cpte.modules.conveyorLine.vo.PointScore;
|
||||
import org.cpte.modules.inventory.entity.Inventory;
|
||||
import org.cpte.modules.inventory.mapper.InventoryMapper;
|
||||
import org.cpte.modules.inventory.service.IInventoryService;
|
||||
|
|
@ -86,6 +85,7 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
@Autowired
|
||||
private BatchUtil batchUtils;
|
||||
|
||||
|
||||
/**
|
||||
* 获取出库单Map
|
||||
*
|
||||
|
|
@ -315,9 +315,9 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
Map<Long, Item> itemMap = iItemService.queryByItemIdsToMap(itemIds);
|
||||
|
||||
//筛选查询库存的条件(非空去重)批次、外部库存状态 、外部仓库
|
||||
List<String> propC1List = pickDetails.stream().map(PickDetail::getPropC1).filter(StringUtils::hasText).distinct().toList();
|
||||
List<String> propC3List = pickDetails.stream().map(PickDetail::getPropC3).filter(StringUtils::hasText).distinct().toList();
|
||||
List<String> whCodeList = pickMap.values().stream().map(Pick::getWhCode).filter(StringUtils::hasText).distinct().toList();
|
||||
List<String> propC1List = pickDetails.stream().map(PickDetail::getPropC1).filter(StringUtils::isNotBlank).distinct().toList();
|
||||
List<String> propC3List = pickDetails.stream().map(PickDetail::getPropC3).filter(StringUtils::isNotBlank).distinct().toList();
|
||||
List<String> whCodeList = pickMap.values().stream().map(Pick::getWhCode).filter(StringUtils::isNotBlank).distinct().toList();
|
||||
|
||||
//查询库存
|
||||
List<Inventory> inventories = inventoryMapper.queryInventory(itemIds, propC1List, propC3List, whCodeList);
|
||||
|
|
@ -376,7 +376,7 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
continue;
|
||||
}
|
||||
|
||||
//智能排序,优先分配移位最小的库位
|
||||
//智能排序,优先分配距离近、移位最少的库位
|
||||
List<InventoryScore> scoredInventory = scoreInventories(matchedInventories);
|
||||
|
||||
for (InventoryScore inventoryScore : scoredInventory) {
|
||||
|
|
@ -416,9 +416,8 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
|
||||
//移位任务
|
||||
if (CollectionUtils.isNotEmpty(inventoryScore.getMovePoints())) {
|
||||
for (Point movePoint : inventoryScore.getMovePoints()) {
|
||||
log.info("生成移位任务:原库位:{}", movePoint.getPointCode());
|
||||
}
|
||||
List<Long> movePointIds = inventoryScore.getMovePoints().stream().map(Point::getId).toList();
|
||||
createToTask.addAll(iTaskService.bulidMoveTask(movePointIds));
|
||||
} else {
|
||||
log.info("无移位任务");
|
||||
}
|
||||
|
|
@ -486,7 +485,7 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
colLayerPointsMap.put(key, points);
|
||||
}
|
||||
|
||||
//获取所以出库口的库位
|
||||
//获取出库口的库位
|
||||
List<Point> outPoints = iPointService.queryPoints(null, null, AreaTypeEnum.CK_DOCK.getValue());
|
||||
|
||||
//获取优化后的库存
|
||||
|
|
@ -497,7 +496,10 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
List<Point> points = colLayerPointsMap.get(key);
|
||||
return calculateMoveCount(inventory, currPoint, points, outPoints);
|
||||
})
|
||||
.sorted(Comparator.comparing(InventoryScore::getScore).reversed())
|
||||
//按分数倒序排序、移动次数升序排序
|
||||
.sorted(Comparator.comparing(InventoryScore::getScore).reversed()
|
||||
.thenComparing(score -> score.getMovePoints().size())
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
|
@ -511,12 +513,14 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
* @return 库位位移次数
|
||||
*/
|
||||
private InventoryScore calculateMoveCount(Inventory inventory, Point currPoint, List<Point> points, List<Point> outPoints) {
|
||||
double totalScore = 0.0;
|
||||
// 位移分数
|
||||
double moveScore;
|
||||
//移位库位
|
||||
List<Point> movePoints;
|
||||
|
||||
// 计算距离分数(权重30%)
|
||||
Point bestPoint = getBestOutboundPoint(currPoint, outPoints);
|
||||
double distanceScore = calculateClusterDistanceCost(currPoint, bestPoint);
|
||||
totalScore += distanceScore * 0.3;
|
||||
double distanceScore = calculateClusterDistanceCost(currPoint, bestPoint) * 0.3;
|
||||
|
||||
// 目标库位的深度位转换为索引
|
||||
int targetIndex = Integer.parseInt(currPoint.getRowNum()) - 1;
|
||||
|
|
@ -524,52 +528,46 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
//双通道
|
||||
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);
|
||||
}
|
||||
}
|
||||
List<Point> leftPoints = calculateUsedPoints(points, 0, targetIndex);
|
||||
|
||||
// 计算右侧占用数
|
||||
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> rightPoints = calculateUsedPoints(points, targetIndex + 1, points.size());
|
||||
|
||||
//取两个集合中,元素最少的那个集合
|
||||
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);
|
||||
movePoints = leftPoints.size() < rightPoints.size() ? leftPoints : rightPoints;
|
||||
moveScore = (100.0 / (movePoints.size() + 1)) * 0.7;
|
||||
} 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);
|
||||
movePoints = calculateUsedPoints(points, 0, targetIndex);
|
||||
moveScore = (100.0 / (movePoints.size() + 1)) * 0.7;
|
||||
}
|
||||
|
||||
//库位得分 = 距离得分 + 移动得分
|
||||
double totalScore = distanceScore + moveScore;
|
||||
|
||||
log.info("【{}】库位距离评分: {}-移位评分: {}-总得分: {}-移位次数: {}",
|
||||
currPoint.getPointCode(), distanceScore, moveScore, totalScore, movePoints.size());
|
||||
|
||||
return new InventoryScore(inventory, totalScore, bestPoint, movePoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* 巷道库位使用情况
|
||||
*
|
||||
* @param points 当前巷道所有库位
|
||||
* @param start 巷道起始库位
|
||||
* @param end 巷道结束库位
|
||||
* @return List<Point>
|
||||
*/
|
||||
private List<Point> calculateUsedPoints(List<Point> points, int start, int end) {
|
||||
List<Point> usedPoints = new ArrayList<>();
|
||||
|
||||
for (int i = start; i < end && i < points.size(); i++) {
|
||||
Point point = points.get(i);
|
||||
if (point != null && point.getStatus().equals(CommonStatusEnum.USED.getValue())) {
|
||||
usedPoints.add(point);
|
||||
}
|
||||
}
|
||||
return usedPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,11 +8,15 @@ import org.cpte.modules.agvTask.service.IAgvTaskService;
|
|||
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.constant.enums.AgvVendorEnum;
|
||||
import org.cpte.modules.constant.enums.BusinessTypeEnum;
|
||||
import org.cpte.modules.constant.enums.InventoryStatusEnum;
|
||||
import org.cpte.modules.base.mapper.PointMapper;
|
||||
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.*;
|
||||
import org.cpte.modules.inventory.entity.Inventory;
|
||||
import org.cpte.modules.inventory.mapper.InventoryMapper;
|
||||
import org.cpte.modules.serialNumber.MoveSerialNumberRule;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import org.cpte.modules.shipping.mapper.TaskMapper;
|
||||
import org.cpte.modules.shipping.service.ITaskService;
|
||||
|
|
@ -38,18 +42,33 @@ import java.util.stream.Collectors;
|
|||
@Slf4j
|
||||
public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements ITaskService {
|
||||
|
||||
@Autowired
|
||||
private PointMapper pointMapper;
|
||||
|
||||
@Autowired
|
||||
private TaskMapper taskMapper;
|
||||
|
||||
@Autowired
|
||||
private InventoryMapper inventoryMapper;
|
||||
|
||||
@Autowired
|
||||
private IItemService iItemService;
|
||||
|
||||
@Autowired
|
||||
private IPointService iPointService;
|
||||
|
||||
@Autowired
|
||||
private IStockService iStockService;
|
||||
|
||||
@Autowired
|
||||
private IAgvTaskService agvTaskService;
|
||||
|
||||
@Autowired
|
||||
private BatchUtil batchUtils;
|
||||
|
||||
@Autowired
|
||||
private MoveSerialNumberRule moveSerialNumberRule;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Task createTask(String taskNo, Integer taskType, Item item, Point fromPoint, Point toPoint, Stock stock, Long pickId, Long pickDetailId, Long inventoryId, BigDecimal planQty, Integer izAll) {
|
||||
|
|
@ -146,7 +165,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements IT
|
|||
Integer izAll = allSplit ? 1 : 0;
|
||||
log.info("任务分组:{}", key);
|
||||
if (!groupToAgvTaskMap.containsKey(key)) {
|
||||
AgvTask agvTask = agvTaskService.bulidAgvTask(null, key.getStockCode(), key.getFromPointCode(), key.getToPointCode(), null, BusinessTypeEnum.OUTBOUND.getValue(),izAll, AgvVendorEnum.TES.getValue());
|
||||
AgvTask agvTask = agvTaskService.bulidAgvTask(null, key.getStockCode(), key.getFromPointCode(), key.getToPointCode(), null, BusinessTypeEnum.OUTBOUND.getValue(), izAll, AgvVendorEnum.TES.getValue());
|
||||
createToAgvTaskList.add(agvTask);
|
||||
groupToAgvTaskMap.put(key, agvTask); // 建立映射
|
||||
log.info("创建AGV任务:{}", agvTask);
|
||||
|
|
@ -180,4 +199,48 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements IT
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Task> bulidMoveTask(List<Long> movePointIds) {
|
||||
List<Task> moveList = new ArrayList<>();
|
||||
List<Inventory> moveInventoryList = inventoryMapper.queryByPointIds(movePointIds);
|
||||
|
||||
//移位物料
|
||||
List<Long> moveItemIds = moveInventoryList.stream().map(Inventory::getItemId).distinct().toList();
|
||||
Map<Long, Item> moveItemMap = iItemService.queryByItemIdsToMap(moveItemIds);
|
||||
|
||||
//移位库位
|
||||
List<Long> fromPointIds = moveInventoryList.stream().map(Inventory::getPointId).distinct().toList();
|
||||
Map<Long, Point> fromPointMap = iPointService.queryByPointIdsToMap(fromPointIds);
|
||||
|
||||
//移位容器
|
||||
List<Long> stockIds = moveInventoryList.stream().map(Inventory::getStockId).distinct().toList();
|
||||
Map<Long, Stock> stockMap = iStockService.queryByStockIdsToMap(stockIds);
|
||||
|
||||
for (Inventory inv : moveInventoryList) {
|
||||
|
||||
//判读改点位是否有出库任务
|
||||
Item moveItem = moveItemMap.get(inv.getItemId());
|
||||
Point fromPoint = fromPointMap.get(inv.getPointId());
|
||||
Stock stock = stockMap.get(inv.getStockId());
|
||||
String taskNo = moveSerialNumberRule.generateSerialNumber(GeneralConstant.MOVE_ORDER_NO);
|
||||
//根据算法找到最优的目标库位
|
||||
Point toPoint=null;
|
||||
Task moveTask = this.bulidTask(taskNo, TaskTypeEnum.MOVE.getValue(), moveItem, fromPoint, toPoint, stock, null, null, inv.getId(), inv.getQuantity(), 0);
|
||||
moveList.add(moveTask);
|
||||
log.info("生成移位任务:{}- 容器:{} - 库位:{} - 库存数量:{}", taskNo, stock.getStockCode(), fromPoint.getPointCode(), inv.getQuantity());
|
||||
inv.setStatus(InventoryStatusEnum.TRANSFER.getValue());
|
||||
inventoryMapper.updateById(inv);
|
||||
}
|
||||
return moveList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择最优移位目标
|
||||
*/
|
||||
private Point findBestMovePoint(){
|
||||
// 1. 获取所有可用库位
|
||||
//List<Point> availablePoints = pointMapper.queryPoints(null, CommonStatusEnum.FREE.getValue(), areaCode);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,10 +229,10 @@ public class ITesAgvServiceImpl implements ITesAgvService {
|
|||
*
|
||||
* @param agvTask 任务
|
||||
*/
|
||||
private void handleEnd(Long asnDetailId, AgvTask agvTask) {
|
||||
private void handleEnd(Long asnId, AgvTask agvTask) {
|
||||
if (BusinessTypeEnum.INBOUND.getValue().equals(agvTask.getType())) {
|
||||
//收货
|
||||
iAsnService.receiveGoods(asnDetailId, agvTask.getEndCode());
|
||||
iAsnService.receiveGoods(asnId, agvTask.getEndCode());
|
||||
} else if (BusinessTypeEnum.OUTBOUND.getValue().equals(agvTask.getType())) {
|
||||
//拣货
|
||||
Point endPoint = iPointService.validatePoint(agvTask.getEndCode());
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ public class ElevatorMapUtil {
|
|||
|
||||
static {
|
||||
dataMap = new ConcurrentHashMap<>();
|
||||
List<String> CKJBK01 = Arrays.asList("DT001", "DT002");
|
||||
List<String> CKJBK02 = Arrays.asList("DT003", "DT004");
|
||||
List<String> ST102 = Arrays.asList("DT001", "DT002");
|
||||
List<String> ST104 = Arrays.asList("DT003", "DT004");
|
||||
// 初始化Map
|
||||
dataMap.put("CKJBK01", CKJBK01);
|
||||
dataMap.put("CKJBK02", CKJBK02);
|
||||
dataMap.put("ST102", ST102);
|
||||
dataMap.put("ST104", ST104);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package org.cpte.modules.utils;
|
|||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class test {
|
||||
public static void main(String[] args) {
|
||||
|
|
|
|||
|
|
@ -88,6 +88,11 @@ public class SysLog implements Serializable {
|
|||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 返回参数
|
||||
*/
|
||||
private String returnData;
|
||||
|
||||
/**
|
||||
* 操作人用户名称
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ public class ShardingSysLog implements Serializable {
|
|||
*/
|
||||
private String logContent;
|
||||
|
||||
/** 返回 */
|
||||
private String returnData;
|
||||
|
||||
/**
|
||||
* 日志类型(1登录日志,2操作日志)
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue