每次批量新增1000行,遇到异常删除表数据

This commit is contained in:
lilin 2026-01-06 16:56:10 +08:00
parent 6065e0e65b
commit aac756e5fa

View File

@ -38,6 +38,7 @@ import com.yfd.platform.system.domain.LoginUser;
import com.yfd.platform.utils.StringUtils; import com.yfd.platform.utils.StringUtils;
import com.yfd.platform.utils.TableNameContextHolder; import com.yfd.platform.utils.TableNameContextHolder;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.val;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -46,6 +47,7 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -70,6 +72,8 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
private static final int BATCH_SIZE = 1000;
//试验任务节点表 Mapper //试验任务节点表 Mapper
@Resource @Resource
private TsNodesMapper tsNodesMapper; private TsNodesMapper tsNodesMapper;
@ -109,53 +113,178 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
@Override @Override
public List<Map<String, Object>> getTsNodesTree(String nodeName, String taskId) { public List<Map<String, Object>> getTsNodesTree(String nodeName, String taskId) {
TsTask tsTask = tsTaskMapper.selectOne(new QueryWrapper<TsTask>().eq("id", taskId)); List<Map<String, Object>> result = new ArrayList<>();
// 查询所有节点数据
// 查询所有节点数据如果taskId为空查询所有任务节点
List<Map<String, Object>> allNodes = getAllNodes(taskId); List<Map<String, Object>> allNodes = getAllNodes(taskId);
// 查找所有根节点parentId为"00"的节点 // 如果taskId为空根据nodeName直接过滤
List<Map<String, Object>> rootNodes = findRootNodes(allNodes, taskId); if (StringUtils.isEmpty(taskId)) {
if (StringUtils.isEmpty(nodeName)) {
// 根节点的基本路径/项目名称/ // 返回所有节点需要按任务分组构建树
String basePath = "/" + tsTask.getTaskName() + "/"; // return buildAllTaskTrees(allNodes);
return result;
// 存储最终结果 } else {
List<Map<String, Object>> result = new ArrayList<>(); // 根据nodeName查询所有匹配节点构建到根节点的路径
Map<String, Object> rootNodeData = new HashMap<>(); return findNodesByName(allNodes, nodeName);
rootNodeData.put("nodeName", "根节点");
rootNodeData.put("path", "/"+tsTask.getTaskName()+"/");
rootNodeData.put("nodeId", tsTask.getId());
rootNodeData.put("nodeOrder", "0");
rootNodeData.put("taskId", tsTask.getId());
rootNodeData.put("parentId", "00");
result.add(rootNodeData);
// 如果 nodeName 为空返回所有根节点的完整树形结构
if (StringUtils.isEmpty(nodeName)) {
if (!rootNodes.isEmpty()) {
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path", basePath);
result.addAll(buildFullTree(rootNode, allNodes));
} }
} else {
// 原有逻辑taskId不为空时按任务查询
TsTask tsTask = tsTaskMapper.selectOne(new QueryWrapper<TsTask>().eq("id", taskId));
if (tsTask == null) {
throw new RuntimeException("任务不存在");
} }
return result;
}
// 否则返回从根节点到目标节点的树形结构 List<Map<String, Object>> rootNodes = findRootNodes(allNodes, taskId);
if (!rootNodes.isEmpty()) { String basePath = "/" + tsTask.getTaskName() + "/";
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path", basePath); Map<String, Object> rootNodeData = new HashMap<>();
List<Map<String, Object>> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName); rootNodeData.put("nodeName", "根节点");
if (!tree.isEmpty()) { rootNodeData.put("path", basePath);
rootNodeData.put("nodeId", tsTask.getId());
rootNodeData.put("nodeOrder", "0");
rootNodeData.put("taskId", tsTask.getId());
rootNodeData.put("taskName", tsTask.getTaskName());
rootNodeData.put("parentId", "00");
result.add(rootNodeData);
if (StringUtils.isEmpty(nodeName)) {
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path", basePath);
result.addAll(buildFullTree(rootNode, allNodes));
}
} else {
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path", basePath);
List<Map<String, Object>> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName);
result.addAll(tree); result.addAll(tree);
} }
} }
return result;
}
}
/**
* 根据节点名称查找所有匹配节点并构建树
*/
private List<Map<String, Object>> findNodesByName(List<Map<String, Object>> allNodes, String nodeName) {
List<Map<String, Object>> result = new ArrayList<>();
// 查找所有匹配的节点
List<Map<String, Object>> matchedNodes = allNodes.stream()
.filter(node -> {
Object nodeNameObj = node.get("nodeName");
if (nodeNameObj instanceof String) {
String currentName = (String) nodeNameObj;
return currentName.toLowerCase().contains(nodeName.toLowerCase());
}
return false;
})
.collect(Collectors.toList());
// 为每个匹配节点构建到根节点的路径
for (Map<String, Object> matchedNode : matchedNodes) {
// 找到该节点的完整路径
List<Map<String, Object>> pathToRoot = findPathToRoot(matchedNode, allNodes);
if (!pathToRoot.isEmpty()) {
result.addAll(pathToRoot);
}
} }
// 返回结果
return result; return result;
} }
/**
* 查找节点到根节点的路径
*/
private List<Map<String, Object>> findPathToRoot(Map<String, Object> node, List<Map<String, Object>> allNodes) {
List<Map<String, Object>> path = new ArrayList<>();
Map<String, Object> currentNode = node;
// 向上查找直到根节点
while (currentNode != null) {
path.add(0, new HashMap<>(currentNode)); // 添加到路径开头
String parentId = (String) currentNode.get("parentId");
if ("00".equals(parentId)) {
// 找到根节点添加任务信息
String taskId = (String) currentNode.get("taskId");
TsTask task = tsTaskMapper.selectOne(new QueryWrapper<TsTask>().eq("id", taskId));
if (task != null) {
Map<String, Object> taskNode = new HashMap<>();
taskNode.put("nodeName", "根节点");
taskNode.put("path", "/" + task.getTaskName() + "/");
taskNode.put("nodeId", task.getId());
taskNode.put("nodeOrder", "0");
taskNode.put("taskId", task.getId());
taskNode.put("taskName", task.getTaskName());
taskNode.put("parentId", "00");
path.add(0, taskNode);
}
break;
}
// 查找父节点
String finalParentId = parentId;
currentNode = allNodes.stream()
.filter(n -> finalParentId.equals(n.get("nodeId")))
.findFirst()
.orElse(null);
}
return path;
}
// public List<Map<String, Object>> getTsNodesTree(String nodeName, String taskId) {
// TsTask tsTask = tsTaskMapper.selectOne(new QueryWrapper<TsTask>().eq("id", taskId));
// // 查询所有节点数据
// List<Map<String, Object>> allNodes = getAllNodes(taskId);
//
// // 查找所有根节点parentId为"00"的节点
// List<Map<String, Object>> rootNodes = findRootNodes(allNodes, taskId);
//
// // 根节点的基本路径/项目名称/
// String basePath = "/" + tsTask.getTaskName() + "/";
//
// // 存储最终结果
// List<Map<String, Object>> result = new ArrayList<>();
// Map<String, Object> rootNodeData = new HashMap<>();
// rootNodeData.put("nodeName", "根节点");
// rootNodeData.put("path", "/"+tsTask.getTaskName()+"/");
// rootNodeData.put("nodeId", tsTask.getId());
// rootNodeData.put("nodeOrder", "0");
// rootNodeData.put("taskId", tsTask.getId());
// rootNodeData.put("parentId", "00");
// result.add(rootNodeData);
//
// // 如果 nodeName 为空返回所有根节点的完整树形结构
// if (StringUtils.isEmpty(nodeName)) {
// if (!rootNodes.isEmpty()) {
// for (Map<String, Object> rootNode : rootNodes) {
// rootNode.put("path", basePath);
// result.addAll(buildFullTree(rootNode, allNodes));
// }
// }
// return result;
// }
//
// // 否则返回从根节点到目标节点的树形结构
// if (!rootNodes.isEmpty()) {
// for (Map<String, Object> rootNode : rootNodes) {
// rootNode.put("path", basePath);
// List<Map<String, Object>> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName);
// if (!tree.isEmpty()) {
// result.addAll(tree);
// }
// }
// }
//
// // 返回结果
// return result;
// }
/** /**
* 查询所有节点数据 * 查询所有节点数据
* *
@ -726,6 +855,7 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
taskStatusHolder.finishTask(asyncKey); taskStatusHolder.finishTask(asyncKey);
WebSocketServer.sendMessageTo("试验数据扫描接口完成", "taskId_" + id); WebSocketServer.sendMessageTo("试验数据扫描接口完成", "taskId_" + id);
} }
} }
/********************************** /**********************************
@ -789,6 +919,15 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
return "扫描完成"; return "扫描完成";
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("执行试验数据扫描时发生未知异常: {}", e.getMessage(), e); LOGGER.error("执行试验数据扫描时发生未知异常: {}", e.getMessage(), e);
//异常的时候删除节点和文件表
LambdaQueryWrapper<TsNodes> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TsNodes::getTaskId, id);
tsNodesMapper.delete(queryWrapper);
LambdaQueryWrapper<TsFiles> queryWrapperTsFile = new LambdaQueryWrapper<>();
queryWrapperTsFile.eq(TsFiles::getTaskId, id);
tsFilesMapper.delete(queryWrapperTsFile);
return "扫描失败:" + e.getMessage(); return "扫描失败:" + e.getMessage();
} finally { } finally {
TableNameContextHolder.clear(); TableNameContextHolder.clear();
@ -978,12 +1117,18 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
// 批量插入文件表忽略重复 // 批量插入文件表忽略重复
if (!tsFilesToCreate.isEmpty()) { if (!tsFilesToCreate.isEmpty()) {
long startBatchInsertFiles = System.currentTimeMillis(); long startBatchInsertFiles = System.currentTimeMillis();
int affectedRowsFiles = tsFilesMapper.batchInsertTsFiles(tsFilesToCreate); //int affectedRowsFiles = tsFilesMapper.batchInsertTsFiles(tsFilesToCreate);
int affected = 0;
for (int i = 0; i < tsFilesToCreate.size(); i += BATCH_SIZE) {
List<TsFiles> sub =
new ArrayList<>(tsFilesToCreate.subList(i, Math.min(i + BATCH_SIZE, tsFilesToCreate.size())));
affected += this.insertOneBatch(sub);
}
LOGGER.info("[批量插入试验任务文件表] 耗时 {} ms | 实际新增数量: {} 条", LOGGER.info("[批量插入试验任务文件表] 耗时 {} ms | 实际新增数量: {} 条",
System.currentTimeMillis() - startBatchInsertFiles, System.currentTimeMillis() - startBatchInsertFiles,
affectedRowsFiles); affected);
} }
// 记录开始时间 // 记录开始时间
long startTimeFiles = System.currentTimeMillis(); long startTimeFiles = System.currentTimeMillis();
// 执行更新操作 taskId, String nodeId // 执行更新操作 taskId, String nodeId
@ -996,6 +1141,14 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
LOGGER.info("文件表中的节点ID更新完成影响 {} 行,总耗时 {} 毫秒", affectedLevelFilesRows, costTimeFiles); LOGGER.info("文件表中的节点ID更新完成影响 {} 行,总耗时 {} 毫秒", affectedLevelFilesRows, costTimeFiles);
} }
/**
* 每批独立事务插入
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public int insertOneBatch(List<TsFiles> batch) {
return tsFilesMapper.batchInsertTsFiles(batch);
}
/** /**
* 确保路径格式为以 "/" 开头和结尾例如 "/data/test/" * 确保路径格式为以 "/" 开头和结尾例如 "/data/test/"
* 若路径为空或非法返回根路径 "/" * 若路径为空或非法返回根路径 "/"