8308e0c162deafb0855f2a20bad3de923ae6a87f..f99e3966686d3891b814ff28d200b001fcdc8e1e
16 小时以前 chen.lin
组托优化
f99e39 对比 | 目录
21 小时以前 chen.lin
RCS地址
6a91d0 对比 | 目录
21 小时以前 chen.lin
启动扫描报错修复
4c1ac9 对比 | 目录
21 小时以前 chen.lin
调整库存-数字为空检查
6e1273 对比 | 目录
21 小时以前 chen.lin
RCS对接
e9543f 对比 | 目录
1个文件已添加
21个文件已修改
1156 ■■■■ 已修改文件
rsf-admin/src/page/basicInfo/matnr/PrintModal.jsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/task/TaskList.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/RcsConstant.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/RcsPubTaskParams.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/TaskReportParams.java 29 ●●●●● 补丁 | 查看 | 原始文档 | 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 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/ServerBoot.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/entity/constant/RcsConstant.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/entity/params/TaskItemParam.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/entity/params/WcsTaskParams.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/MobileServiceImpl.java 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/ReviseLogItem.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaitPakinServiceImpl.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/application-prod.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/matnr/PrintModal.jsx
@@ -226,14 +226,6 @@
                }
            }))
            setData(val)
            setTimeout(() => {
                val.forEach((el) => {
                    jsbarcode(`#barcode${el.code}`, el.code, { height: 30 });
                });
            }, 10);
        } else {
            notify(res.data.msg);
        }
@@ -243,8 +235,26 @@
        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 (
@@ -275,8 +285,7 @@
                                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>
rsf-admin/src/page/task/TaskList.jsx
@@ -357,7 +357,7 @@
        }
    }
    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 />}
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
@@ -137,6 +137,7 @@
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "warehouseId", order: "desc" }}
                // disableSyncWithLocation={false}
                actions={(
                    <TopToolbar>
                        <FilterButton />
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java
@@ -7,9 +7,11 @@
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;
@@ -19,6 +21,7 @@
import java.util.Map;
import java.util.Objects;
@Slf4j
@RestController
@Api("RCS调度交互接口")
@RequestMapping("/rcs")
@@ -97,5 +100,20 @@
        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);
    }
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/RcsConstant.java
@@ -8,4 +8,6 @@
    public static String modifystatus = "/api/open/modify/status";
    public static String cancelTask = "/api/open/task/cancel";
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/RcsPubTaskParams.java
@@ -14,10 +14,10 @@
public class RcsPubTaskParams implements Serializable {
    @ApiModelProperty("批次")
    private String batch;
    private String batchNo;
    @ApiModelProperty("任务明细")
    private List<TaskParam> taskList;
    private List<TaskParam> tasks;
}
@Data
@@ -25,11 +25,8 @@
@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;
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/TaskReportParams.java
New file
@@ -0,0 +1,29 @@
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;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsRcsService.java
@@ -10,6 +10,7 @@
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;
@@ -25,4 +26,6 @@
    List<SyncLocsDto> syncLocs(SyncRcsLocsParam params);
    R modifyLocOrSite(LocSiteParams params);
    CommonResponse reportTask(TaskReportParams params);
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsRcsServiceImpl.java
@@ -19,6 +19,7 @@
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;
@@ -60,28 +61,57 @@
    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;
            }
        }
    }
@@ -93,8 +123,66 @@
     * @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());
            }
        }
    }
    /**
@@ -105,6 +193,24 @@
     */
    @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));
@@ -192,7 +298,7 @@
    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");
@@ -220,4 +326,78 @@
            }
        }
    }
    /**
     * @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;
            }
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/ServerBoot.java
@@ -2,8 +2,10 @@
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) {
rsf-server/src/main/java/com/vincent/rsf/server/api/entity/constant/RcsConstant.java
@@ -2,8 +2,8 @@
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";
@@ -16,4 +16,7 @@
    //上报站点状态
    public static String REPORT_SITE_STATUS = "/rsf-open-api/rcs/modify/status";
    //取消RCS任务
    public static String cancelTask = "/api/open/task/cancel";
}
rsf-server/src/main/java/com/vincent/rsf/server/api/entity/params/TaskItemParam.java
@@ -10,11 +10,11 @@
@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;
rsf-server/src/main/java/com/vincent/rsf/server/api/entity/params/WcsTaskParams.java
@@ -14,9 +14,9 @@
@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;
}
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/MobileServiceImpl.java
@@ -35,6 +35,8 @@
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;
@@ -57,6 +59,8 @@
 */
@Service
public class MobileServiceImpl implements MobileService {
    private static final Logger logger = LoggerFactory.getLogger(MobileServiceImpl.class);
    @Value("${super.pwd}")
    private String superPwd;
@@ -544,9 +548,9 @@
        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("容器号不能为空!!");
//        }
@@ -561,29 +565,215 @@
//            }
//        }
        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);
    }
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java
@@ -463,12 +463,18 @@
    @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)) {
@@ -493,21 +499,40 @@
                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)
@@ -519,22 +544,45 @@
                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));
    }
@@ -547,7 +595,7 @@
    @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");
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/ReviseLogItem.java
@@ -252,7 +252,9 @@
    }
    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$(){
rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
@@ -8,7 +8,6 @@
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;
@@ -252,6 +251,7 @@
    @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,
@@ -261,13 +261,16 @@
                .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) {
@@ -282,7 +285,9 @@
//            }
//        }
        /**下发普通站点任务,报错回滚,不再往下执行*/
        log.info("开始下发{}个任务到RCS", tasks.size());
        taskService.pubTaskToWcs(tasks);
        log.info("定时任务执行完成:任务下发到RCS");
    }
    /**
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -19,6 +19,7 @@
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;
@@ -40,6 +41,7 @@
 * @return
 * @time 2025/3/7 08:02
 */
@Slf4j
@Service("outStockServiceImpl")
public class OutStockServiceImpl extends ServiceImpl<AsnOrderMapper, WkOrder> implements OutStockService {
@@ -488,21 +490,83 @@
            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())
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java
@@ -6,6 +6,7 @@
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;
@@ -17,6 +18,9 @@
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;
@@ -40,6 +44,12 @@
    @Autowired
    private ReviseLogItemService reviseLogItemService;
    @Autowired
    private OutStockService outStockService;
    @Autowired
    private OutStockItemService outStockItemService;
    /**
     * 库存调整单明细添加
@@ -130,6 +140,42 @@
                    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()) {
@@ -166,14 +212,84 @@
                        .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("调整单修改失败!!");
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -777,16 +777,132 @@
    @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) {
            //取消移库任务
@@ -1402,13 +1518,28 @@
    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()));
@@ -1474,27 +1605,50 @@
            }
            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)
@@ -1507,10 +1661,12 @@
                                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)) {
                                /**如果是普通站点,修改站点状态为出库预约*/
@@ -1531,10 +1687,12 @@
                                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);
@@ -1544,15 +1702,27 @@
                            }
                        }
                    });
                    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;
            }
        }
    }/**
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaitPakinServiceImpl.java
@@ -8,6 +8,7 @@
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;
@@ -41,6 +42,8 @@
    private TaskService taskService;
    @Autowired
    private TaskItemService taskItemService;
    @Autowired
    private MatnrMapper matnrMapper;
    /**
@@ -63,31 +66,22 @@
        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();
@@ -112,46 +106,90 @@
        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);
        }
@@ -167,6 +205,11 @@
//        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)) {
rsf-server/src/main/resources/application-prod.yml
@@ -80,9 +80,9 @@
        notify-inspect: /report/inspect
    rcs:
      #链接
      host: http://127.0.0.1
      host: http://10.10.10.200
      #端口
      port: 8081
      port: 8088
#仓库功能参数配置
stock: