refactor(wms): 重构批次分配逻辑

- 移除了死锁检测相关代码
- 修改了批次分配方案的生成逻辑
- 新增了 BatchGroupVo 类用于表示批次分组
- 优化了代码结构和命名,提高了可读性和可维护性
This commit is contained in:
2025-08-14 17:33:42 +08:00
parent 0b8a5cdc05
commit 96da503d0a
4 changed files with 156 additions and 156 deletions

View File

@@ -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);
}
}

View 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;
}

View File

@@ -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);
}

View File

@@ -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;
}
}