自动化立体仓库 - WMS系统
chen.llin
昨天 46168fbb7c925b0ec04def176095e967720e684a
agv查询修复
8个文件已修改
316 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/TaskLogController.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/TaskLog.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/AgvScheduler.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/AgvHandler.java 122 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/config/CoolExceptionHandler.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/web/BaseController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/taskLog/taskLog.js 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/TaskLogController.java
@@ -12,6 +12,7 @@
import com.zy.asrs.entity.TaskLog;
import com.zy.asrs.service.TaskLogService;
import com.zy.common.web.BaseController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -20,6 +21,7 @@
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
public class TaskLogController extends BaseController {
@@ -40,20 +42,33 @@
                  @RequestParam(required = false) String orderByType,
                  @RequestParam(required = false) String condition,
                  @RequestParam Map<String, Object> param) {
        EntityWrapper<TaskLog> wrapper = new EntityWrapper<>();
        excludeTrash(param);
        convert(param, wrapper);
        allLike(TaskLog.class, param.keySet(), wrapper, condition);
        if (!Cools.isEmpty(orderByField)) {
            wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType));
        try {
            log.info("TaskLog查询请求,参数:curr={}, limit={}, param={}", curr, limit, param);
            EntityWrapper<TaskLog> wrapper = new EntityWrapper<>();
            excludeTrash(param);
            convert(param, wrapper);
            allLike(TaskLog.class, param.keySet(), wrapper, condition);
            if (!Cools.isEmpty(orderByField)) {
                wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType));
            }
            wrapper.orderBy("appe_time",false);
            Page<TaskLog> page = taskLogService.selectPage(new Page<>(curr, limit), wrapper);
            log.info("TaskLog查询成功,返回{}条记录", page.getRecords().size());
            return R.ok(page);
        } catch (Exception e) {
            log.error("TaskLog查询失败", e);
            e.printStackTrace();
            return R.error("查询失败:" + e.getMessage());
        }
        wrapper.orderBy("appe_time",false);
        return R.ok(taskLogService.selectPage(new Page<>(curr, limit), wrapper));
    }
    private <T> void convert(Map<String, Object> map, EntityWrapper<T> wrapper) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String val = String.valueOf(entry.getValue());
            // 跳过空值和 "null" 字符串
            if (Cools.isEmpty(val) || "null".equals(val)) {
                continue;
            }
            if (val.contains(RANGE_TIME_LINK)) {
                String[] dates = val.split(RANGE_TIME_LINK);
                wrapper.ge(entry.getKey(), DateUtils.convert(dates[0]));
src/main/java/com/zy/asrs/entity/TaskLog.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.core.common.SpringUtils;
import com.zy.asrs.service.*;
@@ -23,6 +24,13 @@
    @ApiModelProperty(value = "")
    private Long id;
    /**
     * 任务类型: agv  , crn
     */
    @ApiModelProperty(value = "任务类型: agv  , crn")
    @TableField("task_type")
    private String taskType;
    /**
     * 工作号
@@ -457,21 +465,11 @@
    }
    public String getStaNo$() {
        BasDevpService service = SpringUtils.getBean(BasDevpService.class);
        BasDevp basDevp = service.selectById(this.staNo);
        if (!Cools.isEmpty(basDevp)) {
            return String.valueOf(basDevp.getDevNo());
        }
        return null;
        return this.staNo;
    }
    public String getSourceStaNo$() {
        BasDevpService service = SpringUtils.getBean(BasDevpService.class);
        BasDevp basDevp = service.selectById(this.sourceStaNo);
        if (!Cools.isEmpty(basDevp)) {
            return String.valueOf(basDevp.getDevNo());
        }
        return null;
        return this.sourceStaNo;
    }
    public String getSourceLocNo$() {
src/main/java/com/zy/asrs/task/AgvScheduler.java
@@ -146,6 +146,7 @@
            
            Date now = new Date();
            int completedCount = 0;
            List<Task> completedTasks = new ArrayList<>();
            
            for (WrkMast wrkMast : completedWrkMasts) {
                // 查找对应的AGV任务(优先通过wrk_no查询)
@@ -173,11 +174,22 @@
                        agvTask.setWrkSts(9L);
                        agvTask.setModiTime(now);
                        if (taskService.updateById(agvTask)) {
                            completedTasks.add(agvTask);
                            completedCount++;
                            log.info("入库任务工作档已入库成功,完结AGV呼叫单,taskId:{},wrkNo:{},barcode:{}", 
                                agvTask.getId(), wrkMast.getWrkNo(), wrkMast.getBarcode());
                        }
                    }
                }
            }
            // 立即将完成的AGV任务转移到历史表,不保留在Task表中
            if (!completedTasks.isEmpty()) {
                try {
                    agvHandler.moveTaskToHistory(completedTasks);
                    log.info("入库完成,已将{}个AGV任务转移到历史表(不保留在Task表中)", completedTasks.size());
                } catch (Exception e) {
                    log.error("入库完成,转移AGV任务到历史表失败", e);
                }
            }
            
@@ -213,6 +225,7 @@
            
            Date now = new Date();
            int completedCount = 0;
            List<Task> completedTasks = new ArrayList<>();
            
            for (Task agvTask : agvTasks) {
                boolean isCompleted = false;
@@ -288,11 +301,12 @@
                    }
                }
                
                // 如果已完成,更新AGV任务状态
                // 如果已完成,更新AGV任务状态并收集到列表
                if (isCompleted) {
                    agvTask.setWrkSts(9L);
                    agvTask.setModiTime(now);
                    if (taskService.updateById(agvTask)) {
                        completedTasks.add(agvTask);
                        completedCount++;
                        log.info("{},完结AGV呼叫单,taskId:{},wrkNo:{},barcode:{},站点:{}", 
                            reason, agvTask.getId(), agvTask.getWrkNo(), agvTask.getBarcode(), agvTask.getStaNo());
@@ -300,6 +314,16 @@
                }
            }
            
            // 立即将完成的AGV任务转移到历史表,不保留在Task表中
            if (!completedTasks.isEmpty()) {
                try {
                    agvHandler.moveTaskToHistory(completedTasks);
                    log.info("入库/出库完成,已将{}个AGV任务转移到历史表(不保留在Task表中)", completedTasks.size());
                } catch (Exception e) {
                    log.error("入库/出库完成,转移AGV任务到历史表失败", e);
                }
            }
            if (completedCount > 0) {
                log.info("本次检查完结了{}个AGV呼叫单(工作档已完成或已转历史档)", completedCount);
            }
src/main/java/com/zy/asrs/task/handler/AgvHandler.java
@@ -322,14 +322,14 @@
            return null;
        }
        
        // 排除有正在搬运任务的站点(状态8:已呼叫AGV,正在搬运)
        List<BasDevp> availableDevList = new ArrayList<>();
        Integer taskIoType = task.getIoType();
        // 先按规则排序(入库任务数排序)
        devList.sort(Comparator.comparing(BasDevp::getInQty));
        
        // 根据任务类型确定要检查的io_type列表
        Integer taskIoType = task.getIoType();
        List<Integer> checkIoTypes = null;
        String taskTypeName = "";
        if (taskIoType != null) {
            // 根据任务类型确定要检查的io_type列表
            List<Integer> checkIoTypes;
            String taskTypeName;
            if (taskIoType < 100) {
                // 入库任务:只检查入库类型(1, 10, 53, 57)
                checkIoTypes = Arrays.asList(1, 10, 53, 57);
@@ -339,11 +339,54 @@
                checkIoTypes = Arrays.asList(101, 110, 103, 107);
                taskTypeName = "出库";
            }
        }
        // 筛选出任务数最少的站点列表(按规则排序后的候选站点)
        int minInQty = devList.get(0).getInQty();
        List<BasDevp> minTaskSites = devList.stream()
                .filter(dev -> dev.getInQty() == minInQty)
                .collect(Collectors.toList());
        // 根据配置选择分配策略,确定优先分配的站点顺序
        List<BasDevp> orderedSites = new ArrayList<>();
        String strategy = agvProperties.getSiteAllocation().getStrategy();
        boolean enableRoundRobin = agvProperties.getSiteAllocation().isEnableRoundRobin();
        if (minTaskSites.size() > 1 && enableRoundRobin && "round-robin".equals(strategy)) {
            // 轮询分配:先按轮询策略排序
            AtomicInteger counter = siteRoundRobinCounters.computeIfAbsent(groupKey, k -> new AtomicInteger(0));
            int startIndex = counter.get() % minTaskSites.size();
            // 将轮询选中的站点放在最前面
            orderedSites.addAll(minTaskSites.subList(startIndex, minTaskSites.size()));
            orderedSites.addAll(minTaskSites.subList(0, startIndex));
            // 添加其他站点(任务数更多的)
            orderedSites.addAll(devList.stream()
                    .filter(dev -> dev.getInQty() > minInQty)
                    .collect(Collectors.toList()));
            log.debug("使用轮询分配策略,站点组:{},轮询起始索引:{}", groupKey, startIndex);
        } else if (minTaskSites.size() > 1 && enableRoundRobin && "random".equals(strategy)) {
            // 随机分配:先随机排序任务数最少的站点
            List<BasDevp> shuffledMinSites = new ArrayList<>(minTaskSites);
            Collections.shuffle(shuffledMinSites);
            orderedSites.addAll(shuffledMinSites);
            // 添加其他站点(任务数更多的)
            orderedSites.addAll(devList.stream()
                    .filter(dev -> dev.getInQty() > minInQty)
                    .collect(Collectors.toList()));
            log.debug("使用随机分配策略");
        } else {
            // 默认:按入库任务数排序(已经排序好了)
            orderedSites = devList;
        }
        // 依次检查每个站点是否在搬运,找到第一个空闲站点就分配
        BasDevp selectedSite = null;
        for (BasDevp dev : orderedSites) {
            String staNo = String.valueOf(dev.getDevNo());
            
            // 检查每个站点是否有正在搬运的同类型任务
            for (BasDevp dev : devList) {
                String staNo = String.valueOf(dev.getDevNo());
                // 查询该站点是否有状态8(正在搬运)的同类型任务
            // 如果任务类型不为空,检查该站点是否有正在搬运的同类型任务
            boolean isTransporting = false;
            if (checkIoTypes != null && !checkIoTypes.isEmpty()) {
                List<Task> transportingTasks = taskService.selectList(
                    new EntityWrapper<Task>()
                        .eq("sta_no", staNo)
@@ -351,61 +394,29 @@
                        .eq("wrk_sts", 8L) // 只检查正在搬运状态的任务
                        .in("io_type", checkIoTypes)
                );
                isTransporting = !transportingTasks.isEmpty();
                
                if (transportingTasks.isEmpty()) {
                    // 该站点没有正在搬运的任务,可以分配
                    availableDevList.add(dev);
                } else {
                    log.debug("站点{}有{}个正在搬运的{}AGV任务,跳过分配",
                if (isTransporting) {
                    log.debug("站点{}有{}个正在搬运的{}AGV任务,检查下一个站点",
                        staNo, transportingTasks.size(), taskTypeName);
                    continue; // 该站点正在搬运,检查下一个站点
                }
            }
        } else {
            // 如果ioType为空,不进行过滤(保持原有逻辑)
            availableDevList = devList;
            // 找到第一个空闲站点,分配
            selectedSite = dev;
            log.info("任务ID:{}按规则应分配到站点{},该站点空闲,分配成功", task.getId(), staNo);
            break;
        }
        
        // 如果所有站点都在搬运,则不分配站点
        if (availableDevList.isEmpty()) {
            log.warn("任务ID:{}的所有候选站点都有正在搬运的{}任务,暂不分配站点",
        if (selectedSite == null) {
            log.warn("任务ID:{}的所有候选站点都有正在搬运的{}任务,暂不分配站点,等待空闲",
                task.getId(), taskIoType != null && taskIoType < 100 ? "入库" : "出库");
            return null;
        }
        
        // 入库任务数排序
        availableDevList.sort(Comparator.comparing(BasDevp::getInQty));
        // 选择站点
        BasDevp basDevp;
        int minInQty = availableDevList.get(0).getInQty();
        // 筛选出任务数最少的站点列表
        List<BasDevp> minTaskSites = availableDevList.stream()
                .filter(dev -> dev.getInQty() == minInQty)
                .collect(Collectors.toList());
        // 根据配置选择分配策略
        String strategy = agvProperties.getSiteAllocation().getStrategy();
        boolean enableRoundRobin = agvProperties.getSiteAllocation().isEnableRoundRobin();
        if (minTaskSites.size() > 1 && enableRoundRobin && "round-robin".equals(strategy)) {
            // 轮询分配
            AtomicInteger counter = siteRoundRobinCounters.computeIfAbsent(groupKey, k -> new AtomicInteger(0));
            int index = counter.getAndIncrement() % minTaskSites.size();
            basDevp = minTaskSites.get(index);
            log.info("使用轮询分配策略,站点组:{},轮询索引:{},选中站点:{}", groupKey, index, basDevp.getDevNo());
        } else if (minTaskSites.size() > 1 && enableRoundRobin && "random".equals(strategy)) {
            // 随机分配
            Random random = new Random();
            int index = random.nextInt(minTaskSites.size());
            basDevp = minTaskSites.get(index);
            log.info("使用随机分配策略,选中站点:{}", basDevp.getDevNo());
        } else {
            // 默认:选择第一个(任务最少的)
            basDevp = devList.get(0);
        }
        Integer endSite = basDevp.getDevNo();
        Integer endSite = selectedSite.getDevNo();
        
        // 入库暂存+1
        basDevpMapper.incrementInQty(endSite);
@@ -445,10 +456,11 @@
    @Transactional(rollbackFor = Exception.class)
    public void moveTaskToHistory(List<Task> taskList) {
        // 写入历史表
        // 写入历史表,保持ID一致
        for (Task task : taskList) {
            TaskLog log = new TaskLog();
            BeanUtils.copyProperties(task, log);
            // 保持ID一致,不设置为null
            taskLogService.insert(log);
        }
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java
@@ -865,55 +865,66 @@
    @Transactional(rollbackFor = Exception.class)
    public ReturnT<String> agvDoIn(Task wrkMast) {
        if (wrkMast.getIoType().equals(1)) {
            LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo()));
            if (Objects.isNull(locCache)) {
                throw new CoolException("数据错误,库位不存在!!");
            }
            if (!locCache.getLocSts().equals(LocStsType.LOC_STS_TYPE_S.type)) {
                throw new CoolException("当前库位状态为:" + LocStsType.LOC_STS_TYPE_S.type + "." + LocStsType.LOC_STS_TYPE_S.desc + ",不是出库预约状态");
            }
            List<TaskDetl> taskDetls = taskDetlService.selectList(new EntityWrapper<TaskDetl>().eq("task_id", wrkMast.getId()));
            if (Objects.isNull(taskDetls)) {
                throw new CoolException("数据错误:组托数据不存在!!");
            }
            taskDetls.forEach(pakin -> {
                LocCacheDetl detl = new LocCacheDetl();
                BeanUtils.copyProperties(pakin, detl);
                detl.setBarcode(pakin.getBarcode())
                        .setLocId(locCache.getId())
                        .setAnfme(pakin.getAnfme())
                        .setBrand(pakin.getBrand())
                        .setAppeTime(new Date())
                        .setSpecs(pakin.getSpecs())
                        .setColor(pakin.getColor())
                        .setLocNo(locCache.getLocNo())
                        .setAreaId(locCache.getAreaId())
                        .setAreaName(locCache.getAreaName())
                        .setUnit(pakin.getUnit())
                        .setBatch(pakin.getBatch());
                if (!locCacheDetlService.insert(detl)) {
                    throw new CoolException("库位明细保存失败!!");
        Integer ioType = wrkMast.getIoType();
        if (ioType == null) {
            throw new CoolException("数据错误:ioType为空!!");
        }
        // 处理入库任务类型:1(实托入库)、10(空托入库)、53、57
        if (ioType == 1 || ioType == 10 || ioType == 53 || ioType == 57) {
            // ioType == 1 需要处理组托数据
            if (ioType == 1) {
                LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo()));
                if (Objects.isNull(locCache)) {
                    throw new CoolException("数据错误,库位不存在!!");
                }
            });
                if (!locCache.getLocSts().equals(LocStsType.LOC_STS_TYPE_S.type)) {
                    throw new CoolException("当前库位状态为:" + LocStsType.LOC_STS_TYPE_S.type + "." + LocStsType.LOC_STS_TYPE_S.desc + ",不是出库预约状态");
                }
                List<TaskDetl> taskDetls = taskDetlService.selectList(new EntityWrapper<TaskDetl>().eq("task_id", wrkMast.getId()));
                if (Objects.isNull(taskDetls)) {
                    throw new CoolException("数据错误:组托数据不存在!!");
                }
                taskDetls.forEach(pakin -> {
                    LocCacheDetl detl = new LocCacheDetl();
                    BeanUtils.copyProperties(pakin, detl);
                    detl.setBarcode(pakin.getBarcode())
                            .setLocId(locCache.getId())
                            .setAnfme(pakin.getAnfme())
                            .setBrand(pakin.getBrand())
                            .setAppeTime(new Date())
                            .setSpecs(pakin.getSpecs())
                            .setColor(pakin.getColor())
                            .setLocNo(locCache.getLocNo())
                            .setAreaId(locCache.getAreaId())
                            .setAreaName(locCache.getAreaName())
                            .setUnit(pakin.getUnit())
                            .setBatch(pakin.getBatch());
                    if (!locCacheDetlService.insert(detl)) {
                        throw new CoolException("库位明细保存失败!!");
                    }
                });
            locCache.setLocSts(LocStsType.LOC_STS_TYPE_F.type);
            locCache.setModiTime(new Date());
            locCache.setBarcode(wrkMast.getBarcode());
            locCache.setModiTime(new Date());
            locCache.setIoTime(new Date());
            if (!locCacheService.updateById(locCache)) {
                throw new CoolException("库位状态修改失败!");
                locCache.setLocSts(LocStsType.LOC_STS_TYPE_F.type);
                locCache.setModiTime(new Date());
                locCache.setBarcode(wrkMast.getBarcode());
                locCache.setModiTime(new Date());
                locCache.setIoTime(new Date());
                if (!locCacheService.updateById(locCache)) {
                    throw new CoolException("库位状态修改失败!");
                }
            }
            // 更新任务状态为5(库存更新完成)
            wrkMast.setWrkSts(5L);
            wrkMast.setModiTime(new Date());
            if (!taskService.updateById(wrkMast)) {
                throw new CoolException("任务状态修改失败!!");
            }
        }else {
            throw new CoolException("数据错误:ioType不存在!!");
        } else {
            // 非入库任务类型,记录日志但不抛出异常,允许其他类型的任务通过
            log.warn("agvDoIn方法收到非入库任务类型,ioType:{},taskId:{},跳过处理", ioType, wrkMast.getId());
        }
        return SUCCESS;
    }
src/main/java/com/zy/common/config/CoolExceptionHandler.java
@@ -2,6 +2,7 @@
import com.core.common.R;
import com.core.exception.CoolException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -10,11 +11,13 @@
/**
 * Created by vincent on 2019-06-09
 */
@Slf4j
@RestControllerAdvice
public class CoolExceptionHandler {
    @ExceptionHandler(Exception.class)
    public R handlerException(HandlerMethod handler, Exception e) {
        log.error("全局异常处理器捕获异常,handler: {}, 异常信息: {}", handler != null ? handler.getMethod().getName() : "unknown", e.getMessage(), e);
        e.printStackTrace();
        return R.error();
    }
src/main/java/com/zy/common/web/BaseController.java
@@ -163,6 +163,14 @@
                    || Modifier.isTransient(field.getModifiers())) {
                continue;
            }
            // 检查字段是否标记为不存在于数据库表中
            if (field.isAnnotationPresent(TableField.class)) {
                TableField tableField = field.getAnnotation(TableField.class);
                // 如果字段标记为 exist = false,跳过该字段
                if (!tableField.exist()) {
                    continue;
                }
            }
            String column = null;
            if (field.isAnnotationPresent(TableField.class)) {
                column = field.getAnnotation(TableField.class).value();
src/main/webapp/static/js/taskLog/taskLog.js
@@ -44,11 +44,20 @@
            pageSize: 'limit'
        },
        parseData: function (res) {
            // 安全检查,防止res.data为undefined
            if (!res || !res.data) {
                return {
                    'code': res ? res.code : 500,
                    'msg': res ? res.msg : '数据格式错误',
                    'count': 0,
                    'data': []
                };
            }
            return {
                'code': res.code,
                'msg': res.msg,
                'count': res.data.total,
                'data': res.data.records
                'count': res.data.total || 0,
                'data': res.data.records || []
            }
        },
        response: {