chen.lin
18 小时以前 faf593f6161f40279b81e3c2fd961d695821a1aa
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -960,11 +960,19 @@
        // 如果有任务已下发到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);
            // 检查 RCS API 配置是否有效
            if (rcsApi == null || StringUtils.isBlank(rcsApi.getHost()) || StringUtils.isBlank(rcsApi.getPort())) {
                log.error("========== RCS任务取消失败 ==========");
                log.error("RCS API 配置无效!host: {}, port: {}",
                        rcsApi != null ? rcsApi.getHost() : "null",
                        rcsApi != null ? rcsApi.getPort() : "null");
                // 即使配置无效,也继续执行任务删除操作
            } else {
                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()) {
@@ -1010,12 +1018,13 @@
                    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());
                } 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());
                }
            }
        }
        
@@ -1399,16 +1408,36 @@
        if (Objects.isNull(loc)) {
            throw new CoolException("库位不存在!!");
        }
        if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) {
            throw new CoolException("库位状态不处理于R.出库预约!!");
        }
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
        if (taskItems.isEmpty()) {
            throw new CoolException("任务明细不存在!!");
        }
        List<LocItem> locItems = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, loc.getId()));
        // 如果库位状态不是R,检查是否已经处理过
        if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) {
            // 如果库位明细为空,说明已经处理过了,直接更新任务状态为199
            if (locItems.isEmpty()) {
                logger.warn("任务{}的库位{}状态为{},但库位明细为空,可能已经处理过,直接更新任务状态为199",
                        task.getId(), loc.getCode(), loc.getUseStatus());
                if (!this.update(new LambdaUpdateWrapper<Task>()
                        .eq(Task::getId, task.getId())
                        .set(Task::getUpdateBy, loginUserId)
                        .set(Task::getUpdateTime, new Date())
                        .set(Task::getTaskStatus, TaskStsType.WAVE_SEED.id))) {
                    throw new CoolException("任务状态更新失败!!");
                }
                return; // 跳过后续处理
            } else {
                // 库位明细不为空但状态不是R,记录错误但不抛出异常,让定时任务继续处理其他任务
                logger.error("任务{}的库位{}状态为{},不是R.出库预约状态,但库位明细不为空,跳过处理。任务编码:{},库位编码:{}",
                        task.getId(), loc.getCode(), loc.getUseStatus(), task.getTaskCode(), loc.getCode());
                return; // 跳过处理,避免异常中断定时任务
            }
        }
        // 如果库位明细为空,可能是已经被处理过了,允许继续执行
        if (!locItems.isEmpty()) {
            List<LocItemWorking> workings = new ArrayList<>();
@@ -1430,8 +1459,8 @@
            try {
                // 根据任务类型更新库位明细
                if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
                    // 全版出库:删除所有库位明细
                    subtractLocItem(loc);
                    // 全版出库:不删除库位明细,等待PDA快速拣货确认时再删除
                    // subtractLocItem(loc); // 已移除,改为在completeFullOutStock中删除
                } else {
                    // 部分出库(如拣料出库):根据TaskItem数量扣减库位明细
                    subtractLocItemByTaskItems(loc, taskItems, loginUserId);
@@ -1493,6 +1522,8 @@
                throw new CoolException(e.getMessage());
            }
        }
        // 根据任务类型更新库位状态
        if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type) || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)) {
            /**修改为库位状态为S.预约入库,保留原有库位*/
            if (!locService.update(new LambdaUpdateWrapper<Loc>()
@@ -1503,6 +1534,9 @@
                    .eq(Loc::getId, loc.getId()))) {
                throw new CoolException("库位状态修改失败!!");
            }
        } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
            // 全版出库:不更新库位状态为O,等待PDA快速拣货确认时再更新
            // 库位状态保持原样(R.出库预约状态)
        } else {
            /**修改为库位状态为O.空库*/
            if (!locService.update(new LambdaUpdateWrapper<Loc>()
@@ -1667,6 +1701,15 @@
    @Transactional(rollbackFor = Exception.class)
    public void pubTaskToWcs(List<Task> tasks) {
        /**任务下发接口*/
        // 检查 RCS API 配置是否有效
        if (rcsApi == null || StringUtils.isBlank(rcsApi.getHost()) || StringUtils.isBlank(rcsApi.getPort())) {
            log.error("========== RCS任务下发失败 ==========");
            log.error("RCS API 配置无效!host: {}, port: {}",
                    rcsApi != null ? rcsApi.getHost() : "null",
                    rcsApi != null ? rcsApi.getPort() : "null");
            return;
        }
        String pubTakUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.pubTask;
        
        for (Task task : tasks) {
@@ -1761,7 +1804,10 @@
                    // 为每个不同的库位创建一个TaskItemParam
                    for (String locCode : locCodes) {
                        TaskItemParam outItemParam = new TaskItemParam();
                        outItemParam.setTaskNo(task.getTaskCode());
                        String taskNo = locCodes.size() > 1
                                ? task.getTaskCode() + "_" + locCode
                                : task.getTaskCode();
                        outItemParam.setTaskNo(taskNo);
                        outItemParam.setPriority(1);
                        outItemParam.setOriLoc(locCode);
                        outItemParam.setDestSta(task.getTargSite());
@@ -2161,12 +2207,26 @@
                throw new CoolException("库位不存在!!");
            }
            LocItem item = new LocItem();
            LocItem locItem = locItemService.getOne(new LambdaQueryWrapper<LocItem>()
            // 构建查询条件:需要同时匹配物料ID、库位ID、批次和票号
            LambdaQueryWrapper<LocItem> locItemWrapper = new LambdaQueryWrapper<LocItem>()
                    .eq(LocItem::getMatnrId, taskItem.getMatnrId())
                    .eq(LocItem::getLocId, loc.getId())
                    .eq(StringUtils.isNotBlank(taskItem.getBatch()), LocItem::getBatch, taskItem.getBatch())
                    .eq(StringUtils.isNotBlank(taskItem.getFieldsIndex()), LocItem::getFieldsIndex, taskItem.getFieldsIndex())
            );
                    .eq(LocItem::getLocId, loc.getId());
            // 批次匹配:如果taskItem有批次,则必须匹配;如果taskItem没有批次,则查询批次为null或空字符串的记录
            if (StringUtils.isNotBlank(taskItem.getBatch())) {
                locItemWrapper.eq(LocItem::getBatch, taskItem.getBatch());
            } else {
                locItemWrapper.and(wrapper -> wrapper.isNull(LocItem::getBatch).or().eq(LocItem::getBatch, ""));
            }
            // 票号匹配:如果taskItem有票号,则必须匹配;如果taskItem没有票号,则查询票号为null或空字符串的记录
            if (StringUtils.isNotBlank(taskItem.getFieldsIndex())) {
                locItemWrapper.eq(LocItem::getFieldsIndex, taskItem.getFieldsIndex());
            } else {
                locItemWrapper.and(wrapper -> wrapper.isNull(LocItem::getFieldsIndex).or().eq(LocItem::getFieldsIndex, ""));
            }
            LocItem locItem = locItemService.getOne(locItemWrapper);
            if (Objects.isNull(locItem)) {
                // 库位明细不存在,创建新的库位明细
                BeanUtils.copyProperties(taskItem, item);