Junjie
2026-04-19 51e110ca563b40854763ec5a317514da8fda548c
#优化工作档和工作号事务V3.0.0.4
1个文件已添加
14个文件已修改
986 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/mapper/WrkLastnoMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/WrkMastMapper.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/WrkLastnoService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/WrkMastService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WrkLastnoServiceImpl.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/WrkMastFinalizeProcessor.java 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/WrkMastScheduler.java 348 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/service/CommonService.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/enums/RedisKeyType.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/service/WrkCommandRollbackService.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/WrkLastnoMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/WrkMastMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/WrkLastnoMapper.java
@@ -3,10 +3,12 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zy.asrs.entity.WrkLastno;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface WrkLastnoMapper extends BaseMapper<WrkLastno> {
    WrkLastno selectByWrkMkForUpdate(@Param("wrkMk") Integer wrkMk);
}
src/main/java/com/zy/asrs/mapper/WrkMastMapper.java
@@ -14,6 +14,16 @@
    WrkMast selectByWorkNo(Integer workNo);
    int updateWrkStsByWrkNoAndCurrentWrkSts(@Param("wrkNo") Integer wrkNo,
                                            @Param("wrkSts") Long wrkSts,
                                            @Param("currentWrkSts") Long currentWrkSts);
    int deleteByWrkNoAndCurrentWrkSts(@Param("wrkNo") Integer wrkNo,
                                      @Param("currentWrkSts") Long currentWrkSts);
    int deleteByWrkNoAndMkIn(@Param("wrkNo") Integer wrkNo,
                             @Param("mks") List<String> mks);
    List<WrkTaskCountVo> selectCrnTaskCountList(@Param("inIoType") Integer inIoType);
    List<WrkTaskCountVo> selectDualCrnTaskCountList(@Param("inIoType") Integer inIoType);
src/main/java/com/zy/asrs/service/WrkLastnoService.java
@@ -5,4 +5,7 @@
public interface WrkLastnoService extends IService<WrkLastno> {
    WrkLastno selectByWrkMkForUpdate(Integer wrkMk);
    int allocateNextWorkNo(Integer wrkMk);
}
src/main/java/com/zy/asrs/service/WrkMastService.java
@@ -10,6 +10,12 @@
    WrkMast selectByWorkNo(Integer workNo);
    int updateWrkStsByWrkNoAndCurrentWrkSts(Integer wrkNo, Long wrkSts, Long currentWrkSts);
    int deleteByWrkNoAndCurrentWrkSts(Integer wrkNo, Long currentWrkSts);
    int deleteByWrkNoAndMkIn(Integer wrkNo, List<String> mks);
    List<WrkTaskCountVo> selectCrnTaskCountList();
    List<WrkTaskCountVo> selectDualCrnTaskCountList();
src/main/java/com/zy/asrs/service/impl/WrkLastnoServiceImpl.java
@@ -1,12 +1,57 @@
package com.zy.asrs.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.core.exception.CoolException;
import com.zy.asrs.entity.WrkLastno;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.mapper.WrkLastnoMapper;
import com.zy.asrs.service.WrkLastnoService;
import com.zy.asrs.service.WrkMastService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("wrkLastnoService")
public class WrkLastnoServiceImpl extends ServiceImpl<WrkLastnoMapper, WrkLastno> implements WrkLastnoService {
    private final WrkMastService wrkMastService;
    public WrkLastnoServiceImpl(WrkMastService wrkMastService) {
        this.wrkMastService = wrkMastService;
    }
    @Override
    public WrkLastno selectByWrkMkForUpdate(Integer wrkMk) {
        return this.baseMapper.selectByWrkMkForUpdate(wrkMk);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int allocateNextWorkNo(Integer wrkMk) {
        WrkLastno wrkLastno = this.baseMapper.selectByWrkMkForUpdate(wrkMk);
        if (wrkLastno == null) {
            throw new CoolException("数据异常,请联系管理员");
        }
        Integer currentWrkNo = wrkLastno.getWrkNo();
        Integer sNo = wrkLastno.getsNo();
        Integer eNo = wrkLastno.geteNo();
        if (currentWrkNo == null || sNo == null || eNo == null || sNo > eNo) {
            throw new CoolException("工作号配置异常,请联系管理员");
        }
        int rangeSize = eNo - sNo + 1;
        int candidate = currentWrkNo;
        for (int i = 0; i < rangeSize; i++) {
            candidate = candidate >= eNo ? sNo : candidate + 1;
            WrkMast exists = wrkMastService.getOne(new QueryWrapper<WrkMast>().eq("wrk_no", candidate), false);
            if (exists == null) {
                wrkLastno.setWrkNo(candidate);
                this.updateById(wrkLastno);
                return candidate;
            }
        }
        throw new CoolException("当前工作号号段已满,无法分配可用工作号");
    }
}
src/main/java/com/zy/asrs/service/impl/WrkMastServiceImpl.java
@@ -19,6 +19,21 @@
    }
    @Override
    public int updateWrkStsByWrkNoAndCurrentWrkSts(Integer wrkNo, Long wrkSts, Long currentWrkSts) {
        return this.baseMapper.updateWrkStsByWrkNoAndCurrentWrkSts(wrkNo, wrkSts, currentWrkSts);
    }
    @Override
    public int deleteByWrkNoAndCurrentWrkSts(Integer wrkNo, Long currentWrkSts) {
        return this.baseMapper.deleteByWrkNoAndCurrentWrkSts(wrkNo, currentWrkSts);
    }
    @Override
    public int deleteByWrkNoAndMkIn(Integer wrkNo, List<String> mks) {
        return this.baseMapper.deleteByWrkNoAndMkIn(wrkNo, mks);
    }
    @Override
    public List<WrkTaskCountVo> selectCrnTaskCountList() {
        return this.baseMapper.selectCrnTaskCountList(WrkIoType.IN.id);
    }
src/main/java/com/zy/asrs/task/WrkMastFinalizeProcessor.java
New file
@@ -0,0 +1,333 @@
package com.zy.asrs.task;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zy.asrs.domain.enums.NotifyMsgType;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.entity.WrkMastLog;
import com.zy.asrs.service.BasStationService;
import com.zy.asrs.service.LocMastService;
import com.zy.asrs.service.WrkAnalysisService;
import com.zy.asrs.service.WrkMastLogService;
import com.zy.asrs.service.WrkMastService;
import com.zy.asrs.utils.NotifyUtils;
import com.zy.common.utils.RedisUtil;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.LocStsType;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.WrkIoType;
import com.zy.core.enums.WrkStsType;
import com.zy.core.thread.StationThread;
import com.zy.core.utils.StationOperateProcessUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@Component
@Slf4j
public class WrkMastFinalizeProcessor {
    private static final int WRK_LOCK_SECONDS = 30;
    private static final List<String> CANCEL_MKS = Arrays.asList("taskCancel", "taskForceCancel");
    private final WrkMastService wrkMastService;
    private final WrkMastLogService wrkMastLogService;
    private final WrkAnalysisService wrkAnalysisService;
    private final LocMastService locMastService;
    private final NotifyUtils notifyUtils;
    private final StationOperateProcessUtils stationOperateProcessUtils;
    private final BasStationService basStationService;
    private final RedisUtil redisUtil;
    public WrkMastFinalizeProcessor(WrkMastService wrkMastService,
                                    WrkMastLogService wrkMastLogService,
                                    WrkAnalysisService wrkAnalysisService,
                                    LocMastService locMastService,
                                    NotifyUtils notifyUtils,
                                    StationOperateProcessUtils stationOperateProcessUtils,
                                    BasStationService basStationService,
                                    RedisUtil redisUtil) {
        this.wrkMastService = wrkMastService;
        this.wrkMastLogService = wrkMastLogService;
        this.wrkAnalysisService = wrkAnalysisService;
        this.locMastService = locMastService;
        this.notifyUtils = notifyUtils;
        this.stationOperateProcessUtils = stationOperateProcessUtils;
        this.basStationService = basStationService;
        this.redisUtil = redisUtil;
    }
    @Transactional(rollbackFor = Exception.class)
    public void processCompleteInbound(Integer wrkNo) {
        processWithLock(wrkNo, () -> {
            WrkMast wrkMast = wrkMastService.selectByWorkNo(wrkNo);
            if (wrkMast == null) {
                log.error("入库完成清理跳过,未找到工作档。wrkNo={}", wrkNo);
                return;
            }
            if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.COMPLETE_INBOUND.sts)) {
                log.error("入库完成清理跳过,工作状态不匹配。wrkNo={}, wrkSts={}", wrkNo, wrkMast.getWrkSts());
                return;
            }
            Integer taskNo = wrkMast.getWrkNo();
            Integer sourceStaNo = wrkMast.getSourceStaNo();
            Integer staNo = wrkMast.getStaNo();
            LocMast locMast = locMastService.queryByLoc(wrkMast.getLocNo());
            if (locMast == null) {
                log.error("入库完成清理跳过,目标库位不存在。wrkNo={}, locNo={}", wrkNo, wrkMast.getLocNo());
                return;
            }
            if (!"S".equals(locMast.getLocSts())) {
                log.error("入库完成清理跳过,目标库位状态不为S。wrkNo={}, locNo={}, locSts={}", wrkNo, wrkMast.getLocNo(), locMast.getLocSts());
                return;
            }
            locMast.setLocSts("F");
            locMast.setBarcode(wrkMast.getBarcode());
            locMast.setModiTime(new Date());
            if (!locMastService.updateById(locMast)) {
                log.error("入库完成清理失败,目标库位更新失败。wrkNo={}, locNo={}", wrkNo, wrkMast.getLocNo());
                return;
            }
            archiveAndFinish(wrkMast);
            if (wrkMastService.deleteByWrkNoAndCurrentWrkSts(wrkMast.getWrkNo(), WrkStsType.COMPLETE_INBOUND.sts) <= 0) {
                log.error("入库完成清理失败,删除工作档失败或状态已变化。wrkNo={}", wrkNo);
                return;
            }
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_COMPLETE, JSON.toJSONString(wrkMast));
            clearTaskPath(taskNo, sourceStaNo, staNo);
        });
    }
    @Transactional(rollbackFor = Exception.class)
    public void processCompleteOutbound(Integer wrkNo) {
        processWithLock(wrkNo, () -> {
            WrkMast wrkMast = wrkMastService.selectByWorkNo(wrkNo);
            if (wrkMast == null) {
                log.error("出库完成清理跳过,未找到工作档。wrkNo={}", wrkNo);
                return;
            }
            if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.COMPLETE_OUTBOUND.sts)) {
                log.error("出库完成清理跳过,工作状态不匹配。wrkNo={}, wrkSts={}", wrkNo, wrkMast.getWrkSts());
                return;
            }
            Integer taskNo = wrkMast.getWrkNo();
            Integer sourceStaNo = wrkMast.getSourceStaNo();
            Integer staNo = wrkMast.getStaNo();
            LocMast locMast = locMastService.queryByLoc(wrkMast.getSourceLocNo());
            if (locMast == null) {
                log.error("出库完成清理跳过,源库位不存在。wrkNo={}, sourceLocNo={}", wrkNo, wrkMast.getSourceLocNo());
                return;
            }
            if (!("R".equals(locMast.getLocSts()) || "O".equals(locMast.getLocSts()))) {
                log.error("出库完成清理跳过,源库位状态异常。wrkNo={}, sourceLocNo={}, locSts={}", wrkNo, wrkMast.getSourceLocNo(), locMast.getLocSts());
                return;
            }
            locMast.setLocSts("O");
            locMast.setBarcode("");
            locMast.setModiTime(new Date());
            if (!locMastService.updateById(locMast)) {
                log.error("出库完成清理失败,源库位更新失败。wrkNo={}, sourceLocNo={}", wrkNo, wrkMast.getSourceLocNo());
                return;
            }
            archiveAndFinish(wrkMast);
            if (wrkMastService.deleteByWrkNoAndCurrentWrkSts(wrkMast.getWrkNo(), WrkStsType.COMPLETE_OUTBOUND.sts) <= 0) {
                log.error("出库完成清理失败,删除工作档失败或状态已变化。wrkNo={}", wrkNo);
                return;
            }
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_COMPLETE, JSON.toJSONString(wrkMast));
            clearTaskPath(taskNo, sourceStaNo, staNo);
        });
    }
    @Transactional(rollbackFor = Exception.class)
    public void processCompleteLocMove(Integer wrkNo) {
        processWithLock(wrkNo, () -> {
            WrkMast wrkMast = wrkMastService.selectByWorkNo(wrkNo);
            if (wrkMast == null) {
                log.error("移库完成清理跳过,未找到工作档。wrkNo={}", wrkNo);
                return;
            }
            if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.COMPLETE_LOC_MOVE.sts)) {
                log.error("移库完成清理跳过,工作状态不匹配。wrkNo={}, wrkSts={}", wrkNo, wrkMast.getWrkSts());
                return;
            }
            LocMast locMast = locMastService.queryByLoc(wrkMast.getLocNo());
            LocMast sourceLocMast = locMastService.queryByLoc(wrkMast.getSourceLocNo());
            if (locMast == null || sourceLocMast == null) {
                log.error("移库完成清理跳过,源库位或目标库位不存在。wrkNo={}, sourceLocNo={}, locNo={}", wrkNo, wrkMast.getSourceLocNo(), wrkMast.getLocNo());
                return;
            }
            if (!"S".equals(locMast.getLocSts()) || !"R".equals(sourceLocMast.getLocSts())) {
                log.error("移库完成清理跳过,库位状态异常。wrkNo={}, sourceLocNo={}, sourceLocSts={}, locNo={}, locSts={}",
                        wrkNo, wrkMast.getSourceLocNo(), sourceLocMast.getLocSts(), wrkMast.getLocNo(), locMast.getLocSts());
                return;
            }
            locMast.setLocSts("F");
            locMast.setBarcode(wrkMast.getBarcode());
            locMast.setModiTime(new Date());
            if (!locMastService.updateById(locMast)) {
                log.error("移库完成清理失败,目标库位更新失败。wrkNo={}, locNo={}", wrkNo, wrkMast.getLocNo());
                return;
            }
            sourceLocMast.setLocSts("O");
            sourceLocMast.setBarcode("");
            sourceLocMast.setModiTime(new Date());
            if (!locMastService.updateById(sourceLocMast)) {
                log.error("移库完成清理失败,源库位更新失败。wrkNo={}, sourceLocNo={}", wrkNo, wrkMast.getSourceLocNo());
                return;
            }
            archiveAndFinish(wrkMast);
            if (wrkMastService.deleteByWrkNoAndCurrentWrkSts(wrkMast.getWrkNo(), WrkStsType.COMPLETE_LOC_MOVE.sts) <= 0) {
                log.error("移库完成清理失败,删除工作档失败或状态已变化。wrkNo={}", wrkNo);
                return;
            }
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_COMPLETE, JSON.toJSONString(wrkMast));
        });
    }
    @Transactional(rollbackFor = Exception.class)
    public void processCompleteCrnMove(Integer wrkNo) {
        processWithLock(wrkNo, () -> {
            WrkMast wrkMast = wrkMastService.selectByWorkNo(wrkNo);
            if (wrkMast == null) {
                log.error("堆垛机移位完成清理跳过,未找到工作档。wrkNo={}", wrkNo);
                return;
            }
            if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.COMPLETE_CRN_MOVE.sts)) {
                log.error("堆垛机移位完成清理跳过,工作状态不匹配。wrkNo={}, wrkSts={}", wrkNo, wrkMast.getWrkSts());
                return;
            }
            archiveAndFinish(wrkMast);
            if (wrkMastService.deleteByWrkNoAndCurrentWrkSts(wrkMast.getWrkNo(), WrkStsType.COMPLETE_CRN_MOVE.sts) <= 0) {
                log.error("堆垛机移位完成清理失败,删除工作档失败或状态已变化。wrkNo={}", wrkNo);
            }
        });
    }
    @Transactional(rollbackFor = Exception.class)
    public void processCancelTask(Integer wrkNo) {
        processWithLock(wrkNo, () -> {
            WrkMast wrkMast = wrkMastService.selectByWorkNo(wrkNo);
            if (wrkMast == null) {
                log.error("取消任务清理跳过,未找到工作档。wrkNo={}", wrkNo);
                return;
            }
            if (wrkMast.getMk() == null || !CANCEL_MKS.contains(wrkMast.getMk())) {
                log.error("取消任务清理跳过,取消标记不匹配。wrkNo={}, mk={}", wrkNo, wrkMast.getMk());
                return;
            }
            archiveAndFinish(wrkMast);
            if (wrkMastService.deleteByWrkNoAndMkIn(wrkMast.getWrkNo(), CANCEL_MKS) <= 0) {
                log.error("取消任务清理失败,删除工作档失败或标记已变化。wrkNo={}, mk={}", wrkNo, wrkMast.getMk());
                return;
            }
            if (Objects.equals(wrkMast.getIoType(), WrkIoType.IN.id)) {
                LocMast locMast = locMastService.queryByLoc(wrkMast.getLocNo());
                if (locMast != null) {
                    locMast.setLocSts(String.valueOf(LocStsType.O));
                    locMast.setModiTime(new Date());
                    locMastService.updateById(locMast);
                }
            } else if (Objects.equals(wrkMast.getIoType(), WrkIoType.OUT.id)) {
                LocMast locMast = locMastService.queryByLoc(wrkMast.getSourceLocNo());
                if (locMast != null) {
                    locMast.setLocSts(String.valueOf(LocStsType.F));
                    locMast.setModiTime(new Date());
                    locMastService.updateById(locMast);
                }
            } else if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id)) {
                LocMast sourceLocMast = locMastService.queryByLoc(wrkMast.getSourceLocNo());
                LocMast locMast = locMastService.queryByLoc(wrkMast.getLocNo());
                if (sourceLocMast != null && String.valueOf(LocStsType.R).equals(sourceLocMast.getLocSts())) {
                    sourceLocMast.setLocSts(String.valueOf(LocStsType.F));
                    sourceLocMast.setModiTime(new Date());
                    locMastService.updateById(sourceLocMast);
                }
                if (locMast != null && String.valueOf(LocStsType.S).equals(locMast.getLocSts())) {
                    locMast.setLocSts(String.valueOf(LocStsType.O));
                    locMast.setModiTime(new Date());
                    locMastService.updateById(locMast);
                }
            }
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_CANCEL, JSON.toJSONString(wrkMast));
        });
    }
    private void processWithLock(Integer wrkNo, Runnable action) {
        if (wrkNo == null || wrkNo <= 0 || action == null) {
            log.error("WrkMastFinalizeProcessor处理跳过,参数非法。wrkNo={}, actionNull={}", wrkNo, action == null);
            return;
        }
        String lockKey = RedisKeyType.WRK_MAST_SCHEDULER_LOCK.key + wrkNo;
        String lockValue = UUID.randomUUID().toString();
        if (!redisUtil.trySetStringIfAbsent(lockKey, lockValue, WRK_LOCK_SECONDS)) {
            log.error("WrkMastFinalizeProcessor获取工作档处理锁失败。wrkNo={}, lockKey={}", wrkNo, lockKey);
            return;
        }
        try {
            action.run();
        } catch (Exception e) {
            log.error("WrkMastFinalizeProcessor处理工作档异常。wrkNo={}", wrkNo, e);
            throw e;
        } finally {
            if (!redisUtil.compareAndDelete(lockKey, lockValue)) {
                log.error("WrkMastFinalizeProcessor释放工作档处理锁失败。wrkNo={}, lockKey={}", wrkNo, lockKey);
            }
        }
    }
    private void archiveAndFinish(WrkMast wrkMast) {
        WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
        if (wrkMastLog != null) {
            wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
        }
    }
    private void clearTaskPath(Integer taskNo, Integer sourceStaNo, Integer staNo) {
        List<BasStation> basStations = basStationService.list(new QueryWrapper<BasStation>().in("station_id", sourceStaNo, staNo));
        for (BasStation basStation : basStations) {
            StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
            if (stationThread != null) {
                stationOperateProcessUtils.attemptClearTaskPath(stationThread, taskNo);
            }
        }
    }
    private Date resolveFinishTime(WrkMast wrkMast) {
        if (wrkMast == null) {
            return new Date();
        }
        if (wrkMast.getModiTime() != null) {
            return wrkMast.getModiTime();
        }
        if (wrkMast.getIoTime() != null) {
            return wrkMast.getIoTime();
        }
        return new Date();
    }
}
src/main/java/com/zy/asrs/task/WrkMastScheduler.java
@@ -1,21 +1,12 @@
package com.zy.asrs.task;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zy.asrs.domain.enums.NotifyMsgType;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.WrkMastLog;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.service.*;
import com.zy.asrs.utils.NotifyUtils;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.LocStsType;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.WrkIoType;
import com.zy.asrs.service.LocMastService;
import com.zy.asrs.service.WrkMastService;
import com.zy.core.enums.WrkStsType;
import com.zy.core.thread.StationThread;
import com.zy.core.utils.StationOperateProcessUtils;
import com.zy.core.task.MainProcessTaskSubmitter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@@ -24,301 +15,61 @@
import java.util.Date;
import java.util.List;
@Component
@Slf4j
public class WrkMastScheduler {
    private static final long MIN_SUBMIT_INTERVAL_MS = 0L;
    private static final String WRK_MAST_FINALIZE_LANE_PREFIX = "wrk-mast-finalize-";
    private final WrkMastService wrkMastService;
    private final WrkMastLogService wrkMastLogService;
    private final WrkAnalysisService wrkAnalysisService;
    private final LocMastService locMastService;
    private final NotifyUtils notifyUtils;
    private final StationOperateProcessUtils stationOperateProcessUtils;
    private final BasStationService basStationService;
    private final MainProcessTaskSubmitter mainProcessTaskSubmitter;
    private final WrkMastFinalizeProcessor wrkMastFinalizeProcessor;
    public WrkMastScheduler(WrkMastService wrkMastService,
                            WrkMastLogService wrkMastLogService,
                            WrkAnalysisService wrkAnalysisService,
                            LocMastService locMastService,
                            NotifyUtils notifyUtils,
                            StationOperateProcessUtils stationOperateProcessUtils,
                            BasStationService basStationService
    ) {
                            MainProcessTaskSubmitter mainProcessTaskSubmitter,
                            WrkMastFinalizeProcessor wrkMastFinalizeProcessor) {
        this.wrkMastService = wrkMastService;
        this.wrkMastLogService = wrkMastLogService;
        this.wrkAnalysisService = wrkAnalysisService;
        this.locMastService = locMastService;
        this.notifyUtils = notifyUtils;
        this.stationOperateProcessUtils = stationOperateProcessUtils;
        this.basStationService = basStationService;
        this.mainProcessTaskSubmitter = mainProcessTaskSubmitter;
        this.wrkMastFinalizeProcessor = wrkMastFinalizeProcessor;
    }
    @Scheduled(cron = "0/1 * * * * ? ")
    @Transactional
    public void executeIn(){
        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_INBOUND.sts));
        if (wrkMasts.isEmpty()) {
            return;
        }
        for (WrkMast wrkMast : wrkMasts) {
            Integer taskNo = wrkMast.getWrkNo();
            Integer sourceStaNo = wrkMast.getSourceStaNo();
            Integer staNo = wrkMast.getStaNo();
            String locNo = wrkMast.getLocNo();
            LocMast locMast = locMastService.queryByLoc(locNo);
            if (locMast == null) {
                log.info("[workNo={}]库位不存在", wrkMast.getWrkNo());
                continue;
            }
            if (!locMast.getLocSts().equals("S")) {
                log.info("[workNo={}]库位状态不处于S", wrkMast.getWrkNo());
                continue;
            }
            locMast.setLocSts("F");
            locMast.setBarcode(wrkMast.getBarcode());
            locMast.setModiTime(new Date());
            boolean result = locMastService.updateById(locMast);
            if (!result) {
                log.info("[workNo={}]库位状态F更新失败", wrkMast.getWrkNo());
                continue;
            }
            // 保存工作主档历史档
            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
            if (wrkMastLog == null) {
                log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo());
            } else {
                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
            }
            // 删除工作主档
            if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
                log.info("删除工作主档[workNo={}]失败", wrkMast.getWrkNo());
            }
            //上报
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_COMPLETE, JSON.toJSONString(wrkMast));
            //清理路径
            List<BasStation> basStations = basStationService.list(new QueryWrapper<BasStation>().in("station_id", sourceStaNo, staNo));
            if (!basStations.isEmpty()) {
                for (BasStation basStation : basStations) {
                    StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
                    if (stationThread != null) {
                        stationOperateProcessUtils.attemptClearTaskPath(stationThread, taskNo);
                    }
                }
            }
        }
    public void executeIn() {
        submitByWrkNo(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_INBOUND.sts),
                "executeIn",
                wrkMastFinalizeProcessor::processCompleteInbound);
    }
    @Scheduled(cron = "0/1 * * * * ? ")
    @Transactional
    public void executeOut(){
        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_OUTBOUND.sts));
        if (wrkMasts.isEmpty()) {
            return;
        }
        for (WrkMast wrkMast : wrkMasts) {
            Integer taskNo = wrkMast.getWrkNo();
            Integer sourceStaNo = wrkMast.getSourceStaNo();
            Integer staNo = wrkMast.getStaNo();
            String locNo = wrkMast.getSourceLocNo();
            LocMast locMast = locMastService.queryByLoc(locNo);
            if (locMast == null) {
                log.info("[workNo={}]库位不存在", wrkMast.getWrkNo());
                continue;
            }
            if (!(locMast.getLocSts().equals("R") || locMast.getLocSts().equals("O"))) {
                log.info("[workNo={}]库位状态不处于R or O", wrkMast.getWrkNo());
                continue;
            }
            locMast.setLocSts("O");
            locMast.setBarcode("");
            locMast.setModiTime(new Date());
            boolean result = locMastService.updateById(locMast);
            if (!result) {
                log.info("[workNo={}]库位状态O更新失败", wrkMast.getWrkNo());
                continue;
            }
            // 保存工作主档历史档
            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
            if (wrkMastLog == null) {
                log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo());
            } else {
                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
            }
            // 删除工作主档
            if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
                log.info("删除工作主档[workNo={}]失败", wrkMast.getWrkNo());
            }
            //上报
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_COMPLETE, JSON.toJSONString(wrkMast));
            //清理路径
            List<BasStation> basStations = basStationService.list(new QueryWrapper<BasStation>().in("station_id", sourceStaNo, staNo));
            if (!basStations.isEmpty()) {
                for (BasStation basStation : basStations) {
                    StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
                    if (stationThread != null) {
                        stationOperateProcessUtils.attemptClearTaskPath(stationThread, taskNo);
                    }
                }
            }
        }
    public void executeOut() {
        submitByWrkNo(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_OUTBOUND.sts),
                "executeOut",
                wrkMastFinalizeProcessor::processCompleteOutbound);
    }
    @Scheduled(cron = "0/1 * * * * ? ")
    @Transactional
    public void executeLocMove(){
        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_LOC_MOVE.sts));
        if (wrkMasts.isEmpty()) {
            return;
        }
        for (WrkMast wrkMast : wrkMasts) {
            String sourceLocNo = wrkMast.getSourceLocNo();
            String locNo = wrkMast.getLocNo();
            LocMast locMast = locMastService.queryByLoc(locNo);
            if (locMast == null) {
                log.info("[workNo={}]库位不存在", wrkMast.getWrkNo());
                continue;
            }
            if (!locMast.getLocSts().equals("S")) {
                log.info("[workNo={}]库位状态不处于S", wrkMast.getWrkNo());
                continue;
            }
            LocMast sourceLocMast = locMastService.queryByLoc(sourceLocNo);
            if (sourceLocMast == null) {
                log.info("[workNo={}]库位不存在", wrkMast.getWrkNo());
                continue;
            }
            if (!sourceLocMast.getLocSts().equals("R")) {
                log.info("[workNo={}]库位状态不处于R", wrkMast.getWrkNo());
                continue;
            }
            locMast.setLocSts("F");
            locMast.setBarcode(wrkMast.getBarcode());
            locMast.setModiTime(new Date());
            boolean result = locMastService.updateById(locMast);
            if (!result) {
                log.info("[workNo={}]库位状态F更新失败", wrkMast.getWrkNo());
                continue;
            }
            sourceLocMast.setLocSts("O");
            sourceLocMast.setBarcode("");
            sourceLocMast.setModiTime(new Date());
            boolean result2 = locMastService.updateById(sourceLocMast);
            if (!result2) {
                log.info("[workNo={}]库位状态O更新失败", wrkMast.getWrkNo());
                continue;
            }
            // 保存工作主档历史档
            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
            if (wrkMastLog == null) {
                log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo());
            } else {
                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
            }
            // 删除工作主档
            if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
                log.info("删除工作主档[workNo={}]失败", wrkMast.getWrkNo());
            }
            //上报
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_COMPLETE, JSON.toJSONString(wrkMast));
        }
    public void executeLocMove() {
        submitByWrkNo(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_LOC_MOVE.sts),
                "executeLocMove",
                wrkMastFinalizeProcessor::processCompleteLocMove);
    }
    @Scheduled(cron = "0/1 * * * * ? ")
    @Transactional
    public void executeCrnMove(){
        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_CRN_MOVE.sts));
        if (wrkMasts.isEmpty()) {
            return;
        }
        for (WrkMast wrkMast : wrkMasts) {
            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
            if (wrkMastLog == null) {
                log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo());
            } else {
                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
            }
            if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
                log.info("删除工作主档[workNo={}]失败", wrkMast.getWrkNo());
            }
        }
    public void executeCrnMove() {
        submitByWrkNo(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_CRN_MOVE.sts),
                "executeCrnMove",
                wrkMastFinalizeProcessor::processCompleteCrnMove);
    }
    @Scheduled(cron = "0/1 * * * * ? ")
    @Transactional
    public void executeCancelTask(){
        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
                .in("mk", "taskCancel", "taskForceCancel"));
        if (wrkMasts.isEmpty()) {
            return;
        }
        for (WrkMast wrkMast : wrkMasts) {
            // 保存工作主档历史档
            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
            if (wrkMastLog == null) {
                log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo());
            } else {
                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
            }
            // 删除工作主档
            if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
                log.info("删除工作主档[workNo={}]失败", wrkMast.getWrkNo());
            }
            if (wrkMast.getIoType() == WrkIoType.IN.id) {
                LocMast locMast = locMastService.queryByLoc(wrkMast.getLocNo());
                locMast.setLocSts(String.valueOf(LocStsType.O));
                locMast.setModiTime(new Date());
                locMastService.updateById(locMast);
            } else if (wrkMast.getIoType() == WrkIoType.OUT.id) {
                LocMast locMast = locMastService.queryByLoc(wrkMast.getSourceLocNo());
                locMast.setLocSts(String.valueOf(LocStsType.F));
                locMast.setModiTime(new Date());
                locMastService.updateById(locMast);
            } else if (wrkMast.getIoType() == WrkIoType.LOC_MOVE.id) {
                LocMast sourceLocMast = locMastService.queryByLoc(wrkMast.getSourceLocNo());
                LocMast locMast = locMastService.queryByLoc(wrkMast.getLocNo());
                if (sourceLocMast.getLocSts().equals(String.valueOf(LocStsType.R))) {
                    sourceLocMast.setLocSts(String.valueOf(LocStsType.F));
                    sourceLocMast.setModiTime(new Date());
                    locMastService.updateById(sourceLocMast);
                }
                if (locMast.getLocSts().equals(String.valueOf(LocStsType.S))) {
                    locMast.setLocSts(String.valueOf(LocStsType.O));
                    locMast.setModiTime(new Date());
                    locMastService.updateById(locMast);
                }
            }
            //上报
            notifyUtils.notify("task", 1, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.TASK_CANCEL, JSON.toJSONString(wrkMast));
        }
    public void executeCancelTask() {
        submitByWrkNo(new QueryWrapper<WrkMast>().in("mk", "taskCancel", "taskForceCancel"),
                "executeCancelTask",
                wrkMastFinalizeProcessor::processCancelTask);
    }
    @Scheduled(cron = "0/1 * * * * ? ")
