no message
parent
f5304c1f31
commit
da421426d0
|
|
@ -367,5 +367,6 @@
|
|||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -2877,7 +2877,7 @@
|
|||
"advancedSetting": {
|
||||
"defaultValue": {
|
||||
"type": "compose",
|
||||
"value": "https://help.jeecg.com/",
|
||||
"value": "https://help.cpte.com/",
|
||||
"format": "string",
|
||||
"allowFunc": true,
|
||||
"valueSplit": "",
|
||||
|
|
|
|||
|
|
@ -8,8 +8,15 @@ import java.util.stream.Collectors;
|
|||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.cpte.modules.agvTask.mapper.AgvTaskMapper;
|
||||
import org.cpte.modules.constant.enums.AgvStatusEnum;
|
||||
import org.cpte.modules.constant.enums.AgvVendorEnum;
|
||||
import org.cpte.modules.hikAgv.service.IHikAgvService;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import org.cpte.modules.tesAgv.service.ITesAgvService;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.common.system.query.QueryRuleEnum;
|
||||
|
|
@ -22,6 +29,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
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;
|
||||
|
|
@ -38,127 +46,128 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
/**
|
||||
|
||||
/**
|
||||
* @Description: AGV任务表
|
||||
* @author: cpte
|
||||
* @Date: 2025-11-06
|
||||
* @Date: 2025-11-06
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Tag(name="AGV任务表")
|
||||
@Tag(name = "AGV任务表")
|
||||
@RestController
|
||||
@RequestMapping("/agvTask")
|
||||
@Slf4j
|
||||
public class AgvTaskController extends JeecgController<AgvTask, IAgvTaskService> {
|
||||
@Autowired
|
||||
private IAgvTaskService agvTaskService;
|
||||
@Autowired
|
||||
private IAgvTaskService agvTaskService;
|
||||
|
||||
/**
|
||||
* 分页列表查询
|
||||
*
|
||||
* @param agvTask
|
||||
* @param pageNo
|
||||
* @param pageSize
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "AGV任务表-分页列表查询")
|
||||
@Operation(summary="AGV任务表-分页列表查询")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<AgvTask>> queryPageList(AgvTask agvTask,
|
||||
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
/**
|
||||
* 分页列表查询
|
||||
*
|
||||
* @param agvTask
|
||||
* @param pageNo
|
||||
* @param pageSize
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "AGV任务表-分页列表查询")
|
||||
@Operation(summary = "AGV任务表-分页列表查询")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<AgvTask>> queryPageList(AgvTask agvTask,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
|
||||
|
||||
QueryWrapper<AgvTask> queryWrapper = QueryGenerator.initQueryWrapper(agvTask, req.getParameterMap());
|
||||
Page<AgvTask> page = new Page<AgvTask>(pageNo, pageSize);
|
||||
IPage<AgvTask> pageList = agvTaskService.page(page, queryWrapper);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @param agvTask
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-添加")
|
||||
@Operation(summary="AGV任务表-添加")
|
||||
@RequiresPermissions("agvTask:data_agv_task:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody AgvTask agvTask) {
|
||||
agvTaskService.save(agvTask);
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @param agvTask
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-编辑")
|
||||
@Operation(summary="AGV任务表-编辑")
|
||||
@RequiresPermissions("agvTask:data_agv_task:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody AgvTask agvTask) {
|
||||
agvTaskService.updateById(agvTask);
|
||||
return Result.OK("编辑成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-通过id删除")
|
||||
@Operation(summary="AGV任务表-通过id删除")
|
||||
@RequiresPermissions("agvTask:data_agv_task:delete")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
|
||||
agvTaskService.removeById(id);
|
||||
return Result.OK("删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-批量删除")
|
||||
@Operation(summary="AGV任务表-批量删除")
|
||||
@RequiresPermissions("agvTask:data_agv_task:deleteBatch")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
|
||||
this.agvTaskService.removeByIds(Arrays.asList(ids.split(",")));
|
||||
return Result.OK("批量删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "AGV任务表-通过id查询")
|
||||
@Operation(summary="AGV任务表-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<AgvTask> queryById(@RequestParam(name="id",required=true) String id) {
|
||||
AgvTask agvTask = agvTaskService.getById(id);
|
||||
if(agvTask==null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(agvTask);
|
||||
}
|
||||
Page<AgvTask> page = new Page<AgvTask>(pageNo, pageSize);
|
||||
IPage<AgvTask> pageList = agvTaskService.page(page, queryWrapper);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
* @param request
|
||||
* @param agvTask
|
||||
*/
|
||||
* 添加
|
||||
*
|
||||
* @param agvTask
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-添加")
|
||||
@Operation(summary = "AGV任务表-添加")
|
||||
@RequiresPermissions("agvTask:data_agv_task:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody AgvTask agvTask) {
|
||||
agvTaskService.save(agvTask);
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @param agvTask
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-编辑")
|
||||
@Operation(summary = "AGV任务表-编辑")
|
||||
@RequiresPermissions("agvTask:data_agv_task:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody AgvTask agvTask) {
|
||||
agvTaskService.updateById(agvTask);
|
||||
return Result.OK("编辑成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-通过id删除")
|
||||
@Operation(summary = "AGV任务表-通过id删除")
|
||||
@RequiresPermissions("agvTask:data_agv_task:delete")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
|
||||
agvTaskService.removeById(id);
|
||||
return Result.OK("删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "AGV任务表-批量删除")
|
||||
@Operation(summary = "AGV任务表-批量删除")
|
||||
@RequiresPermissions("agvTask:data_agv_task:deleteBatch")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
|
||||
this.agvTaskService.removeByIds(Arrays.asList(ids.split(",")));
|
||||
return Result.OK("批量删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "AGV任务表-通过id查询")
|
||||
@Operation(summary = "AGV任务表-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<AgvTask> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||
AgvTask agvTask = agvTaskService.getById(id);
|
||||
if (agvTask == null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(agvTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
* @param request
|
||||
* @param agvTask
|
||||
*/
|
||||
@RequiresPermissions("agvTask:data_agv_task:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, AgvTask agvTask) {
|
||||
|
|
@ -166,16 +175,15 @@ public class AgvTaskController extends JeecgController<AgvTask, IAgvTaskService>
|
|||
}
|
||||
|
||||
/**
|
||||
* 通过excel导入数据
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
* 通过excel导入数据
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("agvTask:data_agv_task:importExcel")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
return super.importExcel(request, response, AgvTask.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ public interface IAgvTaskService extends IService<AgvTask> {
|
|||
* 创建agvTask任务
|
||||
*
|
||||
* @param businessDetailId 业务ID
|
||||
* @param status 状态
|
||||
* @param carrierCode 容器
|
||||
* @param startCode 起点
|
||||
* @param endCode 终点
|
||||
|
|
@ -24,6 +23,20 @@ public interface IAgvTaskService extends IService<AgvTask> {
|
|||
* @param type 业务类型
|
||||
* @param agvVendor 供应商
|
||||
*/
|
||||
AgvTask createAgvTask(Long businessDetailId, Integer status, String carrierCode, String startCode, String endCode, String taskType, String type, String agvVendor);
|
||||
AgvTask createAgvTask(Long businessDetailId, String carrierCode, String startCode, String endCode, String taskType, String type, String agvVendor);
|
||||
|
||||
/**
|
||||
* 创建agvTask任务
|
||||
*
|
||||
* @param businessDetailId 业务ID
|
||||
* @param carrierCode 容器
|
||||
* @param startCode 起点
|
||||
* @param endCode 终点
|
||||
* @param taskType 任务类型
|
||||
* @param type 业务类型
|
||||
* @param agvVendor 供应商
|
||||
*/
|
||||
AgvTask bulidAgvTask(Long businessDetailId, String carrierCode, String startCode, String endCode, String taskType, String type, String agvVendor);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
package org.cpte.modules.agvTask.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.cpte.modules.agvTask.entity.AgvTask;
|
||||
import org.cpte.modules.agvTask.mapper.AgvTaskMapper;
|
||||
import org.cpte.modules.agvTask.service.IAgvTaskService;
|
||||
import org.cpte.modules.constant.enums.AgvStatusEnum;
|
||||
import org.cpte.modules.constant.enums.AgvVendorEnum;
|
||||
import org.cpte.modules.constant.enums.StockTypeEnum;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -24,7 +28,13 @@ public class AgvTaskServiceImpl extends ServiceImpl<AgvTaskMapper, AgvTask> impl
|
|||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AgvTask createAgvTask(Long businessDetailId, Integer status, String carrierCode, String startCode, String endCode, String taskType, String type, String agvVendor) {
|
||||
public AgvTask createAgvTask(Long businessDetailId, String carrierCode, String startCode, String endCode, String taskType, String type, String agvVendor) {
|
||||
LoginUser sysUser = null;
|
||||
try {
|
||||
sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
} catch (Exception e) {
|
||||
log.error("获取登录用户信息失败");
|
||||
}
|
||||
int priority = 99;
|
||||
if (AgvVendorEnum.TES.getValue().equals(agvVendor)) {
|
||||
priority = 3;
|
||||
|
|
@ -33,14 +43,48 @@ public class AgvTaskServiceImpl extends ServiceImpl<AgvTaskMapper, AgvTask> impl
|
|||
.businessDetailId(businessDetailId)
|
||||
.carrierCode(carrierCode)
|
||||
.type(type)
|
||||
.status(status)
|
||||
.status(AgvStatusEnum.CREATED.getValue())
|
||||
.startCode(startCode)
|
||||
.endCode(endCode)
|
||||
.carrierType(StockTypeEnum.TRAY.getValue())
|
||||
.taskType(taskType)
|
||||
.priority(priority)
|
||||
.agvVendor(agvVendor)
|
||||
.sysOrgCode(sysUser == null ? "A05" : sysUser.getOrgCode())
|
||||
.tenantId(sysUser == null ? 1000L : Long.parseLong(sysUser.getRelTenantIds()))
|
||||
.createBy(sysUser == null ? "system" : sysUser.getUsername())
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
return this.save(agvTask) ? agvTask : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgvTask bulidAgvTask(Long businessDetailId, String carrierCode, String startCode, String endCode, String taskType, String type, String agvVendor) {
|
||||
LoginUser sysUser = null;
|
||||
try {
|
||||
sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
} catch (Exception e) {
|
||||
log.error("获取登录用户信息失败");
|
||||
}
|
||||
int priority = 99;
|
||||
if (AgvVendorEnum.TES.getValue().equals(agvVendor)) {
|
||||
priority = 3;
|
||||
}
|
||||
return AgvTask.builder()
|
||||
.businessDetailId(businessDetailId)
|
||||
.carrierCode(carrierCode)
|
||||
.type(type)
|
||||
.status(AgvStatusEnum.CREATED.getValue())
|
||||
.startCode(startCode)
|
||||
.endCode(endCode)
|
||||
.carrierType(StockTypeEnum.TRAY.getValue())
|
||||
.taskType(taskType)
|
||||
.priority(priority)
|
||||
.agvVendor(agvVendor)
|
||||
.sysOrgCode(sysUser == null ? "A05" : sysUser.getOrgCode())
|
||||
.tenantId(sysUser == null ? 1000L : Long.parseLong(sysUser.getRelTenantIds()))
|
||||
.createBy(sysUser == null ? "system" : sysUser.getUsername())
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,19 +65,19 @@ public class Point implements Serializable {
|
|||
*/
|
||||
@Excel(name = "排", width = 15)
|
||||
@Schema(description = "排")
|
||||
private java.lang.String rows;
|
||||
private java.lang.String rowNum;
|
||||
/**
|
||||
* 列
|
||||
*/
|
||||
@Excel(name = "列", width = 15)
|
||||
@Schema(description = "列")
|
||||
private java.lang.String cols;
|
||||
private java.lang.String colNum;
|
||||
/**
|
||||
* 层
|
||||
*/
|
||||
@Excel(name = "层", width = 15)
|
||||
@Schema(description = "层")
|
||||
private java.lang.String layers;
|
||||
private java.lang.String layerNum;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
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 java.util.List;
|
||||
|
|
@ -19,6 +20,19 @@ public interface IPointService extends IService<Point> {
|
|||
*/
|
||||
Point validatePoint(String pointCode);
|
||||
|
||||
/**
|
||||
* 占用点位
|
||||
* @param point 点位
|
||||
*/
|
||||
void bindPoint(Point point);
|
||||
|
||||
/**
|
||||
* 释放点位
|
||||
* @param point 点位
|
||||
*/
|
||||
void unbindPoint(Point point);
|
||||
|
||||
|
||||
/**
|
||||
* 根据库位编码集合查询库位信息
|
||||
*
|
||||
|
|
@ -42,4 +56,16 @@ public interface IPointService extends IService<Point> {
|
|||
* @return Map<Long, pointEntity>
|
||||
*/
|
||||
Map<Long, Point> queryByPointIdsToMap(List<Long> pointIds);
|
||||
|
||||
/**
|
||||
* 获取输送线工作台点位,均衡分配点位任务
|
||||
*
|
||||
* @param status 状态
|
||||
* @param areaCode 库区编码
|
||||
* @param key 索引
|
||||
* @return pointEntity
|
||||
*/
|
||||
Point getWorkStationPoint(Integer status,String areaCode, String key);
|
||||
|
||||
Point queryToPoint(String pointCode,Integer status,String areaCode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package org.cpte.modules.base.service;
|
||||
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
|
|
@ -21,6 +22,20 @@ public interface IStockService extends IService<Stock> {
|
|||
*/
|
||||
Stock validateStock(String stockCode);
|
||||
|
||||
/**
|
||||
* 验证容器状态
|
||||
* @param stock 容器
|
||||
*/
|
||||
void validateStockStatus(Stock stock);
|
||||
|
||||
/**
|
||||
* 绑定容器
|
||||
*
|
||||
* @param stock 容器
|
||||
* @param point 容器位置
|
||||
*/
|
||||
void bindStock(Stock stock, Point point);
|
||||
|
||||
/**
|
||||
* 根据容器编码集合查询容器信息
|
||||
*
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ package org.cpte.modules.base.service.impl;
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cpte.modules.base.entity.Area;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.base.mapper.AreaMapper;
|
||||
import org.cpte.modules.base.mapper.PointMapper;
|
||||
import org.cpte.modules.base.service.IAreaService;
|
||||
import org.cpte.modules.base.service.IPointService;
|
||||
import org.cpte.modules.constant.enums.AreaTypeEnum;
|
||||
import org.cpte.modules.constant.enums.CommonStatusEnum;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
|
@ -35,6 +34,9 @@ public class PointServiceImpl extends ServiceImpl<PointMapper, Point> implements
|
|||
@Autowired
|
||||
private PointMapper pointMapper;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
/**
|
||||
* 验证库位
|
||||
*
|
||||
|
|
@ -56,6 +58,18 @@ public class PointServiceImpl extends ServiceImpl<PointMapper, Point> implements
|
|||
return point;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPoint(Point point) {
|
||||
point.setStatus(CommonStatusEnum.USED.getValue());
|
||||
pointMapper.updateById(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindPoint(Point point) {
|
||||
point.setStatus(CommonStatusEnum.FREE.getValue());
|
||||
pointMapper.updateById(point);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据库位编码集合查询库位信息
|
||||
*
|
||||
|
|
@ -106,4 +120,25 @@ public class PointServiceImpl extends ServiceImpl<PointMapper, Point> implements
|
|||
}
|
||||
return pointMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getWorkStationPoint(Integer status, String areaCode, String key) {
|
||||
List<Point> dstPointList = pointMapper.queryPoints(null, status, areaCode);
|
||||
if (dstPointList.isEmpty()) {
|
||||
String desc = AreaTypeEnum.getDescByValue(areaCode);
|
||||
throw new RuntimeException("【" + desc + "】" + "无空闲库位");
|
||||
}
|
||||
long currentIndex = redisUtil.incr(key, 1) % dstPointList.size();
|
||||
return dstPointList.get((int) currentIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point queryToPoint(String pointCode, Integer status, String areaCode) {
|
||||
Point dstPoint = null;
|
||||
List<Point> dstPointList = pointMapper.queryPoints(pointCode, status, areaCode);
|
||||
if (dstPointList.isEmpty()) {
|
||||
throw new RuntimeException("【" + AreaTypeEnum.CPCCQ.getDesc() + "】无空闲库位");
|
||||
}
|
||||
return dstPoint = dstPointList.get(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ package org.cpte.modules.base.service.impl;
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.base.mapper.StockMapper;
|
||||
import org.cpte.modules.base.mapper.StockMapper;
|
||||
import org.cpte.modules.base.service.IStockService;
|
||||
import org.cpte.modules.constant.enums.CommonStatusEnum;
|
||||
import org.cpte.modules.constant.enums.StockTypeEnum;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
|
@ -51,6 +54,20 @@ public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements
|
|||
return stock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateStockStatus(Stock stock) {
|
||||
if (CommonStatusEnum.USED.getValue().equals(stock.getStatus())) {
|
||||
throw new RuntimeException(stock.getStockCode() + "容器已占用,请更换容器!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindStock(Stock stock, Point point) {
|
||||
stock.setPointId(point.getId());
|
||||
stock.setStatus(CommonStatusEnum.USED.getValue());
|
||||
stockMapper.updateById(stock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据容器编码集合查询容器信息
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package org.cpte.modules.constant;
|
||||
|
||||
/**
|
||||
* 通用常量
|
||||
*/
|
||||
public interface CommonConstant {
|
||||
/**
|
||||
* 入库输送线任务均衡索引
|
||||
*/
|
||||
String RK_DOCK_TASK_INDEX = "rk_dock_task_index";
|
||||
|
||||
/**
|
||||
* 入库单规则编码
|
||||
*/
|
||||
String ASN_ORDER_NO = "asn_order_no";
|
||||
|
||||
/**
|
||||
* 出库单规则编码
|
||||
*/
|
||||
String PICK_ORDER_NO = "pick_order_no";
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package org.cpte.modules.constant;
|
||||
|
||||
/**
|
||||
* 通用常量
|
||||
*/
|
||||
public interface GeneralConstant {
|
||||
/**
|
||||
* 接口开关
|
||||
*/
|
||||
final String OPEN_FLAG = "OPEN";
|
||||
|
||||
/**
|
||||
* 成功码
|
||||
*/
|
||||
final String AGV_SUCCESS_CODE = "SUCCESS";
|
||||
|
||||
/**
|
||||
* 失败码
|
||||
*/
|
||||
final String AGV_FAIL_CODE = "FAILED";
|
||||
|
||||
/**
|
||||
* 成功码
|
||||
*/
|
||||
final Integer TES_SUCCESS_CODE = 0;
|
||||
|
||||
/**
|
||||
* 失败码
|
||||
*/
|
||||
final Integer TES_FAIL_CODE = 500;
|
||||
|
||||
/**
|
||||
* 入库输送线任务均衡索引
|
||||
*/
|
||||
String RK_DOCK_TASK_INDEX = "rk_dock_task_index";
|
||||
|
||||
/**
|
||||
* 出库输送线任务均衡索引
|
||||
*/
|
||||
String CK_DOCK_TASK_INDEX = "ck_dock_task_index";
|
||||
|
||||
|
||||
/**
|
||||
* 入库单规则编码
|
||||
*/
|
||||
String ASN_ORDER_NO = "asn_order_no";
|
||||
|
||||
/**
|
||||
* 出库单规则编码
|
||||
*/
|
||||
String PICK_ORDER_NO = "pick_order_no";
|
||||
|
||||
/**
|
||||
* 入库回传接口
|
||||
*/
|
||||
String INBOUND_CALLBACK = "7Q7sqpIh";
|
||||
}
|
||||
|
|
@ -31,4 +31,19 @@ public enum AreaTypeEnum {
|
|||
this.desc = desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取描述
|
||||
*
|
||||
* @param value 值
|
||||
* @return 枚举
|
||||
*/
|
||||
public static String getDescByValue(String value) {
|
||||
for (AreaTypeEnum item : AreaTypeEnum.values()) {
|
||||
if (item.value.equals(value)) {
|
||||
return item.desc;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
package org.cpte.modules.constant.enums;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONAware;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 枚举类接口
|
||||
*
|
||||
* @Author YouChain: 胡克
|
||||
* @Date 2018-07-17 21:22:12
|
||||
* @Email huoj@youchain56.com
|
||||
*/
|
||||
public interface BaseEnum {
|
||||
|
||||
/**
|
||||
* 获取枚举类的值
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Object getValue();
|
||||
|
||||
/**
|
||||
* 获取枚举类的说明
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String getDesc();
|
||||
|
||||
/**
|
||||
* 比较参数是否与枚举类的value相同
|
||||
*
|
||||
* @param value
|
||||
* @return boolean
|
||||
*/
|
||||
default boolean equalsValue(Object value) {
|
||||
return Objects.equals(getValue(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较枚举类是否相同
|
||||
*
|
||||
* @param baseEnum
|
||||
* @return boolean
|
||||
*/
|
||||
default boolean equals(BaseEnum baseEnum) {
|
||||
return Objects.equals(getValue(), baseEnum.getValue()) && Objects.equals(getDesc(), baseEnum.getDesc());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回枚举类的说明
|
||||
*
|
||||
* @param clazz 枚举类类对象
|
||||
* @return
|
||||
*/
|
||||
static String getInfo(Class<? extends BaseEnum> clazz) {
|
||||
BaseEnum[] enums = clazz.getEnumConstants();
|
||||
LinkedHashMap<String, JSONObject> json = new LinkedHashMap<>(enums.length);
|
||||
for (BaseEnum e : enums) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("value", new DeletedQuotationAware(e.getValue()));
|
||||
jsonObject.put("desc", new DeletedQuotationAware(e.getDesc()));
|
||||
json.put(e.toString(), jsonObject);
|
||||
}
|
||||
|
||||
String enumJson = JSON.toJSONString(json, true);
|
||||
enumJson = enumJson.replaceAll("\"", "");
|
||||
enumJson = enumJson.replaceAll("\t", " ");
|
||||
enumJson = enumJson.replaceAll("\n", "<br>");
|
||||
String prefix = " <br> export const " + CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, clazz.getSimpleName() + " = <br> ");
|
||||
return prefix + enumJson + " <br>";
|
||||
}
|
||||
|
||||
@Data
|
||||
class DeletedQuotationAware implements JSONAware {
|
||||
|
||||
private String value;
|
||||
|
||||
public DeletedQuotationAware(Object value) {
|
||||
if (value instanceof String) {
|
||||
this.value = "'" + value + "'";
|
||||
} else {
|
||||
this.value = value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJSONString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package org.cpte.modules.constant.enums;
|
|||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 业务类型枚举
|
||||
* AGV业务类型枚举
|
||||
*
|
||||
* @author: cpte
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package org.cpte.modules.constant.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum TaskTypeEnum {
|
||||
PICK(1, "出库"),
|
||||
;
|
||||
|
||||
TaskTypeEnum(Integer value, String desc) {
|
||||
this.value = value;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
final Integer value;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
final String desc;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import org.cpte.modules.base.entity.Point;
|
|||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.base.mapper.PointMapper;
|
||||
import org.cpte.modules.base.mapper.StockMapper;
|
||||
import org.cpte.modules.base.service.IPointService;
|
||||
import org.cpte.modules.constant.enums.*;
|
||||
import org.cpte.modules.conveyorLine.request.ScanTrayRequest;
|
||||
import org.cpte.modules.conveyorLine.service.IConveyorLineService;
|
||||
|
|
@ -38,6 +39,9 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
|||
@Autowired
|
||||
private InventoryMapper inventoryMapper;
|
||||
|
||||
@Autowired
|
||||
private IPointService pointService;
|
||||
|
||||
@Autowired
|
||||
private IAgvTaskService iAgvTaskService;
|
||||
|
||||
|
|
@ -64,19 +68,17 @@ public class IConveyorLineServiceImpl implements IConveyorLineService {
|
|||
throw new RuntimeException("【" + scanTrayRequest.getStockCode() + "】托盘已扫描,请勿重复扫描");
|
||||
}
|
||||
|
||||
//输送线的起点
|
||||
Point srcPoint=pointMapper.selectById(asnDetail.getToPointId());
|
||||
|
||||
//通过算法获取目标点位
|
||||
List<Point> dstPointList = pointMapper.queryPoints(null, CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CPCCQ.getValue());
|
||||
if (dstPointList.isEmpty()) {
|
||||
throw new RuntimeException("【" + AreaTypeEnum.CPCCQ.getDesc() + "】无空闲库位");
|
||||
}
|
||||
Point dstPoint = dstPointList.get(0);
|
||||
Point dstPoint=pointService.queryToPoint(null, CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CPCCQ.getValue());
|
||||
|
||||
//锁定目标库位
|
||||
dstPoint.setStatus(CommonStatusEnum.USED.getValue());
|
||||
pointMapper.updateById(dstPoint);
|
||||
pointService.bindPoint(dstPoint);
|
||||
|
||||
//验证通过,生成Tes任务
|
||||
iAgvTaskService.createAgvTask(asnDetail.getId(), AgvStatusEnum.CREATED.getValue(), stock.getStockCode(), null, dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), AgvVendorEnum.TES.getValue());
|
||||
iAgvTaskService.createAgvTask(asnDetail.getId(), stock.getStockCode(), srcPoint.getPointCode(), dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), AgvVendorEnum.TES.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ public class HikAgvController {
|
|||
@AutoLog(value = "海康AGV-任务上报")
|
||||
@Operation(summary = "海康AGV-任务上报")
|
||||
@PostMapping(value = "/reporter/task")
|
||||
@IgnoreAuth
|
||||
public HikResult taskReporter(@RequestBody TaskReporterRequest taskReporterRequest) {
|
||||
try {
|
||||
iHikAgvService.taskReporter(taskReporterRequest);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.cpte.modules.agvTask.entity.AgvTask;
|
||||
import org.cpte.modules.agvTask.mapper.AgvTaskMapper;
|
||||
import org.cpte.modules.agvTask.service.IAgvTaskService;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
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.hikAgv.request.SubmitRequest;
|
||||
import org.cpte.modules.hikAgv.request.TaskReporterRequest;
|
||||
|
|
@ -25,27 +30,18 @@ import java.util.*;
|
|||
@Slf4j
|
||||
public class IHikAgvServiceImpl implements IHikAgvService {
|
||||
|
||||
/**
|
||||
* 接口开关
|
||||
*/
|
||||
final String open_flag = "OPEN";
|
||||
|
||||
/**
|
||||
* 成功码
|
||||
*/
|
||||
final String success_code = "SUCCESS";
|
||||
|
||||
/**
|
||||
* 失败码
|
||||
*/
|
||||
final String fail_code = "FAILED";
|
||||
|
||||
@Autowired
|
||||
private SysDictMapper sysDictMapper;
|
||||
|
||||
@Autowired
|
||||
private AgvTaskMapper agvTaskMapper;
|
||||
|
||||
@Autowired
|
||||
private IStockService iStockService;
|
||||
|
||||
@Autowired
|
||||
private IPointService iPointService;
|
||||
|
||||
@Autowired
|
||||
private IAgvTaskService iAgvTaskService;
|
||||
|
||||
|
|
@ -61,7 +57,7 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
SubmitRequest.TargetRoute dstTargetRoute = createDstTargetRoute(agvTask);
|
||||
|
||||
submitRequest.setTargetRoute(Arrays.asList(srcTargetRoute, dstTargetRoute));
|
||||
submitRequest.setInitPriority( agvTask.getPriority());
|
||||
submitRequest.setInitPriority(agvTask.getPriority());
|
||||
|
||||
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
|
||||
String deadline = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"));
|
||||
|
|
@ -85,8 +81,8 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
public void sendHikAgvTask(String url, String json, AgvTask agvTask) {
|
||||
log.info("请求报文:{}", json);
|
||||
// 检查接口开关, 未开启则返回
|
||||
if (sysDictMapper.queryByDictCode(open_flag) == null) {
|
||||
updateAgvTaskResponse(agvTask, "接口未开启", fail_code);
|
||||
if (sysDictMapper.queryByDictCode(GeneralConstant.OPEN_FLAG) == null) {
|
||||
updateAgvTaskResponse(agvTask, "接口未开启", GeneralConstant.AGV_FAIL_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -112,16 +108,14 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
code = resulObject.getString("code");
|
||||
message = resulObject.getString("message");
|
||||
|
||||
if (!success_code.equals(code)) {
|
||||
if (!GeneralConstant.AGV_SUCCESS_CODE.equals(code)) {
|
||||
throw new RuntimeException("AGV返回消息:" + message);
|
||||
}
|
||||
|
||||
// 更新任务状态
|
||||
updateAgvTaskResponse(agvTask, message, code);
|
||||
} catch (Exception e) {
|
||||
// 记录异常到 AgvTask
|
||||
updateAgvTaskResponse(agvTask, e.getMessage(), fail_code);
|
||||
throw e; // 继续向上抛出异常供 Controller 层处理
|
||||
updateAgvTaskResponse(agvTask, e.getMessage(), GeneralConstant.AGV_FAIL_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +126,8 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
if (agvTask == null) {
|
||||
throw new RuntimeException("【" + taskReporterRequest.getRobotTaskCode() + "】任务不存在");
|
||||
}
|
||||
Stock stock = iStockService.validateStock(agvTask.getCarrierCode());
|
||||
Point point = iPointService.validatePoint(agvTask.getEndCode());
|
||||
String status = taskReporterRequest.getExtra().getValues().getMethod();
|
||||
switch (status) {
|
||||
case "outbin":
|
||||
|
|
@ -140,7 +136,7 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
break;
|
||||
case "end":
|
||||
//任务完成
|
||||
handleEnd(agvTask);
|
||||
handleEnd(agvTask, stock, point);
|
||||
break;
|
||||
case "resend":
|
||||
//重新发送
|
||||
|
|
@ -164,7 +160,10 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
*
|
||||
* @param agvTask 任务
|
||||
*/
|
||||
private void handleEnd(AgvTask agvTask) {
|
||||
private void handleEnd(AgvTask agvTask, Stock stock, Point point) {
|
||||
//绑定容器和点位
|
||||
iStockService.bindStock(stock, point);
|
||||
//更新任务状态
|
||||
agvTask.setStatus(AgvStatusEnum.COMPLETED.getValue());
|
||||
agvTask.setEndTime(new Date());
|
||||
agvTaskMapper.updateById(agvTask);
|
||||
|
|
@ -176,11 +175,11 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
* @param agvTask 任务
|
||||
*/
|
||||
private void handleResend(AgvTask agvTask) {
|
||||
Long count = agvTaskMapper.existsByStockCode(agvTask.getCarrierCode(),AgvVendorEnum.HIK.getValue());
|
||||
Long count = agvTaskMapper.existsByStockCode(agvTask.getCarrierCode(), AgvVendorEnum.HIK.getValue());
|
||||
if (count > 0) {
|
||||
throw new RuntimeException("任务已重新生成,请勿重复操作! ");
|
||||
}
|
||||
AgvTask newAgvTask = iAgvTaskService.createAgvTask(agvTask.getBusinessDetailId(),AgvStatusEnum.CREATED.getValue(), agvTask.getCarrierCode(), agvTask.getStartCode(), agvTask.getEndCode(), null, agvTask.getType(),AgvVendorEnum.HIK.getValue());
|
||||
AgvTask newAgvTask = iAgvTaskService.createAgvTask(agvTask.getBusinessDetailId(), agvTask.getCarrierCode(), agvTask.getStartCode(), agvTask.getEndCode(), null, agvTask.getType(), AgvVendorEnum.HIK.getValue());
|
||||
switch (agvTask.getType()) {
|
||||
case "INBOUND":
|
||||
case "OUTBOUND":
|
||||
|
|
@ -195,7 +194,7 @@ public class IHikAgvServiceImpl implements IHikAgvService {
|
|||
|
||||
private void updateAgvTaskResponse(AgvTask agvTask, String message, String code) {
|
||||
if (agvTask != null) {
|
||||
if (success_code.equals(code)) {
|
||||
if (GeneralConstant.AGV_SUCCESS_CODE.equals(code)) {
|
||||
agvTask.setStatus(AgvStatusEnum.EXECUTING.getValue());
|
||||
}
|
||||
agvTask.setResMessage(message);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class InventoryServiceImpl extends ServiceImpl<InventoryMapper, Inventory
|
|||
public Inventory createInventory(Long stockId, BigDecimal receivedQty, Asn asn, AsnDetail asnDetail, ReceiveRecord receiveRecord) {
|
||||
Inventory inventory = Inventory.builder()
|
||||
.itemId(asnDetail.getItemId())
|
||||
.pointId(receiveRecord.getPointId())
|
||||
.pointId(receiveRecord.getToPointId())
|
||||
.stockId(asnDetail.getStockId())
|
||||
.quantity(receivedQty)
|
||||
.queuedQty(BigDecimal.ZERO)
|
||||
|
|
@ -58,7 +58,6 @@ public class InventoryServiceImpl extends ServiceImpl<InventoryMapper, Inventory
|
|||
.createBy(asnDetail.getCreateBy())
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
this.save(inventory);
|
||||
return inventory;
|
||||
return this.save(inventory)?inventory: null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package org.cpte.modules.quartz.job;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.cpte.modules.shipping.entity.PickDetail;
|
||||
import org.cpte.modules.shipping.mapper.PickDetailMapper;
|
||||
import org.cpte.modules.shipping.entity.Pick;
|
||||
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.IPickService;
|
||||
import org.cpte.modules.shipping.service.ITaskService;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.modules.base.service.BaseCommonService;
|
||||
import org.quartz.Job;
|
||||
|
|
@ -24,11 +27,17 @@ import java.util.stream.Collectors;
|
|||
@Slf4j
|
||||
public class AllocateJob implements Job {
|
||||
@Autowired
|
||||
private PickDetailMapper pickDetailMapper;
|
||||
private PickMapper pickMapper;
|
||||
|
||||
@Autowired
|
||||
private TaskMapper taskMapper;
|
||||
|
||||
@Autowired
|
||||
private IPickService iPickService;
|
||||
|
||||
@Autowired
|
||||
private ITaskService iTaskService;
|
||||
|
||||
@Autowired
|
||||
private BaseCommonService baseCommonService;
|
||||
|
||||
|
|
@ -40,14 +49,14 @@ public class AllocateJob implements Job {
|
|||
|
||||
@Override
|
||||
public void execute(JobExecutionContext jobExecutionContext) {
|
||||
// 查询未分配或者部分分配的出库明细
|
||||
List<PickDetail> pickDetailList = pickDetailMapper.queryUnallocatedPickDetail();
|
||||
if (pickDetailList.isEmpty()) {
|
||||
// 查询未分配或者部分分配的出库
|
||||
List<Long> pickList = pickMapper.queryUnallocatedPick();
|
||||
if (pickList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 分配出库明细
|
||||
// 分配出库
|
||||
long startTime = System.currentTimeMillis();
|
||||
List<String> resultMsg = iPickService.allocatePickDetail2(pickDetailList);
|
||||
List<String> resultMsg = iPickService.allocatePick2(pickList);
|
||||
long endTime = System.currentTimeMillis();
|
||||
log.info("分配出库明细耗时:{}ms", endTime - startTime);
|
||||
if (CollectionUtils.isNotEmpty(resultMsg)) {
|
||||
|
|
@ -69,6 +78,13 @@ public class AllocateJob implements Job {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//生成出库AGV出库任务
|
||||
long startTime2 = System.currentTimeMillis();
|
||||
List<Task> taskList = taskMapper.queryUnallocatedTask();
|
||||
iTaskService.generateAgvTask(taskList);
|
||||
long endTime2 = System.currentTimeMillis();
|
||||
log.info("生成AGV出库任务耗时:{}ms", endTime2 - startTime2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,19 +30,15 @@ public class HikAgvJob implements Job {
|
|||
}
|
||||
String taskSubmitUrl = "http://localhost:8000/cpte-wms/rcs/rtas/api/robot/controller/task/submit";
|
||||
for (AgvTask agvTask : agvTaskList) {
|
||||
try {
|
||||
// 判断起点无任务才允许下发
|
||||
boolean isStartCodeAvailable = agvTaskMapper.existsByStartCode(agvTask.getStartCode(), AgvVendorEnum.HIK.getValue()) == 0;
|
||||
if (isStartCodeAvailable) {
|
||||
hikAgvService.sendHikAgvTask(
|
||||
taskSubmitUrl,
|
||||
hikAgvService.generateHikAgvTaskJson(agvTask),
|
||||
agvTask
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送AGV任务失败,任务ID: {}", agvTask.getId(), e);
|
||||
boolean isStartCodeAvailable = agvTaskMapper.existsByStartCode(agvTask.getStartCode(), AgvVendorEnum.HIK.getValue()) == 0;
|
||||
if (isStartCodeAvailable) {
|
||||
hikAgvService.sendHikAgvTask(
|
||||
taskSubmitUrl,
|
||||
hikAgvService.generateHikAgvTaskJson(agvTask),
|
||||
agvTask
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,15 +31,12 @@ public class TesAgvJob implements Job {
|
|||
}
|
||||
String taskSubmitUrl = "http://localhost:8000/cpte-wms/tes/apiv2/newMovePodTask";
|
||||
for (AgvTask agvTask : agvTaskList) {
|
||||
try {
|
||||
tesAgvService.sendTesAgvTask(
|
||||
taskSubmitUrl,
|
||||
tesAgvService.generateTesAgvTaskJson(agvTask),
|
||||
agvTask
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("发送AGV任务失败,任务ID: {}", agvTask.getId(), e);
|
||||
}
|
||||
tesAgvService.sendTesAgvTask(
|
||||
taskSubmitUrl,
|
||||
tesAgvService.generateTesAgvTaskJson(agvTask),
|
||||
agvTask
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import java.util.Map;
|
|||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.cpte.modules.constant.CommonConstant;
|
||||
import org.cpte.modules.constant.GeneralConstant;
|
||||
import org.cpte.modules.serialNumber.AsnSerialNumberRule;
|
||||
import org.jeecgframework.poi.excel.ExcelImportUtil;
|
||||
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
|
||||
|
|
@ -92,7 +92,7 @@ public class AsnController {
|
|||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody AsnPage asnPage) {
|
||||
Asn asn = new Asn();
|
||||
String orderNo = asnSerialNumberRule.generateSerialNumber(CommonConstant.ASN_ORDER_NO);
|
||||
String orderNo = asnSerialNumberRule.generateSerialNumber(GeneralConstant.ASN_ORDER_NO);
|
||||
asn.setOrderNo(orderNo);
|
||||
BeanUtils.copyProperties(asnPage, asn);
|
||||
asnService.saveMain(asn, asnPage.getAsnDetailList());
|
||||
|
|
|
|||
|
|
@ -111,6 +111,22 @@ public class Asn implements Serializable {
|
|||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "订单日期")
|
||||
private java.util.Date orderDate;
|
||||
|
||||
/**
|
||||
* 返回报文
|
||||
*/
|
||||
@Excel(name = "返回报文", width = 15)
|
||||
@Schema(description = "返回报文")
|
||||
private java.lang.String resMessage;
|
||||
|
||||
/**
|
||||
* 回传时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "回传时间")
|
||||
private java.util.Date resTime;
|
||||
|
||||
/**
|
||||
* 所属部门
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -65,13 +65,20 @@ public class AsnDetail implements Serializable {
|
|||
@Schema(description = "容器")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long stockId;
|
||||
|
||||
/**
|
||||
* 库位
|
||||
* 起点库位
|
||||
*/
|
||||
@Excel(name = "库位", width = 15)
|
||||
@Schema(description = "库位")
|
||||
@Schema(description = "起点库位")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long pointId;
|
||||
private java.lang.Long fromPointId;
|
||||
/**
|
||||
* 终点库位
|
||||
*/
|
||||
@Schema(description = "终点库位")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long toPointId;
|
||||
|
||||
/**
|
||||
* 行号
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -45,11 +45,18 @@ public class ReceiveRecord implements Serializable {
|
|||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long stockId;
|
||||
/**
|
||||
* 库位
|
||||
* 起点库位
|
||||
*/
|
||||
@Schema(description = "库位")
|
||||
@Schema(description = "起点库位")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long pointId;
|
||||
private java.lang.Long fromPointId;
|
||||
/**
|
||||
* 终点库位
|
||||
*/
|
||||
@Schema(description = "终点库位")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long toPointId;
|
||||
|
||||
/**
|
||||
* 物料
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package org.cpte.modules.receive.service;
|
||||
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.receive.entity.AsnDetail;
|
||||
import org.cpte.modules.receive.entity.Asn;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
|
@ -54,4 +55,12 @@ public interface IAsnService extends IService<Asn> {
|
|||
*/
|
||||
void receiveGoods(Long asnDetailId, String pointCode);
|
||||
|
||||
/**
|
||||
* 入库任务回传
|
||||
*
|
||||
* @param asn 入库单
|
||||
* @param asnDetail 入库明细
|
||||
* @param stock 容器
|
||||
*/
|
||||
void receiveCallback(Asn asn, AsnDetail asnDetail, Stock stock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
package org.cpte.modules.receive.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.base.mapper.PointMapper;
|
||||
import org.cpte.modules.base.mapper.StockMapper;
|
||||
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.AsnStatusEnum;
|
||||
import org.cpte.modules.constant.enums.CommonStatusEnum;
|
||||
import org.cpte.modules.inventory.entity.Inventory;
|
||||
import org.cpte.modules.inventory.mapper.InventoryMapper;
|
||||
import org.cpte.modules.inventory.service.IInventoryService;
|
||||
import org.cpte.modules.inventoryLog.service.IInventoryLogService;
|
||||
import org.cpte.modules.receive.entity.Asn;
|
||||
|
|
@ -18,6 +26,11 @@ import org.cpte.modules.receive.mapper.AsnMapper;
|
|||
import org.cpte.modules.receive.mapper.ReceiveRecordMapper;
|
||||
import org.cpte.modules.receive.service.IAsnService;
|
||||
import org.cpte.modules.utils.BigDecimalUtil;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.openapi.mapper.OpenApiMapper;
|
||||
import org.jeecg.modules.system.mapper.SysDictMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -40,6 +53,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
@Service
|
||||
@Slf4j
|
||||
public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnService {
|
||||
|
||||
@Autowired
|
||||
private StockMapper stockMapper;
|
||||
@Autowired
|
||||
|
|
@ -51,6 +65,16 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
@Autowired
|
||||
private ReceiveRecordMapper receiveRecordMapper;
|
||||
@Autowired
|
||||
private InventoryMapper inventoryMapper;
|
||||
@Autowired
|
||||
private OpenApiMapper openApiMapper;
|
||||
@Autowired
|
||||
private SysDictMapper sysDictMapper;
|
||||
@Autowired
|
||||
private IStockService iStockService;
|
||||
@Autowired
|
||||
private IPointService iPointService;
|
||||
@Autowired
|
||||
private IInventoryService iInventoryService;
|
||||
@Autowired
|
||||
private IInventoryLogService iInventoryLogService;
|
||||
|
|
@ -58,13 +82,16 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMain(Asn asn, List<AsnDetail> asnDetailList) {
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
asn.setTenantId(Long.parseLong(sysUser.getRelTenantIds()));
|
||||
asn.setSysOrgCode(sysUser.getOrgCode());
|
||||
asnMapper.insert(asn);
|
||||
|
||||
if (asnDetailList == null || asnDetailList.isEmpty()) {
|
||||
throw new RuntimeException("请新增入库明细");
|
||||
}
|
||||
|
||||
if(asnDetailList.size()>1){
|
||||
if (asnDetailList.size() > 1) {
|
||||
throw new RuntimeException("入库明细只允许新增一条");
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +101,8 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
entity.setLineNo(lineNoCounter.getAndIncrement());
|
||||
}
|
||||
entity.setAsnId(asn.getId());
|
||||
asn.setTenantId(Long.parseLong(sysUser.getRelTenantIds()));
|
||||
asn.setSysOrgCode(sysUser.getOrgCode());
|
||||
asnDetailMapper.insert(entity);
|
||||
}
|
||||
//刷新入库单
|
||||
|
|
@ -124,7 +153,7 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
throw new RuntimeException("请新增入库明细");
|
||||
}
|
||||
|
||||
if(asnDetailList.size()>1){
|
||||
if (asnDetailList.size() > 1) {
|
||||
throw new RuntimeException("入库明细只允许新增一条");
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +212,11 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
throw new RuntimeException("未匹配到入库任务【" + asnDetailId + "】");
|
||||
}
|
||||
|
||||
//验证容器是否入库
|
||||
if (inventoryMapper.queryByStockId(asnDetail.getStockId()) != null) {
|
||||
throw new RuntimeException("【" + asnDetail.getStockId() + "】容器已入库");
|
||||
}
|
||||
|
||||
//入库单
|
||||
Asn asn = this.getById(asnDetail.getAsnId());
|
||||
|
||||
|
|
@ -214,10 +248,90 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
refreshAsn(asn, asnDetailMapper.selectByMainId(asn.getId()));
|
||||
|
||||
//更新容器状态和位置
|
||||
updateStockPoint(stock, dstPoint);
|
||||
iStockService.bindStock(stock, dstPoint);
|
||||
iPointService.bindPoint(dstPoint);
|
||||
|
||||
//添加库存日志
|
||||
iInventoryLogService.addInboundInventoryLog(inventory, asnDetail.getPointId(), receivedQty, asn.getOrderNo(), asnDetail.getId(), asnDetail.getDescription());
|
||||
iInventoryLogService.addInboundInventoryLog(inventory, asnDetail.getToPointId(), receivedQty, asn.getOrderNo(), asnDetail.getId(), asnDetail.getDescription());
|
||||
|
||||
//回传
|
||||
receiveCallback(asn, asnDetail, stock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 入库任务回传JSON
|
||||
*/
|
||||
private String receiveCallbackJson(Asn asn, AsnDetail asnDetail, Stock stock) {
|
||||
JSONObject jsonObject = new JSONObject(true);
|
||||
jsonObject.put("No", asn.getNo());
|
||||
jsonObject.put("OrderNo", asn.getThirdOrderNo());
|
||||
jsonObject.put("State", 5);
|
||||
jsonObject.put("LineNo", asnDetail.getLineNo());
|
||||
jsonObject.put("Lpn", stock.getStockCode());
|
||||
jsonObject.put("Qty", asnDetail.getOrderQty());
|
||||
jsonObject.put("Project", asnDetail.getProject());
|
||||
jsonObject.put("TaskNo", asnDetail.getTaskNo());
|
||||
jsonObject.put("LotAtt04", asnDetail.getPropC1());
|
||||
jsonObject.put("LotAtt010", asnDetail.getPropC3());
|
||||
return jsonObject.toJSONString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCallback(Asn asn, AsnDetail asnDetail, Stock stock) {
|
||||
String url = openApiMapper.getRequestUrl(GeneralConstant.INBOUND_CALLBACK);
|
||||
String json = receiveCallbackJson(asn, asnDetail, stock);
|
||||
|
||||
log.info("入库回传请求报文:{}", json);
|
||||
// 检查接口开关, 未开启则返回
|
||||
if (sysDictMapper.queryByDictCode(GeneralConstant.OPEN_FLAG) == null) {
|
||||
updateAsnDetailResponse(asn, "接口未开启");
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean Success = null;
|
||||
String Message = null;
|
||||
try {
|
||||
//String result = HttpPostUtil.sendPostReq(url, json);
|
||||
String result = "{\n" +
|
||||
" \"Success\": false,\n" +
|
||||
" \"Message\": \"入库回传失败\"\n" +
|
||||
"}";
|
||||
if (StringUtils.isEmpty(result)) {
|
||||
Message = "入库回传返回信息:接口调用失败";
|
||||
throw new RuntimeException(Message);
|
||||
}
|
||||
|
||||
JSONObject resulObject = JSON.parseObject(result);
|
||||
if (resulObject == null) {
|
||||
Message = "入库回传返回信息:接口返回为空";
|
||||
throw new RuntimeException(Message);
|
||||
}
|
||||
|
||||
Success = resulObject.getBoolean("Success");
|
||||
Message = resulObject.getString("Message");
|
||||
|
||||
if (!Success) {
|
||||
throw new RuntimeException("入库回传返回信息:" + Message);
|
||||
}
|
||||
updateAsnDetailResponse(asn, Message);
|
||||
} catch (Exception e) {
|
||||
updateAsnDetailResponse(asn, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务状态
|
||||
*
|
||||
* @param asn 入库单
|
||||
* @param message 信息
|
||||
*/
|
||||
private void updateAsnDetailResponse(Asn asn, String message) {
|
||||
if (asn != null) {
|
||||
asn.setResMessage(message);
|
||||
asn.setResTime(new Date());
|
||||
asnMapper.updateById(asn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -227,7 +341,8 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
ReceiveRecord receiveRecord = ReceiveRecord.builder()
|
||||
.asnDetailId(asnDetail.getId())
|
||||
.stockId(asnDetail.getStockId())
|
||||
.pointId(dstPointId)
|
||||
.fromPointId(asnDetail.getToPointId())
|
||||
.toPointId(dstPointId)
|
||||
.itemId(asnDetail.getItemId())
|
||||
.receivedQty(receivedQty)
|
||||
.propC1(asnDetail.getPropC1())
|
||||
|
|
@ -244,19 +359,4 @@ public class AsnServiceImpl extends ServiceImpl<AsnMapper, Asn> implements IAsnS
|
|||
return receiveRecord;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新容器位置和状态
|
||||
*/
|
||||
private void updateStockPoint(Stock stock, Point point) {
|
||||
stock.setPointId(point.getId());
|
||||
stock.setStatus(CommonStatusEnum.USED.getValue());
|
||||
stockMapper.updateById(stock);
|
||||
|
||||
// 更新库位状态为占用
|
||||
point.setStatus(CommonStatusEnum.USED.getValue()); // 占用状态
|
||||
pointMapper.updateById(point);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ public class SaiWmsController {
|
|||
@AutoLog(value = "入库任务下发")
|
||||
@Operation(summary = "赛意WMS-入库任务下发")
|
||||
@PostMapping(value = "/inBound/inBoundTask")
|
||||
@IgnoreAuth
|
||||
public Result<String> inBoundTask(@RequestBody @Valid InboundRequest inboundRequest) {
|
||||
iSaiWmsService.inBoundTask(inboundRequest);
|
||||
return Result.OK("操作成功!");
|
||||
|
|
@ -47,7 +46,6 @@ public class SaiWmsController {
|
|||
@AutoLog(value = "出库任务下发")
|
||||
@Operation(summary = "赛意WMS-出库任务下发")
|
||||
@PostMapping(value = "/outBound/outBoundTask")
|
||||
@IgnoreAuth
|
||||
public Result<String> outBoundTask(@RequestBody @Valid OutboundRequest outboundRequest) {
|
||||
iSaiWmsService.outBoundTask(outboundRequest);
|
||||
return Result.OK("操作成功!");
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package org.cpte.modules.saiWms.request;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package org.cpte.modules.saiWms.service;
|
||||
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.receive.entity.Asn;
|
||||
import org.cpte.modules.receive.entity.AsnDetail;
|
||||
import org.cpte.modules.saiWms.request.InboundRequest;
|
||||
import org.cpte.modules.saiWms.request.OutboundRequest;
|
||||
|
||||
|
|
@ -17,4 +20,6 @@ public interface ISaiWmsService {
|
|||
* @param outboundRequest 请求参数
|
||||
*/
|
||||
void outBoundTask(OutboundRequest outboundRequest);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ 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.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.CommonConstant;
|
||||
import org.cpte.modules.constant.GeneralConstant;
|
||||
import org.cpte.modules.constant.enums.*;
|
||||
import org.cpte.modules.receive.entity.Asn;
|
||||
import org.cpte.modules.receive.entity.AsnDetail;
|
||||
|
|
@ -24,7 +23,6 @@ import org.cpte.modules.shipping.entity.Pick;
|
|||
import org.cpte.modules.shipping.entity.PickDetail;
|
||||
import org.cpte.modules.shipping.mapper.PickMapper;
|
||||
import org.cpte.modules.shipping.service.IPickService;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
|
@ -36,10 +34,6 @@ import java.util.*;
|
|||
@Slf4j
|
||||
public class ISaiWmsServiceImpl implements ISaiWmsService {
|
||||
|
||||
|
||||
@Autowired
|
||||
private PointMapper pointMapper;
|
||||
|
||||
@Autowired
|
||||
private AsnMapper asnMapper;
|
||||
|
||||
|
|
@ -61,7 +55,6 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
@Autowired
|
||||
private IPickService pickService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private IAgvTaskService iAgvTaskService;
|
||||
|
||||
|
|
@ -71,8 +64,6 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
@Autowired
|
||||
private PickSerialNumberRule pickSerialNumberRule;
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
|
@ -99,25 +90,22 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
Stock stock = iStockService.validateStock(stockCode);
|
||||
|
||||
//获取输送线工作台点位,均衡分配点位任务-轮询方式
|
||||
List<Point> dstPointList = pointMapper.queryPoints(null, CommonStatusEnum.FREE.getValue(), AreaTypeEnum.RK_DOCK.getValue());
|
||||
if (dstPointList.isEmpty()) {
|
||||
throw new RuntimeException("【" + AreaTypeEnum.RK_DOCK.getDesc() + "】" + "无空闲库位");
|
||||
}
|
||||
String key = CommonConstant.RK_DOCK_TASK_INDEX;
|
||||
long currentIndex = redisUtil.incr(key, 1) % dstPointList.size();
|
||||
Point dstPoint = dstPointList.get((int) currentIndex);
|
||||
Point dstPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.RK_DOCK.getValue(), GeneralConstant.RK_DOCK_TASK_INDEX);
|
||||
|
||||
// 创建入库单和明细
|
||||
Asn createAsn = buildAsn(inboundRequest);
|
||||
AsnDetail asnDetail = buildAsnDetail(detail, srcPoint, item, stock);
|
||||
AsnDetail asnDetail = buildAsnDetail(detail, srcPoint,dstPoint, item, stock);
|
||||
|
||||
// 保存入库单和入库明细
|
||||
asnService.saveMain(createAsn, Collections.singletonList(asnDetail));
|
||||
|
||||
//绑定容器和起点
|
||||
iStockService.bindStock(stock, srcPoint);
|
||||
|
||||
//成品入库需要生成AGV
|
||||
if (AsnOrderTypeEnum.PRODUCT.getValue().equals(createAsn.getOrderType())) {
|
||||
//创建AGV任务
|
||||
iAgvTaskService.createAgvTask(asnDetail.getId(), AgvStatusEnum.CREATED.getValue(), stock.getStockCode(), srcPoint.getPointCode(), dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), AgvVendorEnum.HIK.getValue());
|
||||
iAgvTaskService.createAgvTask(asnDetail.getId(), stock.getStockCode(), srcPoint.getPointCode(), dstPoint.getPointCode(), null, BusinessTypeEnum.INBOUND.getValue(), AgvVendorEnum.HIK.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +144,7 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
* 构建入库单
|
||||
*/
|
||||
private Asn buildAsn(InboundRequest inboundRequest) {
|
||||
String orderNo = asnSerialNumberRule.generateSerialNumber(CommonConstant.ASN_ORDER_NO);
|
||||
String orderNo = asnSerialNumberRule.generateSerialNumber(GeneralConstant.ASN_ORDER_NO);
|
||||
return Asn.builder()
|
||||
.orderNo(orderNo)
|
||||
.thirdOrderNo(inboundRequest.getOrderNo())
|
||||
|
|
@ -166,16 +154,13 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
.orderType(inboundRequest.getType())
|
||||
.status(AsnStatusEnum.CREATED.getValue())
|
||||
.orderDate(new Date())
|
||||
.sysOrgCode("A05")
|
||||
.tenantId(1000L)
|
||||
.createBy("赛意")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建入库明细
|
||||
*/
|
||||
private AsnDetail buildAsnDetail(InboundRequest.InboundDetail detail, Point srcPoint, Item item, Stock stock) {
|
||||
private AsnDetail buildAsnDetail(InboundRequest.InboundDetail detail, Point srcPoint, Point dstPoint, Item item, Stock stock) {
|
||||
// 由于明细只有一条,直接构建单个明细对象
|
||||
return AsnDetail.builder()
|
||||
.lineNo(Integer.parseInt(detail.getLineNo()))
|
||||
|
|
@ -184,7 +169,8 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
.orderQty(detail.getQty())
|
||||
.receivedQty(BigDecimal.ZERO)
|
||||
.stockId(stock.getId())
|
||||
.pointId(srcPoint.getId())
|
||||
.fromPointId(srcPoint.getId())
|
||||
.toPointId(dstPoint.getId())
|
||||
.status(AsnStatusEnum.CREATED.getValue())
|
||||
.project(detail.getProject())
|
||||
.taskNo(detail.getTaskNo())
|
||||
|
|
@ -198,7 +184,7 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
* 构建出库单
|
||||
*/
|
||||
private Pick buildPick(OutboundRequest outboundRequest) {
|
||||
String orderNo = pickSerialNumberRule.generateSerialNumber(CommonConstant.PICK_ORDER_NO);
|
||||
String orderNo = pickSerialNumberRule.generateSerialNumber(GeneralConstant.PICK_ORDER_NO);
|
||||
return Pick.builder()
|
||||
.orderNo(orderNo)
|
||||
.thirdOrderNo(outboundRequest.getOrderNo())
|
||||
|
|
@ -208,9 +194,6 @@ public class ISaiWmsServiceImpl implements ISaiWmsService {
|
|||
.orderType(outboundRequest.getType())
|
||||
.status(PickStatusEnum.CREATED.getValue())
|
||||
.orderDate(new Date())
|
||||
.sysOrgCode("A05")
|
||||
.tenantId(1000L)
|
||||
.createBy("赛意")
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,15 @@ import java.util.Map;
|
|||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.cpte.modules.constant.CommonConstant;
|
||||
import jakarta.validation.Valid;
|
||||
import org.cpte.modules.constant.GeneralConstant;
|
||||
import org.cpte.modules.saiWms.request.InboundRequest;
|
||||
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;
|
||||
|
|
@ -53,11 +60,17 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
|
|||
@RequestMapping("/shipping/pick")
|
||||
@Slf4j
|
||||
public class PickController {
|
||||
@Autowired
|
||||
private PickMapper pickMapper;
|
||||
@Autowired
|
||||
private TaskMapper taskMapper;
|
||||
@Autowired
|
||||
private IPickService pickService;
|
||||
@Autowired
|
||||
private IPickDetailService pickDetailService;
|
||||
@Autowired
|
||||
private ITaskService taskService;
|
||||
@Autowired
|
||||
private PickSerialNumberRule pickSerialNumberRule;
|
||||
|
||||
/**
|
||||
|
|
@ -94,7 +107,7 @@ public class PickController {
|
|||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody PickPage pickPage) {
|
||||
Pick pick = new Pick();
|
||||
String orderNo = pickSerialNumberRule.generateSerialNumber(CommonConstant.PICK_ORDER_NO);
|
||||
String orderNo = pickSerialNumberRule.generateSerialNumber(GeneralConstant.PICK_ORDER_NO);
|
||||
pick.setOrderNo(orderNo);
|
||||
BeanUtils.copyProperties(pickPage, pick);
|
||||
pickService.saveMain(pick, pickPage.getPickDetailList());
|
||||
|
|
@ -267,4 +280,19 @@ public class PickController {
|
|||
return Result.OK("文件导入失败!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 出库分配
|
||||
*/
|
||||
@AutoLog(value = "出库分配")
|
||||
@Operation(summary = "出库分配")
|
||||
@PostMapping(value = "/allocatedPick")
|
||||
@IgnoreAuth
|
||||
public Result<String> allocatedPick() {
|
||||
List<Long> pickList = pickMapper.queryUnallocatedPick();
|
||||
pickService.allocatePick2(pickList);
|
||||
List<Task> taskList = taskMapper.queryUnallocatedTask();
|
||||
taskService.generateAgvTask(taskList);
|
||||
return Result.OK("操作成功!");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,197 @@
|
|||
package org.cpte.modules.shipping.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.*;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* @Description: 任务
|
||||
* @author: cpte
|
||||
* @Date: 2025-11-17
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@TableName("data_task")
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "任务")
|
||||
public class Task implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "id")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long id;
|
||||
/**
|
||||
* 任务编号
|
||||
*/
|
||||
@Excel(name = "任务编号", width = 15)
|
||||
@Schema(description = "任务编号")
|
||||
private java.lang.String taskNo;
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
@Excel(name = "商品ID", width = 15)
|
||||
@Schema(description = "商品ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long itemId;
|
||||
|
||||
@Schema(description = "商品")
|
||||
private java.lang.String itemCode;
|
||||
|
||||
/**
|
||||
* 原库位
|
||||
*/
|
||||
@Excel(name = "原库位", width = 15)
|
||||
@Schema(description = "原库位")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long fromPointId;
|
||||
|
||||
@Excel(name = "原库位", width = 15)
|
||||
private java.lang.String fromPointCode;
|
||||
|
||||
/**
|
||||
* 目标库位
|
||||
*/
|
||||
@Excel(name = "目标库位", width = 15)
|
||||
@Schema(description = "目标库位")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long toPointId;
|
||||
|
||||
@Schema(description = "目标库位")
|
||||
private java.lang.String toPointCode;
|
||||
/**
|
||||
* 容器
|
||||
*/
|
||||
@Excel(name = "容器", width = 15)
|
||||
@Schema(description = "容器")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long stockId;
|
||||
|
||||
@Schema(description = "容器")
|
||||
private java.lang.String stockCode;
|
||||
/**
|
||||
* 出库单ID
|
||||
*/
|
||||
@Excel(name = "出库单ID", width = 15)
|
||||
@Schema(description = "出库单ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long pickId;
|
||||
/**
|
||||
* 出库明细ID
|
||||
*/
|
||||
@Excel(name = "出库明细ID", width = 15)
|
||||
@Schema(description = "出库明细ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long pickDetailId;
|
||||
/**
|
||||
* 库存ID
|
||||
*/
|
||||
@Excel(name = "库存ID", width = 15)
|
||||
@Schema(description = "库存ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private java.lang.Long inventoryId;
|
||||
|
||||
/**
|
||||
* agvID
|
||||
*/
|
||||
@Excel(name = "agvID", width = 15)
|
||||
@Schema(description = "agvID")
|
||||
private java.lang.Long agvTaskId;
|
||||
|
||||
/**
|
||||
* 计划数量
|
||||
*/
|
||||
@Excel(name = "计划数量", width = 15)
|
||||
@Schema(description = "计划数量")
|
||||
private java.math.BigDecimal planQty;
|
||||
/**
|
||||
* 已完成数量
|
||||
*/
|
||||
@Excel(name = "已完成数量", width = 15)
|
||||
@Schema(description = "已完成数量")
|
||||
private java.math.BigDecimal moveQty;
|
||||
/**
|
||||
* 任务类型:1=拣货任务
|
||||
*/
|
||||
@Excel(name = "任务类型", width = 15)
|
||||
@Schema(description = "任务类型")
|
||||
private java.lang.Integer taskType;
|
||||
/**
|
||||
* 任务状态:1=已创建,2=已完成,3=已取消
|
||||
*/
|
||||
@Excel(name = "任务状态", width = 15)
|
||||
@Schema(description = "任务状态")
|
||||
private java.lang.Integer taskStatus;
|
||||
|
||||
/**
|
||||
* 是否整托0整托,1拆托
|
||||
*/
|
||||
@Excel(name = "是否整托", width = 15)
|
||||
@Schema(description = "是否整托")
|
||||
private java.lang.Integer izAll;
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
@Excel(name = "开始时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "开始时间")
|
||||
private java.util.Date startTime;
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@Excel(name = "完成时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "完成时间")
|
||||
private java.util.Date completeTime;
|
||||
/**
|
||||
* 所属部门
|
||||
*/
|
||||
@Schema(description = "所属部门")
|
||||
private java.lang.String sysOrgCode;
|
||||
/**
|
||||
* 仓库ID
|
||||
*/
|
||||
@Excel(name = "仓库ID", width = 15)
|
||||
@Schema(description = "仓库ID")
|
||||
private java.lang.Long tenantId;
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@Schema(description = "创建人")
|
||||
private java.lang.String createBy;
|
||||
/**
|
||||
* 创建日期
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "创建日期")
|
||||
private java.util.Date createTime;
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@Schema(description = "更新人")
|
||||
private java.lang.String updateBy;
|
||||
/**
|
||||
* 更新日期
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "更新日期")
|
||||
private java.util.Date updateTime;
|
||||
}
|
||||
|
|
@ -31,10 +31,13 @@ public interface PickDetailMapper extends BaseMapper<PickDetail> {
|
|||
*/
|
||||
public List<PickDetail> selectByMainId(@Param("mainId") Long mainId);
|
||||
|
||||
@Select("select * from data_pick_detail where status = 1 " +
|
||||
"union all " +
|
||||
"select * from data_pick_detail where status = 2 ")
|
||||
List<PickDetail> queryUnallocatedPickDetail();
|
||||
/**
|
||||
* 通过出库单id集合查询出库明细
|
||||
*
|
||||
* @param pickIds 出库单id集合
|
||||
* @return List<PickDetail>
|
||||
*/
|
||||
List<PickDetail> queryByPickIds(@Param("pickIds") List<Long> pickIds);
|
||||
|
||||
@Select("select MAX(line_no) from data_pick_detail where pick_id = #{pickId} ")
|
||||
Integer queryMaxLineNoByPickId(@Param("pickId") Long pickId);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import org.apache.ibatis.annotations.Param;
|
|||
import org.apache.ibatis.annotations.Select;
|
||||
import org.cpte.modules.shipping.entity.Pick;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.cpte.modules.shipping.entity.PickDetail;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 出库单
|
||||
|
|
@ -23,4 +26,14 @@ public interface PickMapper extends BaseMapper<Pick> {
|
|||
@Select("select * from data_pick where no = #{no} for update ")
|
||||
Pick queryByNo(@Param("no") String no);
|
||||
|
||||
/**
|
||||
* 查询未分配的出库单:已创建或者部分分配
|
||||
*
|
||||
* @return List<Pick>
|
||||
*/
|
||||
@Select("select id from data_pick where status = 1 " +
|
||||
"union all " +
|
||||
"select id from data_pick where status = 2 ")
|
||||
List<Long> queryUnallocatedPick();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package org.cpte.modules.shipping.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.cpte.modules.shipping.entity.Pick;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 任务
|
||||
* @author: cpte
|
||||
* @Date: 2025-11-17
|
||||
* @Version: V1.0
|
||||
*/
|
||||
public interface TaskMapper extends BaseMapper<Task> {
|
||||
/**
|
||||
* 查询未分配的AGV的Task任务
|
||||
*
|
||||
* @return List<Pick>
|
||||
*/
|
||||
@Select("select * from data_task where agv_task_id is null order by create_time")
|
||||
List<Task> queryUnallocatedTask();
|
||||
}
|
||||
|
|
@ -15,4 +15,14 @@
|
|||
WHERE
|
||||
pick_id = #{mainId}
|
||||
</select>
|
||||
|
||||
<select id="queryByPickIds" resultType="org.cpte.modules.shipping.entity.PickDetail">
|
||||
SELECT *
|
||||
FROM data_pick_detail
|
||||
WHERE
|
||||
pick_id IN
|
||||
<foreach item="pickId" index="index" collection="pickIds" open="(" separator="," close=")">
|
||||
#{pickId}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.cpte.modules.shipping.mapper.TaskMapper">
|
||||
|
||||
</mapper>
|
||||
|
|
@ -47,17 +47,17 @@ public interface IPickService extends IService<Pick> {
|
|||
public void delBatchMain(Collection<? extends Serializable> idList);
|
||||
|
||||
/**
|
||||
* 分配出库单明细
|
||||
* 分配出库单
|
||||
*
|
||||
* @param pickDetails 出库单明细集合
|
||||
* @param pickIds 出库单集合
|
||||
*/
|
||||
List<String> allocatePickDetail(List<PickDetail> pickDetails);
|
||||
List<String> allocatePick(List<Long> pickIds);
|
||||
|
||||
/**
|
||||
* 分配出库单明细
|
||||
* 分配出库单
|
||||
*
|
||||
* @param pickDetails 出库单明细集合
|
||||
* @param pickIds 出库单集合
|
||||
*/
|
||||
List<String> allocatePickDetail2(List<PickDetail> pickDetails);
|
||||
List<String> allocatePick2(List<Long> pickIds);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package org.cpte.modules.shipping.service;
|
||||
|
||||
import org.cpte.modules.base.entity.Item;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 任务
|
||||
* @author: cpte
|
||||
* @Date: 2025-11-17
|
||||
* @Version: V1.0
|
||||
*/
|
||||
public interface ITaskService extends IService<Task> {
|
||||
/**
|
||||
* 创建任务
|
||||
*
|
||||
* @param taskNo 任务编号
|
||||
* @param taskType 任务类型
|
||||
* @param item 物料
|
||||
* @param fromPoint 原点位
|
||||
* @param toPoint 目标点位
|
||||
* @param stock 容器
|
||||
* @param pickId 出库单ID
|
||||
* @param pickDetailId 出库单明细ID
|
||||
* @param inventoryId 库存ID
|
||||
* @param planQty 计划数量
|
||||
* @param izAll 是否整托
|
||||
* @return Task
|
||||
*/
|
||||
Task createTask(String taskNo, Integer taskType, Item item, Point fromPoint, Point toPoint, Stock stock, Long pickId, Long pickDetailId, Long inventoryId, BigDecimal planQty, Integer izAll);
|
||||
|
||||
/**
|
||||
* 构建任务
|
||||
*
|
||||
* @param taskNo 任务编号
|
||||
* @param taskType 任务类型
|
||||
* @param item 物料
|
||||
* @param fromPoint 原点位
|
||||
* @param toPoint 目标点位
|
||||
* @param stock 容器
|
||||
* @param pickId 出库单ID
|
||||
* @param pickDetailId 出库单明细ID
|
||||
* @param inventoryId 库存ID
|
||||
* @param planQty 计划数量
|
||||
* @param izAll 是否整托
|
||||
* @return Task
|
||||
*/
|
||||
Task bulidTask(String taskNo, Integer taskType, Item item, Point fromPoint, Point toPoint, Stock stock, Long pickId, Long pickDetailId, Long inventoryId, BigDecimal planQty, Integer izAll);
|
||||
|
||||
/**
|
||||
* 根据Task集合生成AGV出库任务
|
||||
*/
|
||||
void generateAgvTask(List<Task> taskList);
|
||||
|
||||
}
|
||||
|
|
@ -2,28 +2,31 @@ package org.cpte.modules.shipping.service.impl;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
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;
|
||||
import org.cpte.modules.base.entity.Stock;
|
||||
import org.cpte.modules.base.service.IItemService;
|
||||
import org.cpte.modules.base.service.IPointService;
|
||||
import org.cpte.modules.base.service.IStockService;
|
||||
import org.cpte.modules.constant.enums.AsnStatusEnum;
|
||||
import org.cpte.modules.constant.enums.InventoryStatusEnum;
|
||||
import org.cpte.modules.constant.enums.PickStatusEnum;
|
||||
import org.cpte.modules.constant.enums.*;
|
||||
import org.cpte.modules.inventory.entity.Inventory;
|
||||
import org.cpte.modules.inventory.mapper.InventoryMapper;
|
||||
import org.cpte.modules.inventory.service.IInventoryService;
|
||||
import org.cpte.modules.inventoryLog.service.IInventoryLogService;
|
||||
import org.cpte.modules.shipping.entity.Pick;
|
||||
import org.cpte.modules.shipping.entity.PickDetail;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import org.cpte.modules.shipping.mapper.PickDetailMapper;
|
||||
import org.cpte.modules.shipping.mapper.PickMapper;
|
||||
import org.cpte.modules.shipping.service.IPickService;
|
||||
import org.cpte.modules.shipping.service.ITaskService;
|
||||
import org.cpte.modules.shipping.vo.InventoryGroupKey;
|
||||
import org.cpte.modules.utils.BatchUtils;
|
||||
import org.cpte.modules.utils.BigDecimalUtil;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.modules.base.service.BaseCommonService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
|
@ -59,9 +62,13 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
@Autowired
|
||||
private IPointService iPointService;
|
||||
@Autowired
|
||||
private ITaskService iTaskService;
|
||||
@Autowired
|
||||
private IInventoryLogService iInventoryLogService;
|
||||
@Autowired
|
||||
private BaseCommonService baseCommonService;
|
||||
@Autowired
|
||||
private BatchUtils batchUtils;
|
||||
|
||||
/**
|
||||
* 获取出库单Map
|
||||
|
|
@ -82,6 +89,9 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMain(Pick pick, List<PickDetail> pickDetailList) {
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
pick.setTenantId(Long.parseLong(sysUser.getRelTenantIds()));
|
||||
pick.setSysOrgCode(sysUser.getOrgCode());
|
||||
pickMapper.insert(pick);
|
||||
|
||||
if (pickDetailList == null || pickDetailList.isEmpty()) {
|
||||
|
|
@ -97,6 +107,8 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
entity.setLineNo(lineNoCounter.getAndIncrement());
|
||||
}
|
||||
entity.setPickId(pick.getId());
|
||||
entity.setTenantId(Long.parseLong(sysUser.getRelTenantIds()));
|
||||
entity.setSysOrgCode(sysUser.getOrgCode());
|
||||
pickDetailMapper.insert(entity);
|
||||
}
|
||||
//刷新出库单
|
||||
|
|
@ -231,13 +243,15 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<String> allocatePickDetail(List<PickDetail> pickDetails) {
|
||||
public List<String> allocatePick(List<Long> pickIds) {
|
||||
List<String> errorMsgList = new ArrayList<>();
|
||||
|
||||
//查询出库单
|
||||
List<Long> pickIds = pickDetails.stream().map(PickDetail::getPickId).distinct().toList();
|
||||
Map<Long, Pick> pickMap = queryByPickIdsToMap(pickIds);
|
||||
|
||||
//查询出库单明细
|
||||
List<PickDetail> pickDetails = pickDetailMapper.queryByPickIds(pickIds);
|
||||
|
||||
//获取不为null、空字符的批次号
|
||||
List<String> propC1List = pickDetails.stream().map(PickDetail::getPropC1).filter(Objects::nonNull).filter(e -> !e.isEmpty()).distinct().toList();
|
||||
|
||||
|
|
@ -378,15 +392,17 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<String> allocatePickDetail2(List<PickDetail> pickDetails) {
|
||||
public List<String> allocatePick2(List<Long> pickIds) {
|
||||
// 错误信息去重(LinkedHashSet保证顺序和唯一)
|
||||
Set<String> errorMsgSet = new LinkedHashSet<>();
|
||||
|
||||
// -------------------------- 1. 查询关联数据(批量查询,减少DB交互)--------------------------
|
||||
//获取出库单
|
||||
List<Long> pickIds = pickDetails.stream().map(PickDetail::getPickId).distinct().toList();
|
||||
//查询出库单
|
||||
Map<Long, Pick> pickMap = queryByPickIdsToMap(pickIds);
|
||||
|
||||
//查询出库单明细
|
||||
List<PickDetail> pickDetails = pickDetailMapper.queryByPickIds(pickIds);
|
||||
|
||||
//获取物料
|
||||
List<Long> itemIds = pickDetails.stream().map(PickDetail::getItemId).distinct().toList();
|
||||
Map<Long, Item> itemMap = iItemService.queryByItemIdsToMap(itemIds);
|
||||
|
|
@ -426,6 +442,7 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
// -------------------------- 3. 创建更新列表---------------------------
|
||||
List<Inventory> updateToInventory = new ArrayList<>();
|
||||
List<PickDetail> updateToPickDetail = new ArrayList<>();
|
||||
List<Task> createToTask = new ArrayList<>();
|
||||
|
||||
// -------------------------- 4. 循环分配库存 -------------------
|
||||
for (PickDetail pickDetail : pickDetails) {
|
||||
|
|
@ -451,6 +468,11 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
item.getItemCode(), item.getId(), pickDetail.getPropC1(), pickDetail.getPropC3(), pick.getWhCode()));
|
||||
continue;
|
||||
}
|
||||
|
||||
//获取输送线工作台点位,均衡分配点位任务-轮询方式
|
||||
Point toPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CK_DOCK.getValue(), GeneralConstant.CK_DOCK_TASK_INDEX);
|
||||
//记录当前容器
|
||||
Long currStockId = 0L;
|
||||
for (Inventory inventory : matchedInventories) {
|
||||
if (unAllocatedQty.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
break;
|
||||
|
|
@ -475,13 +497,23 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
//容器
|
||||
Stock stock = stockMap.get(inventory.getStockId());
|
||||
//库位
|
||||
Point point = pointMap.get(inventory.getPointId());
|
||||
String tuoType = inventory.getQuantity().compareTo(allocateQty) == 0 ? "整托" : "拆托";
|
||||
log.info("生成拣货任务: 容器:{} - 库位:{} - 类型:{}- 分配数量:{}", stock.getStockCode(), point.getPointCode(), tuoType, allocateQty);
|
||||
Point fromPoint = pointMap.get(inventory.getPointId());
|
||||
//是否整托:0整托、1拆托
|
||||
Integer izAll = inventory.getQuantity().compareTo(allocateQty) == 0 ? 0 : 1;
|
||||
//判断当前容器是否一致,同一个容器去同一个目标库位
|
||||
if (!currStockId.equals(stock.getId())) {
|
||||
toPoint = iPointService.getWorkStationPoint(CommonStatusEnum.FREE.getValue(), AreaTypeEnum.CK_DOCK.getValue(), GeneralConstant.CK_DOCK_TASK_INDEX);
|
||||
}
|
||||
//构建拣货任务存入集合批量新增
|
||||
Task task = iTaskService.bulidTask(pick.getNo() + "_" + pickDetail.getLineNo(), TaskTypeEnum.PICK.getValue(), item, fromPoint, toPoint, stock, pick.getId(), pickDetail.getId(), inventory.getId(), allocateQty, izAll);
|
||||
createToTask.add(task);
|
||||
log.info("生成拣货任务:{}- 容器:{} - 库位:{} - 类型:{}- 分配数量:{}", task.getTaskNo(), stock.getStockCode(), fromPoint.getPointCode(), izAll, allocateQty);
|
||||
//分配库存日志
|
||||
iInventoryLogService.addAllocInventoryLog(inventory, allocateQty, pick.getOrderNo(), pickDetail.getId(), pickDetail.getDescription());
|
||||
//更新未分配数量
|
||||
unAllocatedQty = BigDecimalUtil.subtract(unAllocatedQty, allocateQty, 0);
|
||||
//更新当前容器
|
||||
currStockId = stock.getId();
|
||||
}
|
||||
//分配后仍有缺货,记录错误
|
||||
if (unAllocatedQty.compareTo(BigDecimal.ZERO) > 0) {
|
||||
|
|
@ -494,10 +526,13 @@ public class PickServiceImpl extends ServiceImpl<PickMapper, Pick> implements IP
|
|||
|
||||
// -------------------------- 5. 批量更新(减少DB交互)--------------------------
|
||||
if (CollectionUtils.isNotEmpty(updateToInventory)) {
|
||||
inventoryMapper.updateById(updateToInventory);
|
||||
batchUtils.updateBatchInventory(updateToInventory);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(updateToPickDetail)) {
|
||||
pickDetailMapper.updateById(updateToPickDetail);
|
||||
batchUtils.updateBatchPickDetail(updateToPickDetail);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(createToTask)) {
|
||||
batchUtils.saveBatchTask(createToTask);
|
||||
}
|
||||
|
||||
// -------------------------- 6. 刷新出库单状态 --------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
package org.cpte.modules.shipping.service.impl;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.cpte.modules.agvTask.entity.AgvTask;
|
||||
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.shipping.entity.Task;
|
||||
import org.cpte.modules.shipping.mapper.TaskMapper;
|
||||
import org.cpte.modules.shipping.service.ITaskService;
|
||||
import org.cpte.modules.shipping.vo.TaskGroupKey;
|
||||
import org.cpte.modules.utils.BatchUtils;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Description: 任务
|
||||
* @author: cpte
|
||||
* @Date: 2025-11-17
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Service
|
||||
public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements ITaskService {
|
||||
|
||||
@Autowired
|
||||
private IAgvTaskService agvTaskService;
|
||||
|
||||
@Autowired
|
||||
private BatchUtils batchUtils;
|
||||
|
||||
@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) {
|
||||
LoginUser sysUser = null;
|
||||
try {
|
||||
sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
} catch (Exception e) {
|
||||
log.error("获取登录用户信息失败");
|
||||
}
|
||||
Task task = Task.builder()
|
||||
.taskNo(taskNo)
|
||||
.itemId(item.getId())
|
||||
.itemCode(item.getItemCode())
|
||||
.fromPointId(fromPoint.getId())
|
||||
.fromPointCode(fromPoint.getPointCode())
|
||||
.toPointId(toPoint.getId())
|
||||
.toPointCode(toPoint.getPointCode())
|
||||
.stockId(stock.getId())
|
||||
.stockCode(stock.getStockCode())
|
||||
.pickId(pickId)
|
||||
.pickDetailId(pickDetailId)
|
||||
.inventoryId(inventoryId)
|
||||
.planQty(planQty)
|
||||
.moveQty(BigDecimal.ZERO)
|
||||
.taskStatus(1)
|
||||
.taskType(taskType)
|
||||
.izAll(izAll)
|
||||
.sysOrgCode(sysUser == null ? "A05" : sysUser.getOrgCode())
|
||||
.tenantId(sysUser == null ? 1000L : Long.parseLong(sysUser.getRelTenantIds()))
|
||||
.createBy(sysUser == null ? "system" : sysUser.getUsername())
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
this.save(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task bulidTask(String taskNo, Integer taskType, Item item, Point fromPoint, Point toPoint, Stock stock, Long pickId, Long pickDetailId, Long inventoryId, BigDecimal planQty, Integer izAll) {
|
||||
LoginUser sysUser = null;
|
||||
try {
|
||||
sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
} catch (Exception e) {
|
||||
log.error("获取登录用户信息失败");
|
||||
}
|
||||
return Task.builder()
|
||||
.taskNo(taskNo)
|
||||
.itemId(item.getId())
|
||||
.itemCode(item.getItemCode())
|
||||
.fromPointId(fromPoint.getId())
|
||||
.fromPointCode(fromPoint.getPointCode())
|
||||
.toPointId(toPoint.getId())
|
||||
.toPointCode(toPoint.getPointCode())
|
||||
.stockId(stock.getId())
|
||||
.stockCode(stock.getStockCode())
|
||||
.pickId(pickId)
|
||||
.pickDetailId(pickDetailId)
|
||||
.inventoryId(inventoryId)
|
||||
.planQty(planQty)
|
||||
.moveQty(BigDecimal.ZERO)
|
||||
.taskStatus(1)
|
||||
.taskType(taskType)
|
||||
.izAll(izAll)
|
||||
.sysOrgCode(sysUser == null ? "A05" : sysUser.getOrgCode())
|
||||
.tenantId(sysUser == null ? 1000L : Long.parseLong(sysUser.getRelTenantIds()))
|
||||
.createBy(sysUser == null ? "system" : sysUser.getUsername())
|
||||
.createTime(new Date())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void generateAgvTask(List<Task> taskList) {
|
||||
if (CollectionUtils.isEmpty(taskList)) {
|
||||
return;
|
||||
}
|
||||
//根据stockCode、fromPointCode、toPointCode 分组
|
||||
Map<TaskGroupKey, List<Task>> taskGroupMap = taskList.stream()
|
||||
.sorted(Comparator.comparing(Task::getCreateTime))
|
||||
.collect(Collectors.groupingBy(task -> TaskGroupKey.of(
|
||||
task.getStockCode(),
|
||||
task.getFromPointCode(),
|
||||
task.getToPointCode()
|
||||
)));
|
||||
|
||||
//批量创建AGV任务
|
||||
List<AgvTask> createToAgvTaskList = new ArrayList<>();
|
||||
Map<TaskGroupKey, AgvTask> groupToAgvTaskMap = new HashMap<>();
|
||||
for (Map.Entry<TaskGroupKey, List<Task>> entry : taskGroupMap.entrySet()) {
|
||||
TaskGroupKey key = entry.getKey();
|
||||
if(!groupToAgvTaskMap.containsKey(key)){
|
||||
AgvTask agvTask = agvTaskService.bulidAgvTask(null, key.getStockCode(), key.getFromPointCode(), key.getToPointCode(), null, BusinessTypeEnum.OUTBOUND.getValue(), AgvVendorEnum.TES.getValue());
|
||||
createToAgvTaskList.add(agvTask);
|
||||
groupToAgvTaskMap.put(key, agvTask); // 建立映射
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(createToAgvTaskList)) {
|
||||
batchUtils.saveBatchAgvTask(createToAgvTaskList);
|
||||
}
|
||||
|
||||
//批量更新任务
|
||||
List<Task> updateToTaskList = new ArrayList<>();
|
||||
for (Map.Entry<TaskGroupKey, List<Task>> entry : taskGroupMap.entrySet()) {
|
||||
TaskGroupKey key = entry.getKey();
|
||||
AgvTask agvTask = groupToAgvTaskMap.get(key); // 通过key获取
|
||||
List<Task> list = entry.getValue();
|
||||
for (Task task : list) {
|
||||
task.setAgvTaskId(agvTask.getId());
|
||||
updateToTaskList.add(task);
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(updateToTaskList)) {
|
||||
batchUtils.updateBatchTask(updateToTaskList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package org.cpte.modules.shipping.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
public class TaskGroupKey {
|
||||
private String stockCode; // 容器
|
||||
private String fromPointCode; // 原点位
|
||||
private String toPointCode; // 目标点位
|
||||
|
||||
public static TaskGroupKey of(String stockCode, String fromPointCode, String toPointCode) {
|
||||
return new TaskGroupKey(
|
||||
StringUtils.defaultIfBlank(stockCode, ""), // 空值统一为"",避免分组不一致
|
||||
StringUtils.defaultIfBlank(fromPointCode, ""),
|
||||
StringUtils.defaultIfBlank(toPointCode, "")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,6 @@ public class TesAgvController {
|
|||
@AutoLog(value = "TesAGV-任务上报")
|
||||
@Operation(summary = "TesAGV-任务上报")
|
||||
@PostMapping(value = "/callBackTask")
|
||||
@IgnoreAuth
|
||||
public TesResult callBackTask(@RequestBody TesCallbackRequest tesCallbackRequest) {
|
||||
try {
|
||||
iTesAgvService.callBackTask(tesCallbackRequest);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.cpte.modules.agvTask.entity.AgvTask;
|
||||
import org.cpte.modules.agvTask.mapper.AgvTaskMapper;
|
||||
import org.cpte.modules.agvTask.service.IAgvTaskService;
|
||||
import org.cpte.modules.constant.GeneralConstant;
|
||||
import org.cpte.modules.constant.enums.AgvStatusEnum;
|
||||
import org.cpte.modules.constant.enums.AgvVendorEnum;
|
||||
import org.cpte.modules.receive.service.IAsnService;
|
||||
|
|
@ -24,21 +25,6 @@ import java.util.*;
|
|||
@Slf4j
|
||||
public class ITesAgvServiceImpl implements ITesAgvService {
|
||||
|
||||
/**
|
||||
* 接口开关
|
||||
*/
|
||||
final String open_flag = "OPEN";
|
||||
|
||||
/**
|
||||
* 成功码
|
||||
*/
|
||||
final Integer success_code = 0;
|
||||
|
||||
/**
|
||||
* 失败码
|
||||
*/
|
||||
final Integer fail_code = 500;
|
||||
|
||||
@Autowired
|
||||
private SysDictMapper sysDictMapper;
|
||||
|
||||
|
|
@ -83,8 +69,8 @@ public class ITesAgvServiceImpl implements ITesAgvService {
|
|||
public void sendTesAgvTask(String url, String json, AgvTask agvTask) {
|
||||
log.info("请求报文:{}", json);
|
||||
// 检查接口开关, 未开启则返回
|
||||
if (sysDictMapper.queryByDictCode(open_flag) == null) {
|
||||
updateAgvTaskResponse(agvTask, "接口未开启", fail_code);
|
||||
if (sysDictMapper.queryByDictCode(GeneralConstant.OPEN_FLAG) == null) {
|
||||
updateAgvTaskResponse(agvTask, "接口未开启", GeneralConstant.TES_FAIL_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +80,7 @@ public class ITesAgvServiceImpl implements ITesAgvService {
|
|||
//String result = HttpPostUtil.sendPostReq(url, json);
|
||||
String result = "{" +
|
||||
" \"returnCode\":0," +
|
||||
" \"returnMsg\":\"succ\"," +
|
||||
" \"returnMsg\":\"成功\"," +
|
||||
" \"returnUserMsg\":\"成功\"," +
|
||||
" \"data\":{" +
|
||||
" \"taskID\":74602" +
|
||||
|
|
@ -112,9 +98,9 @@ public class ITesAgvServiceImpl implements ITesAgvService {
|
|||
}
|
||||
|
||||
returnCode = resulObject.getInteger("returnCode");
|
||||
returnMsg = resulObject.getString("returnUserMsg");
|
||||
returnMsg = resulObject.getString("returnMsg");
|
||||
|
||||
if (!success_code.equals(returnCode)) {
|
||||
if (!GeneralConstant.TES_SUCCESS_CODE.equals(returnCode)) {
|
||||
throw new RuntimeException("Tes返回消息:" + returnMsg);
|
||||
}
|
||||
|
||||
|
|
@ -122,8 +108,7 @@ public class ITesAgvServiceImpl implements ITesAgvService {
|
|||
updateAgvTaskResponse(agvTask, returnMsg, returnCode);
|
||||
} catch (Exception e) {
|
||||
// 记录异常到 AgvTask
|
||||
updateAgvTaskResponse(agvTask, e.getMessage(), fail_code);
|
||||
throw e; // 继续向上抛出异常供 Controller 层处理
|
||||
updateAgvTaskResponse(agvTask, e.getMessage(), GeneralConstant.TES_FAIL_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +171,7 @@ public class ITesAgvServiceImpl implements ITesAgvService {
|
|||
if (count > 0) {
|
||||
throw new RuntimeException("任务已重新生成,请勿重复操作! ");
|
||||
}
|
||||
AgvTask newAgvTask = iAgvTaskService.createAgvTask(agvTask.getBusinessDetailId(), AgvStatusEnum.CREATED.getValue(), agvTask.getCarrierCode(), agvTask.getStartCode(), agvTask.getEndCode(), null, agvTask.getType(), AgvVendorEnum.TES.getValue());
|
||||
AgvTask newAgvTask = iAgvTaskService.createAgvTask(agvTask.getBusinessDetailId(),agvTask.getCarrierCode(), agvTask.getStartCode(), agvTask.getEndCode(), null, agvTask.getType(), AgvVendorEnum.TES.getValue());
|
||||
switch (agvTask.getType()) {
|
||||
case "INBOUND":
|
||||
case "OUTBOUND":
|
||||
|
|
@ -208,7 +193,7 @@ public class ITesAgvServiceImpl implements ITesAgvService {
|
|||
*/
|
||||
private void updateAgvTaskResponse(AgvTask agvTask, String message, Integer code) {
|
||||
if (agvTask != null) {
|
||||
if (success_code.equals(code)) {
|
||||
if (GeneralConstant.TES_SUCCESS_CODE.equals(code)) {
|
||||
agvTask.setStatus(AgvStatusEnum.EXECUTING.getValue());
|
||||
}
|
||||
agvTask.setResMessage(message);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,294 @@
|
|||
package org.cpte.modules.utils;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import org.cpte.modules.agvTask.entity.AgvTask;
|
||||
import org.cpte.modules.base.entity.Item;
|
||||
import org.cpte.modules.base.entity.Point;
|
||||
import org.cpte.modules.inventory.entity.Inventory;
|
||||
import org.cpte.modules.shipping.entity.PickDetail;
|
||||
import org.cpte.modules.shipping.entity.Task;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@Service
|
||||
public class BatchUtils {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
// 默认批次大小
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
|
||||
/**
|
||||
* 泛型批量插入方法
|
||||
*/
|
||||
private <T> void batchInsert(String sql, List<T> dataList, BiConsumer<PreparedStatement, T> setter) {
|
||||
batchExecute(sql, dataList, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 泛型批量更新方法
|
||||
*/
|
||||
private <T> void batchUpdate(String sql, List<T> dataList, BiConsumer<PreparedStatement, T> setter) {
|
||||
batchExecute(sql, dataList, setter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分批执行SQL语句(支持插入与更新)
|
||||
*
|
||||
* @param sql SQL语句
|
||||
* @param dataList 数据列表
|
||||
* @param setter 设置每个对象对应PreparedStatement值的方法
|
||||
* @param <T> 对象类型
|
||||
*/
|
||||
private <T> void batchExecute(String sql, List<T> dataList, BiConsumer<PreparedStatement, T> setter) {
|
||||
for (int i = 0; i < dataList.size(); i += BATCH_SIZE) {
|
||||
int endIndex = Math.min(i + BATCH_SIZE, dataList.size());
|
||||
List<T> subList = dataList.subList(i, endIndex);
|
||||
|
||||
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
|
||||
@Override
|
||||
public void setValues(PreparedStatement ps, int idx) throws SQLException {
|
||||
T data = subList.get(idx);
|
||||
setter.accept(ps, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return subList.size();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量插入物料数据
|
||||
*/
|
||||
@Transactional
|
||||
public void saveBatchItem(List<Item> items) {
|
||||
String sql = "INSERT INTO base_item (item_code,item_name,description,iz_active, del_flag,sys_org_code,tenant_id,create_by,create_time) VALUES (?,?,?,?,?,?,?,?,?)";
|
||||
batchInsert(sql, items, (ps, item) -> {
|
||||
try {
|
||||
ps.setString(1, item.getItemCode());
|
||||
ps.setString(2, item.getItemName());
|
||||
ps.setString(3, item.getDescription());
|
||||
ps.setInt(4, item.getIzActive());
|
||||
ps.setInt(5, item.getDelFlag());
|
||||
ps.setString(6, item.getSysOrgCode());
|
||||
ps.setLong(7, item.getTenantId());
|
||||
ps.setString(8, item.getCreateBy());
|
||||
ps.setDate(9, new Date(item.getCreateTime().getTime()));
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新物料
|
||||
*/
|
||||
@Transactional
|
||||
public void updateBatchItem(List<Item> items) {
|
||||
String sql = "UPDATE base_item SET item_code = ?, item_name = ?, description = ?, iz_active = ?, del_flag = ? WHERE id = ?";
|
||||
batchUpdate(sql, items, (ps, item) -> {
|
||||
try {
|
||||
ps.setString(1, item.getItemCode());
|
||||
ps.setString(2, item.getItemName());
|
||||
ps.setString(3, item.getDescription());
|
||||
ps.setInt(4, item.getIzActive());
|
||||
ps.setInt(5, item.getDelFlag());
|
||||
ps.setLong(6, item.getId());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入点位数据
|
||||
*/
|
||||
@Transactional
|
||||
public void saveBatchPoint(List<Point> points) {
|
||||
String sql = "INSERT INTO base_point (area_id, point_code,status,row_num,col_num,layer_num,description,iz_active,del_flag,sys_org_code,tenant_id,create_by,create_time) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||
batchInsert(sql, points, (ps, point) -> {
|
||||
try {
|
||||
ps.setLong(1, point.getAreaId());
|
||||
ps.setString(2, point.getPointCode());
|
||||
ps.setInt(3, point.getStatus());
|
||||
ps.setString(4, point.getRowNum());
|
||||
ps.setString(5, point.getColNum());
|
||||
ps.setString(6, point.getLayerNum());
|
||||
ps.setString(7, point.getDescription());
|
||||
ps.setInt(8, point.getIzActive());
|
||||
ps.setInt(9, point.getDelFlag());
|
||||
ps.setString(10, point.getSysOrgCode());
|
||||
ps.setLong(11, point.getTenantId());
|
||||
ps.setString(12, point.getCreateBy());
|
||||
ps.setDate(13, new Date(point.getCreateTime().getTime()));
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新点位
|
||||
*/
|
||||
@Transactional
|
||||
public void batchUpdatePoints(List<Point> points) {
|
||||
String sql = "UPDATE base_point SET area_id = ?, point_code = ?, status = ?, row_num = ?, col_num = ?, layer_num = ?, description = ?, iz_active = ?, del_flag = ? WHERE id = ?";
|
||||
batchUpdate(sql, points, (ps, point) -> {
|
||||
try {
|
||||
ps.setLong(1, point.getAreaId());
|
||||
ps.setString(2, point.getPointCode());
|
||||
ps.setInt(3, point.getStatus());
|
||||
ps.setString(4, point.getRowNum());
|
||||
ps.setString(5, point.getColNum());
|
||||
ps.setString(6, point.getLayerNum());
|
||||
ps.setString(7, point.getDescription());
|
||||
ps.setInt(8, point.getIzActive());
|
||||
ps.setInt(9, point.getDelFlag());
|
||||
ps.setLong(10, point.getId());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量更新出库明细
|
||||
*/
|
||||
@Transactional
|
||||
public void updateBatchPickDetail(List<PickDetail> pickDetails) {
|
||||
String sql = "UPDATE data_pick_detail SET allocated_qty = ?, status = ? where id = ?";
|
||||
batchUpdate(sql, pickDetails, (ps, pickDetail) -> {
|
||||
try {
|
||||
ps.setBigDecimal(1, pickDetail.getAllocatedQty());
|
||||
ps.setInt(2, pickDetail.getStatus());
|
||||
ps.setLong(3, pickDetail.getId());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插Task任务
|
||||
*/
|
||||
@Transactional
|
||||
public void saveBatchTask(List<Task> tasks) {
|
||||
String sql = "INSERT INTO data_task (id,task_no,item_id,item_code,from_point_id,from_point_code,to_point_id,to_point_code,stock_id,stock_code,pick_id,pick_detail_id,inventory_id,plan_qty,move_qty,task_type,task_status,iz_all,sys_org_code,tenant_id,create_by,create_time) " +
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||
batchInsert(sql, tasks, (ps, task) -> {
|
||||
try {
|
||||
ps.setLong(1, IdWorker.getId());
|
||||
ps.setString(2, task.getTaskNo());
|
||||
ps.setLong(3, task.getItemId());
|
||||
ps.setString(4, task.getItemCode());
|
||||
ps.setLong(5, task.getFromPointId());
|
||||
ps.setString(6, task.getFromPointCode());
|
||||
ps.setLong(7, task.getToPointId());
|
||||
ps.setString(8, task.getToPointCode());
|
||||
ps.setLong(9, task.getStockId());
|
||||
ps.setString(10, task.getStockCode());
|
||||
ps.setLong(11, task.getPickId());
|
||||
ps.setLong(12, task.getPickDetailId());
|
||||
ps.setLong(13, task.getInventoryId());
|
||||
ps.setBigDecimal(14, task.getPlanQty());
|
||||
ps.setBigDecimal(15, task.getMoveQty());
|
||||
ps.setInt(16, task.getTaskType());
|
||||
ps.setInt(17, task.getTaskStatus());
|
||||
ps.setInt(18, task.getIzAll());
|
||||
ps.setString(19, task.getSysOrgCode());
|
||||
ps.setLong(20, task.getTenantId());
|
||||
ps.setString(21, task.getCreateBy());
|
||||
ps.setTimestamp(22, new Timestamp(task.getCreateTime().getTime()));
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新Task任务
|
||||
*/
|
||||
@Transactional
|
||||
public void updateBatchTask(List<Task> tasks) {
|
||||
String sql = "UPDATE data_task SET agv_task_id = ? where id = ? ";
|
||||
batchUpdate(sql, tasks, (ps, task) -> {
|
||||
try {
|
||||
ps.setObject(1, task.getAgvTaskId());
|
||||
ps.setLong(2, task.getId());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插AgvTask任务
|
||||
*/
|
||||
@Transactional
|
||||
public void saveBatchAgvTask(List<AgvTask> agvTasks) {
|
||||
String sql = "INSERT INTO data_agv_task (id,business_detail_id,carrier_code,carrier_type,task_type,type,status,priority,start_code,end_code,agv_vendor,sys_org_code,tenant_id,create_by,create_time) " +
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||
batchInsert(sql, agvTasks, (ps, agvTask) -> {
|
||||
try {
|
||||
long id = IdWorker.getId();
|
||||
agvTask.setId(id); // 关键:将生成的ID赋值给对象,解决null问题
|
||||
ps.setLong(1, id);
|
||||
ps.setObject(2, agvTask.getBusinessDetailId());
|
||||
ps.setString(3, agvTask.getCarrierCode());
|
||||
ps.setString(4, agvTask.getCarrierType());
|
||||
ps.setString(5, agvTask.getTaskType());
|
||||
ps.setString(6, agvTask.getType());
|
||||
ps.setInt(7, agvTask.getStatus());
|
||||
ps.setInt(8, agvTask.getPriority());
|
||||
ps.setString(9, agvTask.getStartCode());
|
||||
ps.setString(10, agvTask.getEndCode());
|
||||
ps.setString(11, agvTask.getAgvVendor());
|
||||
ps.setString(12, agvTask.getSysOrgCode());
|
||||
ps.setLong(13, agvTask.getTenantId());
|
||||
ps.setString(14, agvTask.getCreateBy());
|
||||
ps.setTimestamp(15, new Timestamp(agvTask.getCreateTime().getTime()));
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新库存
|
||||
*/
|
||||
@Transactional
|
||||
public void updateBatchInventory(List<Inventory> inventories) {
|
||||
String sql = "UPDATE data_inventory SET queued_qty = ?, status = ? where id = ?";
|
||||
batchUpdate(sql, inventories, (ps, inventory) -> {
|
||||
try {
|
||||
ps.setBigDecimal(1, inventory.getQueuedQty());
|
||||
ps.setInt(2, inventory.getStatus());
|
||||
ps.setLong(3, inventory.getId());
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +200,6 @@ public class ApiAuthFilter implements Filter {
|
|||
public static void main(String[] args) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
System.out.println("timestamp:" + timestamp);
|
||||
System.out.println("signature:" + md5("ak-eAU25mrMxhtaZsyS" + "rjxMqB6YyUXpSHAz4DCIz8vZ5aozQQiV" + timestamp));
|
||||
System.out.println("signature:" + md5("ak-h98TNj1bO5258uuS" + "E20j9w4vkTkLkv6tjALrzYqcybrvDaCx" + timestamp));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,18 @@ package org.jeecg.modules.openapi.mapper;
|
|||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.jeecg.modules.openapi.entity.OpenApi;
|
||||
|
||||
@Mapper
|
||||
public interface OpenApiMapper extends BaseMapper<OpenApi> {
|
||||
/**
|
||||
* 通过接口地址获取原始地址
|
||||
*
|
||||
* @param requestUrl 接口地址
|
||||
* @return String
|
||||
*/
|
||||
@Select("select origin_url from open_api where request_url = #{requestUrl}")
|
||||
String getRequestUrl(@Param("requestUrl") String requestUrl);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ spring:
|
|||
merge-sql: true
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://47.103.100.52:53306/cpte-wms?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
url: jdbc:mysql://47.103.100.52:53306/cpte-wms?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: Youchain@56
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ spring:
|
|||
slow-sql-millis: 5000
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://10.180.9.60:3306/cpte-wms?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
|
||||
url: jdbc:mysql://10.180.9.60:3306/cpte-wms?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: cpte@123
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import org.springframework.http.HttpMethod;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* @Description: TODO
|
||||
* @author: scott
|
||||
|
|
@ -14,7 +17,11 @@ import org.springframework.http.ResponseEntity;
|
|||
*/
|
||||
public class TestMain {
|
||||
public static void main(String[] args) {
|
||||
// 请求地址
|
||||
long timestamp = System.currentTimeMillis();
|
||||
System.out.println("timestamp:" + timestamp);
|
||||
System.out.println("signature:" + md5("ak-h98TNj1bO5258uuS" + "E20j9w4vkTkLkv6tjALrzYqcybrvDaCx" + timestamp));
|
||||
|
||||
/* // 请求地址
|
||||
String url = "https://api3.boot.jeecg.com/sys/user/list";
|
||||
// 请求 Header (用于传递Token)
|
||||
HttpHeaders headers = getHeaders();
|
||||
|
|
@ -30,7 +37,32 @@ public class TestMain {
|
|||
System.out.println("返回结果:" + result.getBody().toJSONString());
|
||||
} else {
|
||||
System.out.println("查询失败");
|
||||
}*/
|
||||
}
|
||||
|
||||
public static String md5(String sourceStr) {
|
||||
String result = "";
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
md.update(sourceStr.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] hash = md.digest();
|
||||
int i;
|
||||
StringBuffer buf = new StringBuffer(32);
|
||||
for (int offset = 0; offset < hash.length; offset++) {
|
||||
i = hash[offset];
|
||||
if (i < 0) {
|
||||
i += 256;
|
||||
}
|
||||
if (i < 16) {
|
||||
buf.append("0");
|
||||
}
|
||||
buf.append(Integer.toHexString(i));
|
||||
}
|
||||
result = buf.toString();
|
||||
} catch (Exception e) {
|
||||
System.out.println("sign签名错误" + e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private static HttpHeaders getHeaders() {
|
||||
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.50h-g6INOZRVnznExiawFb1U6PPjcVVA4POeYRA5a5Q";
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import java.security.MessageDigest;
|
|||
|
||||
|
||||
public class SampleOpenApiTest {
|
||||
private final String base_url = "http://localhost:8080/jeecg-boot";
|
||||
private final String base_url = "http://localhost:8080/cpte-wms";
|
||||
private final String appKey = "ak-pFjyNHWRsJEFWlu6";
|
||||
private final String searchKey = "4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS";
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue