| src/main/java/com/zy/asrs/controller/TaskLogController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/entity/TaskLog.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/task/AgvScheduler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/task/handler/AgvHandler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/common/config/CoolExceptionHandler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/zy/common/web/BaseController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/webapp/static/js/taskLog/taskLog.js | ●●●●● 补丁 | 查看 | 原始文档 | 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: {