@@ -352,22 +103,37 @@
            boolean result = locMastService.updateById(locMast);
            if (!result) {
                log.info("[workNo={}]库位状态O更新失败", wrkMast.getWrkNo());
                continue;
            }
        }
    }
    private Date resolveFinishTime(WrkMast wrkMast) {
        if (wrkMast == null) {
            return new Date();
    private void submitByWrkNo(QueryWrapper<WrkMast> queryWrapper, String taskNamePrefix, WrkNoHandler handler) {
        List<WrkMast> wrkMasts = wrkMastService.list(queryWrapper);
        if (wrkMasts.isEmpty()) {
            return;
        }
        if (wrkMast.getModiTime() != null) {
            return wrkMast.getModiTime();
        for (WrkMast wrkMast : wrkMasts) {
            if (wrkMast == null || wrkMast.getWrkNo() == null || wrkMast.getWrkNo() <= 0) {
                log.error("WrkMastScheduler提交任务跳过,工作档为空或工作号非法。taskNamePrefix={}, wrkMast={}", taskNamePrefix, wrkMast);
                continue;
            }
            Integer wrkNo = wrkMast.getWrkNo();
            boolean submitted = mainProcessTaskSubmitter.submitKeyedSerialTask(
                    WRK_MAST_FINALIZE_LANE_PREFIX,
                    wrkNo,
                    taskNamePrefix + "-" + wrkNo,
                    MIN_SUBMIT_INTERVAL_MS,
                    () -> handler.handle(wrkNo)
            );
            if (!submitted) {
                log.error("WrkMastScheduler提交单任务处理失败。taskNamePrefix={}, wrkNo={}, lanePrefix={}",
                        taskNamePrefix, wrkNo, WRK_MAST_FINALIZE_LANE_PREFIX);
            }
        }
        if (wrkMast.getIoTime() != null) {
            return wrkMast.getIoTime();
        }
        return new Date();
    }
    @FunctionalInterface
    private interface WrkNoHandler {
        void handle(Integer wrkNo);
    }
}
src/main/java/com/zy/common/service/CommonService.java
@@ -64,36 +64,10 @@
     * 生成工作号
     * @return workNo(工作号)
     */
    public synchronized int getWorkNo(Integer wrkMk) {
        WrkLastno wrkLastno = wrkLastnoService.getById(wrkMk);
        if (Cools.isEmpty(wrkLastno)) {
            throw new CoolException("数据异常,请联系管理员");
        }
        int workNo = wrkLastno.getWrkNo();
        int sNo = wrkLastno.getsNo();
        int eNo = wrkLastno.geteNo();
        workNo = workNo>=eNo ? sNo : workNo+1;
        while (true) {
            WrkMast wrkMast = wrkMastService.selectByWorkNo(workNo);
            if (null != wrkMast) {
                workNo = workNo>=eNo ? sNo : workNo+1;
            } else {
                break;
            }
        }
        // 修改序号记录
        if (workNo > 0){
            wrkLastno.setWrkNo(workNo);
            wrkLastnoService.updateById(wrkLastno);
        }
        // 检验
        if (workNo == 0) {
    public int getWorkNo(Integer wrkMk) {
        int workNo = wrkLastnoService.allocateNextWorkNo(wrkMk);
        if (workNo <= 0) {
            throw new CoolException("生成工作号失败,请联系管理员");
        } else {
            if (wrkMastService.selectByWorkNo(workNo)!=null) {
                throw new CoolException("生成工作号" + workNo + "在工作档中已存在");
            }
        }
        return workNo;
    }
@@ -115,18 +89,29 @@
            throw new CoolException("任务不存在");
        }
        Long currentWrkSts = wrkMast.getWrkSts();
        Long targetWrkSts;
        if (wrkMast.getIoType() == WrkIoType.IN.id) {
            wrkMast.setWrkSts(WrkStsType.COMPLETE_INBOUND.sts);
            targetWrkSts = WrkStsType.COMPLETE_INBOUND.sts;
        }else if (wrkMast.getIoType() == WrkIoType.OUT.id) {
            wrkMast.setWrkSts(WrkStsType.COMPLETE_OUTBOUND.sts);
            targetWrkSts = WrkStsType.COMPLETE_OUTBOUND.sts;
        } else if (wrkMast.getIoType() == WrkIoType.LOC_MOVE.id) {
            wrkMast.setWrkSts(WrkStsType.COMPLETE_LOC_MOVE.sts);
            targetWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
        } else if (wrkMast.getIoType() == WrkIoType.CRN_MOVE.id) {
            wrkMast.setWrkSts(WrkStsType.COMPLETE_CRN_MOVE.sts);
            targetWrkSts = WrkStsType.COMPLETE_CRN_MOVE.sts;
        } else {
            throw new CoolException("任务类型不支持完成");
        }
        wrkMast.setModiTime(new Date());
        wrkMastService.updateById(wrkMast);
        boolean updated = wrkMastService.update(null, new UpdateWrapper<WrkMast>()
                .set("wrk_sts", targetWrkSts)
                .set("modi_time", new Date())
                .set("memo", "手动完成")
                .eq("wrk_no", wrkMast.getWrkNo())
                .eq("wrk_sts", currentWrkSts));
        if (!updated) {
            throw new CoolException("任务状态已变化,完成失败");
        }
        return true;
    }
@@ -169,6 +154,7 @@
                .eq("wrk_no", wrkMast.getWrkNo())
                .eq("wrk_sts", expectedWrkSts)
                .set("mk", "taskCancel")
                .set("memo", "手动取消")
                .set("modi_time", new Date()));
        if (!updated) {
            throw new CoolException("任务状态已变化,取消失败");
@@ -197,6 +183,7 @@
        boolean updated = wrkMastService.update(null, new UpdateWrapper<WrkMast>()
                .eq("wrk_no", wrkMast.getWrkNo())
                .set("mk", "taskForceCancel")
                .set("memo", "手动完成")
                .set("modi_time", new Date()));
        if (!updated) {
            throw new CoolException("任务强制取消失败");
src/main/java/com/zy/core/enums/RedisKeyType.java
@@ -43,7 +43,9 @@
    CHECK_OUT_STATION_STAY_TIME_OUT_LIMIT("check_out_station_stay_time_out_limit_"),
    CHECK_IN_STATION_STAY_TIME_OUT_LIMIT("check_in_station_stay_time_out_limit_"),
    CRN_IO_EXECUTE_FINISH_LIMIT("crn_io_execute_finish_limit_"),
    CRN_IO_EXECUTE_FINISH_OWNER_LOCK("crn_io_execute_finish_owner_lock_"),
    DUAL_CRN_IO_EXECUTE_FINISH_LIMIT("dual_crn_io_execute_finish_limit_"),
    WRK_MAST_SCHEDULER_LOCK("wrk_mast_scheduler_lock_"),
    STATION_IN_EXECUTE_LIMIT("station_in_execute_limit_"),
    STATION_OUT_EXECUTE_LIMIT("station_out_execute_limit_"),
    STATION_OUT_PENDING_DISPATCH_("station_out_pending_dispatch_"),
src/main/java/com/zy/core/service/WrkCommandRollbackService.java
@@ -1,6 +1,7 @@
package com.zy.core.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.core.common.Cools;
import com.core.exception.CoolException;
import com.zy.asrs.domain.param.ManualRollbackTaskParam;
@@ -70,13 +71,21 @@
        saveWrkLog(wrkMast.getWrkNo());
        wrkMast.setWrkSts(updateStatus);
        wrkMast.setSendFailCount(nextFailCount);
        wrkMast.setErrorTime(new Date());
        wrkMast.setErrorMemo(failMessage);
        wrkMast.setSystemMsg(failMessage);
        wrkMast.setModiTime(new Date());
        wrkMastService.updateById(wrkMast);
        Long currentWrkSts = wrkMast.getWrkSts();
        Date now = new Date();
        boolean updated = wrkMastService.update(null, new UpdateWrapper<WrkMast>()
                .set("wrk_sts", updateStatus)
                .set("send_fail_count", nextFailCount)
                .set("error_time", now)
                .set("error_memo", failMessage)
                .set("system_msg", failMessage)
                .set("modi_time", now)
                .eq("wrk_no", wrkMast.getWrkNo())
                .eq("wrk_sts", currentWrkSts));
        if (!updated) {
            log.info("工作档状态已变化,跳过命令失败回滚。wrkNo={}", wrkMast.getWrkNo());
            return;
        }
        if (manualRequired) {
            News.taskError(wrkMast.getWrkNo(), "{}下发命令失败,已达到自动回滚上限({}),请在工作档手动回滚。原因: {}", deviceName, rollbackLimit, failMessage);
@@ -99,11 +108,17 @@
        saveWrkLog(wrkMast.getWrkNo());
        wrkMast.setWrkSts(rollbackStatus);
        wrkMast.setSendFailCount(0);
        wrkMast.setSystemMsg("人工回滚完成,等待重新下发");
        wrkMast.setModiTime(new Date());
        wrkMastService.updateById(wrkMast);
        Long currentWrkSts = wrkMast.getWrkSts();
        boolean updated = wrkMastService.update(null, new UpdateWrapper<WrkMast>()
                .set("wrk_sts", rollbackStatus)
                .set("send_fail_count", 0)
                .set("system_msg", "人工回滚完成,等待重新下发")
                .set("modi_time", new Date())
                .eq("wrk_no", wrkMast.getWrkNo())
                .eq("wrk_sts", currentWrkSts));
        if (!updated) {
            throw new CoolException("任务状态已变化,请刷新后重试");
        }
        News.taskInfo(wrkMast.getWrkNo(), "人工回滚完成,任务状态已恢复为待执行");
        return true;
    }
@@ -118,12 +133,17 @@
        saveWrkLog(wrkMast.getWrkNo());
        wrkMast.setSendFailCount(0);
        wrkMast.setErrorTime(null);
        wrkMast.setErrorMemo(null);
        wrkMast.setSystemMsg("");
        wrkMast.setModiTime(new Date());
        wrkMastService.updateById(wrkMast);
        boolean updated = wrkMastService.update(null, new UpdateWrapper<WrkMast>()
                .set("send_fail_count", 0)
                .set("error_time", null)
                .set("error_memo", null)
                .set("system_msg", "")
                .set("modi_time", new Date())
                .eq("wrk_no", wrkMast.getWrkNo())
                .eq("wrk_sts", wrkMast.getWrkSts()));
        if (!updated) {
            log.info("工作档状态已变化,跳过清理下发失败标记。wrkNo={}", wrkMast.getWrkNo());
        }
    }
    private WrkMast findWrkMast(Integer wrkNo, String taskNo) {
src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java
@@ -37,15 +37,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
@Component
public class CrnOperateProcessUtils {
@@ -726,36 +718,53 @@
                return;
            }
            Long updateWrkSts = null;
            Date now = new Date();
            if(wrkMast.getWrkSts() == WrkStsType.INBOUND_RUN.sts){
                updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts;
                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_COMPLETE, null);
            }else if(wrkMast.getWrkSts() == WrkStsType.OUTBOUND_RUN.sts){
                updateWrkSts = WrkStsType.OUTBOUND_RUN_COMPLETE.sts;
                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_COMPLETE, null);
            }else if(wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts){
                updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_COMPLETE, null);
            }else if(wrkMast.getWrkSts() == WrkStsType.CRN_MOVE_RUN.sts){
                updateWrkSts = WrkStsType.COMPLETE_CRN_MOVE.sts;
            }else{
                News.error("堆垛机处于等待确认且任务完成状态,但工作状态异常。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
            String ownerLockKey = RedisKeyType.CRN_IO_EXECUTE_FINISH_OWNER_LOCK.key + wrkMast.getWrkNo();
            String ownerLockToken = UUID.randomUUID().toString();
            if (!redisUtil.trySetStringIfAbsent(ownerLockKey, ownerLockToken, 10)) {
                return;
            }
            try {
                wrkMast = wrkMastService.selectByWorkNo(crnProtocol.getTaskNo());
                if (wrkMast == null) {
                    News.error("堆垛机处于等待确认且任务完成状态,但未找到工作档。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                    return;
                }
            wrkMast.setWrkSts(updateWrkSts);
            wrkMast.setSystemMsg("");
            wrkMast.setIoTime(now);
            wrkMast.setModiTime(now);
            if (wrkMastService.updateById(wrkMast)) {
                wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts);
                CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo());
                MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand));
                News.info("堆垛机任务状态更新成功,堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                Long currentWrkSts = wrkMast.getWrkSts();
                Long updateWrkSts = null;
                Date now = new Date();
                if(currentWrkSts == WrkStsType.INBOUND_RUN.sts){
                    updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts;
                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_COMPLETE, null);
                }else if(currentWrkSts == WrkStsType.OUTBOUND_RUN.sts){
                    updateWrkSts = WrkStsType.OUTBOUND_RUN_COMPLETE.sts;
                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_COMPLETE, null);
                }else if(currentWrkSts == WrkStsType.LOC_MOVE_RUN.sts){
                    updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_COMPLETE, null);
                }else if(currentWrkSts == WrkStsType.CRN_MOVE_RUN.sts){
                    updateWrkSts = WrkStsType.COMPLETE_CRN_MOVE.sts;
                }else{
                    News.error("堆垛机处于等待确认且任务完成状态,但工作状态异常。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                    return;
                }
                if (wrkMastService.updateWrkStsByWrkNoAndCurrentWrkSts(wrkMast.getWrkNo(), updateWrkSts, currentWrkSts) > 0) {
                    wrkMast.setWrkSts(updateWrkSts);
                    wrkMast.setSystemMsg("");
                    wrkMast.setIoTime(now);
                    wrkMast.setModiTime(now);
                    wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts);
                    CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo());
                    MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand));
                    News.info("堆垛机任务状态更新成功,堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                    redisUtil.set(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo(), "lock",10);
                } else {
                    News.info("堆垛机完成确认跳过,任务状态已变化。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                }
            } finally {
                redisUtil.compareAndDelete(ownerLockKey, ownerLockToken);
            }
            redisUtil.set(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo(), "lock",10);
        }
    }
src/main/resources/application.yml
@@ -1,6 +1,6 @@
# 系统版本信息
app:
  version: 3.0.0.3
  version: 3.0.0.4
  version-type: prd  # prd 或 dev
  i18n:
    default-locale: zh-CN
src/main/resources/mapper/WrkLastnoMapper.xml
@@ -16,4 +16,10 @@
    </resultMap>
    <select id="selectByWrkMkForUpdate" resultMap="BaseResultMap">
        select * from asr_wrk_lastno
        where wrk_mk = #{wrkMk}
        for update
    </select>
</mapper>
src/main/resources/mapper/WrkMastMapper.xml
@@ -39,6 +39,28 @@
        limit 0,1
    </select>
    <update id="updateWrkStsByWrkNoAndCurrentWrkSts">
        update asr_wrk_mast
        set wrk_sts = #{wrkSts}
        where wrk_no = #{wrkNo}
          and wrk_sts = #{currentWrkSts}
    </update>
    <delete id="deleteByWrkNoAndCurrentWrkSts">
        delete from asr_wrk_mast
        where wrk_no = #{wrkNo}
          and wrk_sts = #{currentWrkSts}
    </delete>
    <delete id="deleteByWrkNoAndMkIn">
        delete from asr_wrk_mast
        where wrk_no = #{wrkNo}
          and mk in
        <foreach collection="mks" item="mk" open="(" separator="," close=")">
            #{mk}
        </foreach>
    </delete>
    <select id="selectCrnTaskCountList" resultType="com.zy.asrs.domain.vo.WrkTaskCountVo">
        select
            crn_no as deviceNo,