chen.lin
14 小时以前 7a717a0bbc0eac8e494cd84207482e983ef56a3c
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java
@@ -1,12 +1,27 @@
package com.vincent.rsf.server.api.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.api.config.RemotesInfoProperties;
import com.vincent.rsf.server.api.entity.CommonResponse;
import com.vincent.rsf.server.api.entity.constant.RcsConstant;
import com.vincent.rsf.server.api.entity.dto.InTaskMsgDto;
import com.vincent.rsf.server.api.entity.dto.LocTypeDto;
import com.vincent.rsf.server.api.controller.params.TaskInParam;
import com.vincent.rsf.server.api.controller.erp.params.TaskInParam;
import com.vincent.rsf.server.api.entity.dto.SyncLocsDto;
import com.vincent.rsf.server.api.entity.enums.CallBackEvent;
import com.vincent.rsf.server.api.entity.params.CommonRequest;
import com.vincent.rsf.server.api.entity.params.ExMsgParams;
import com.vincent.rsf.server.api.entity.params.WcsTaskParams;
import com.vincent.rsf.server.manager.enums.*;
import com.vincent.rsf.server.api.service.WcsService;
import com.vincent.rsf.server.api.utils.LocUtils;
@@ -14,19 +29,34 @@
import com.vincent.rsf.server.manager.entity.*;
import com.vincent.rsf.server.manager.service.*;
import com.vincent.rsf.server.manager.service.impl.LocServiceImpl;
import com.vincent.rsf.server.system.utils.SystemAuthUtils;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.manager.enums.LocStsType;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Service
public class WcsServiceImpl implements WcsService {
    @Autowired
@@ -51,6 +81,12 @@
    private WaitPakinItemService waitPakinItemService;
    @Autowired
    private BasStationService basStationService;
    @Autowired
    private LocItemWorkingService locItemWorkingService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RemotesInfoProperties.RcsApi rcsApi;
    @Override
@@ -88,8 +124,179 @@
        // 验证设备站点
        DeviceSite deviceSite = validateDeviceSite(param);
        // 验证组拖状态
        WaitPakin waitPakin = validateWaitPakin(param.getBarcode());
        // 提前定义waitPakin,避免作用域问题
        WaitPakin waitPakin = null;
        // 先验证组拖状态,获取组托明细信息(用于批号匹配和单号检查)
        waitPakin = validateWaitPakin(param.getBarcode());
        List<WaitPakinItem> waitPakinItems = waitPakinItemService.list(
                new LambdaQueryWrapper<WaitPakinItem>()
                        .eq(WaitPakinItem::getPakinId, waitPakin.getId()));
        // 先检查是否有拣料入库任务(需要同时匹配箱号和批号)
        Task pickInTask = null;
        // 检查是否有盘点入库任务(需要同时匹配箱号和批号)
        Task checkInTask = null;
        if (!waitPakinItems.isEmpty()) {
            // 获取组托明细中的批号列表(去重)
            List<String> batchList = waitPakinItems.stream()
                    .map(WaitPakinItem::getBatch)
                    .filter(Objects::nonNull)
                    .filter(batch -> !batch.trim().isEmpty())
                    .distinct()
                    .collect(Collectors.toList());
            if (!batchList.isEmpty()) {
                log.info("检查组托明细批号 - 批号列表:{}", batchList);
                // 查询拣料入库任务:箱号匹配且状态为1或2
                List<Task> pickInTasks = taskService.list(new LambdaQueryWrapper<Task>()
                        .eq(Task::getBarcode, param.getBarcode())
                        .eq(Task::getTaskType, TaskType.TASK_TYPE_PICK_IN.type)
                        .in(Task::getTaskStatus, TaskStsType.GENERATE_IN.id, TaskStsType.WCS_EXECUTE_IN.id)
                        .orderByDesc(Task::getCreateTime));
                // 通过TaskItem的batch字段匹配批号
                for (Task task : pickInTasks) {
                    List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>()
                            .eq(TaskItem::getTaskId, task.getId())
                            .in(TaskItem::getBatch, batchList));
                    if (!taskItems.isEmpty()) {
                        pickInTask = task;
                        log.info("找到匹配的拣料入库任务(箱号和批号都匹配) - 任务编码:{},批号:{}",
                                task.getTaskCode(), batchList);
                        break;
                    }
                }
                // 查询盘点入库任务:箱号匹配且状态为1或2
                List<Task> checkInTasks = taskService.list(new LambdaQueryWrapper<Task>()
                        .eq(Task::getBarcode, param.getBarcode())
                        .eq(Task::getTaskType, TaskType.TASK_TYPE_CHECK_IN.type)
                        .in(Task::getTaskStatus, TaskStsType.GENERATE_IN.id, TaskStsType.WCS_EXECUTE_IN.id)
                        .orderByDesc(Task::getCreateTime));
                // 通过TaskItem的batch字段匹配批号
                for (Task task : checkInTasks) {
                    List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>()
                            .eq(TaskItem::getTaskId, task.getId())
                            .in(TaskItem::getBatch, batchList));
                    if (!taskItems.isEmpty()) {
                        checkInTask = task;
                        log.info("找到匹配的盘点入库任务(箱号和批号都匹配) - 任务编码:{},批号:{}",
                                task.getTaskCode(), batchList);
                        break;
                    }
                }
            }
        }
        // 如果是拣料入库任务,直接返回,不校验组托
        if (Objects.nonNull(pickInTask)) {
            log.info("拣料入库任务,直接返回,不校验组托 - 任务编码:{}", pickInTask.getTaskCode());
            // 更新入库站点信息(如果与当前申请的站点不同)
            if (StringUtils.isNotBlank(param.getSourceStaNo()) &&
                !param.getSourceStaNo().equals(pickInTask.getOrgSite())) {
                log.info("更新拣料入库任务的入库站点 - 任务编码:{},原站点:{},新站点:{}",
                        pickInTask.getTaskCode(), pickInTask.getOrgSite(), param.getSourceStaNo());
                pickInTask.setOrgSite(param.getSourceStaNo());
                if (!taskService.updateById(pickInTask)) {
                    log.warn("更新拣料入库任务的入库站点失败 - 任务编码:{}", pickInTask.getTaskCode());
                }
            }
            // 返回拣料入库任务的信息
            InTaskMsgDto msgDto = new InTaskMsgDto();
            msgDto.setWorkNo(pickInTask.getTaskCode());
            msgDto.setTaskId(pickInTask.getId());
            msgDto.setLocNo(pickInTask.getTargLoc());
            msgDto.setSourceStaNo(pickInTask.getOrgSite());
            msgDto.setStaNo(pickInTask.getTargSite());
            return msgDto;
        }
        // 如果是盘点入库任务,直接返回,不校验组托
        if (Objects.nonNull(checkInTask)) {
            log.info("盘点入库任务,直接返回,不校验组托 - 任务编码:{}", checkInTask.getTaskCode());
            // 更新入库站点信息(如果与当前申请的站点不同)
            if (StringUtils.isNotBlank(param.getSourceStaNo()) &&
                !param.getSourceStaNo().equals(checkInTask.getOrgSite())) {
                log.info("更新盘点入库任务的入库站点 - 任务编码:{},原站点:{},新站点:{}",
                        checkInTask.getTaskCode(), checkInTask.getOrgSite(), param.getSourceStaNo());
                checkInTask.setOrgSite(param.getSourceStaNo());
                if (!taskService.updateById(checkInTask)) {
                    log.warn("更新盘点入库任务的入库站点失败 - 任务编码:{}", checkInTask.getTaskCode());
                }
            }
            // 返回盘点入库任务的信息
            InTaskMsgDto msgDto = new InTaskMsgDto();
            msgDto.setWorkNo(checkInTask.getTaskCode());
            msgDto.setTaskId(checkInTask.getId());
            msgDto.setLocNo(checkInTask.getTargLoc());
            msgDto.setSourceStaNo(checkInTask.getOrgSite());
            msgDto.setStaNo(checkInTask.getTargSite());
            return msgDto;
        }
        // 检查其他入库任务类型(用箱号查询,状态为1或2)
        // 注意:盘点入库已单独处理,不再包含在此列表中
        List<Integer> otherInboundTaskTypes = Arrays.asList(
                TaskType.TASK_TYPE_IN.type,
                TaskType.TASK_TYPE_MERGE_IN.type,
                TaskType.TASK_TYPE_EMPITY_IN.type
        );
        Task existingInTask = taskService.getOne(new LambdaQueryWrapper<Task>()
                .eq(Task::getBarcode, param.getBarcode())
                .in(Task::getTaskType, otherInboundTaskTypes)
                .in(Task::getTaskStatus, TaskStsType.GENERATE_IN.id, TaskStsType.WCS_EXECUTE_IN.id)
                .orderByDesc(Task::getCreateTime)
                .last("LIMIT 1"));
        if (Objects.nonNull(existingInTask)) {
            log.info("找到匹配的其他入库任务 - 任务编码:{},任务类型:{},箱号:{}",
                    existingInTask.getTaskCode(), existingInTask.getTaskType(), param.getBarcode());
            // 检查组托明细是否有订单编码(任务编号)
            List<WaitPakinItem> itemsWithAsnCode = waitPakinItems.stream()
                    .filter(item -> StringUtils.isNotBlank(item.getAsnCode()))
                    .collect(Collectors.toList());
            if (!itemsWithAsnCode.isEmpty()) {
                log.info("组托档有任务编号,使用现有入库任务单号 - 任务编码:{},箱号:{},任务编号数量:{}",
                        existingInTask.getTaskCode(), param.getBarcode(), itemsWithAsnCode.size());
                // 更新入库站点信息(如果与当前申请的站点不同)
                if (StringUtils.isNotBlank(param.getSourceStaNo()) &&
                    !param.getSourceStaNo().equals(existingInTask.getOrgSite())) {
                    log.info("更新入库任务的入库站点 - 任务编码:{},原站点:{},新站点:{}",
                            existingInTask.getTaskCode(), existingInTask.getOrgSite(), param.getSourceStaNo());
                    existingInTask.setOrgSite(param.getSourceStaNo());
                    if (!taskService.updateById(existingInTask)) {
                        log.warn("更新入库任务的入库站点失败 - 任务编码:{}", existingInTask.getTaskCode());
                    }
                }
                // 返回现有入库任务的信息
                InTaskMsgDto msgDto = new InTaskMsgDto();
                msgDto.setWorkNo(existingInTask.getTaskCode());
                msgDto.setTaskId(existingInTask.getId());
                msgDto.setLocNo(existingInTask.getTargLoc());
                msgDto.setSourceStaNo(existingInTask.getOrgSite());
                msgDto.setStaNo(existingInTask.getTargSite());
                return msgDto;
            } else {
                log.info("组托档没有任务编号,继续创建新任务 - 箱号:{}", param.getBarcode());
            }
        } else {
            log.info("未找到匹配的其他入库任务,继续创建新任务 - 箱号:{}", param.getBarcode());
        }
        // 生成任务编码
        String ruleCode = generateTaskCode();
@@ -110,7 +317,7 @@
        updateLocStatus(task.getTargLoc(), waitPakin.getBarcode());
        // 获取并验证组拖明细
        List<WaitPakinItem> waitPakinItems = getWaitPakinItems(waitPakin.getId());
        waitPakinItems = getWaitPakinItems(waitPakin.getId());
        // 创建并保存任务明细
        saveTaskItems(task.getId(), waitPakinItems, param.getUser());
@@ -120,6 +327,7 @@
        // 设置工作单号并返回
        locNo.setWorkNo(ruleCode);
        locNo.setTaskId(task.getId());
        return locNo;
    }
@@ -144,7 +352,7 @@
    private WaitPakin validateWaitPakin(String barcode) {
        WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>()
                .eq(WaitPakin::getBarcode, barcode)
                .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val));
                .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val));
        if (Cools.isEmpty(waitPakin)) {
            throw new CoolException("请检查组拖状态是否完成!!");
@@ -201,13 +409,30 @@
    /**
     * 获取并验证组拖明细
     * 只返回未完全使用的明细(workQty < anfme 或 workQty 为 null)
     */
    private List<WaitPakinItem> getWaitPakinItems(Long pakinId) {
        List<WaitPakinItem> waitPakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getPakinId, pakinId));
        List<WaitPakinItem> waitPakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>()
                .eq(WaitPakinItem::getPakinId, pakinId));
        if (waitPakinItems.isEmpty()) {
            throw new CoolException("数据错误:组拖明细不存在");
        }
        return waitPakinItems;
        // 过滤掉已完全使用的明细(workQty >= anfme)
        List<WaitPakinItem> availableItems = waitPakinItems.stream()
                .filter(item -> {
                    Double workQty = item.getWorkQty() == null ? 0.0 : item.getWorkQty();
                    Double anfme = item.getAnfme() == null ? 0.0 : item.getAnfme();
                    return workQty < anfme; // 只返回未完全使用的明细
                })
                .collect(Collectors.toList());
        if (availableItems.isEmpty()) {
            throw new CoolException("组拖明细已全部使用,无法再次创建任务");
        }
        return availableItems;
    }
    /**
@@ -231,6 +456,15 @@
        if (!taskItemService.saveBatch(taskItems)) {
            throw new CoolException("任务明细保存失败!!");
        }
        // 更新组托明细的执行数量,标记明细已被提取使用
        waitPakinItems.forEach(item -> {
            if (!waitPakinItemService.update(new LambdaUpdateWrapper<WaitPakinItem>()
                    .set(WaitPakinItem::getWorkQty, item.getAnfme())
                    .eq(WaitPakinItem::getId, item.getId()))) {
                throw new CoolException("组托明细执行数量修改失败!!");
            }
        });
    }
    /**
@@ -361,18 +595,392 @@
        //根据立库类型获取获取库位
        if (warehouseArea.getType().equals(WarehType.WAREHOUSE_TYPE_CRN.val)) {
            //堆垛机
            dto = getLocNoCrn(param.getOrgLoc(),deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
            dto = getLocNoCrn(param.getOrgLoc(), deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
        } else if (warehouseArea.getType().equals(WarehType.WAREHOUSE_TYPE_FOUR_DIRECTIONS.val)) {
            //四向库
        }  else {
        } else {
            //CTU 库以及其它
            dto = getLocNoCtu(deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
        }
        return dto;
    }
    private InTaskMsgDto getLocNoCrn(String orgLoc,DeviceBind deviceBind, Long area, Integer sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
    /**
     * @author Ryan
     * @date 2025/8/28
     * @description: Wcs库位同步
     * @version 1.0
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncLocsToWms() {
        /**RCS基础配置链接*/
        CommonRequest params = new CommonRequest();
        params.setPageSize(100);
        ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
        scheduled.scheduleWithFixedDelay(new Runnable() {
            int current = 1;
            @Override
            public void run() {
                params.setCurrent(current);
                List<SyncLocsDto> dtos = syncLocs(params);
                if (dtos.isEmpty()) {
                    scheduled.shutdown();
                }
                for (SyncLocsDto dto : dtos) {
                    Loc loc = new Loc();
                    String string = new Random().nextInt(10000000) + "";
                    Loc one = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, dto.getLocNo()));
                    if (!Objects.isNull(one)) {
                        string = new Random().nextInt(10000000) + "";
                    }
                    loc.setCode(dto.getLocNo())
                            .setBarcode(dto.getBarcode())
                            .setRow(dto.getRow())
                            .setLev(dto.getLev())
                            .setId(null)
                            .setAreaId(42L)
                            .setWarehouseId(29L)
                            .setBarcode(string)
                            .setCol(dto.getBay())
                            .setType(dto.getLocType().equals("16") ? "17" : "15")
                            .setStatus(dto.getStatusBool())
                            .setUseStatus(LocStsType.getLocSts(dto.getLocSts()));
                    if (!locService.save(loc)) {
                        scheduled.shutdown();
                        throw new CoolException("WCS库位同步失败!!");
                    }
                }
                current++;
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
    /**
     * 异常信息上报
     *
     * @return
     */
    @Override
    public R receiveExMsg(ExMsgParams params) {
        if (Objects.isNull(params)) {
            log.error("RCS回调为空!");
            return R.error("参数不能为空!!");
        }
        log.info("========== 接收RCS回调 ==========");
        log.info("任务编号:{},批次号:{},事件类型:{}", params.getSeqNum(), params.getBatchNo(), params.getEventType());
        // 优先使用批次号查询任务主表(批次号 = 任务编码,更可靠)
        String taskCode = params.getBatchNo();
        String taskNo = params.getSeqNum();
        String locCode = null; // 库位代码(多库位任务时从任务号中提取)
        // 如果批次号为空,使用任务号查询
        if (StringUtils.isBlank(taskCode)) {
            taskCode = taskNo;
        }
        // 处理出库任务多库位的情况:任务号格式可能是 "任务编码_库位代码"
        // 如果任务号包含下划线,提取库位代码
        if (StringUtils.isNotBlank(taskNo) && taskNo.contains("_")) {
            locCode = taskNo.substring(taskNo.lastIndexOf("_") + 1);
            // 如果批次号为空,从任务号中提取原始任务编码
            if (StringUtils.isBlank(taskCode)) {
                taskCode = taskNo.substring(0, taskNo.lastIndexOf("_"));
            }
            log.info("检测到多库位任务,提取库位代码:{},原始任务编码:{}", locCode, taskCode);
        }
        // 使用批次号(任务编码)查询任务主表
        Task task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, taskCode));
        if (Objects.isNull(task)) {
            log.error("任务不存在或已结束!任务编号:{},批次号:{}", taskNo, taskCode);
            throw new CoolException("任务不存在或已结束!!任务编号:" + taskNo + ",批次号:" + taskCode);
        }
        // 验证批次号和任务号的关联关系
        if (StringUtils.isNotBlank(params.getBatchNo()) && !params.getBatchNo().equals(task.getTaskCode())) {
            log.warn("批次号与任务编码不匹配!批次号:{},任务编码:{}", params.getBatchNo(), task.getTaskCode());
        }
        log.info("查询到任务 - 任务编码:{},任务类型:{},当前状态:{}",
                task.getTaskCode(), task.getTaskType(), task.getTaskStatus());
        // 对于多库位出库任务,通过库位代码精确关联任务明细
        List<TaskItem> relatedTaskItems = null;
        if (StringUtils.isNotBlank(locCode)) {
            // 1. 通过库位代码查询LocItem
            LocItem locItem = locItemService.getOne(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocCode, locCode));
            if (Objects.isNull(locItem)) {
                log.warn("未找到库位明细!库位代码:{},任务编码:{}", locCode, task.getTaskCode());
            } else {
                // 2. 通过任务ID和LocItem.id(即TaskItem.source)查询对应的任务明细
                relatedTaskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>()
                        .eq(TaskItem::getTaskId, task.getId())
                        .eq(TaskItem::getSource, locItem.getId()));
                if (relatedTaskItems.isEmpty()) {
                    log.warn("未找到对应的任务明细!任务编码:{},库位代码:{},LocItem.id:{}",
                            task.getTaskCode(), locCode, locItem.getId());
                } else {
                    log.info("通过库位代码精确关联到任务明细 - 任务编码:{},库位代码:{},任务明细数量:{}",
                            task.getTaskCode(), locCode, relatedTaskItems.size());
                }
            }
        }
        // 验证任务状态:只有已下发到RCS的任务才能被回调处理
        // 入库任务:状态 >= WCS_EXECUTE_IN(2) 表示已下发
        // 出库任务:状态 >= WCS_EXECUTE_OUT(102) 表示已下发
        boolean isValidStatus = false;
        if (task.getTaskType() < 100) {
            // 入库任务
            if (task.getTaskStatus() >= TaskStsType.WCS_EXECUTE_IN.id) {
                isValidStatus = true;
            }
        } else {
            // 出库任务
            if (task.getTaskStatus() >= TaskStsType.WCS_EXECUTE_OUT.id) {
                isValidStatus = true;
            }
        }
        if (!isValidStatus) {
            log.error("任务状态不正确,无法处理回调!任务编码:{},任务类型:{},当前状态:{},期望状态:已下发到RCS",
                    task.getTaskCode(), task.getTaskType(), task.getTaskStatus());
            throw new CoolException("任务状态不正确,无法处理回调!任务编码:" + task.getTaskCode() +
                    ",当前状态:" + task.getTaskStatus() + ",任务可能未下发到RCS或已被处理");
        }
        /**料箱搬运中, 修改站点状态*/
