chen.lin
13 小时以前 8f8b6a47ce277cad0b640c328ab3962419e1c024
RCS完整流程
2个文件已添加
8个文件已修改
497 ■■■■ 已修改文件
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/LocationAllocateResponse.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/LocationAllocateParams.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsRcsService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsRcsServiceImpl.java 102 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/WcsService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/domain/PageParam.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java
@@ -5,6 +5,7 @@
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import com.vincent.rsf.openApi.entity.params.ExMsgCallbackParams;
import com.vincent.rsf.openApi.entity.params.LocSiteParams;
import com.vincent.rsf.openApi.entity.params.LocationAllocateParams;
import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams;
import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam;
import com.vincent.rsf.openApi.entity.params.TaskReportParams;
@@ -116,4 +117,29 @@
        return wmsRcsService.reportTask(params);
    }
    /**
     * @author Ryan
     * @date 2026/2/6
     * @description: 申请入库任务
     * @version 1.0
     */
    @ApiOperation("申请入库任务")
    @PostMapping("/api/open/location/allocate")
    public R allocateLocation(@RequestBody LocationAllocateParams params) {
        log.info("申请入库任务,请求参数:{}", params);
        if (Objects.isNull(params)) {
            return R.error("参数不能为空!!");
        }
        if (Objects.isNull(params.getBarcode()) || params.getBarcode().isEmpty()) {
            return R.error("料箱码不能为空!!");
        }
        if (Objects.isNull(params.getStaNo()) || params.getStaNo().isEmpty()) {
            return R.error("入库站点不能为空!!");
        }
        if (Objects.isNull(params.getType())) {
            params.setType(18);
        }
        return wmsRcsService.allocateLocation(params);
    }
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/LocationAllocateResponse.java
New file
@@ -0,0 +1,19 @@
package com.vincent.rsf.openApi.entity.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@ApiModel(value = "LocationAllocateResponse", description = "申请入库任务返回结果")
public class LocationAllocateResponse implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty("库位号")
    private String locNo;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/LocationAllocateParams.java
New file
@@ -0,0 +1,25 @@
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;
@Data
@Accessors(chain = true)
@ApiModel(value = "LocationAllocateParams", description = "申请入库任务参数")
public class LocationAllocateParams implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "料箱码", required = true)
    private String barcode;
    @ApiModelProperty(value = "入库站点", required = true)
    private String staNo;
    @ApiModelProperty(value = "入库类型", required = true)
    private Integer type;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsRcsService.java
@@ -8,6 +8,7 @@
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.LocationAllocateParams;
import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams;
import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam;
import com.vincent.rsf.openApi.entity.params.TaskReportParams;
@@ -28,4 +29,6 @@
    R modifyLocOrSite(LocSiteParams params);
    CommonResponse reportTask(TaskReportParams params);
    R allocateLocation(LocationAllocateParams params);
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsRcsServiceImpl.java
@@ -15,8 +15,10 @@
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.dto.LocationAllocateResponse;
import com.vincent.rsf.openApi.entity.params.ExMsgCallbackParams;
import com.vincent.rsf.openApi.entity.params.LocSiteParams;
import com.vincent.rsf.openApi.entity.params.LocationAllocateParams;
import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams;
import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam;
import com.vincent.rsf.openApi.entity.params.TaskReportParams;
@@ -335,7 +337,7 @@
     */
    @Override
    public CommonResponse reportTask(TaskReportParams params) {
        log.info("任务执行通知上报,请求参数: {}", JSONObject.toJSONString(params));
        log.info("RCS回调,请求参数: {}", JSONObject.toJSONString(params));
        
        // 参数校验
        if (Objects.isNull(params)) {
@@ -362,21 +364,22 @@
        
        // 将任务上报回调转发到WMS系统
        String callUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + WmsConstant.callBack;
        log.info("任务执行通知上报,请求地址: {}, 转换后参数: {}", callUrl, exMsgParams.toJSONString());
        log.info("RCS回调-WMS-SERVER任务上报-,请求地址: {}, 转换后参数: {}", 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);
        log.info("RCS回调-WMS-SERVER任务上报-,响应结果: {}", exchange);
        
        if (Objects.isNull(exchange.getBody())) {
            // 如果回调失败,返回成功响应(避免RCS重复回调)
            CommonResponse response = new CommonResponse();
            response.setCode(200);
            response.setMsg("接收成功");
            log.warn("任务执行通知上报回调失败,但返回成功响应,任务编号:{},批次:{}", params.getTaskNo(), params.getBatchNo());
            return response;
            // 回调失败,抛出异常
            log.error("========== RCS回调-WMS-SERVER任务上报-失败 ==========");
            log.error("RCS回调-WMS-SERVER任务上报-响应体为空,无法解析响应结果");
            log.error("RCS回调-WMS-SERVER任务上报-请求地址:{}", callUrl);
            log.error("RCS回调-WMS-SERVER任务上报-请求参数:{}", exMsgParams.toJSONString());
            log.error("RCS回调-WMS-SERVER任务上报-失败的任务编号:{},批次:{}", params.getTaskNo(), params.getBatchNo());
            throw new CoolException("RCS回调-WMS-SERVER任务上报-失败,WMS响应体为空!任务编号:" + params.getTaskNo() + ",批次:" + params.getBatchNo());
        } else {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults()
@@ -384,14 +387,14 @@
            try {
                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
                if (result.getCode() == 200) {
                    log.info("任务执行通知上报成功,任务编号:{},批次:{}", params.getTaskNo(), params.getBatchNo());
                    log.info("RCS回调-WMS-SERVER任务上报-成功,任务编号:{},批次:{}", params.getTaskNo(), params.getBatchNo());
                    return result;
                } else {
                    log.warn("任务执行通知上报回调返回非200状态,任务编号:{},批次:{},响应:{}", params.getTaskNo(), params.getBatchNo(), exchange.getBody());
                    log.warn("RCS回调-WMS-SERVER任务上报-返回非200状态,任务编号:{},批次:{},响应:{}", params.getTaskNo(), params.getBatchNo(), exchange.getBody());
                    return result;
                }
            } catch (JsonProcessingException e) {
                log.error("任务执行通知上报回调响应解析失败,任务编号:{},批次:{},错误:{}", params.getTaskNo(), params.getBatchNo(), e.getMessage());
                log.error("RCS回调-WMS-SERVER任务上报-响应解析失败,任务编号:{},批次:{},错误:{}", params.getTaskNo(), params.getBatchNo(), e.getMessage());
                // 解析失败时返回成功响应,避免RCS重复回调
                CommonResponse response = new CommonResponse();
                response.setCode(200);
@@ -400,4 +403,79 @@
            }
        }
    }
    /**
     * @author Ryan
     * @date 2026/2/6
     * @description: 申请入库任务,分配库位
     * @version 1.0
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R allocateLocation(LocationAllocateParams params) {
        log.info("========== 开始申请入库任务,分配库位 ==========");
        log.info("料箱码:{},入库站点:{},入库类型:{}", params.getBarcode(), params.getStaNo(), params.getType());
        // 调用WMS server内部接口进行库位分配
        String wmsUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + "/rsf-server/wcs/allocate/location";
        log.info("WMS请求地址:{}", wmsUrl);
        // 构建请求参数
        JSONObject requestParams = new JSONObject();
        requestParams.put("barcode", params.getBarcode());
        requestParams.put("staNo", params.getStaNo());
        requestParams.put("type", params.getType());
        log.info("请求参数:{}", requestParams.toJSONString());
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        HttpEntity httpEntity = new HttpEntity(requestParams, headers);
        long startTime = System.currentTimeMillis();
        ResponseEntity<String> exchange = restTemplate.exchange(wmsUrl, HttpMethod.POST, httpEntity, String.class);
        long endTime = System.currentTimeMillis();
        log.info("WMS响应耗时:{}ms", (endTime - startTime));
        log.info("WMS响应状态码:{}", exchange.getStatusCode());
        log.info("WMS响应体:{}", exchange.getBody());
        if (Objects.isNull(exchange.getBody())) {
            log.error("========== 申请入库任务失败 ==========");
            log.error("WMS响应体为空,无法解析响应结果");
            return R.error("申请入库任务失败,WMS响应体为空!!");
        } else {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults()
                    .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            try {
                JSONObject responseJson = JSONObject.parseObject(exchange.getBody());
                Integer code = responseJson.getInteger("code");
                String msg = responseJson.getString("msg");
                if (code != null && code == 200) {
                    JSONObject data = responseJson.getJSONObject("data");
                    if (data != null) {
                        String locNo = data.getString("locNo");
                        log.info("========== 申请入库任务成功 ==========");
                        log.info("分配库位号:{}", locNo);
                        LocationAllocateResponse response = new LocationAllocateResponse();
                        response.setLocNo(locNo);
                        return R.ok(response);
                    } else {
                        log.error("========== 申请入库任务失败 ==========");
                        log.error("响应数据为空");
                        return R.error("申请入库任务失败,响应数据为空!!");
                    }
                } else {
                    log.error("========== 申请入库任务失败 ==========");
                    log.error("WMS返回错误 - code:{},msg:{}", code, msg);
                    return R.error(msg != null ? msg : "申请入库任务失败!!");
                }
            } catch (Exception e) {
                log.error("========== 申请入库任务异常 ==========");
                log.error("解析WMS响应失败,响应体:{}", exchange.getBody(), e);
                return R.error("解析WMS响应失败:" + e.getMessage());
            }
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/controller/WcsController.java
@@ -18,6 +18,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/wcs")
@Api(tags = "wcs接口对接")
@@ -121,5 +123,34 @@
        return wcsService.receiveExMsg(params);
    }
    /**
     * @author Ryan
     * @date 2026/2/6
     * @description: 申请入库任务,分配库位(内部接口)
     * @version 1.0
     */
    @ApiOperation("申请入库任务,分配库位")
    @PostMapping("/allocate/location")
    public R allocateLocation(@RequestBody Map<String, Object> params) {
        if (Cools.isEmpty(params)) {
            return R.error("参数不能为空!!");
        }
        String barcode = (String) params.get("barcode");
        String staNo = (String) params.get("staNo");
        Integer type = params.get("type") != null ? Integer.valueOf(params.get("type").toString()) : null;
        if (Cools.isEmpty(barcode)) {
            return R.error("料箱码不能为空!!");
        }
        if (Cools.isEmpty(staNo)) {
            return R.error("入库站点不能为空!!");
        }
        if (type == null) {
            return R.error("入库类型不能为空!!");
        }
        return wcsService.allocateLocation(barcode, staNo, type);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/WcsService.java
@@ -18,4 +18,6 @@
    R receiveExMsg(ExMsgParams params);
    R pubWcsTask(WcsTaskParams params);
    R allocateLocation(String barcode, String staNo, Integer type);
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java
@@ -914,6 +914,7 @@
        int deviceNo = 0;
        Loc loc = new Loc();
        InTaskMsgDto inTaskMsgDto = new InTaskMsgDto();
        locTypeDto.setLocType1(18);
        List<Loc> loc1 = locService.list(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getAreaId, area)
                .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
@@ -969,4 +970,46 @@
        inTaskMsgDto.setLocNo(locNo);
        return inTaskMsgDto;
    }
    /**
     * @author Ryan
     * @date 2026/2/6
     * @description: 申请入库任务,分配库位
     * @version 1.0
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R allocateLocation(String barcode, String staNo, Integer type) {
        log.info("========== 开始申请入库任务,分配库位 ==========");
        log.info("料箱码:{},入库站点:{},入库类型:{}", barcode, staNo, type);
        // 构建 TaskInParam 参数,与 /wcs/create/in/task 接口参数一致
        TaskInParam param = new TaskInParam();
        param.setBarcode(barcode);
        param.setSourceStaNo(staNo);
        param.setIoType(TaskType.TASK_TYPE_IN.type); // 入库类型
        param.setLocType1(type); // 库位类型(高低检测信号)
        param.setUser(1L); // 默认用户ID,可以根据实际需求调整
        // 调用 createInTask 方法,创建完整的入库任务
        // 该方法会执行以下流程:
        // 1. 验证设备站点
        // 2. 验证组拖状态
        // 3. 生成任务编码
        // 4. 获取库位号
        // 5. 创建并保存任务
        // 6. 更新库位状态
        // 7. 获取并验证组拖明细
        // 8. 创建并保存任务明细
        // 9. 更新组托状态
        InTaskMsgDto msgDto = createInTask(param);
        log.info("========== 申请入库任务成功 ==========");
        log.info("任务编码:{},库位号:{}", msgDto.getWorkNo(), msgDto.getLocNo());
        // 返回结果,只返回库位号(根据接口文档要求)
        JSONObject result = new JSONObject();
        result.put("locNo", msgDto.getLocNo());
        return R.ok(result);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/common/domain/PageParam.java
@@ -320,8 +320,10 @@
//                                column, temp[0], cls != null ? cls.getSimpleName() : "null", requestInfo);
                        continue;
                    }
                    // 检查实体类字段的 @TableField 注解,如果字段是保留关键字,使用注解中指定的列名(带反引号)
                    String dbColumn = getDbColumnName(column);
                    boolean asc = temp.length == 1 || !temp[temp.length - 1].toLowerCase().equals(ORDER_DESC_VALUE);
                    orders.add(new OrderItem(column, asc));
                    orders.add(new OrderItem(dbColumn, asc));
                }
            }
        }
@@ -329,6 +331,57 @@
    }
    /**
     * 获取数据库列名,如果是保留关键字则添加反引号
     * @param fieldName 字段名(下划线格式)
     * @return 数据库列名
     */
    private String getDbColumnName(String fieldName) {
        if (cls == null) {
            // 如果没有实体类,检查是否是常见的保留关键字
            return wrapReservedKeyword(fieldName);
        }
        // 查找实体类中对应的字段
        for (Field field : Cools.getAllFields(cls)) {
            String column = Utils.toSymbolCase(field.getName(), '_');
            if (column.equals(fieldName)) {
                // 检查是否有 @TableField 注解
                if (field.isAnnotationPresent(TableField.class)) {
                    TableField annotation = field.getAnnotation(TableField.class);
                    String value = annotation.value();
                    // 如果注解值不为空,使用注解值(可能已经包含反引号)
                    if (!Cools.isEmpty(value) && !value.equals(fieldName)) {
                        return value;
                    }
                }
                // 如果没有注解或注解值为空,检查是否是保留关键字
                return wrapReservedKeyword(fieldName);
            }
        }
        // 如果找不到字段,检查是否是保留关键字
        return wrapReservedKeyword(fieldName);
    }
    /**
     * 如果是 MySQL 保留关键字,则添加反引号
     * @param column 列名
     * @return 处理后的列名
     */
    private String wrapReservedKeyword(String column) {
        // MySQL 常见保留关键字列表
        Set<String> reservedKeywords = new HashSet<>(Arrays.asList(
            "row", "type", "length", "channel", "status", "order", "group", "key", "index",
            "table", "database", "user", "select", "insert", "update", "delete", "from", "where"
        ));
        if (reservedKeywords.contains(column.toLowerCase())) {
            return "`" + column + "`";
        }
        return column;
    }
    /**
     * 获取当前请求信息(路径和方法)
     * @return 请求信息字符串,格式:HTTP方法 请求路径
     */
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -1516,26 +1516,23 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pubTaskToWcs(List<Task> tasks) {
        WcsTaskParams taskParams = new WcsTaskParams();
        List<TaskItemParam> items = new ArrayList<>();
        /**任务下发接口*/
        String pubTakUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask;
        
        // 设置批次编号(使用第一个任务的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 -> {
        for (Task task : tasks) {
            WcsTaskParams taskParams = new WcsTaskParams();
            List<TaskItemParam> items = new ArrayList<>();
            // 设置批次编号(使用当前任务的任务编码)
            String batchNo = task.getTaskCode();
            if (StringUtils.isBlank(batchNo)) {
                // 如果任务编号为空,生成一个默认值
                batchNo = "BATCH_" + System.currentTimeMillis();
            }
            taskParams.setBatchNo(batchNo);
            log.info("任务批次编号:{}", batchNo);
            // 构建当前任务的参数
            TaskItemParam itemParam = new TaskItemParam();
            //任务编码(对应seqNum)
            itemParam.setTaskNo(task.getTaskCode());
@@ -1597,58 +1594,91 @@
                        throw new CoolException("当前站点不是F.在库状态!!");
                    }
                }
                if (station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) {
                    if (!station.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) {
                // 站点间移库需要获取目标站点
                BasStation targetStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>().eq(BasStation::getStationName, task.getTargSite()));
                if (Objects.isNull(targetStation)) {
                    throw new CoolException("目标站点不存在!!");
                }
                if (targetStation.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) {
                    if (!targetStation.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) {
                        throw new CoolException("目标站点不是O.空闲状态!!");
                    }
                }
            }
            items.add(itemParam);
        });
        taskParams.setTasks(items);
        /**任务下发接口*/
        String pubTakUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask;
        /**RCS基础配置链接*/
        log.info("========== 开始下发任务到RCS ==========");
        log.info("RCS请求地址:{}", pubTakUrl);
        log.info("批次编号:{}", batchNo);
        log.info("任务数量:{}", tasks.size());
        log.info("任务列表详情:");
        tasks.forEach(task -> {
            log.info("  - 任务编码:{},任务类型:{},源库位:{},目标库位:{},源站点:{},目标站点:{}",
            taskParams.setTasks(items);
            // 记录当前任务信息
            log.info("  RCS- 任务编码:{},任务类型:{},源库位:{},目标库位:{},源站点:{},目标站点:{}",
                    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);
        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("请求地址:{}", pubTakUrl);
            log.error("请求参数:{}", JSONObject.toJSONString(taskParams));
            throw new CoolException("任务下发失败,RCS响应体为空!!");
        } else {
            log.info("RCS-请求参数:{}", 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);
            /**RCS基础配置链接*/
            log.info("========== 开始下发任务到RCS ==========");
            log.info("请求RCS-地址:{}", pubTakUrl);
            log.info("请求RCS-参数:{}", JSONObject.toJSONString(taskParams));
            long startTime = System.currentTimeMillis();
            ResponseEntity<String> exchange = null;
            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());
                exchange = restTemplate.exchange(pubTakUrl, 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());
            } catch (org.springframework.web.client.ResourceAccessException e) {
                long endTime = System.currentTimeMillis();
                log.error("========== RCS任务下发资源访问异常 ==========");
                log.error("请求RCS-资源访问异常(可能包含连接超时),耗时:{}ms,任务编码:{}", (endTime - startTime), task.getTaskCode(), e);
                log.error("请求RCS-请求地址:{}", pubTakUrl);
                log.error("请求RCS-请求参数:{}", JSONObject.toJSONString(taskParams));
                // 检查是否是连接超时异常
                Throwable cause = e.getCause();
                String errorMsg = e.getMessage();
                if (cause instanceof java.net.SocketTimeoutException ||
                    (cause instanceof java.net.ConnectException && cause.getMessage() != null && cause.getMessage().contains("timed out")) ||
                    (errorMsg != null && (errorMsg.contains("Connection timed out") || errorMsg.contains("timed out") || errorMsg.contains("timeout")))) {
                    throw new CoolException("RCS连接超时,任务下发失败!任务编码:" + task.getTaskCode() + ",错误信息:" + errorMsg);
                }
                throw new CoolException("RCS资源访问异常,任务下发失败!任务编码:" + task.getTaskCode() + ",错误信息:" + errorMsg);
            } catch (Exception e) {
                long endTime = System.currentTimeMillis();
                log.error("========== RCS任务下发异常 ==========");
                log.error("请求RCS-异常,耗时:{}ms,任务编码:{}", (endTime - startTime), task.getTaskCode(), e);
                log.error("请求RCS-地址:{}", pubTakUrl);
                log.error("请求RCS-参数:{}", JSONObject.toJSONString(taskParams));
                String errorMsg = e.getMessage();
                // 检查是否是连接超时相关的异常
                if (errorMsg != null && (errorMsg.contains("Connection timed out") || errorMsg.contains("timed out") || errorMsg.contains("timeout"))) {
                    throw new CoolException("RCS连接超时,任务下发失败!任务编码:" + task.getTaskCode() + ",错误信息:" + errorMsg);
                }
                throw new CoolException("RCS任务下发异常!任务编码:" + task.getTaskCode() + ",错误信息:" + errorMsg);
            }
            if (Objects.isNull(exchange) || Objects.isNull(exchange.getBody())) {
                log.error("========== RCS任务下发失败 ==========");
                log.error("请求RCS-RCS响应体为空,无法解析响应结果");
                log.error("请求RCS-地址:{}", pubTakUrl);
                log.error("请求RCS-参数:{}", JSONObject.toJSONString(taskParams));
                log.error("请求RCS-失败的任务编码:{}", task.getTaskCode());
                throw new CoolException("任务下发失败,RCS响应体为空!!任务编码:" + task.getTaskCode());
            } 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任务下发成功,开始更新任务状态 - 任务编码:{}", task.getTaskCode());
                        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)
@@ -1701,28 +1731,25 @@
                                }
                            }
                        }
                    });
                    log.info("========== RCS任务下发完成,共{}个任务状态已更新 ==========", tasks.size());
                } else {
                    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("任务下发失败!!");
                    } else {
                        log.error("========== RCS任务下发失败 ==========");
                        log.error("RCS返回错误 - code:{},msg:{},data:{}",
                                result.getCode(), result.getMsg(), result.getData());
                        log.error("失败的任务编码:{},任务类型:{}", task.getTaskCode(), task.getTaskType());
                        throw new CoolException("任务下发失败!!任务编码:" + task.getTaskCode());
                    }
                } catch (JsonProcessingException e) {
                    log.error("========== RCS任务下发异常 ==========");
                    log.error("解析RCS响应失败,响应体:{},任务编码:{}", exchange.getBody(), task.getTaskCode(), e);
                    throw new CoolException("解析RCS响应失败:" + e.getMessage() + ",任务编码:" + task.getTaskCode());
                } catch (Exception e) {
                    log.error("========== RCS任务下发异常 ==========");
                    log.error("任务下发过程中发生异常,任务编码:{}", task.getTaskCode(), e);
                    throw e;
                }
            } 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;
            }
        }
        log.info("========== RCS任务下发完成,共{}个任务已处理 ==========", tasks.size());
    }/**