package com.vincent.rsf.openApi.service.impl; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.cfg.CoercionAction; import com.fasterxml.jackson.databind.cfg.CoercionInputShape; import com.vincent.rsf.framework.common.R; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.openApi.config.PlatformProperties; import com.vincent.rsf.openApi.entity.Loc; import com.vincent.rsf.openApi.entity.constant.WmsConstant; import com.vincent.rsf.openApi.entity.dto.CommonResponse; import com.vincent.rsf.openApi.entity.constant.RcsConstant; import com.vincent.rsf.openApi.entity.dto.SyncLocsDto; import com.vincent.rsf.openApi.entity.params.ExMsgCallbackParams; 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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; import java.util.Objects; @Slf4j @Service("wmsRcsService") public class WmsRcsServiceImpl extends ServiceImpl implements WmsRcsService { @Autowired private PlatformProperties rcsApi; @Autowired private PlatformProperties.WmsApi wmsApi; @Autowired private RestTemplate restTemplate; /** * @author Ryan * @date 2025/8/27 * @description: 任务下发 * @version 1.0 */ @Override @Transactional(rollbackFor = Exception.class) public CommonResponse pubTasks(RcsPubTaskParams params) { /**RCS基础配置链接*/ String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask; 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 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())) { 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) { 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; } } } /** * @author Ryan * @date 2025/8/27 * @description: 取消任务 * @version 1.0 */ @Override @Transactional(rollbackFor = Exception.class) public CommonResponse cancelTasks(Map params) { /**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 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()); } } } /** * @author Ryan * @date 2025/8/27 * @description: RCS状态回写 * @version 1.0 */ @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)); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json"); headers.add("api-version", "v2.0"); HttpEntity httpEntity = new HttpEntity(params, headers); ResponseEntity exchange = restTemplate.exchange(callUrl, HttpMethod.POST, httpEntity, String.class); log.info("任务执行状态上报,响应结果: {}", exchange); if (Objects.isNull(exchange.getBody())) { return new CommonResponse(); } 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) { return result; } else { return result; // throw new CoolException("任务执行状态上报失败!!"); } } catch (JsonProcessingException e) { throw new CoolException(e.getMessage()); } } } /** * @author Ryan * @date 2025/8/27 * @description: 库位信息同步 * @version 1.0 */ @Override public List syncLocs(SyncRcsLocsParam params) { return syncRcsLocs(params); } /** * @author Ryan * @date 2025/11/10 * @description: 修改库位或站点状态 * @version 1.0 */ @Override public R modifyLocOrSite(LocSiteParams params) { /**RCS基础配置链接*/ String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.modifystatus; log.info("库位或站点状态修改,请求地址: {}, 请求参数: {}", rcsUrl , 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); ResponseEntity exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class); log.info("库位或站点状态修改,响应结果: {}", exchange); 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) { return R.ok(); } else { throw new CoolException("状态修改失败!!"); } } catch (JsonProcessingException e) { throw new CoolException(e.getMessage()); } } } /** * @author Ryan * @date 2025/8/28 * @description: 拉取RCS库位,同步至本地 * @version 1.0 */ @Transactional(rollbackFor = Exception.class) public List syncRcsLocs(SyncRcsLocsParam params) { /**RCS基础配置链接*/ String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.syncLocs; log.info("任务下发,请求地址2: {}, 请求参数: {}", rcsUrl , 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); ResponseEntity exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class); log.info("任务下发后,响应结果: {}", exchange); 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) { JSONObject jsonObject = JSONObject.parseObject(exchange.getBody()).getJSONObject("data"); List array = JSONArray.parseArray(jsonObject.getJSONArray("records").toJSONString(), SyncLocsDto.class); log.info("records结果: {}", array); return array; } else { throw new CoolException("任务下发失败!!"); } } catch (JsonProcessingException e) { throw new CoolException(e.getMessage()); } } } /** * @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 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; } } } }