chen.lin
8 小时以前 da15785661f7832cdcb78fb6504752c0cb385e63
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -605,9 +605,9 @@
            throw new CoolException("当前任务不是全版出库任务,无法执行此操作!!");
        }
        // 检查任务状态
        if (task.getTaskStatus().equals(TaskStsType.COMPLETE_OUT.id)) {
            throw new CoolException("任务已完成,无需重复完结!!");
        // 检查任务状态:必须是199(WAVE_SEED)状态才能手动完结
        if (!task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)) {
            throw new CoolException("任务状态不是等待确认状态(199),无法执行此操作!!当前状态:" + task.getTaskStatus());
        }
        // 查询库位
@@ -650,8 +650,8 @@
            }
        }
        // 更新任务状态为出库完成
        task.setTaskStatus(TaskStsType.COMPLETE_OUT.id)
        // 更新任务状态为库存更新完成(200)
        task.setTaskStatus(TaskStsType.UPDATED_OUT.id)
                .setUpdateBy(loginUserId)
                .setUpdateTime(new Date());
@@ -873,6 +873,9 @@
            }
            if (task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_IN.type)) {
                locWorking.setAnfme(taskItem.getAnfme());
            } else if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_IN.type) && taskItem.getQty() != null && taskItem.getQty().compareTo(0.0) > 0) {
                // 拣料再入库:入库数量为本次拣料数量(taskItem.qty),保证与出库扣减一致
                locWorking.setAnfme(taskItem.getQty());
            }
            BeanUtils.copyProperties(locWorking, locItem);
            locItem.setWorkQty(0.0).setQty(0.0).setLocCode(loc.getCode()).setLocId(loc.getId()).setId(null).setUpdateBy(loginUserId).setUpdateTime(new Date());
@@ -960,11 +963,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 +1021,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());
                }
            }
        }
        
@@ -1295,6 +1307,11 @@
            throw new CoolException("数据错误:任务明细为空!!");
        }
        // 拣料入库:在生成拣料入库单时扣减原库位数量,不依赖出库完成时库位状态为R
        if (TaskType.TASK_TYPE_PICK_IN.type.equals(type)) {
            subtractLocItemByTaskItems(loc, taskItems, SystemAuthUtils.getLoginUserId());
        }
        tempLocs.forEach(working -> {
            taskItems.forEach(taskItem -> {
                if (Objects.equals(taskItem.getFieldsIndex(), working.getFieldsIndex())) {
@@ -1367,6 +1384,10 @@
                    .setQty(0.0)
                    .setLocId(loc1.getId())
                    .setLocCode(loc1.getCode());
            // 拣料再入库:目标库位数量应为本次拣料数量(taskItem.qty),不是原库位剩余数量(taskItem.anfme)
            if (TaskType.TASK_TYPE_PICK_IN.type.equals(task.getTaskType()) && taskItem.getQty() != null && taskItem.getQty().compareTo(0.0) > 0) {
                itemWorking.setAnfme(taskItem.getQty());
            }
            workings.add(itemWorking);
        });
@@ -1399,16 +1420,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,10 +1471,10 @@
            try {
                // 根据任务类型更新库位明细
                if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
                    // 全版出库:删除所有库位明细
                    subtractLocItem(loc);
                } else {
                    // 部分出库(如拣料出库):根据TaskItem数量扣减库位明细
                    // 全版出库:不删除库位明细,等待PDA快速拣货确认时再删除
                    // subtractLocItem(loc); // 已移除,改为在completeFullOutStock中删除
                } else if (!TaskType.TASK_TYPE_PICK_AGAIN_OUT.type.equals(task.getTaskType())) {
                    // 部分出库(如盘点出库):根据TaskItem数量扣减库位明细;拣料出库在生成拣料入库单时扣减
                    subtractLocItemByTaskItems(loc, taskItems, loginUserId);
                }
            } catch (Exception e) {
@@ -1493,6 +1534,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 +1546,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 +1713,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 +1816,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 +2219,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);