no message

main
HUOJIN\92525 2025-12-09 19:30:37 +08:00
parent 126414e875
commit 90a27ac851
34 changed files with 624 additions and 254 deletions

View File

@ -17,6 +17,9 @@ public class LogDTO implements Serializable {
/**内容*/
private String logContent;
/** 返回 */
private String returnData;
/**日志类型(0:操作日志;1:登录日志;2:定时任务) */
private Integer logType;

View File

@ -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;

View File

@ -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},

View File

@ -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);
/**
* SKUPO
*
*
* @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")

View File

@ -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>

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -55,6 +55,11 @@ public interface GeneralConstant {
*/
String PICK_ORDER_NO = "pick_order_no";
/**
*
*/
String MOVE_ORDER_NO = "move_order_no";
/**
* TES
*/

View File

@ -5,6 +5,7 @@ import lombok.Getter;
@Getter
public enum TaskTypeEnum {
PICK(1, "出库"),
MOVE(2, "移位"),
;
TaskTypeEnum(Integer value, String desc) {

View File

@ -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);
}
/**
*
*

View File

@ -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;
/**
*
*/

View File

@ -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);
}

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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;
/**
*
*/

View File

@ -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))

View File

@ -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;
/**
*
*/

View File

@ -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;
/**
*
*/

View File

@ -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);

View File

@ -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);
/**
*

View File

@ -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())

View File

@ -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())

View File

@ -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);
}
}

View File

@ -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;

View File

@ -57,4 +57,12 @@ public interface ITaskService extends IService<Task> {
*/
void generateAgvTask();
/**
* Task
*
* @param movePointIds
* @return Task
*/
List<Task> bulidMoveTask(List<Long> movePointIds);
}

View File

@ -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;
}
/**

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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);
}
/**

View File

@ -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) {

View File

@ -88,6 +88,11 @@ public class SysLog implements Serializable {
*/
private String method;
/**
*
*/
private String returnData;
/**
*
*/

View File

@ -94,6 +94,9 @@ public class ShardingSysLog implements Serializable {
*/
private String logContent;
/** 返回 */
private String returnData;
/**
* 12
*/