refactor(wms): 重构批次分配逻辑
- 移除了死锁检测相关代码 - 修改了批次分配方案的生成逻辑 - 新增了 BatchGroupVo 类用于表示批次分组 - 优化了代码结构和命名,提高了可读性和可维护性
This commit is contained in:
@@ -4,6 +4,7 @@ import java.util.List;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import com.klp.domain.vo.BatchGroupVo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.constraints.*;
|
||||
@@ -99,27 +100,45 @@ public class WmsBatchController extends BaseController {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测任务执行是否会产生死锁
|
||||
*
|
||||
* @param rows 任务执行顺序数组
|
||||
* @return 是否存在死锁
|
||||
*/
|
||||
@PostMapping("/check")
|
||||
public R<Boolean> checkDeadlock(@RequestBody List<List<Map<String, Object>>> rows) {
|
||||
boolean hasDeadlock = iWmsBatchService.checkDeadlock(rows);
|
||||
return R.ok(hasDeadlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成不会产生死锁的批次分配方案
|
||||
* 相同processId的任务会合并到一个批次组中
|
||||
* 不同processId的任务会放在不同的批次组中
|
||||
*
|
||||
* @param rows 任务执行顺序数组
|
||||
* @return 批次分配方案
|
||||
*/
|
||||
@PostMapping("/generate")
|
||||
public R<List<String>> generateNonDeadlockBatches(@RequestBody List<List<Map<String, Object>>> rows) {
|
||||
List<String> batches = iWmsBatchService.generateNonDeadlockBatches(rows);
|
||||
return R.ok(batches);
|
||||
public R<List<BatchGroupVo>> generateNonDeadlockBatches(@RequestBody List<List<Map<String, Object>>> rows) {
|
||||
// 先获取原始的批次分配方案
|
||||
List<BatchGroupVo> originalBatches = iWmsBatchService.generateNonDeadlockBatches(rows);
|
||||
|
||||
// 使用Java 8 Stream API按processId分组并合并任务
|
||||
Map<String, List<BatchGroupVo>> groupedByProcessId = originalBatches.stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(BatchGroupVo::getProcessId));
|
||||
|
||||
// 合并相同processId的批次组
|
||||
List<BatchGroupVo> mergedBatches = new java.util.ArrayList<>();
|
||||
java.util.concurrent.atomic.AtomicInteger groupCounter = new java.util.concurrent.atomic.AtomicInteger(1);
|
||||
|
||||
groupedByProcessId.forEach((processId, groups) -> {
|
||||
// 创建一个新的合并后的批次组
|
||||
BatchGroupVo mergedGroup = new BatchGroupVo();
|
||||
mergedGroup.setGroupId("Merged-Group-" + groupCounter.getAndIncrement());
|
||||
mergedGroup.setProcessId(processId);
|
||||
|
||||
// 合并所有taskIds
|
||||
List<String> allTaskIds = groups.stream()
|
||||
.flatMap(group -> group.getTaskIds().stream())
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
mergedGroup.setTaskIds(allTaskIds);
|
||||
|
||||
mergedBatches.add(mergedGroup);
|
||||
});
|
||||
|
||||
return R.ok(mergedBatches);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
34
klp-wms/src/main/java/com/klp/domain/vo/BatchGroupVo.java
Normal file
34
klp-wms/src/main/java/com/klp/domain/vo/BatchGroupVo.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.klp.domain.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批次分组视图对象
|
||||
*
|
||||
* @author CodeBuddy
|
||||
* @date 2025-08-14
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BatchGroupVo {
|
||||
|
||||
/**
|
||||
* 批次组ID
|
||||
*/
|
||||
private String groupId;
|
||||
|
||||
/**
|
||||
* 工艺ID
|
||||
*/
|
||||
private String processId;
|
||||
|
||||
/**
|
||||
* 批次中的任务ID列表
|
||||
*/
|
||||
private List<String> taskIds;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.klp.service;
|
||||
|
||||
import com.klp.domain.vo.BatchGroupVo;
|
||||
import com.klp.domain.vo.WmsBatchVo;
|
||||
import com.klp.domain.bo.WmsBatchBo;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
@@ -48,19 +49,11 @@ public interface IWmsBatchService {
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
|
||||
/**
|
||||
* 检测任务执行是否会产生死锁
|
||||
*
|
||||
* @param rows 任务执行顺序数组
|
||||
* @return 是否存在死锁
|
||||
*/
|
||||
boolean checkDeadlock(List<List<Map<String, Object>>> rows);
|
||||
|
||||
/**
|
||||
* 生成不会产生死锁的批次分配方案
|
||||
*
|
||||
* @param rows 任务执行顺序数组
|
||||
* @return 批次分配方案
|
||||
*/
|
||||
List<String> generateNonDeadlockBatches(List<List<Map<String, Object>>> rows);
|
||||
List<BatchGroupVo> generateNonDeadlockBatches(List<List<Map<String, Object>>> rows);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.klp.common.utils.StringUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.klp.domain.bo.WmsBatchBo;
|
||||
import com.klp.domain.vo.BatchGroupVo;
|
||||
import com.klp.domain.vo.WmsBatchVo;
|
||||
import com.klp.domain.WmsBatch;
|
||||
import com.klp.mapper.WmsBatchMapper;
|
||||
@@ -115,32 +116,16 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测任务执行是否会产生死锁
|
||||
*
|
||||
* @param rows 任务执行顺序数组
|
||||
* @return 是否存在死锁
|
||||
*/
|
||||
@Override
|
||||
public boolean checkDeadlock(List<List<Map<String, Object>>> rows) {
|
||||
// 保存任务执行顺序数组
|
||||
this.rows = rows;
|
||||
|
||||
// 构建进程依赖图
|
||||
Map<String, Set<String>> graph = buildDependencyGraph(rows);
|
||||
|
||||
// 检测是否存在环(死锁)
|
||||
return hasCycle(graph);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成不会产生死锁的批次分配方案
|
||||
* 相同processId的任务会合并到一个批次组中
|
||||
* 不同processId的任务会放在不同的批次组中
|
||||
*
|
||||
* @param rows 任务执行顺序数组
|
||||
* @return 批次分配方案
|
||||
*/
|
||||
@Override
|
||||
public List<String> generateNonDeadlockBatches(List<List<Map<String, Object>>> rows) {
|
||||
public List<BatchGroupVo> generateNonDeadlockBatches(List<List<Map<String, Object>>> rows) {
|
||||
// 保存任务执行顺序数组
|
||||
this.rows = rows;
|
||||
|
||||
@@ -152,83 +137,46 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
||||
}
|
||||
}
|
||||
|
||||
// 构建进程依赖图
|
||||
Map<String, Set<String>> processGraph = buildProcessDependencyGraph(rows);
|
||||
// // 构建进程依赖图
|
||||
// Map<String, Set<String>> processGraph = buildProcessDependencyGraph(rows);
|
||||
|
||||
// 构建任务依赖图
|
||||
Map<String, Set<String>> taskGraph = buildTaskDependencyGraph(rows);
|
||||
|
||||
// 使用拓扑排序找出可行的批次分配方案
|
||||
List<Set<String>> batches = topologicalSort(processGraph, taskGraph, allTasks);
|
||||
|
||||
// 将批次转换为字符串格式
|
||||
return batches.stream()
|
||||
.map(batch -> String.join(",", batch))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建依赖图
|
||||
* 如果进程A的任务必须在进程B的任务之前执行,则B依赖于A
|
||||
*/
|
||||
private Map<String, Set<String>> buildDependencyGraph(List<List<Map<String, Object>>> rows) {
|
||||
Map<String, Set<String>> graph = new HashMap<>();
|
||||
Map<String, Integer> processSequence = new HashMap<>();
|
||||
|
||||
// 遍历每一行,记录每个进程在每一行的执行顺序
|
||||
// 获取任务与进程的映射关系
|
||||
Map<String, String> taskToProcess = new HashMap<>();
|
||||
// 获取任务详细信息
|
||||
Map<String, Map<String, Object>> taskDetails = new HashMap<>();
|
||||
for (List<Map<String, Object>> row : rows) {
|
||||
// 按sequare排序
|
||||
row.sort(Comparator.comparingInt(task -> Integer.parseInt(task.get("sequence").toString())));
|
||||
|
||||
// 记录每个进程的执行顺序
|
||||
for (int i = 0; i < row.size(); i++) {
|
||||
Map<String, Object> task = row.get(i);
|
||||
for (Map<String, Object> task : row) {
|
||||
String taskId = task.get("taskId").toString();
|
||||
String processId = task.get("processId").toString();
|
||||
|
||||
// 如果当前进程已经有更早的执行顺序,则保留更早的顺序
|
||||
processSequence.putIfAbsent(processId, i);
|
||||
processSequence.put(processId, Math.min(processSequence.get(processId), i));
|
||||
}
|
||||
|
||||
// 构建依赖关系
|
||||
for (int i = 0; i < row.size() - 1; i++) {
|
||||
String currentProcess = row.get(i).get("processId").toString();
|
||||
String nextProcess = row.get(i + 1).get("processId").toString();
|
||||
|
||||
// 添加依赖:nextProcess依赖于currentProcess
|
||||
graph.putIfAbsent(nextProcess, new HashSet<>());
|
||||
graph.get(nextProcess).add(currentProcess);
|
||||
taskToProcess.put(taskId, processId);
|
||||
taskDetails.put(taskId, task);
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
// 使用拓扑排序找出可行的批次分配方案
|
||||
List<List<String>> batchGroups = generateBatchGroups(taskGraph, allTasks, taskToProcess);
|
||||
|
||||
/**
|
||||
* 构建进程依赖图
|
||||
*/
|
||||
private Map<String, Set<String>> buildProcessDependencyGraph(List<List<Map<String, Object>>> rows) {
|
||||
Map<String, Set<String>> graph = new HashMap<>();
|
||||
|
||||
// 遍历每一行,记录进程间的依赖关系
|
||||
for (List<Map<String, Object>> row : rows) {
|
||||
// 按sequare排序
|
||||
row.sort(Comparator.comparingInt(task -> Integer.parseInt(task.get("sequence").toString())));
|
||||
|
||||
// 构建依赖关系
|
||||
for (int i = 0; i < row.size() - 1; i++) {
|
||||
String currentProcess = row.get(i).get("processId").toString();
|
||||
String nextProcess = row.get(i + 1).get("processId").toString();
|
||||
|
||||
// 添加依赖:nextProcess依赖于currentProcess
|
||||
graph.putIfAbsent(nextProcess, new HashSet<>());
|
||||
graph.get(nextProcess).add(currentProcess);
|
||||
// 将批次转换为BatchGroupVo格式
|
||||
List<BatchGroupVo> result = new ArrayList<>();
|
||||
int groupId = 1;
|
||||
for (List<String> group : batchGroups) {
|
||||
if (!group.isEmpty()) {
|
||||
String processId = taskToProcess.get(group.get(0));
|
||||
BatchGroupVo batchGroup = new BatchGroupVo();
|
||||
batchGroup.setGroupId("Group-" + groupId++);
|
||||
batchGroup.setProcessId(processId);
|
||||
batchGroup.setTaskIds(group);
|
||||
result.add(batchGroup);
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建任务依赖图
|
||||
*/
|
||||
@@ -254,29 +202,6 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测图中是否存在环(死锁)
|
||||
*/
|
||||
private boolean hasCycle(Map<String, Set<String>> graph) {
|
||||
// 所有节点的状态:0=未访问,1=访问中,2=已访问
|
||||
Map<String, Integer> visited = new HashMap<>();
|
||||
|
||||
// 初始化所有节点为未访问
|
||||
for (String node : graph.keySet()) {
|
||||
visited.put(node, 0);
|
||||
}
|
||||
|
||||
// 对每个未访问的节点进行DFS
|
||||
for (String node : graph.keySet()) {
|
||||
if (visited.get(node) == 0) {
|
||||
if (hasCycleDFS(node, graph, visited)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* DFS检测环
|
||||
@@ -308,20 +233,12 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用拓扑排序生成批次分配方案
|
||||
* 确保相同processId的任务才能合并到同一个批次
|
||||
* 生成批次分组
|
||||
* 相同processId的任务会合并到一个列表中
|
||||
* 不同processId的任务会放在不同的列表中
|
||||
*/
|
||||
private List<Set<String>> topologicalSort(Map<String, Set<String>> processGraph, Map<String, Set<String>> taskGraph, Set<String> allTasks) {
|
||||
// 获取任务与进程的映射关系
|
||||
Map<String, String> taskToProcess = new HashMap<>();
|
||||
for (List<Map<String, Object>> row : rows) {
|
||||
for (Map<String, Object> task : row) {
|
||||
String taskId = task.get("taskId").toString();
|
||||
String processId = task.get("processId").toString();
|
||||
taskToProcess.put(taskId, processId);
|
||||
}
|
||||
}
|
||||
|
||||
private List<List<String>> generateBatchGroups( Map<String, Set<String>> taskGraph,
|
||||
Set<String> allTasks, Map<String, String> taskToProcess) {
|
||||
// 计算每个任务的入度
|
||||
Map<String, Integer> inDegree = new HashMap<>();
|
||||
for (String task : allTasks) {
|
||||
@@ -342,23 +259,27 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
||||
}
|
||||
}
|
||||
|
||||
// 存储批次分配方案
|
||||
List<Set<String>> batches = new ArrayList<>();
|
||||
// 存储批次分组方案
|
||||
List<List<String>> batchGroups = new ArrayList<>();
|
||||
|
||||
// 记录已经分配到批次的进程ID
|
||||
Set<String> processedProcessIds = new HashSet<>();
|
||||
|
||||
// 进行拓扑排序
|
||||
while (!queue.isEmpty()) {
|
||||
// 按进程ID分组的当前批次任务
|
||||
Map<String, Set<String>> processBatches = new HashMap<>();
|
||||
|
||||
// 处理当前队列中的所有任务(这些任务可以并行执行)
|
||||
// 当前层级可以并行执行的任务
|
||||
List<String> currentLevelTasks = new ArrayList<>();
|
||||
int size = queue.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
String task = queue.poll();
|
||||
String processId = taskToProcess.get(task);
|
||||
|
||||
// 按进程ID分组
|
||||
processBatches.putIfAbsent(processId, new HashSet<>());
|
||||
processBatches.get(processId).add(task);
|
||||
for (int i = 0; i < size; i++) {
|
||||
currentLevelTasks.add(queue.poll());
|
||||
}
|
||||
|
||||
// 按进程ID分组
|
||||
Map<String, List<String>> processBatches = new HashMap<>();
|
||||
for (String task : currentLevelTasks) {
|
||||
String processId = taskToProcess.get(task);
|
||||
processBatches.computeIfAbsent(processId, k -> new ArrayList<>()).add(task);
|
||||
|
||||
// 更新依赖于当前任务的任务的入度
|
||||
for (Map.Entry<String, Set<String>> entry : taskGraph.entrySet()) {
|
||||
@@ -377,15 +298,48 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
||||
}
|
||||
|
||||
// 将每个进程的任务作为一个批次添加
|
||||
for (Set<String> batch : processBatches.values()) {
|
||||
if (!batch.isEmpty()) {
|
||||
batches.add(batch);
|
||||
for (Map.Entry<String, List<String>> entry : processBatches.entrySet()) {
|
||||
String processId = entry.getKey();
|
||||
List<String> tasks = entry.getValue();
|
||||
|
||||
if (!tasks.isEmpty()) {
|
||||
// 检查该进程是否已经有批次
|
||||
boolean merged = false;
|
||||
|
||||
// 如果该进程ID已经处理过,则不能再合并
|
||||
if (processedProcessIds.contains(processId)) {
|
||||
batchGroups.add(tasks);
|
||||
} else {
|
||||
// 尝试合并到现有批次
|
||||
for (List<String> existingBatch : batchGroups) {
|
||||
// 获取批次中第一个任务的进程ID
|
||||
if (!existingBatch.isEmpty()) {
|
||||
String existingProcessId = taskToProcess.get(existingBatch.get(0));
|
||||
|
||||
// 如果进程ID相同,则合并
|
||||
if (processId.equals(existingProcessId)) {
|
||||
existingBatch.addAll(tasks);
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有合并到现有批次,则创建新批次
|
||||
if (!merged) {
|
||||
batchGroups.add(tasks);
|
||||
}
|
||||
|
||||
// 标记该进程ID已处理
|
||||
processedProcessIds.add(processId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return batches;
|
||||
return batchGroups;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user