//        if (params.getEventType().equals(CallBackEvent.CALL_BACK_EVENT_OBIT.event)) {
//            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)
//                    || task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_IN.type)
//                    || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_IN.type)
//                    || task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) {
//
//            }
//            /**取箱完成, 修改任务状态*/
//        } else
        if (params.getEventType().equals(CallBackEvent.CALL_BACK_EVENT_END.event)) {
            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)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_IN.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_IN.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_LOC_MOVE.type)) {
                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());
                    }
                }
                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)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_MERGE_OUT.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)
                    || task.getTaskType().equals(TaskType.TASK_TYPE_EMPITY_OUT.type)) {
                /**修改出库站点状态*/
                BasStation station = basStationService.getOne(new LambdaQueryWrapper<BasStation>()
                        .eq(BasStation::getStationName, task.getTargSite()));
                if (Objects.isNull(station)) {
                    log.error("出库站点不存在 - 站点名称:{},任务编码:{}", task.getTargSite(), task.getTaskCode());
                    throw new CoolException("数据错误,站点不存在!!");
                }
                log.info("查询到出库站点 - 站点名称:{},站点类型:{},当前状态:{}",
                        station.getStationName(), station.getType(), station.getUseStatus());
                if (station.getType().equals(StationTypeEnum.STATION_TYPE_NORMAL.type)) {
                    log.info("更新出库站点状态 - 站点名称:{},新状态:{}", station.getStationName(), LocStsType.LOC_STS_TYPE_F.type);
                    station.setUseStatus(LocStsType.LOC_STS_TYPE_F.type);
                    if (!basStationService.updateById(station)) {
                        log.error("出库站点状态修改失败 - 站点名称:{}", station.getStationName());
                        throw new CoolException("站点状态修改失败!!");
                    }
                    log.info("出库站点状态更新成功 - 站点名称:{}", station.getStationName());
                }
                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());
                    // 全版出库在RCS回调后直接处理并设置为200
                    if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
                        log.info("全版出库任务,开始处理库存并更新状态为200 - 任务编码:{}", task.getTaskCode());
                        try {
                            // 重新查询任务以获取最新状态(198)
                            task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode()));
                            // 调用completeTask处理库存(会设置为199)
                            List<Task> taskList = new ArrayList<>();
                            taskList.add(task);
                            taskService.completeTask(taskList);
                            // 重新查询任务以获取最新状态(199)
                            task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode()));
                            // 如果状态已经是199,继续处理并设置为200
                            if (task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)) {
                                Long loginUserId = SystemAuthUtils.getLoginUserId();
                                if (loginUserId == null) {
                                    log.warn("无法获取系统用户ID,使用默认值1");
                                    loginUserId = 1L;
                                }
                                // 删除作业中库存记录(LocItemWorking)
                                locItemWorkingService.remove(new LambdaQueryWrapper<LocItemWorking>()
                                        .eq(LocItemWorking::getTaskId, task.getId()));
                                // 更新任务状态为库存更新完成(200)
                                boolean finalUpdated = taskService.update(new LambdaUpdateWrapper<Task>()
                                        .eq(Task::getTaskCode, task.getTaskCode())
                                        .eq(Task::getTaskStatus, TaskStsType.WAVE_SEED.id)
                                        .set(Task::getTaskStatus, TaskStsType.UPDATED_OUT.id)
                                        .set(Task::getUpdateBy, loginUserId)
                                        .set(Task::getUpdateTime, new Date()));
                                if (!finalUpdated) {
                                    log.warn("全版出库任务状态更新为200失败,可能状态已变更 - 任务编码:{},当前状态:{}",
                                            task.getTaskCode(), task.getTaskStatus());
                                } else {
                                    log.info("全版出库任务状态已更新为200(库存更新完成) - 任务编码:{}", task.getTaskCode());
                                }
                            } else {
                                log.warn("全版出库任务状态不是199,无法更新为200 - 任务编码:{},当前状态:{}",
                                        task.getTaskCode(), task.getTaskStatus());
                            }
                        } catch (Exception e) {
                            log.error("全版出库任务处理失败 - 任务编码:{},错误:{}", task.getTaskCode(), e.getMessage(), e);
                            // 不抛出异常,避免影响RCS回调的正常返回
                        }
                    }
                }
            }
        } else {
            log.warn("未处理的事件类型 - 事件类型:{},任务编码:{},任务类型:{}",
                    params.getEventType(), task.getTaskCode(), task.getTaskType());
        }
        log.info("========== 任务执行通知上报处理完成 ==========");
        log.info("处理结果:{}", JSONObject.toJSONString(params));
        return R.ok(JSONObject.toJSONString(params));
    }
    /**
     * 下发任务至中转API
     *
     * @param params
     * @return
     */
    @Override
    public R pubWcsTask(WcsTaskParams params) {
        String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask;
        log.info("任务下发,请求地址3: {}, 请求参数: {}", rcsUrl, JSONObject.toJSONString(params));
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("api-version", "v2.0");
        HttpEntity httpEntity = new HttpEntity(params, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
        log.info("任务下发后,响应结果: {}", exchange);
        if (Objects.isNull(exchange.getBody())) {
            throw new CoolException("任务下发失败!!");
        } else {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults()
                    .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            try {
                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
                if (result.getCode() == 200) {
                    return R.ok();
                } else {
                    throw new CoolException("任务下发失败!!");
                }
            } catch (JsonProcessingException e) {
                throw new CoolException(e.getMessage());
            }
        }
    }
    private List<SyncLocsDto> syncLocs(CommonRequest params) {
        String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.syncLocs;
        log.info("库位同步,请求地址: {}, 请求参数: {}", rcsUrl, JSONObject.toJSONString(params));
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("api-version", "v2.0");
        HttpEntity httpEntity = new HttpEntity(params, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
        log.info("库位同步,响应结果: {}", exchange);
        if (Objects.isNull(exchange.getBody())) {
            throw new CoolException("库位同步下发失败!!");
        } else {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults()
                    .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            try {
                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
                if (result.getCode() == 200) {
                    JSONObject jsonObject = JSONObject.parseObject(exchange.getBody());
                    return JSONArray.parseArray(jsonObject.getJSONArray("data").toJSONString(), SyncLocsDto.class);
                } else {
                    throw new CoolException("库位同步失败!!");
                }
            } catch (JsonProcessingException e) {
                throw new CoolException(e.getMessage());
            }
        }
    }
    private InTaskMsgDto getLocNoCrn(String orgLoc, DeviceBind deviceBind, Long area, String sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
        if (Cools.isEmpty(matnr)) {  //物料号
            matnr = "";
        }
@@ -388,23 +996,20 @@
        InTaskMsgDto inTaskMsgDto = new InTaskMsgDto();
        int sRow = deviceBind.getStartRow();
        int eRow = deviceBind.getEndRow();
        int deviceQty = deviceBind.getDeviceQty();
        // ===============>>>> 开始执行
        curRow = deviceBind.getCurrentRow();
        //此程序用于优化堆垛机异常时的运行时间
        for (int i = times; i <= deviceQty * 2; i++) {
            int[] locNecessaryParameters = LocUtils.LocNecessaryParameters(deviceBind, curRow, deviceQty);
            curRow = locNecessaryParameters[1];
            channel = locNecessaryParameters[2];
            rowCount = locNecessaryParameters[0];
            nearRow = locNecessaryParameters[3];
            break;
        }
        //获取排
        int[] locNecessaryParameters = LocUtils.LocNecessaryParameters(deviceBind, curRow, deviceQty);
        curRow = locNecessaryParameters[1];
        channel = locNecessaryParameters[2];
        rowCount = locNecessaryParameters[0];
        nearRow = locNecessaryParameters[3];
        if (nearRow == 0) {
            throw new CoolException("无可用堆垛机");
        }
@@ -464,7 +1069,7 @@
        if (Cools.isEmpty(deviceSite)) {
            channel = 0;
        } else {
            inTaskMsgDto.setStaNo(Integer.parseInt(deviceSite.getDeviceSite()));
            inTaskMsgDto.setStaNo(deviceSite.getDeviceSite());
        }
        //更新当前排
@@ -502,7 +1107,7 @@
                    );
                    if (!Cools.isEmpty(locMast2)) {
                        if (null != orgMoveLoc){
                        if (null != orgMoveLoc) {
                            if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                break;
                            }
@@ -519,7 +1124,7 @@
                    );
                    if (!Cools.isEmpty(locMast2)) {
                        if (null != orgMoveLoc){
                        if (null != orgMoveLoc) {
                            if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                break;
                            }
@@ -534,7 +1139,7 @@
                        );
                        if (!Cools.isEmpty(locMast2)) {
                            if (null != orgMoveLoc){
                            if (null != orgMoveLoc) {
                                if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                    break;
                                }
@@ -559,7 +1164,7 @@
                        );
                        if (!Cools.isEmpty(locMast2)) {
                            if (null != orgMoveLoc){
                            if (null != orgMoveLoc) {
                                if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                    break;
                                }
@@ -574,7 +1179,7 @@
                            );
                            if (!Cools.isEmpty(locMast2)) {
                                if (null != orgMoveLoc){
                                if (null != orgMoveLoc) {
                                    if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                        break;
                                    }
@@ -607,14 +1212,14 @@
            // 当前巷道无空库位时,递归调整至下一巷道,检索全部巷道无果后,跳出递归
            if (times < rowCount * 2) {
                times = times + 1;
                return getLocNoCrn(orgLoc,deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, times, ioType);
                return getLocNoCrn(orgLoc, deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, times, ioType);
            }
            // 2.库位当前所属尺寸无空库位时,调整尺寸参数,向上兼容检索库位
            if (locTypeDto.getLocType1() < 3) {
                int i = locTypeDto.getLocType1() + 1;
                locTypeDto.setLocType1(i);
                return getLocNoCrn(orgLoc,deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, 0, ioType);
                return getLocNoCrn(orgLoc, deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, 0, ioType);
            }
            throw new CoolException("没有空库位");
        }
@@ -628,7 +1233,7 @@
        return inTaskMsgDto;
    }
    private InTaskMsgDto getLocNoCtu(DeviceBind deviceBind, Long area, Integer sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
    private InTaskMsgDto getLocNoCtu(DeviceBind deviceBind, Long area, String sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
        if (Cools.isEmpty(matnr)) {  //物料号
            matnr = "";
        }
@@ -638,10 +1243,13 @@
        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)
                .eq(Loc::getType, locTypeDto.getLocType1())
                .ge(Loc::getRow, deviceBind.getStartRow())
                .le(Loc::getRow, deviceBind.getEndRow())
                .orderByAsc(Loc::getLev)
                .orderByAsc(Loc::getCol)
                .orderByAsc(Loc::getRow)
@@ -657,13 +1265,13 @@
        DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                .eq(DeviceSite::getType, ioType)
                .eq(DeviceSite::getSite, sourceStaNo)
                .eq(DeviceSite::getDeviceCode, loc.getDeviceNo())
                .eq(!Objects.isNull(loc.getDeviceNo()), DeviceSite::getDeviceCode, loc.getDeviceNo())
        );
        if (Cools.isEmpty(deviceSite)) {
            deviceNo = 0;
            loc = null;
        } else {
            inTaskMsgDto.setStaNo(Integer.parseInt(deviceSite.getDeviceSite()));
            inTaskMsgDto.setStaNo(deviceSite.getDeviceSite());
        }
        // 递归查询
        if (Cools.isEmpty(loc) || !loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) {
@@ -691,4 +1299,49 @@
        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);
        //RCS已经在输送线上,所以不需要下发任务
        taskService.updateById(new Task(){{setId(msgDto.getTaskId());setTaskStatus(2);}});
        log.info("========== RCS-申请入库任务成功 ==========");
        log.info("RCS-返回 任务编码:{},库位号:{}", msgDto.getWorkNo(), msgDto.getLocNo());
        // 返回结果,只返回库位号(根据接口文档要求)
        JSONObject result = new JSONObject();
        result.put("locNo", msgDto.getLocNo());
        result.put("batchNo", msgDto.getWorkNo());
        result.put("taskNo", msgDto.getWorkNo());
        return R.ok(result);
    }
}