| | |
| | | } |
| | | })) |
| | | setData(val) |
| | | setTimeout(() => { |
| | | val.forEach((el) => { |
| | | jsbarcode(`#barcode${el.code}`, el.code, { height: 30 }); |
| | | }); |
| | | }, 10); |
| | | |
| | | |
| | | |
| | | } else { |
| | | notify(res.data.msg); |
| | | } |
| | |
| | | if (rows?.length > 0) { |
| | | http(); |
| | | } |
| | | |
| | | }, [rows]); |
| | | |
| | | // 在数据更新后生成条码 |
| | | useEffect(() => { |
| | | if (data.length > 0) { |
| | | // 等待DOM更新后再生成条码 |
| | | setTimeout(() => { |
| | | data.forEach((item) => { |
| | | const barcodeElement = document.getElementById(`barcode${item.code}`); |
| | | if (barcodeElement) { |
| | | try { |
| | | jsbarcode(`#barcode${item.code}`, item.code, { height: 30 }); |
| | | } catch (error) { |
| | | console.error(`生成条码失败: ${item.code}`, error); |
| | | } |
| | | } |
| | | }); |
| | | }, 100); |
| | | } |
| | | }, [data]); |
| | | |
| | | |
| | | return ( |
| | |
| | | colSpan={9} |
| | | style={{ border: '1px solid black' }} |
| | | > |
| | | {/* <img id={"barcode" + item.code} style={{ width: '70%', verticalAlign: 'middle' }} /> */} |
| | | <img className="template-code" src={item.barcode} style={{ width: '90%', verticalAlign: 'middle' }} alt="Barcode" /> |
| | | <img id={`barcode${item.code}`} style={{ width: '90%', verticalAlign: 'middle' }} alt="Barcode" /> |
| | | <div style={{ letterSpacing: '2px', marginTop: '1px', textAlign: 'center' }}> |
| | | <span>{item.code}</span> |
| | | </div> |
| | |
| | | } |
| | | } |
| | | return ( |
| | | (record.taskStatus == 1 || record.taskStatus == 101) && (record.taskType == 1 || record.taskType == 101 || record.taskType == 10 || record.taskType == 107 || record.taskType == 103 || record.taskType == 11) ? |
| | | (record.taskStatus == 1 || record.taskStatus == 101 || record.taskStatus == 199) && (record.taskType == 1 || record.taskType == 101 || record.taskType == 10 || record.taskType == 107 || record.taskType == 103 || record.taskType == 11) ? |
| | | <ConfirmButton |
| | | onConfirm={clickCancel} |
| | | startIcon={<CancelIcon />} |
| | |
| | | empty={<EmptyData onClick={() => { setCreateDialog(true) }} />} |
| | | filters={filters} |
| | | sort={{ field: "warehouseId", order: "desc" }} |
| | | // disableSyncWithLocation={false} |
| | | actions={( |
| | | <TopToolbar> |
| | | <FilterButton /> |
| | |
| | | import com.vincent.rsf.openApi.entity.params.LocSiteParams; |
| | | import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams; |
| | | import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam; |
| | | import com.vincent.rsf.openApi.entity.params.TaskReportParams; |
| | | import com.vincent.rsf.openApi.service.WmsRcsService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | @Slf4j |
| | | @RestController |
| | | @Api("RCS调度交互接口") |
| | | @RequestMapping("/rcs") |
| | |
| | | return wmsRcsService.modifyLocOrSite(params); |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @date 2026/2/3 |
| | | * @description: RCS回调接口 |
| | | * @version 1.0 |
| | | */ |
| | | @ApiOperation("RCS回调接口") |
| | | @PostMapping("/api/open/task/report") |
| | | public CommonResponse reportTask(@RequestBody TaskReportParams params) { |
| | | log.debug("RCS回调:{}", params); |
| | | if (Objects.isNull(params)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | return wmsRcsService.reportTask(params); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | public static String modifystatus = "/api/open/modify/status"; |
| | | |
| | | public static String cancelTask = "/api/open/task/cancel"; |
| | | |
| | | } |
| | |
| | | public class RcsPubTaskParams implements Serializable { |
| | | |
| | | @ApiModelProperty("批次") |
| | | private String batch; |
| | | private String batchNo; |
| | | |
| | | @ApiModelProperty("任务明细") |
| | | private List<TaskParam> taskList; |
| | | private List<TaskParam> tasks; |
| | | } |
| | | |
| | | @Data |
| | |
| | | @ApiModel(value = "TaskItem", description = "任务列表") |
| | | class TaskParam { |
| | | |
| | | @ApiModelProperty("任务类型{LOC_TO_LOC: 移库, LOC_TO_STA: 出库, STA_TO_LOC: 入库, STA_TO_STA: 站点间搬运}") |
| | | private String taskType; |
| | | |
| | | @ApiModelProperty("任务号") |
| | | private String seqNum; |
| | | private String taskNo; |
| | | |
| | | @ApiModelProperty("起始库位") |
| | | private String oriLoc; |
| New file |
| | |
| | | package com.vincent.rsf.openApi.entity.params; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @date 2026/2/3 |
| | | * @description: RCS回调 |
| | | * @version 1.0 |
| | | */ |
| | | @Data |
| | | @Accessors(chain = true) |
| | | @ApiModel(value = "TaskReportParams", description = "RCS回调") |
| | | public class TaskReportParams implements Serializable { |
| | | |
| | | @ApiModelProperty(value = "任务批次", required = true) |
| | | private String batchNo; |
| | | |
| | | @ApiModelProperty(value = "任务编号", required = true) |
| | | private String taskNo; |
| | | |
| | | @ApiModelProperty(value = "时间戳", required = true) |
| | | private String timestamp; |
| | | } |
| | |
| | | import com.vincent.rsf.openApi.entity.params.LocSiteParams; |
| | | import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams; |
| | | import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam; |
| | | import com.vincent.rsf.openApi.entity.params.TaskReportParams; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | List<SyncLocsDto> syncLocs(SyncRcsLocsParam params); |
| | | |
| | | R modifyLocOrSite(LocSiteParams params); |
| | | |
| | | CommonResponse reportTask(TaskReportParams params); |
| | | } |
| | |
| | | import com.vincent.rsf.openApi.entity.params.LocSiteParams; |
| | | import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams; |
| | | import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam; |
| | | import com.vincent.rsf.openApi.entity.params.TaskReportParams; |
| | | import com.vincent.rsf.openApi.mapper.LocMapper; |
| | | import com.vincent.rsf.openApi.service.WmsRcsService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | public CommonResponse pubTasks(RcsPubTaskParams params) { |
| | | /**RCS基础配置链接*/ |
| | | String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask; |
| | | log.info("任务下发,请求地址: {}, 请求参数: {}", rcsUrl , JSONObject.toJSONString(params)); |
| | | log.info("========== 开始下发任务到RCS =========="); |
| | | log.info("RCS请求地址:{}", rcsUrl); |
| | | if (params != null) { |
| | | log.info("批次编号:{}", params.getBatchNo()); |
| | | if (params.getTasks() != null) { |
| | | log.info("任务数量:{}", params.getTasks().size()); |
| | | } |
| | | } |
| | | log.info("请求参数:{}", JSONObject.toJSONString(params)); |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | | HttpEntity httpEntity = new HttpEntity(params, headers); |
| | | long startTime = System.currentTimeMillis(); |
| | | ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class); |
| | | log.info("任务下发后,响应结果: {}", exchange); |
| | | long endTime = System.currentTimeMillis(); |
| | | log.info("RCS响应耗时:{}ms", (endTime - startTime)); |
| | | log.info("RCS响应状态码:{}", exchange.getStatusCode()); |
| | | log.info("RCS响应头:{}", exchange.getHeaders()); |
| | | log.info("RCS响应体:{}", exchange.getBody()); |
| | | if (Objects.isNull(exchange.getBody())) { |
| | | throw new CoolException("任务下发失败!!"); |
| | | log.error("========== RCS任务下发失败 =========="); |
| | | log.error("RCS响应体为空,无法解析响应结果"); |
| | | log.error("请求地址:{}", rcsUrl); |
| | | log.error("请求参数:{}", JSONObject.toJSONString(params)); |
| | | throw new CoolException("任务下发失败,RCS响应体为空!!"); |
| | | } else { |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | objectMapper.coercionConfigDefaults() |
| | | .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty); |
| | | try { |
| | | CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class); |
| | | log.info("RCS响应解析结果 - code:{},msg:{},data:{}", |
| | | result.getCode(), result.getMsg(), result.getData()); |
| | | if (result.getCode() == 200) { |
| | | log.info("========== RCS任务下发成功 =========="); |
| | | return result; |
| | | } else { |
| | | log.error("========== RCS任务下发失败 =========="); |
| | | log.error("RCS返回错误 - code:{},msg:{},data:{}", |
| | | result.getCode(), result.getMsg(), result.getData()); |
| | | return result; |
| | | } |
| | | } catch (JsonProcessingException e) { |
| | | throw new CoolException(e.getMessage()); |
| | | log.error("========== RCS任务下发异常 =========="); |
| | | log.error("解析RCS响应失败,响应体:{}", exchange.getBody(), e); |
| | | throw new CoolException("解析RCS响应失败:" + e.getMessage()); |
| | | } catch (Exception e) { |
| | | log.error("========== RCS任务下发异常 =========="); |
| | | log.error("任务下发过程中发生异常", e); |
| | | throw e; |
| | | } |
| | | } |
| | | } |
| | |
| | | * @version 1.0 |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public CommonResponse cancelTasks(Map<String, Object> params) { |
| | | return new CommonResponse(); |
| | | /**RCS基础配置链接*/ |
| | | String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.cancelTask; |
| | | log.info("========== 开始取消RCS任务(open-rcs接口) =========="); |
| | | log.info("RCS取消任务请求地址:{}", rcsUrl); |
| | | log.info("RCS取消任务请求参数:{}", JSONObject.toJSONString(params)); |
| | | |
| | | // 打印详细的请求参数信息 |
| | | if (params != null) { |
| | | Object batchNo = params.get("batchNo"); |
| | | Object tasks = params.get("tasks"); |
| | | if (batchNo != null) { |
| | | log.info("批次编号:{}", batchNo); |
| | | } |
| | | if (tasks != null) { |
| | | if (tasks instanceof List) { |
| | | log.info("任务数量:{}", ((List<?>) tasks).size()); |
| | | log.info("任务编号列表:{}", tasks); |
| | | } else { |
| | | log.info("任务参数:{}", tasks); |
| | | } |
| | | } |
| | | } |
| | | |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | | HttpEntity httpEntity = new HttpEntity(params, headers); |
| | | |
| | | long startTime = System.currentTimeMillis(); |
| | | ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class); |
| | | long endTime = System.currentTimeMillis(); |
| | | |
| | | log.info("RCS取消任务响应耗时:{}ms", (endTime - startTime)); |
| | | log.info("RCS取消任务响应状态码:{}", exchange.getStatusCode()); |
| | | log.info("RCS取消任务响应头:{}", exchange.getHeaders()); |
| | | log.info("RCS取消任务响应体:{}", exchange.getBody()); |
| | | if (Objects.isNull(exchange.getBody())) { |
| | | throw new CoolException("取消任务失败!!"); |
| | | } else { |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | objectMapper.coercionConfigDefaults() |
| | | .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty); |
| | | try { |
| | | CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class); |
| | | if (result.getCode() == 200) { |
| | | log.info("========== RCS任务取消成功(open-rcs接口) =========="); |
| | | log.info("成功取消的任务编号:{}", params.get("tasks")); |
| | | return result; |
| | | } else { |
| | | log.error("========== RCS任务取消失败(open-rcs接口) =========="); |
| | | log.error("RCS返回错误码:{},错误信息:{}", result.getCode(), result.getMsg()); |
| | | throw new CoolException("取消任务失败!!" + (result.getMsg() != null ? ":" + result.getMsg() : "")); |
| | | } |
| | | } catch (JsonProcessingException e) { |
| | | log.error("RCS取消任务响应解析失败:{}", e.getMessage(), e); |
| | | throw new CoolException("RCS取消任务响应解析失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public CommonResponse callBackEvent(ExMsgCallbackParams params) { |
| | | // 参数校验 |
| | | if (Objects.isNull(params)) { |
| | | log.error("RCS回调事件参数为空!"); |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | |
| | | // 详细记录接收到的参数 |
| | | log.info("RCS回调事件接收参数 - seqNum: {}, eventType: {}, robotCode: {}, zpallet: {}", |
| | | params.getSeqNum(), params.getEventType(), params.getRobotCode(), params.getZpallet()); |
| | | |
| | | // 检查关键字段是否为空 |
| | | if (Objects.isNull(params.getSeqNum()) || params.getSeqNum().isEmpty()) { |
| | | log.warn("RCS回调事件参数seqNum为空!完整参数:{}", JSONObject.toJSONString(params)); |
| | | } |
| | | if (Objects.isNull(params.getEventType()) || params.getEventType().isEmpty()) { |
| | | log.warn("RCS回调事件参数eventType为空!完整参数:{}", JSONObject.toJSONString(params)); |
| | | } |
| | | |
| | | String callUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + WmsConstant.callBack; |
| | | /**WMS基础配置链接*/ |
| | | log.info("任务执行状态上报,请求地址: {}, 请求参数: {}", callUrl , JSONObject.toJSONString(params)); |
| | |
| | | public List<SyncLocsDto> syncRcsLocs(SyncRcsLocsParam params) { |
| | | /**RCS基础配置链接*/ |
| | | String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.syncLocs; |
| | | log.info("任务下发,请求地址: {}, 请求参数: {}", rcsUrl , JSONObject.toJSONString(params)); |
| | | log.info("任务下发,请求地址2: {}, 请求参数: {}", rcsUrl , JSONObject.toJSONString(params)); |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @date 2026/2/3 |
| | | * @description: 任务执行通知上报(RCS回调接口) |
| | | * @version 1.0 |
| | | */ |
| | | @Override |
| | | public CommonResponse reportTask(TaskReportParams params) { |
| | | log.info("任务执行通知上报,请求参数: {}", JSONObject.toJSONString(params)); |
| | | |
| | | // 参数校验 |
| | | if (Objects.isNull(params)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | if (Objects.isNull(params.getBatchNo()) || params.getBatchNo().isEmpty()) { |
| | | throw new CoolException("任务批次不能为空!!"); |
| | | } |
| | | if (Objects.isNull(params.getTaskNo()) || params.getTaskNo().isEmpty()) { |
| | | throw new CoolException("任务编号不能为空!!"); |
| | | } |
| | | if (Objects.isNull(params.getTimestamp()) || params.getTimestamp().isEmpty()) { |
| | | throw new CoolException("时间戳不能为空!!"); |
| | | } |
| | | |
| | | // 将TaskReportParams转换为ExMsgParams格式(taskNo -> seqNum) |
| | | // 根据RCS新接口规范,taskNo对应旧接口的seqNum |
| | | JSONObject exMsgParams = new JSONObject(); |
| | | exMsgParams.put("seqNum", params.getTaskNo()); // taskNo映射到seqNum |
| | | // eventType设置为END,表示任务完成(根据业务需求可能需要调整) |
| | | exMsgParams.put("eventType", "END"); |
| | | exMsgParams.put("robotCode", null); |
| | | exMsgParams.put("zpallet", null); |
| | | |
| | | // 将任务上报回调转发到WMS系统 |
| | | String callUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + WmsConstant.callBack; |
| | | log.info("任务执行通知上报,请求地址: {}, 转换后参数: {}", callUrl, exMsgParams.toJSONString()); |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | | HttpEntity httpEntity = new HttpEntity(exMsgParams, headers); |
| | | ResponseEntity<String> exchange = restTemplate.exchange(callUrl, HttpMethod.POST, httpEntity, String.class); |
| | | log.info("任务执行通知上报,响应结果: {}", exchange); |
| | | |
| | | if (Objects.isNull(exchange.getBody())) { |
| | | // 如果回调失败,返回成功响应(避免RCS重复回调) |
| | | CommonResponse response = new CommonResponse(); |
| | | response.setCode(200); |
| | | response.setMsg("接收成功"); |
| | | log.warn("任务执行通知上报回调失败,但返回成功响应,任务编号:{},批次:{}", params.getTaskNo(), params.getBatchNo()); |
| | | return response; |
| | | } else { |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | objectMapper.coercionConfigDefaults() |
| | | .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty); |
| | | try { |
| | | CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class); |
| | | if (result.getCode() == 200) { |
| | | log.info("任务执行通知上报成功,任务编号:{},批次:{}", params.getTaskNo(), params.getBatchNo()); |
| | | return result; |
| | | } else { |
| | | log.warn("任务执行通知上报回调返回非200状态,任务编号:{},批次:{},响应:{}", params.getTaskNo(), params.getBatchNo(), exchange.getBody()); |
| | | return result; |
| | | } |
| | | } catch (JsonProcessingException e) { |
| | | log.error("任务执行通知上报回调响应解析失败,任务编号:{},批次:{},错误:{}", params.getTaskNo(), params.getBatchNo(), e.getMessage()); |
| | | // 解析失败时返回成功响应,避免RCS重复回调 |
| | | CommonResponse response = new CommonResponse(); |
| | | response.setCode(200); |
| | | response.setMsg("接收成功"); |
| | | return response; |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | import org.springframework.boot.SpringApplication; |
| | | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| | | import org.springframework.context.annotation.ComponentScan; |
| | | |
| | | @SpringBootApplication |
| | | @ComponentScan(basePackages = {"com.vincent.rsf.server", "com.vincent.rsf.common"}) |
| | | public class ServerBoot { |
| | | |
| | | public static void main(String[] args) { |
| | |
| | | |
| | | public class RcsConstant { |
| | | |
| | | //下发任务至RCS |
| | | public static String pubTask = "/rsf-open-api/rcs/pub/task"; |
| | | //下发任务至RCS(新接口路径) |
| | | public static String pubTask = "/api/open/bus/submit"; |
| | | |
| | | //同步RCS库位信息 |
| | | public static String syncLocs = "/rsf-open-api/rcs/sync/locs"; |
| | |
| | | |
| | | //上报站点状态 |
| | | public static String REPORT_SITE_STATUS = "/rsf-open-api/rcs/modify/status"; |
| | | |
| | | //取消RCS任务 |
| | | public static String cancelTask = "/api/open/task/cancel"; |
| | | } |
| | |
| | | @ApiModel(value = "TaskItem", description = "任务列表") |
| | | public class TaskItemParam { |
| | | |
| | | @ApiModelProperty("任务类型{LOC_TO_LOC: 移库, LOC_TO_STA: 出库, STA_TO_LOC: 入库, STA_TO_STA: 站点间搬运}") |
| | | private String taskType; |
| | | // @ApiModelProperty("任务类型{LOC_TO_LOC: 移库, LOC_TO_STA: 出库, STA_TO_LOC: 入库, STA_TO_STA: 站点间搬运}") |
| | | // private String taskType; |
| | | |
| | | @ApiModelProperty("任务号") |
| | | private String seqNum; |
| | | private String taskNo;//seqNum |
| | | |
| | | @ApiModelProperty("起始库位") |
| | | private String oriLoc; |
| | |
| | | @ApiModel(value = "WcsTaskParams", description = "任务下发参数") |
| | | public class WcsTaskParams implements Serializable { |
| | | |
| | | @ApiModelProperty("批次") |
| | | private String batch; |
| | | @ApiModelProperty("批次编号") |
| | | private String batchNo; |
| | | |
| | | @ApiModelProperty("任务明细") |
| | | private List<TaskItemParam> taskList; |
| | | @ApiModelProperty("任务数组") |
| | | private List<TaskItemParam> tasks; |
| | | } |
| | |
| | | import com.vincent.rsf.server.system.service.UserService; |
| | | import com.vincent.rsf.server.system.utils.SerialRuleUtils; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | |
| | | */ |
| | | @Service |
| | | public class MobileServiceImpl implements MobileService { |
| | | |
| | | private static final Logger logger = LoggerFactory.getLogger(MobileServiceImpl.class); |
| | | |
| | | @Value("${super.pwd}") |
| | | private String superPwd; |
| | |
| | | String batch = params.get("batch"); |
| | | // String barcode = params.get("barcode"); |
| | | |
| | | if (Objects.isNull(crushNo)) { |
| | | throw new CoolException("票号不能为空!!"); |
| | | } |
| | | // if (Objects.isNull(crushNo)) { |
| | | // throw new CoolException("票号不能为空!!"); |
| | | // } |
| | | // if (Objects.isNull(code)) { |
| | | // throw new CoolException("容器号不能为空!!"); |
| | | // } |
| | |
| | | // } |
| | | // } |
| | | |
| | | String fieldIndex = null; |
| | | String fieldIndexTemp = null; |
| | | if (!Objects.isNull(crushNo)) { |
| | | FieldsItem fieldsItem = fieldsItemService.getOne(new LambdaQueryWrapper<FieldsItem>().eq(FieldsItem::getValue, crushNo).last("Limit 1")); |
| | | if (!Objects.isNull(fieldsItem)) { |
| | | fieldIndex = fieldsItem.getUuid(); |
| | | fieldIndexTemp = fieldsItem.getUuid(); |
| | | } |
| | | } |
| | | //TODO 后续需根据策略配置,获取组拖数据。如:混装,按批次混装等 |
| | | LambdaQueryWrapper<WarehouseAreasItem> queryWrapper = new LambdaQueryWrapper<WarehouseAreasItem>() |
| | | .or().eq(!Cools.isEmpty(code), WarehouseAreasItem::getTrackCode, code) |
| | | .or().eq(!Cools.isEmpty(batch), WarehouseAreasItem::getSplrBatch, batch) |
| | | .or().eq(WarehouseAreasItem::getFieldsIndex, fieldIndex) |
| | | .or().eq(!Cools.isEmpty(matnrCode), WarehouseAreasItem::getMatnrCode, matnrCode) |
| | | .or().eq(!Cools.isEmpty(asnCode), WarehouseAreasItem::getAsnCode, asnCode); |
| | | List<WarehouseAreasItem> list = warehouseAreasItemService.list(queryWrapper); |
| | | if (!list.isEmpty()) { |
| | | list.removeIf(e -> e.getAnfme() <= e.getWorkQty()); |
| | | final String fieldIndex = fieldIndexTemp; |
| | | |
| | | // 检查是否有至少一个有效查询条件 |
| | | boolean hasValidCondition = !Cools.isEmpty(code) || !Cools.isEmpty(batch) |
| | | || !Objects.isNull(fieldIndex) || !Cools.isEmpty(matnrCode) || !Cools.isEmpty(asnCode); |
| | | |
| | | if (!hasValidCondition) { |
| | | throw new CoolException("请至少输入一个查询条件:物料编码、ASN单号、跟踪码、批次或票号"); |
| | | } |
| | | |
| | | // 如果扫描物料编码且ASN单号为空,直接从物料信息表获取,不查询收货区 |
| | | if (!Cools.isEmpty(matnrCode) && Cools.isEmpty(asnCode) && Cools.isEmpty(code) |
| | | && Cools.isEmpty(batch) && Objects.isNull(fieldIndex)) { |
| | | logger.info("=== 从物料信息表查询物料信息(不查询收货区) ==="); |
| | | logger.info("查询参数 - matnrCode: {}", matnrCode); |
| | | |
| | | Matnr matnr = matnrMapper.selectOne(new LambdaQueryWrapper<Matnr>() |
| | | .eq(Matnr::getCode, matnrCode) |
| | | .eq(Matnr::getDeleted, 0) |
| | | .last("LIMIT 1")); |
| | | |
| | | if (matnr == null) { |
| | | logger.warn("物料信息表中未找到物料编码: {}", matnrCode); |
| | | return R.ok(new ArrayList<>()); |
| | | } |
| | | |
| | | // 构造一个虚拟的 WarehouseAreasItem 对象返回物料信息 |
| | | WarehouseAreasItem item = new WarehouseAreasItem(); |
| | | item.setMatnrId(matnr.getId()); |
| | | item.setMatnrCode(matnr.getCode()); |
| | | item.setMaktx(matnr.getName()); |
| | | item.setStockUnit(matnr.getStockUnit()); |
| | | item.setUnit(matnr.getUnit()); |
| | | item.setAnfme(0.0); // 数量设为0,因为不是从收货区查询的 |
| | | item.setWorkQty(0.0); |
| | | item.setQty(0.0); |
| | | |
| | | // 设置扩展字段 |
| | | Map<String, String> fields = FieldsUtils.getFields(matnr.getFieldsIndex()); |
| | | item.setExtendFields(fields); |
| | | |
| | | List<WarehouseAreasItem> resultList = new ArrayList<>(); |
| | | resultList.add(item); |
| | | |
| | | logger.info("从物料信息表返回 1 条物料数据"); |
| | | return R.ok(resultList); |
| | | } |
| | | |
| | | //TODO 后续需根据策略配置,获取组拖数据。如:混装,按批次混装等 |
| | | LambdaQueryWrapper<WarehouseAreasItem> queryWrapper = new LambdaQueryWrapper<>(); |
| | | |
| | | // 构建OR查询条件组 |
| | | // 统计有效条件数量 |
| | | int conditionCount = 0; |
| | | if (!Cools.isEmpty(code)) conditionCount++; |
| | | if (!Cools.isEmpty(batch)) conditionCount++; |
| | | if (!Objects.isNull(fieldIndex)) conditionCount++; |
| | | if (!Cools.isEmpty(matnrCode)) conditionCount++; |
| | | if (!Cools.isEmpty(asnCode)) conditionCount++; |
| | | |
| | | // 如果只有一个条件,直接使用eq;如果有多个条件,使用and包裹or条件组 |
| | | if (conditionCount == 1) { |
| | | // 单个条件,直接查询 |
| | | if (!Cools.isEmpty(code)) { |
| | | queryWrapper.eq(WarehouseAreasItem::getTrackCode, code); |
| | | } else if (!Cools.isEmpty(batch)) { |
| | | queryWrapper.eq(WarehouseAreasItem::getSplrBatch, batch); |
| | | } else if (!Objects.isNull(fieldIndex)) { |
| | | queryWrapper.eq(WarehouseAreasItem::getFieldsIndex, fieldIndex); |
| | | } else if (!Cools.isEmpty(matnrCode)) { |
| | | queryWrapper.eq(WarehouseAreasItem::getMatnrCode, matnrCode); |
| | | } else if (!Cools.isEmpty(asnCode)) { |
| | | queryWrapper.eq(WarehouseAreasItem::getAsnCode, asnCode); |
| | | } |
| | | } else { |
| | | // 多个条件,使用OR连接 |
| | | queryWrapper.and(wrapper -> { |
| | | boolean first = true; |
| | | if (!Cools.isEmpty(code)) { |
| | | wrapper.eq(WarehouseAreasItem::getTrackCode, code); |
| | | first = false; |
| | | } |
| | | if (!Cools.isEmpty(batch)) { |
| | | if (!first) wrapper.or(); |
| | | wrapper.eq(WarehouseAreasItem::getSplrBatch, batch); |
| | | first = false; |
| | | } |
| | | if (!Objects.isNull(fieldIndex)) { |
| | | if (!first) wrapper.or(); |
| | | wrapper.eq(WarehouseAreasItem::getFieldsIndex, fieldIndex); |
| | | first = false; |
| | | } |
| | | if (!Cools.isEmpty(matnrCode)) { |
| | | if (!first) wrapper.or(); |
| | | wrapper.eq(WarehouseAreasItem::getMatnrCode, matnrCode); |
| | | first = false; |
| | | } |
| | | if (!Cools.isEmpty(asnCode)) { |
| | | if (!first) wrapper.or(); |
| | | wrapper.eq(WarehouseAreasItem::getAsnCode, asnCode); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 打印查询参数到控制台 |
| | | logger.info("=== 查询可组托物料 ==="); |
| | | logger.info("查询参数 - code: {}, matnrCode: {}, asnCode: {}, batch: {}, fieldIndex: {}", |
| | | code, matnrCode, asnCode, batch, fieldIndex); |
| | | |
| | | List<WarehouseAreasItem> list = warehouseAreasItemService.list(queryWrapper); |
| | | logger.info("查询到 {} 条收货区库存数据", list.size()); |
| | | |
| | | // 过滤条件:只返回可组托数量 > 0 的数据 |
| | | // 可组托数量 = anfme - workQty - qty |
| | | if (!list.isEmpty()) { |
| | | int beforeFilterSize = list.size(); |
| | | list.removeIf(e -> { |
| | | // 如果anfme为null,移除该数据 |
| | | Double anfme = e.getAnfme(); |
| | | if (Objects.isNull(anfme) || anfme <= 0) { |
| | | logger.debug("过滤数据 - ID: {}, matnrCode: {}, anfme为null或<=0: {}", |
| | | e.getId(), e.getMatnrCode(), anfme); |
| | | return true; // 移除数量为null或<=0的数据 |
| | | } |
| | | |
| | | Double workQty = Objects.isNull(e.getWorkQty()) ? 0.0 : e.getWorkQty(); |
| | | Double qty = Objects.isNull(e.getQty()) ? 0.0 : e.getQty(); |
| | | |
| | | // 可组托数量 = 总数量 - 已执行数量 - 已收货数量 |
| | | Double availableQty = anfme - workQty - qty; |
| | | if (availableQty <= 0) { |
| | | logger.debug("过滤数据 - ID: {}, matnrCode: {}, anfme: {}, workQty: {}, qty: {}, 可组托数量: {} <= 0", |
| | | e.getId(), e.getMatnrCode(), anfme, workQty, qty, availableQty); |
| | | } |
| | | return availableQty <= 0; // 移除可组托数量 <= 0 的数据 |
| | | }); |
| | | |
| | | // 如果过滤后数据为空,说明所有数据都被过滤掉了 |
| | | if (list.isEmpty() && beforeFilterSize > 0) { |
| | | logger.warn("未找到可组托的物料,请检查:1.物料编码是否正确 2.物料是否已收货至收货区 3.是否还有可组托数量"); |
| | | logger.warn("查询条件 - code: {}, matnrCode: {}, asnCode: {}, batch: {}, fieldIndex: {}", |
| | | code, matnrCode, asnCode, batch, fieldIndex); |
| | | logger.warn("查询到 {} 条数据,但全部被过滤(可组托数量 <= 0)", beforeFilterSize); |
| | | } |
| | | } else { |
| | | logger.warn("未找到可组托的物料,请检查:1.物料编码是否正确 2.物料是否已收货至收货区 3.是否还有可组托数量"); |
| | | logger.warn("查询条件 - code: {}, matnrCode: {}, asnCode: {}, batch: {}, fieldIndex: {}", |
| | | code, matnrCode, asnCode, batch, fieldIndex); |
| | | logger.warn("数据库查询结果为空,可能原因:1.物料编码不匹配 2.物料未收货至收货区 3.数据已被删除"); |
| | | } |
| | | |
| | | // 如果扫描物料编码加载物料并且ASN单号为空时,从物料信息表获取物料信息并更新 |
| | | if (!Cools.isEmpty(matnrCode) && Cools.isEmpty(asnCode) && !list.isEmpty()) { |
| | | // 收集所有唯一的物料编码 |
| | | Set<String> matnrCodes = list.stream() |
| | | .map(WarehouseAreasItem::getMatnrCode) |
| | | .filter(matCode -> !Cools.isEmpty(matCode)) |
| | | .collect(Collectors.toSet()); |
| | | |
| | | // 批量查询物料信息 |
| | | if (!matnrCodes.isEmpty()) { |
| | | Map<String, Matnr> matnrMap = new HashMap<>(); |
| | | for (String matCode : matnrCodes) { |
| | | Matnr matnr = matnrMapper.selectOne(new LambdaQueryWrapper<Matnr>() |
| | | .eq(Matnr::getCode, matCode) |
| | | .eq(Matnr::getDeleted, 0) |
| | | .last("LIMIT 1")); |
| | | if (matnr != null) { |
| | | matnrMap.put(matCode, matnr); |
| | | } |
| | | } |
| | | |
| | | // 更新列表中的物料信息 |
| | | list.forEach(item -> { |
| | | String itemMatnrCode = item.getMatnrCode(); |
| | | if (!Cools.isEmpty(itemMatnrCode) && matnrMap.containsKey(itemMatnrCode)) { |
| | | Matnr matnr = matnrMap.get(itemMatnrCode); |
| | | // 从物料信息表更新物料名称 |
| | | if (!Cools.isEmpty(matnr.getName())) { |
| | | item.setMaktx(matnr.getName()); |
| | | } |
| | | // 从物料信息表更新库存单位 |
| | | if (!Cools.isEmpty(matnr.getStockUnit())) { |
| | | item.setStockUnit(matnr.getStockUnit()); |
| | | } |
| | | // 从物料信息表更新单位 |
| | | if (!Cools.isEmpty(matnr.getUnit())) { |
| | | item.setUnit(matnr.getUnit()); |
| | | } |
| | | logger.debug("已从物料信息表更新物料信息 - matnrCode: {}, name: {}, stockUnit: {}", |
| | | itemMatnrCode, matnr.getName(), matnr.getStockUnit()); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | list.forEach(item -> { |
| | | Map<String, String> fields = FieldsUtils.getFields(item.getFieldsIndex()); |
| | | item.setExtendFields(fields); |
| | | }); |
| | | |
| | | logger.info("返回 {} 条可组托物料数据", list.size()); |
| | | return R.ok(list); |
| | | } |
| | | |
| | |
| | | @Override |
| | | public R receiveExMsg(ExMsgParams params) { |
| | | if (Objects.isNull(params)) { |
| | | log.error("RCS回调为空!"); |
| | | return R.error("参数不能为空!!"); |
| | | } |
| | | log.info("========== 接收RCS回调 =========="); |
| | | log.info("任务编号:{},事件类型:{}", params.getSeqNum(), params.getEventType()); |
| | | Task task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, params.getSeqNum())); |
| | | if (Objects.isNull(task)) { |
| | | throw new CoolException("任务不存在可已结束!!"); |
| | | log.error("任务不存在或已结束!任务编号:{}", params.getSeqNum()); |
| | | throw new CoolException("任务不存在或已结束!!"); |
| | | } |
| | | log.info("查询到任务 - 任务编码:{},任务类型:{},当前状态:{}", |
| | | task.getTaskCode(), task.getTaskType(), task.getTaskStatus()); |
| | | |
| | | /**料箱搬运中, 修改站点状态*/ |
| | | // if (params.getEventType().equals(CallBackEvent.CALL_BACK_EVENT_OBIT.event)) { |
| | |
| | | if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | BasStation station = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, task.getOrgSite())); |
| | | if (Objects.isNull(station)) { |
| | | log.error("入库站点不存在 - 站点名称:{},任务编码:{}", task.getOrgSite(), task.getTaskCode()); |
| | | throw new CoolException("数据错误,站点不存在!!"); |
| | | } |
| | | log.info("查询到入库站点 - 站点名称:{},站点类型:{},当前状态:{}", |
| | | station.getStationName(), station.getType(), station.getUseStatus()); |
| | | if (station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) { |
| | | log.info("更新入库站点状态 - 站点名称:{},新状态:{}", station.getStationName(), LocStsType.LOC_STS_TYPE_O.type); |
| | | station.setUseStatus(LocStsType.LOC_STS_TYPE_O.type); |
| | | if (!basStationService.updateById(station)) { |
| | | log.error("入库站点状态修改失败 - 站点名称:{}", station.getStationName()); |
| | | throw new CoolException("站点状态修改失败!!"); |
| | | } |
| | | log.info("入库站点状态更新成功 - 站点名称:{}", station.getStationName()); |
| | | } |
| | | } |
| | | |
| | | if (!taskService.update(new LambdaUpdateWrapper<Task>() |
| | | .lt(Task::getTaskStatus, TaskStsType.COMPLETE_IN.id) |
| | | .eq(Task::getTaskCode, task.getTaskCode()) |
| | | .set(Task::getTaskStatus, TaskStsType.COMPLETE_IN.id))) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | log.info("准备更新入库任务状态 - 任务编码:{},当前状态:{},目标状态:{}", |
| | | task.getTaskCode(), task.getTaskStatus(), TaskStsType.COMPLETE_IN.id); |
| | | |
| | | // 如果任务状态已经大于等于目标状态,跳过更新 |
| | | if (task.getTaskStatus() >= TaskStsType.COMPLETE_IN.id) { |
| | | log.warn("入库任务状态已大于等于目标状态,跳过更新 - 任务编码:{},当前状态:{},目标状态:{}", |
| | | task.getTaskCode(), task.getTaskStatus(), TaskStsType.COMPLETE_IN.id); |
| | | } else { |
| | | boolean updated = taskService.update(new LambdaUpdateWrapper<Task>() |
| | | .lt(Task::getTaskStatus, TaskStsType.COMPLETE_IN.id) |
| | | .eq(Task::getTaskCode, task.getTaskCode()) |
| | | .set(Task::getTaskStatus, TaskStsType.COMPLETE_IN.id)); |
| | | if (!updated) { |
| | | log.error("入库任务状态修改失败 - 任务编码:{},当前状态:{},目标状态:{},可能任务状态已大于等于目标状态", |
| | | task.getTaskCode(), task.getTaskStatus(), TaskStsType.COMPLETE_IN.id); |
| | | throw new CoolException("任务状态修改失败!!当前任务状态:" + task.getTaskStatus() + ",目标状态:" + TaskStsType.COMPLETE_IN.id); |
| | | } |
| | | log.info("入库任务状态更新成功 - 任务编码:{}", task.getTaskCode()); |
| | | } |
| | | } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type) |
| | |
| | | BasStation station = basStationService.getOne(new LambdaQueryWrapper<BasStation>() |
| | | .eq(BasStation::getStationName, task.getTargSite())); |
| | | if (Objects.isNull(station)) { |
| | | log.error("出库站点不存在 - 站点名称:{},任务编码:{}", task.getTargSite(), task.getTaskCode()); |
| | | throw new CoolException("数据错误,站点不存在!!"); |
| | | } |
| | | log.info("查询到出库站点 - 站点名称:{},站点类型:{},当前状态:{}", |
| | | station.getStationName(), station.getType(), station.getUseStatus()); |
| | | if (station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) { |
| | | log.info("更新出库站点状态 - 站点名称:{},新状态:{}", station.getStationName(), LocStsType.LOC_STS_TYPE_F.type); |
| | | station.setUseStatus(LocStsType.LOC_STS_TYPE_F.type); |
| | | if (!basStationService.updateById(station)) { |
| | | log.error("出库站点状态修改失败 - 站点名称:{}", station.getStationName()); |
| | | throw new CoolException("站点状态修改失败!!"); |
| | | } |
| | | log.info("出库站点状态更新成功 - 站点名称:{}", station.getStationName()); |
| | | } |
| | | if (!taskService.update(new LambdaUpdateWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode()) |
| | | .lt(Task::getTaskStatus, TaskStsType.COMPLETE_OUT.id) |
| | | .set(Task::getTaskStatus, TaskStsType.COMPLETE_OUT.id))) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | log.info("准备更新出库任务状态 - 任务编码:{},当前状态:{},目标状态:{}", |
| | | task.getTaskCode(), task.getTaskStatus(), TaskStsType.COMPLETE_OUT.id); |
| | | |
| | | // 如果任务状态已经大于等于目标状态,跳过更新 |
| | | if (task.getTaskStatus() >= TaskStsType.COMPLETE_OUT.id) { |
| | | log.warn("出库任务状态已大于等于目标状态,跳过更新 - 任务编码:{},当前状态:{},目标状态:{}", |
| | | task.getTaskCode(), task.getTaskStatus(), TaskStsType.COMPLETE_OUT.id); |
| | | } else { |
| | | boolean updated = taskService.update(new LambdaUpdateWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode()) |
| | | .lt(Task::getTaskStatus, TaskStsType.COMPLETE_OUT.id) |
| | | .set(Task::getTaskStatus, TaskStsType.COMPLETE_OUT.id)); |
| | | if (!updated) { |
| | | log.error("出库任务状态修改失败 - 任务编码:{},当前状态:{},目标状态:{},可能任务状态已大于等于目标状态", |
| | | task.getTaskCode(), task.getTaskStatus(), TaskStsType.COMPLETE_OUT.id); |
| | | throw new CoolException("任务状态修改失败!!当前任务状态:" + task.getTaskStatus() + ",目标状态:" + TaskStsType.COMPLETE_OUT.id); |
| | | } |
| | | log.info("出库任务状态更新成功 - 任务编码:{}", task.getTaskCode()); |
| | | } |
| | | } |
| | | } else { |
| | | log.warn("未处理的事件类型 - 事件类型:{},任务编码:{},任务类型:{}", |
| | | params.getEventType(), task.getTaskCode(), task.getTaskType()); |
| | | } |
| | | log.info(JSONObject.toJSONString(params)); |
| | | log.info("========== 任务执行通知上报处理完成 =========="); |
| | | log.info("处理结果:{}", JSONObject.toJSONString(params)); |
| | | return R.ok(JSONObject.toJSONString(params)); |
| | | } |
| | | |
| | |
| | | @Override |
| | | public R pubWcsTask(WcsTaskParams params) { |
| | | String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask; |
| | | log.info("任务下发,请求地址: {}, 请求参数: {}", rcsUrl, JSONObject.toJSONString(params)); |
| | | log.info("任务下发,请求地址3: {}, 请求参数: {}", rcsUrl, JSONObject.toJSONString(params)); |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | |
| | | } |
| | | |
| | | public Double getDiffQty() { |
| | | return Math.round((this.reviseQty - this.anfme) * 1000000) / 1000000.0; |
| | | Double reviseQtyValue = this.reviseQty != null ? this.reviseQty : 0.0; |
| | | Double anfmeValue = this.anfme != null ? this.anfme : 0.0; |
| | | return Math.round((reviseQtyValue - anfmeValue) * 1000000) / 1000000.0; |
| | | } |
| | | |
| | | public String getUpdateBy$(){ |
| | |
| | | import com.fasterxml.jackson.databind.cfg.CoercionAction; |
| | | import com.fasterxml.jackson.databind.cfg.CoercionInputShape; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.framework.common.DateUtils; |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.api.config.RemotesInfoProperties; |
| | |
| | | @Scheduled(cron = "0/55 * * * * ? ") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void pubTaskToWcs() { |
| | | log.info("定时任务开始执行:任务下发到RCS"); |
| | | Long loginUserId = SystemAuthUtils.getLoginUserId(); |
| | | List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_LOC_MOVE.type, TaskType.TASK_TYPE_EMPITY_IN.type |
| | | , TaskType.TASK_TYPE_CHECK_IN.type, TaskType.TASK_TYPE_MERGE_IN.type, TaskType.TASK_TYPE_EMPITY_OUT.type, TaskType.TASK_TYPE_PICK_IN.type, |
| | |
| | | .in(Task::getTaskType, list) |
| | | .in(Task::getTaskStatus, integers) |
| | | .orderByDesc(Task::getSort)); |
| | | log.info("查询到待下发任务数量:{}", tasks.size()); |
| | | if (tasks.isEmpty()) { |
| | | log.debug("没有待下发的任务,定时任务结束"); |
| | | return; |
| | | } |
| | | Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_RUN_CHECK_ORDERS)); |
| | | if (!Objects.isNull(config) && !Objects.isNull(config.getVal())) { |
| | | if (Boolean.parseBoolean(config.getVal())) { |
| | | return; |
| | | log.info("配置项AUTO_RUN_CHECK_ORDERS为true,跳过任务下发"); |
| | | return; |
| | | } |
| | | } |
| | | // for (Task task : tasks) { |
| | |
| | | // } |
| | | // } |
| | | /**下发普通站点任务,报错回滚,不再往下执行*/ |
| | | log.info("开始下发{}个任务到RCS", tasks.size()); |
| | | taskService.pubTaskToWcs(tasks); |
| | | log.info("定时任务执行完成:任务下发到RCS"); |
| | | } |
| | | |
| | | /** |
| | |
| | | import com.vincent.rsf.server.manager.utils.OptimalAlgorithmUtil; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.system.utils.SerialRuleUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | |
| | | * @return |
| | | * @time 2025/3/7 08:02 |
| | | */ |
| | | @Slf4j |
| | | @Service("outStockServiceImpl") |
| | | public class OutStockServiceImpl extends ServiceImpl<AsnOrderMapper, WkOrder> implements OutStockService { |
| | | |
| | |
| | | if (!Objects.isNull(loc)) { |
| | | List<LocItem> locItems = new ArrayList<>(); |
| | | LocItem locItem = locItemService.getById(param.getId()); |
| | | |
| | | if (Objects.isNull(locItem)) { |
| | | throw new CoolException("库位明细不存在,ID:" + param.getId()); |
| | | } |
| | | |
| | | // 修复:构建查询条件,先构建基础条件,再根据值是否为空动态添加 |
| | | // 优先使用供应商批次匹配,如果没有则使用库存批次匹配 |
| | | LambdaQueryWrapper<WkOrderItem> orderItemWrapper = new LambdaQueryWrapper<WkOrderItem>() |
| | | .eq(WkOrderItem::getOrderId, outId) |
| | | .eq(WkOrderItem::getMatnrId, locItem.getMatnrId()); |
| | | if (StringUtils.isNotBlank(locItem.getBatch())) { |
| | | orderItemWrapper.eq(WkOrderItem::getSplrBatch, locItem.getBatch()); |
| | | |
| | | // 优先使用供应商批次匹配 |
| | | if (StringUtils.isNotBlank(locItem.getSplrBatch())) { |
| | | orderItemWrapper.eq(WkOrderItem::getSplrBatch, locItem.getSplrBatch()); |
| | | } else if (StringUtils.isNotBlank(locItem.getBatch())) { |
| | | // 如果LocItem只有batch字段,尝试同时匹配WkOrderItem的batch和splrBatch字段 |
| | | // 因为某些情况下LocItem的batch可能对应WkOrderItem的splrBatch |
| | | orderItemWrapper.and(wrapper -> wrapper |
| | | .eq(WkOrderItem::getBatch, locItem.getBatch()) |
| | | .or() |
| | | .eq(WkOrderItem::getSplrBatch, locItem.getBatch()) |
| | | ); |
| | | } |
| | | |
| | | if (StringUtils.isNotBlank(locItem.getFieldsIndex())) { |
| | | orderItemWrapper.eq(WkOrderItem::getFieldsIndex, locItem.getFieldsIndex()); |
| | | } |
| | | WkOrderItem orderItem = outStockItemService.getOne(orderItemWrapper); |
| | | |
| | | // 如果找不到单据明细,且LocItem来自库存调整,则自动创建WkOrderItem |
| | | if (Objects.isNull(orderItem)) { |
| | | throw new CoolException("单据明细不存在!!"); |
| | | // 检查是否是库存调整产生的库存 |
| | | if (locItem.getWkType() != null && |
| | | locItem.getWkType().equals(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_REVISE.type))) { |
| | | // 获取出库单信息 |
| | | WkOrder outOrder = outStockService.getById(outId); |
| | | if (Objects.isNull(outOrder)) { |
| | | throw new CoolException("出库单据不存在!!"); |
| | | } |
| | | |
| | | log.info("库存调整产生的库存,自动创建WkOrderItem - 出库单ID:{},物料ID:{},批次:{}", |
| | | outId, locItem.getMatnrId(), locItem.getBatch()); |
| | | |
| | | // 创建WkOrderItem |
| | | orderItem = new WkOrderItem(); |
| | | orderItem.setOrderId(outId) |
| | | .setOrderCode(outOrder.getCode()) |
| | | .setMatnrId(locItem.getMatnrId()) |
| | | .setMatnrCode(locItem.getMatnrCode()) |
| | | .setMaktx(locItem.getMaktx()) |
| | | .setBatch(StringUtils.isNotBlank(param.getBatch()) ? param.getBatch() : locItem.getBatch()) |
| | | .setSplrBatch(StringUtils.isNotBlank(locItem.getSplrBatch()) ? locItem.getSplrBatch() : |
| | | (StringUtils.isNotBlank(locItem.getBatch()) ? locItem.getBatch() : null)) |
| | | .setFieldsIndex(locItem.getFieldsIndex()) |
| | | .setAnfme(param.getOutQty()) |
| | | .setWorkQty(0.0) |
| | | .setStockUnit(locItem.getUnit()) |
| | | .setPurUnit(locItem.getUnit()) |
| | | .setSpec(locItem.getSpec()) |
| | | .setModel(locItem.getModel()) |
| | | .setCreateBy(loginUserId) |
| | | .setUpdateBy(loginUserId) |
| | | .setCreateTime(new Date()) |
| | | .setUpdateTime(new Date()); |
| | | |
| | | if (!outStockItemService.save(orderItem)) { |
| | | throw new CoolException("库存调整单据明细创建失败!!"); |
| | | } |
| | | |
| | | log.info("WkOrderItem创建成功 - ID:{},出库单ID:{},物料ID:{}", |
| | | orderItem.getId(), outId, locItem.getMatnrId()); |
| | | } else { |
| | | throw new CoolException("单据明细不存在!!出库单ID:" + outId + ",物料ID:" + locItem.getMatnrId() + |
| | | (StringUtils.isNotBlank(locItem.getSplrBatch()) ? ",供应商批次:" + locItem.getSplrBatch() : |
| | | StringUtils.isNotBlank(locItem.getBatch()) ? ",库存批次:" + locItem.getBatch() : "") + |
| | | (StringUtils.isNotBlank(locItem.getFieldsIndex()) ? ",字段索引:" + locItem.getFieldsIndex() : "")); |
| | | } |
| | | } |
| | | |
| | | locItem.setOutQty(param.getOutQty()) |
| | |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.manager.controller.params.ReviseLogParams; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.enums.AsnExceStatus; |
| | | import com.vincent.rsf.server.manager.enums.CommonExceStatus; |
| | | import com.vincent.rsf.server.manager.enums.OrderType; |
| | | import com.vincent.rsf.server.manager.enums.OrderWorkType; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | |
| | | |
| | | @Autowired |
| | | private ReviseLogItemService reviseLogItemService; |
| | | |
| | | @Autowired |
| | | private OutStockService outStockService; |
| | | |
| | | @Autowired |
| | | private OutStockItemService outStockItemService; |
| | | |
| | | /** |
| | | * 库存调整单明细添加 |
| | |
| | | return item.getLocId() != null ? String.valueOf(item.getLocId()) : item.getLocCode(); |
| | | })); |
| | | |
| | | // 优化:在循环外查询所有未完成的出库单和明细,避免重复查询 |
| | | // 查询所有未完成的出库单状态:初始化、待处理、生成波次、生成工作档、作业中 |
| | | List<WkOrder> outOrders = outStockService.list(new LambdaQueryWrapper<WkOrder>() |
| | | .eq(WkOrder::getType, OrderType.ORDER_OUT.type) |
| | | .in(WkOrder::getExceStatus, |
| | | AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val, |
| | | AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.val, |
| | | AsnExceStatus.OUT_STOCK_STATUS_TASK_WAVE.val, |
| | | AsnExceStatus.OUT_STOCK_STATUS_TASK_CREATE.val, |
| | | AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val)); |
| | | |
| | | // 预先查询所有出库单的明细,建立索引以提高查询效率 |
| | | Map<Long, List<WkOrderItem>> orderItemsMap = new HashMap<>(); |
| | | Map<Long, Boolean> orderHasItemsMap = new HashMap<>(); |
| | | if (!outOrders.isEmpty()) { |
| | | Set<Long> orderIds = outOrders.stream().map(WkOrder::getId).collect(Collectors.toSet()); |
| | | List<WkOrderItem> allOrderItems = outStockItemService.list( |
| | | new LambdaQueryWrapper<WkOrderItem>().in(WkOrderItem::getOrderId, orderIds)); |
| | | |
| | | // 按orderId分组建立索引 |
| | | orderItemsMap = allOrderItems.stream() |
| | | .collect(Collectors.groupingBy(WkOrderItem::getOrderId)); |
| | | |
| | | // 记录每个出库单是否有明细 |
| | | for (Long orderId : orderIds) { |
| | | orderHasItemsMap.put(orderId, orderItemsMap.containsKey(orderId) && !orderItemsMap.get(orderId).isEmpty()); |
| | | } |
| | | } |
| | | |
| | | // 使用final变量以便在lambda中使用 |
| | | final Map<Long, List<WkOrderItem>> finalOrderItemsMap = orderItemsMap; |
| | | final Map<Long, Boolean> finalOrderHasItemsMap = orderHasItemsMap; |
| | | |
| | | // 批量收集需要创建的WkOrderItem |
| | | List<WkOrderItem> newOrderItems = new ArrayList<>(); |
| | | |
| | | listMap.keySet().forEach(key -> { |
| | | List<ReviseLogItem> reviseItems = listMap.get(key); |
| | | if (Objects.isNull(reviseItems) || reviseItems.isEmpty()) { |
| | |
| | | .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_REVISE.type)) |
| | | .setLocCode(finalLoc.getCode()) |
| | | .setAnfme(logItem.getReviseQty()) |
| | | // 如果batch不为空,同时设置splrBatch,以便查询时能匹配 |
| | | .setSplrBatch(StringUtils.isNotBlank(logItem.getBatch()) ? logItem.getBatch() : null) |
| | | .setUpdateBy(loginUserId) |
| | | .setId(null) |
| | | .setCreateBy(loginUserId); |
| | | if (!locItemService.save(locDetl)) { |
| | | throw new CoolException("库存明细保存失败!!"); |
| | | } |
| | | |
| | | // 为库存调整产生的库存创建对应的WkOrderItem |
| | | // 遍历所有未完成的出库单,检查是否需要这些物料 |
| | | for (WkOrder outOrder : outOrders) { |
| | | // 从预加载的索引中查找该出库单的明细 |
| | | List<WkOrderItem> orderItems = finalOrderItemsMap.getOrDefault(outOrder.getId(), new ArrayList<>()); |
| | | |
| | | // 检查该出库单是否已有匹配的明细 |
| | | boolean hasMatchingItem = orderItems.stream().anyMatch(item -> { |
| | | // 物料ID必须匹配 |
| | | if (!Objects.equals(item.getMatnrId(), logItem.getMatnrId())) { |
| | | return false; |
| | | } |
| | | |
| | | // 如果有批次信息,需要匹配批次 |
| | | if (StringUtils.isNotBlank(logItem.getBatch())) { |
| | | boolean batchMatch = Objects.equals(item.getBatch(), logItem.getBatch()) || |
| | | Objects.equals(item.getSplrBatch(), logItem.getBatch()); |
| | | if (!batchMatch) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 如果有字段索引,需要匹配 |
| | | if (StringUtils.isNotBlank(logItem.getFieldsIndex())) { |
| | | if (!Objects.equals(item.getFieldsIndex(), logItem.getFieldsIndex())) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | }); |
| | | |
| | | // 如果该出库单需要这个物料但没有对应的明细,则创建 |
| | | if (!hasMatchingItem) { |
| | | // 为所有未完成的出库单创建WkOrderItem,即使出库单没有其他明细 |
| | | // 因为库存调整的物料可能需要出库 |
| | | WkOrderItem newOrderItem = new WkOrderItem(); |
| | | newOrderItem.setOrderId(outOrder.getId()) |
| | | .setOrderCode(outOrder.getCode()) |
| | | .setMatnrId(logItem.getMatnrId()) |
| | | .setMatnrCode(logItem.getMatnrCode()) |
| | | .setMaktx(logItem.getMaktx()) |
| | | .setBatch(logItem.getBatch()) |
| | | // 如果batch不为空,同时设置splrBatch,以便查询时能匹配 |
| | | .setSplrBatch(StringUtils.isNotBlank(logItem.getBatch()) ? logItem.getBatch() : null) |
| | | .setFieldsIndex(logItem.getFieldsIndex()) |
| | | .setAnfme(logItem.getReviseQty()) |
| | | .setWorkQty(0.0) |
| | | .setStockUnit(logItem.getUnit()) |
| | | .setPurUnit(logItem.getUnit()) |
| | | .setSpec(logItem.getSpec()) |
| | | .setModel(logItem.getModel()) |
| | | .setCreateBy(loginUserId) |
| | | .setUpdateBy(loginUserId) |
| | | .setCreateTime(new Date()) |
| | | .setUpdateTime(new Date()); |
| | | |
| | | newOrderItems.add(newOrderItem); |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | // 批量保存WkOrderItem,减少数据库操作次数 |
| | | if (!newOrderItems.isEmpty()) { |
| | | if (!outStockItemService.saveBatch(newOrderItems)) { |
| | | throw new CoolException("库存调整单据明细创建失败!!"); |
| | | } |
| | | } |
| | | revise.setExceStatus(CommonExceStatus.COMMON_EXCE_STATUS_TASK_DONE.val); |
| | | if (!locReviseService.updateById(revise)) { |
| | | throw new CoolException("调整单修改失败!!"); |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R removeTask(Long[] ids, Long loginUserId) { |
| | | List<Integer> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | // 先查询所有任务(不限制状态),用于检查RCS任务 |
| | | List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type, |
| | | TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_EMPITY_IN.type, TaskType.TASK_TYPE_LOC_MOVE.type, |
| | | TaskType.TASK_TYPE_EMPITY_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type); |
| | | List<Task> allTasks = this.list(new LambdaQueryWrapper<Task>() |
| | | .in(Task::getTaskType, list) |
| | | .in(Task::getId, (Object[]) ids)); |
| | | |
| | | if (allTasks.isEmpty()) { |
| | | throw new CoolException("任务不存在!!"); |
| | | } |
| | | |
| | | // 收集需要取消的RCS任务编号和批次编号(不限制状态,只要已下发到RCS就需要取消) |
| | | List<String> rcsTaskCodes = new ArrayList<>(); |
| | | String batchNo = null; |
| | | for (Task task : allTasks) { |
| | | // 判断任务是否已下发到RCS |
| | | // 入库任务:状态 >= WCS_EXECUTE_IN(2) 表示已下发 |
| | | // 出库任务:状态 >= WCS_EXECUTE_OUT(102) 表示已下发 |
| | | boolean isRcsTask = false; |
| | | if (task.getTaskType() < 100) { |
| | | // 入库任务 |
| | | if (task.getTaskStatus() >= TaskStsType.WCS_EXECUTE_IN.id) { |
| | | isRcsTask = true; |
| | | } |
| | | } else { |
| | | // 出库任务 |
| | | if (task.getTaskStatus() >= TaskStsType.WCS_EXECUTE_OUT.id) { |
| | | isRcsTask = true; |
| | | } |
| | | } |
| | | |
| | | if (isRcsTask && StringUtils.isNotBlank(task.getTaskCode())) { |
| | | rcsTaskCodes.add(task.getTaskCode()); |
| | | // 获取批次编号,优先使用任务的barcode,如果没有则使用任务编号 |
| | | if (StringUtils.isBlank(batchNo)) { |
| | | if (StringUtils.isNotBlank(task.getBarcode())) { |
| | | batchNo = task.getBarcode(); |
| | | } else if (StringUtils.isNotBlank(task.getTaskCode())) { |
| | | // 如果任务没有barcode,使用任务编号作为批次编号 |
| | | batchNo = task.getTaskCode(); |
| | | } |
| | | } |
| | | log.info("任务已下发到RCS,需要取消RCS任务 - 任务ID:{},任务编号:{},任务状态:{},托盘码:{}", |
| | | task.getId(), task.getTaskCode(), task.getTaskStatus(), task.getBarcode()); |
| | | } |
| | | } |
| | | |
| | | // 如果有任务已下发到RCS,先调用RCS取消接口 |
| | | boolean rcsCancelSuccess = false; |
| | | if (!rcsTaskCodes.isEmpty()) { |
| | | try { |
| | | log.info("========== 开始取消RCS任务 =========="); |
| | | log.info("需要取消的RCS任务编号:{}", rcsTaskCodes); |
| | | String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.cancelTask; |
| | | log.info("RCS取消任务请求地址:{}", rcsUrl); |
| | | |
| | | // 如果没有批次编号,使用第一个任务编号作为批次编号 |
| | | if (StringUtils.isBlank(batchNo) && !rcsTaskCodes.isEmpty()) { |
| | | batchNo = rcsTaskCodes.get(0); |
| | | } |
| | | |
| | | Map<String, Object> cancelParams = new HashMap<>(); |
| | | cancelParams.put("tasks", rcsTaskCodes); |
| | | if (StringUtils.isNotBlank(batchNo)) { |
| | | cancelParams.put("batchNo", batchNo); |
| | | } |
| | | |
| | | log.info("RCS取消任务请求参数:{}", JSONObject.toJSONString(cancelParams)); |
| | | |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | | HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(cancelParams, headers); |
| | | |
| | | long startTime = System.currentTimeMillis(); |
| | | ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class); |
| | | long endTime = System.currentTimeMillis(); |
| | | |
| | | log.info("RCS取消任务响应耗时:{}ms", (endTime - startTime)); |
| | | log.info("RCS取消任务响应状态码:{}", exchange.getStatusCode()); |
| | | log.info("RCS取消任务响应体:{}", exchange.getBody()); |
| | | |
| | | if (Objects.isNull(exchange.getBody())) { |
| | | log.error("RCS取消任务失败:响应体为空"); |
| | | throw new CoolException("RCS取消任务失败:响应体为空"); |
| | | } |
| | | |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | objectMapper.coercionConfigDefaults() |
| | | .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty); |
| | | CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class); |
| | | |
| | | if (result.getCode() == 200) { |
| | | log.info("========== RCS任务取消成功 =========="); |
| | | log.info("成功取消的RCS任务编号:{}", rcsTaskCodes); |
| | | rcsCancelSuccess = true; |
| | | } else { |
| | | log.error("RCS取消任务失败:{}", result.getMsg()); |
| | | throw new CoolException("RCS取消任务失败:" + result.getMsg()); |
| | | } |
| | | } catch (JsonProcessingException e) { |
| | | log.error("RCS取消任务响应解析失败:{}", e.getMessage(), e); |
| | | throw new CoolException("RCS取消任务响应解析失败:" + e.getMessage()); |
| | | } catch (Exception e) { |
| | | log.error("RCS取消任务异常:{}", e.getMessage(), e); |
| | | throw new CoolException("RCS取消任务异常:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | // 查询符合取消条件的任务(状态为1、101、199) |
| | | List<Integer> allowedStatuses = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id, TaskStsType.WAVE_SEED.id); |
| | | List<Task> tasks = this.list(new LambdaQueryWrapper<Task>() |
| | | .in(Task::getTaskType, list) |
| | | .in(Task::getId, ids) |
| | | .in(Task::getTaskStatus, longs)); |
| | | if (tasks.isEmpty()) { |
| | | .in(Task::getId, (Object[]) ids) |
| | | .in(Task::getTaskStatus, allowedStatuses)); |
| | | |
| | | // 如果符合取消条件的任务为空,但RCS取消成功,允许继续(可能是任务状态已变更) |
| | | if (tasks.isEmpty() && !rcsCancelSuccess) { |
| | | throw new CoolException("任务已处执行状态不可取消!!"); |
| | | } |
| | | |
| | | // 如果符合取消条件的任务为空,但RCS取消成功,记录日志并返回 |
| | | if (tasks.isEmpty() && rcsCancelSuccess) { |
| | | log.warn("WMS内部任务状态不符合取消条件,但RCS任务已成功取消 - 任务ID:{}", Arrays.toString(ids)); |
| | | return R.ok("RCS任务已取消,WMS内部任务状态已变更"); |
| | | } |
| | | for (Task task : tasks) { |
| | | //取消移库任务 |
| | |
| | | public void pubTaskToWcs(List<Task> tasks) { |
| | | WcsTaskParams taskParams = new WcsTaskParams(); |
| | | List<TaskItemParam> items = new ArrayList<>(); |
| | | |
| | | // 设置批次编号(使用第一个任务的barcode,如果为null则使用任务编码) |
| | | String batchNo = null; |
| | | if (!tasks.isEmpty()) { |
| | | Task firstTask = tasks.get(0); |
| | | batchNo = StringUtils.isNotBlank(firstTask.getBarcode()) |
| | | ? firstTask.getBarcode() |
| | | : firstTask.getTaskCode(); |
| | | } |
| | | if (StringUtils.isBlank(batchNo)) { |
| | | // 如果批次编号仍为空,生成一个默认值 |
| | | batchNo = "BATCH_" + System.currentTimeMillis(); |
| | | } |
| | | |
| | | taskParams.setBatchNo(batchNo); |
| | | log.info("设置批次编号:{}", batchNo); |
| | | |
| | | tasks.forEach(task -> { |
| | | TaskItemParam itemParam = new TaskItemParam(); |
| | | //任务类型,任务编码 |
| | | itemParam.setTaskType(RcsTaskType.getTypeDesc(task.getTaskType())) |
| | | .setSeqNum(task.getTaskCode()); |
| | | //主参数 |
| | | taskParams.setBatch(task.getBarcode()); |
| | | //任务编码(对应seqNum) |
| | | itemParam.setTaskNo(task.getTaskCode()); |
| | | itemParam.setPriority(1); |
| | | BasStation station = null; |
| | | if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | station = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, task.getTargSite())); |
| | |
| | | } |
| | | items.add(itemParam); |
| | | }); |
| | | taskParams.setTaskList(items); |
| | | taskParams.setTasks(items); |
| | | /**任务下发接口*/ |
| | | String pubTakUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask; |
| | | /**RCS基础配置链接*/ |
| | | log.info("任务下发,请求地址: {}, 请求参数: {}", pubTakUrl, JSONObject.toJSONString(taskParams)); |
| | | log.info("========== 开始下发任务到RCS =========="); |
| | | log.info("RCS请求地址:{}", pubTakUrl); |
| | | log.info("批次编号:{}", batchNo); |
| | | log.info("任务数量:{}", tasks.size()); |
| | | log.info("任务列表详情:"); |
| | | tasks.forEach(task -> { |
| | | log.info(" - 任务编码:{},任务类型:{},源库位:{},目标库位:{},源站点:{},目标站点:{}", |
| | | task.getTaskCode(), task.getTaskType(), task.getOrgLoc(), |
| | | task.getTargLoc(), task.getOrgSite(), task.getTargSite()); |
| | | }); |
| | | log.info("请求参数:{}", JSONObject.toJSONString(taskParams)); |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.add("Content-Type", "application/json"); |
| | | headers.add("api-version", "v2.0"); |
| | | HttpEntity httpEntity = new HttpEntity(taskParams, headers); |
| | | long startTime = System.currentTimeMillis(); |
| | | ResponseEntity<String> exchange = restTemplate.exchange(pubTakUrl, HttpMethod.POST, httpEntity, String.class); |
| | | log.info("任务下发后,响应结果: {}", exchange); |
| | | long endTime = System.currentTimeMillis(); |
| | | log.info("RCS响应耗时:{}ms", (endTime - startTime)); |
| | | log.info("RCS响应状态码:{}", exchange.getStatusCode()); |
| | | log.info("RCS响应头:{}", exchange.getHeaders()); |
| | | log.info("RCS响应体:{}", exchange.getBody()); |
| | | if (Objects.isNull(exchange.getBody())) { |
| | | throw new CoolException("任务下发失败!!"); |
| | | log.error("========== RCS任务下发失败 =========="); |
| | | log.error("RCS响应体为空,无法解析响应结果"); |
| | | log.error("请求地址:{}", pubTakUrl); |
| | | log.error("请求参数:{}", JSONObject.toJSONString(taskParams)); |
| | | throw new CoolException("任务下发失败,RCS响应体为空!!"); |
| | | } else { |
| | | try { |
| | | ObjectMapper objectMapper = new ObjectMapper(); |
| | | objectMapper.coercionConfigDefaults() |
| | | .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty); |
| | | CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class); |
| | | log.info("RCS响应解析结果 - code:{},msg:{},data:{}", |
| | | result.getCode(), result.getMsg(), result.getData()); |
| | | if (result.getCode() == 200) { |
| | | log.info("RCS任务下发成功,开始更新任务状态"); |
| | | tasks.forEach(task -> { |
| | | log.info("更新任务状态 - 任务编码:{},任务类型:{}", task.getTaskCode(), task.getTaskType()); |
| | | if (task.getTaskType().equals(TaskType.TASK_TYPE_IN.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_IN.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_IN.type) |
| | |
| | | throw new CoolException("站点不存在!!"); |
| | | } |
| | | |
| | | log.info("更新入库任务状态 - 任务编码:{},新状态:{}", task.getTaskCode(), TaskStsType.WCS_EXECUTE_IN.id); |
| | | if (!taskService.update(new LambdaUpdateWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode()) |
| | | .set(Task::getTaskStatus, TaskStsType.WCS_EXECUTE_IN.id))) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | log.info("入库任务状态更新成功 - 任务编码:{}", task.getTaskCode()); |
| | | /**排除移库功能*/ |
| | | if (!task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) { |
| | | /**如果是普通站点,修改站点状态为出库预约*/ |
| | |
| | | throw new CoolException("站点不存在!!"); |
| | | } |
| | | |
| | | log.info("更新出库任务状态 - 任务编码:{},新状态:{}", task.getTaskCode(), TaskStsType.WCS_EXECUTE_OUT.id); |
| | | if (!taskService.update(new LambdaUpdateWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode()) |
| | | .set(Task::getTaskStatus, TaskStsType.WCS_EXECUTE_OUT.id))) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | log.info("出库任务状态更新成功 - 任务编码:{}", task.getTaskCode()); |
| | | /**如果是普通站点,修改站点状态为入库预约*/ |
| | | if (curSta.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) { |
| | | curSta.setUseStatus(LocStsType.LOC_STS_TYPE_S.type); |
| | |
| | | } |
| | | } |
| | | }); |
| | | log.info("========== RCS任务下发完成,共{}个任务状态已更新 ==========", tasks.size()); |
| | | } else { |
| | | log.error(JSONObject.toJSONString(result)); |
| | | log.error("========== RCS任务下发失败 =========="); |
| | | log.error("RCS返回错误 - code:{},msg:{},data:{}", |
| | | result.getCode(), result.getMsg(), result.getData()); |
| | | log.error("失败的任务列表:"); |
| | | tasks.forEach(task -> { |
| | | log.error(" - 任务编码:{},任务类型:{}", task.getTaskCode(), task.getTaskType()); |
| | | }); |
| | | // throw new CoolException("任务下发失败!!"); |
| | | } |
| | | } catch (JsonProcessingException e) { |
| | | throw new CoolException(e.getMessage()); |
| | | log.error("========== RCS任务下发异常 =========="); |
| | | log.error("解析RCS响应失败,响应体:{}", exchange.getBody(), e); |
| | | throw new CoolException("解析RCS响应失败:" + e.getMessage()); |
| | | } catch (Exception e) { |
| | | log.error("========== RCS任务下发异常 =========="); |
| | | log.error("任务下发过程中发生异常", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | }/** |
| | | |
| | | |
| | |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.enums.PakinIOStatus; |
| | | import com.vincent.rsf.server.manager.mapper.WaitPakinMapper; |
| | | import com.vincent.rsf.server.manager.mapper.MatnrMapper; |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | |
| | | private TaskService taskService; |
| | | @Autowired |
| | | private TaskItemService taskItemService; |
| | | @Autowired |
| | | private MatnrMapper matnrMapper; |
| | | |
| | | |
| | | /** |
| | |
| | | WaitPakin pakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, waitPakin.getBarcode())); |
| | | |
| | | // 如果容器号已经组托过,提示已组托,请更换容器条码 |
| | | if (!Objects.isNull(pakin)) { |
| | | throw new CoolException("已组托,请更换容器条码"); |
| | | } |
| | | |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, waitPakin.getBarcode())); |
| | | if (!tasks.isEmpty()) { |
| | | throw new CoolException("当前托盘已有任务档在执行,不能再次组托!!"); |
| | | } |
| | | if (!Objects.isNull(pakin)) { |
| | | List<WaitPakinItem> waitPakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(!Objects.isNull(pakin.getId()) ,WaitPakinItem::getPakinId, pakin.getId())); |
| | | waitPakinItems.forEach(item -> { |
| | | for (PakinItem pakinItem : waitPakin.getItems()) { |
| | | WarehouseAreasItem warehouseAreasItems = warehouseAreasItemService.getById(pakinItem.getId()); |
| | | if (Objects.isNull(warehouseAreasItems)) { |
| | | throw new CoolException("物料未送至收货区或已组托完成移出收货区!!"); |
| | | } |
| | | if (item.getFieldsIndex().equals(warehouseAreasItems.getFieldsIndex())) { |
| | | throw new CoolException("票号:" + item.getExtendFields().get("crushNo") + "已组托,不可重复组托!!"); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | | // 检查容器号是否在库存中存在(通过库位表查询) |
| | | List<Loc> locs = locService.list(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, waitPakin.getBarcode())); |
| | | if (!locs.isEmpty()) { |
| | | List<String> locCodes = locs.stream().map(Loc::getCode).collect(Collectors.toList()); |
| | | String join = StringUtils.join(locCodes, ","); |
| | | throw new CoolException("托盘码:" + waitPakin.getBarcode() + "已被库位:" + join + "使用!!"); |
| | | throw new CoolException("在库,请更换容器条码"); |
| | | } |
| | | |
| | | double sum = waitPakin.getItems().stream().mapToDouble(PakinItem::getReceiptQty).sum(); |
| | | |
| | | WaitPakin waitPakin1 = new WaitPakin(); |
| | |
| | | |
| | | List<WaitPakinItem> items = new ArrayList<>(); |
| | | for (PakinItem pakinItem1 : waitPakin.getItems()) { |
| | | //不良标签组托 |
| | | WarehouseAreasItem warehouseAreasItems = warehouseAreasItemService.getById(pakinItem1.getId()); |
| | | if (null == warehouseAreasItems) { |
| | | throw new CoolException("物料未送至收货区!!"); |
| | | } |
| | | WaitPakinItem pakinItem = new WaitPakinItem(); |
| | | pakinItem.setAnfme(warehouseAreasItems.getAnfme()) |
| | | .setPakinId(waitPakin1.getId()) |
| | | .setSource(warehouseAreasItems.getId()) |
| | | .setAsnId(warehouseAreasItems.getAsnId()) |
| | | .setAsnCode(warehouseAreasItems.getAsnCode()) |
| | | .setAsnItemId(warehouseAreasItems.getAsnItemId()) |
| | | .setIsptResult(warehouseAreasItems.getIsptResult()) |
| | | .setPlatItemId(warehouseAreasItems.getPlatItemId()) |
| | | .setPlatOrderCode(warehouseAreasItems.getPlatOrderCode()) |
| | | .setPlatWorkCode(warehouseAreasItems.getPlatWorkCode()) |
| | | .setProjectCode(warehouseAreasItems.getProjectCode()) |
| | | .setBatch(warehouseAreasItems.getSplrBatch()) |
| | | .setUnit(warehouseAreasItems.getStockUnit()) |
| | | .setFieldsIndex(warehouseAreasItems.getFieldsIndex()) |
| | | .setMatnrId(warehouseAreasItems.getMatnrId()) |
| | | .setMaktx(warehouseAreasItems.getMaktx()) |
| | | .setUpdateBy(userId) |
| | | .setCreateBy(userId) |
| | | .setMatnrCode(warehouseAreasItems.getMatnrCode()); |
| | | WkOrder order = asnOrderService.getById(warehouseAreasItems.getAsnId()); |
| | | if (!Objects.isNull(order)) { |
| | | pakinItem.setType(null == order.getType() ? null : order.getType()) |
| | | .setWkType(null == order.getWkType() ? null : Short.parseShort(order.getWkType())); |
| | | } |
| | | |
| | | // 如果ASN单号为空,从物料信息表获取物料信息,不查询收货区 |
| | | if (StringUtils.isBlank(pakinItem1.getAsnCode())) { |
| | | if (Objects.isNull(pakinItem1.getMatnrId())) { |
| | | throw new CoolException("物料ID不能为空!!"); |
| | | } |
| | | |
| | | // 从物料信息表获取物料信息 |
| | | Matnr matnr = matnrMapper.selectById(pakinItem1.getMatnrId()); |
| | | if (Objects.isNull(matnr)) { |
| | | throw new CoolException("物料信息不存在,物料ID:" + pakinItem1.getMatnrId()); |
| | | } |
| | | |
| | | // 设置组托明细信息(不设置source,因为不在收货区) |
| | | pakinItem.setPakinId(waitPakin1.getId()) |
| | | .setSource(null) // 不在收货区,source设为null |
| | | .setAsnId(null) |
| | | .setAsnCode(null) |
| | | .setAsnItemId(null) |
| | | .setIsptResult(null) |
| | | .setPlatItemId(null) |
| | | .setPlatOrderCode(null) |
| | | .setPlatWorkCode(null) |
| | | .setProjectCode(null) |
| | | .setBatch(null) |
| | | .setUnit(matnr.getStockUnit()) |
| | | .setFieldsIndex(matnr.getFieldsIndex()) |
| | | .setMatnrId(matnr.getId()) |
| | | .setMaktx(matnr.getName()) |
| | | .setUpdateBy(userId) |
| | | .setCreateBy(userId) |
| | | .setMatnrCode(matnr.getCode()); |
| | | |
| | | // 设置组托数量 |
| | | if (pakinItem1.getReceiptQty() == null || pakinItem1.getReceiptQty().compareTo(0.0) <= 0) { |
| | | throw new CoolException("组托数量不能小于等于零!!"); |
| | | } |
| | | pakinItem.setAnfme(pakinItem1.getReceiptQty()) |
| | | .setTrackCode(pakinItem1.getTrackCode()); |
| | | } else { |
| | | // 有ASN单号,从收货区获取物料信息 |
| | | WarehouseAreasItem warehouseAreasItems = warehouseAreasItemService.getById(pakinItem1.getId()); |
| | | if (null == warehouseAreasItems) { |
| | | throw new CoolException("物料未送至收货区!!"); |
| | | } |
| | | |
| | | pakinItem.setAnfme(warehouseAreasItems.getAnfme()) |
| | | .setPakinId(waitPakin1.getId()) |
| | | .setSource(warehouseAreasItems.getId()) |
| | | .setAsnId(warehouseAreasItems.getAsnId()) |
| | | .setAsnCode(warehouseAreasItems.getAsnCode()) |
| | | .setAsnItemId(warehouseAreasItems.getAsnItemId()) |
| | | .setIsptResult(warehouseAreasItems.getIsptResult()) |
| | | .setPlatItemId(warehouseAreasItems.getPlatItemId()) |
| | | .setPlatOrderCode(warehouseAreasItems.getPlatOrderCode()) |
| | | .setPlatWorkCode(warehouseAreasItems.getPlatWorkCode()) |
| | | .setProjectCode(warehouseAreasItems.getProjectCode()) |
| | | .setBatch(warehouseAreasItems.getSplrBatch()) |
| | | .setUnit(warehouseAreasItems.getStockUnit()) |
| | | .setFieldsIndex(warehouseAreasItems.getFieldsIndex()) |
| | | .setMatnrId(warehouseAreasItems.getMatnrId()) |
| | | .setMaktx(warehouseAreasItems.getMaktx()) |
| | | .setUpdateBy(userId) |
| | | .setCreateBy(userId) |
| | | .setMatnrCode(warehouseAreasItems.getMatnrCode()); |
| | | |
| | | WkOrder order = asnOrderService.getById(warehouseAreasItems.getAsnId()); |
| | | if (!Objects.isNull(order)) { |
| | | pakinItem.setType(null == order.getType() ? null : order.getType()) |
| | | .setWkType(null == order.getWkType() ? null : Short.parseShort(order.getWkType())); |
| | | } |
| | | |
| | | for (PakinItem waitPakinItem : waitPakin.getItems()) { |
| | | if (waitPakinItem.getId().equals(warehouseAreasItems.getId())) { |
| | | if (waitPakinItem.getReceiptQty() > warehouseAreasItems.getAnfme() || waitPakinItem.getReceiptQty().compareTo(0.0) <= 0) { |
| | | throw new CoolException("组托数量不能大于收货数量且不能小于零!!"); |
| | | for (PakinItem waitPakinItem : waitPakin.getItems()) { |
| | | if (waitPakinItem.getId().equals(warehouseAreasItems.getId())) { |
| | | if (waitPakinItem.getReceiptQty() > warehouseAreasItems.getAnfme() || waitPakinItem.getReceiptQty().compareTo(0.0) <= 0) { |
| | | throw new CoolException("组托数量不能大于收货数量且不能小于零!!"); |
| | | } |
| | | pakinItem.setAnfme(waitPakinItem.getReceiptQty()) |
| | | .setTrackCode(waitPakinItem.getTrackCode()); |
| | | } |
| | | pakinItem.setAnfme(waitPakinItem.getReceiptQty()) |
| | | .setTrackCode(waitPakinItem.getTrackCode()); |
| | | } |
| | | } |
| | | |
| | | items.add(pakinItem); |
| | | } |
| | | |
| | |
| | | // Double total = Math.round((sum + waitSum) * 100) / 100.0; |
| | | |
| | | for (WaitPakinItem pakinItem : items) { |
| | | // 如果source为空(没有ASN单号,不在收货区),跳过收货区更新 |
| | | if (Objects.isNull(pakinItem.getSource())) { |
| | | continue; |
| | | } |
| | | |
| | | WarehouseAreasItem one = warehouseAreasItemService.getOne(new LambdaQueryWrapper<WarehouseAreasItem>() |
| | | .eq(WarehouseAreasItem::getId, pakinItem.getSource())); |
| | | if (Objects.isNull(one)) { |
| | |
| | | notify-inspect: /report/inspect |
| | | rcs: |
| | | #链接 |
| | | host: http://127.0.0.1 |
| | | host: http://10.10.10.200 |
| | | #端口 |
| | | port: 8081 |
| | | port: 8088 |
| | | |
| | | #仓库功能参数配置 |
| | | stock: |