From 32b34fa7552e6b7185a984d961fd1c5790ef0969 Mon Sep 17 00:00:00 2001 From: lilin Date: Tue, 15 Jul 2025 16:37:53 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=8E=8B=E7=BC=A9=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E5=85=A8=E5=B1=9E=E6=80=A7=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/component/ExtractTaskStatus.java | 35 +++ .../platform/component/TaskStatusHolder.java | 20 ++ .../controller/TsFilesController.java | 81 +++++-- .../controller/TsTaskController.java | 52 ++--- .../service/ITsFilesService.java | 4 + .../service/ITsTaskService.java | 4 +- .../service/impl/TsFilesServiceImpl.java | 91 ++++++-- .../service/impl/TsTaskServiceImpl.java | 211 ++++++++++-------- 8 files changed, 332 insertions(+), 166 deletions(-) create mode 100644 java/src/main/java/com/yfd/platform/component/ExtractTaskStatus.java diff --git a/java/src/main/java/com/yfd/platform/component/ExtractTaskStatus.java b/java/src/main/java/com/yfd/platform/component/ExtractTaskStatus.java new file mode 100644 index 0000000..87c7d0a --- /dev/null +++ b/java/src/main/java/com/yfd/platform/component/ExtractTaskStatus.java @@ -0,0 +1,35 @@ +package com.yfd.platform.component; + + +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +@Component +public class ExtractTaskStatus { + + private final ConcurrentHashMap extractTaskStatusMap = new ConcurrentHashMap<>(); + // 存入方法 + public void putTaskStatus(String key, String value) { + extractTaskStatusMap.put(key, value); + } + + // 通过value获取key的方法 + public Set getKeysByValue(String value) { + Set matchingKeys = new HashSet<>(); + for (String key : extractTaskStatusMap.keySet()) { + if (extractTaskStatusMap.get(key).equals(value)) { + matchingKeys.add(key); + } + } + return matchingKeys; + } + + + // 通过key删除任务 + public void removeTask(String key) { + extractTaskStatusMap.remove(key); + } +} diff --git a/java/src/main/java/com/yfd/platform/component/TaskStatusHolder.java b/java/src/main/java/com/yfd/platform/component/TaskStatusHolder.java index 86e37b4..0abb34c 100644 --- a/java/src/main/java/com/yfd/platform/component/TaskStatusHolder.java +++ b/java/src/main/java/com/yfd/platform/component/TaskStatusHolder.java @@ -2,7 +2,10 @@ package com.yfd.platform.component; import org.springframework.stereotype.Component; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @Component @@ -14,6 +17,10 @@ public class TaskStatusHolder { public String generateKey(String taskId, String nodeId) { return taskId + ":" + nodeId; } + // 生成唯一解压缩的Key:taskId + id + public String extractKey(String id) { + return id; + } // 生成专项扫描唯一Key:project_id public String specialGenerateKey(String id) { @@ -47,4 +54,17 @@ public class TaskStatusHolder { public String getValue(String key) { return taskStatusMap.get(key); } + + // 通过value获取所有对应的key + public Set findKeysByValue(String value) { + Set result = new HashSet<>(); + + for (Map.Entry entry : taskStatusMap.entrySet()) { + if (entry.getValue().equals(value)) { + result.add(entry.getKey()); + } + } + + return result; + } } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java index 1f2b5c6..9201751 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java @@ -7,11 +7,14 @@ import cn.hutool.json.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.annotation.Log; +import com.yfd.platform.component.ExtractTaskStatus; import com.yfd.platform.component.TaskStatusHolder; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.modules.experimentalData.domain.*; import com.yfd.platform.modules.experimentalData.service.ITsFilesService; +import com.yfd.platform.modules.experimentalData.service.ITsTaskService; import com.yfd.platform.modules.storage.model.result.FileItemResult; +import com.yfd.platform.utils.TableNameContextHolder; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; @@ -19,9 +22,7 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; @@ -44,6 +45,9 @@ public class TsFilesController { @Autowired private TaskStatusHolder taskStatusHolder; + @Resource + private ExtractTaskStatus extractTaskStatus; + /********************************** * 用途说明: 分页查询试验数据管理-文档内容 * 参数说明 @@ -66,7 +70,7 @@ public class TsFilesController { //分页查询 int currentPage = (int) page.getCurrent(); // 先尝试从缓存获取 如果搜索条件为空 从Redis获取 - if(StrUtil.isBlank(fileName)&&StrUtil.isBlank(keywords) &&StrUtil.isBlank(startDate) &&StrUtil.isBlank(endDate)&& !"00".equals(id)) { + if (StrUtil.isBlank(fileName) && StrUtil.isBlank(keywords) && StrUtil.isBlank(startDate) && StrUtil.isBlank(endDate) && !"00".equals(id)) { if (!StrUtil.isEmpty(id)) { IPage cachedPage = tsFilesService.getCachedTsFilesPage(taskId, nodeId, currentPage, id); if (cachedPage != null) { @@ -167,12 +171,12 @@ public class TsFilesController { @PostMapping("/deleteTsFilesById") @ApiOperation("根据ID删除试验数据管理文档内容") @PreAuthorize("@el.check('del:tsFiles')") - public ResponseResult deleteTsFilesById(@RequestParam String id, @RequestParam String type,@RequestParam String taskId) { + public ResponseResult deleteTsFilesById(@RequestParam String id, @RequestParam String type, @RequestParam String taskId) { if (StrUtil.isBlank(id) || StrUtil.isBlank(taskId)) { return ResponseResult.error("参数为空"); } List dataset = Arrays.asList(id); - return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset, type,taskId)); + return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset, type, taskId)); } /********************************** @@ -185,14 +189,14 @@ public class TsFilesController { @PostMapping("/deleteTsFilesByIds") @ApiOperation("批量删除试验数据管理文档内容") @PreAuthorize("@el.check('del:tsFiles')") - public ResponseResult deleteTsFilesByIds(@RequestParam String ids, @RequestParam String type,@RequestParam String taskId) { + public ResponseResult deleteTsFilesByIds(@RequestParam String ids, @RequestParam String type, @RequestParam String taskId) { if (StrUtil.isBlank(ids) || StrUtil.isBlank(taskId)) { return ResponseResult.error("参数为空"); } String[] splitIds = ids.split(","); // 数组转集合 List dataset = Arrays.asList(splitIds); - return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset, type,taskId)); + return ResponseResult.success(tsFilesService.deleteTsFilesByIds(dataset, type, taskId)); } /**************************压缩 解压缩********************************/ @@ -211,13 +215,13 @@ public class TsFilesController { @Log(module = "实验数据管理", value = "压缩文件夹接口!") @PostMapping("/compress") @ApiOperation("压缩文件夹接口") - public ResponseResult compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId, String path,String taskId) { + public ResponseResult compressFolder(String ids, String compressedFormat, String compressedName, String compressedPath, String covered, String parentId, String path, String taskId) { try { if (StrUtil.isBlank(ids) && StrUtil.isBlank(compressedFormat) && StrUtil.isBlank(compressedName) && StrUtil.isBlank(compressedPath) && StrUtil.isBlank(path) && StrUtil.isBlank(taskId)) { return ResponseResult.error("参数为空"); } - return ResponseResult.success(tsFilesService.compressFolder(ids, compressedFormat, compressedName, compressedPath, covered, parentId, path,taskId)); + return ResponseResult.success(tsFilesService.compressFolder(ids, compressedFormat, compressedName, compressedPath, covered, parentId, path, taskId)); } catch (Exception e) { System.out.print("压缩异常原因" + e); return ResponseResult.error("压缩失败"); @@ -235,18 +239,51 @@ public class TsFilesController { @Log(module = "实验数据管理", value = "解压缩接口!") @PostMapping("/decompression") @ApiOperation("解压缩接口") - public ResponseResult decompressionFolder(String id, String decompressionPath, String parentId, String path,String taskId) { + public ResponseResult decompressionFolder(String id, String decompressionPath, String parentId, String path, String taskId) { try { - if (StrUtil.isBlank(id)|| StrUtil.isBlank(taskId)) { + if (StrUtil.isBlank(id) || StrUtil.isBlank(taskId)) { return ResponseResult.error("参数为空"); } - return ResponseResult.success(tsFilesService.decompressionFolder(id, decompressionPath, parentId, path,taskId)); + extractTaskStatus.putTaskStatus(id,taskId); + + // 生成唯一Key + String extractKey = taskStatusHolder.extractKey(taskId); + + // 检查任务是否已存在 + String existingStatus = taskStatusHolder.getStatus(extractKey); + if ("IN_PROGRESS".equals(existingStatus)) { + return ResponseResult.success("解压缩任务正在处理中!"); + } else if ("COMPLETED".equals(existingStatus)) { + return ResponseResult.success("解压缩任务已完成!"); + } + // 原子性启动新任务 + if (taskStatusHolder.startTaskIfAbsent(extractKey)) { + // 直接异步执行并推送结果 + tsFilesService.decompressionFolderAsync(id, decompressionPath, parentId, path, taskId); + return ResponseResult.success("解压缩任务开始处理!"); + } else { + return ResponseResult.success("解压缩任务已由其他请求启动"); + } } catch (Exception e) { System.out.print("解压缩异常原因" + e); return ResponseResult.error("解压缩失败"); } } + /********************************** + * 用途说明: 获取解压异步信息 + * 参数说明 taskId 所属项目ID + * 参数说明 nodeId 所属节点ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败 + ***********************************/ + @Log(module = "根据项目和节点获取异步信息", value = "根据项目和节点获取异步信息!") + @PostMapping("/decompressionFolderData") + @ApiOperation("获取解压异步信息") + public ResponseResult decompressionFolderData( String taskId) throws Exception { + return ResponseResult.successData(tsFilesService.decompressionFolderData(taskId)); + } + + /** * 移动文件或文件夹 * 参数说明 newPath 新路径 @@ -617,7 +654,7 @@ public class TsFilesController { @Log(module = "实验数据管理", value = "文件自动备份!") @PostMapping("/automaticFileBackupByIds") @ApiOperation("自动备份本地文件到备份空间通过ID") - public ResponseResult automaticFileBackupByIds(String id,String taskId, String nodeId) throws IOException { + public ResponseResult automaticFileBackupByIds(String id, String taskId, String nodeId) throws IOException { if (StrUtil.isEmpty(id)) { return ResponseResult.error("参数为空"); } @@ -636,7 +673,7 @@ public class TsFilesController { // 原子性启动新任务 if (taskStatusHolder.startTaskIfAbsent(asyncKey)) { // 直接异步执行并推送结果 - tsFilesService.automaticFileBackupAsyncByIds(dataset,taskId,nodeId); + tsFilesService.automaticFileBackupAsyncByIds(dataset, taskId, nodeId); return ResponseResult.success("任务开始处理!"); } else { return ResponseResult.success("任务已由其他请求启动"); @@ -654,12 +691,12 @@ public class TsFilesController { @Log(module = "实验数据管理", value = "实时获取轨迹数据!") @PostMapping("/startSimpleNavi") @ApiOperation("实时获取轨迹数据") - public ResponseResult startSimpleNavi(String id, int samTimes, String token,String taskId) { + public ResponseResult startSimpleNavi(String id, int samTimes, String token, String taskId) { try { // 使用线程池异步执行任务 CompletableFuture.runAsync(() -> { try { - tsFilesService.batchSendNaviOutDataJob(id, samTimes, token,taskId); + tsFilesService.batchSendNaviOutDataJob(id, samTimes, token, taskId); } catch (Exception e) { e.printStackTrace(); } @@ -694,7 +731,7 @@ public class TsFilesController { @Log(module = "实验数据管理", value = "查询文件内容!") @GetMapping("/api/files/content") @ApiOperation("查询文件内容") - public ResponseResult getFileContent(@RequestParam String id,String taskId) { + public ResponseResult getFileContent(@RequestParam String id, String taskId) { try { if (StrUtil.isBlank(id) && StrUtil.isBlank(taskId)) { return ResponseResult.error("参数为空"); @@ -715,7 +752,7 @@ public class TsFilesController { @Log(module = "实验数据管理", value = "保存文件内容!") @PostMapping("/save/files/content") @ApiOperation("保存文件内容") - public ResponseResult saveFileContent(String id, String content,String taskId) { + public ResponseResult saveFileContent(String id, String content, String taskId) { try { if (StrUtil.isBlank(id) && StrUtil.isBlank(content) && StrUtil.isBlank(taskId)) { return ResponseResult.error("参数为空"); @@ -737,7 +774,7 @@ public class TsFilesController { @PostMapping("/batchModify") @ApiOperation("批量修改文件中多行多列的内容") public ResponseResult batchModifyFile(@RequestBody BatchModifyRequest request) throws IOException { - tsFilesService.batchUpdateFile(request.getId(), request.getModifications(),request.getTaskId()); + tsFilesService.batchUpdateFile(request.getId(), request.getModifications(), request.getTaskId()); return ResponseResult.success("文件保存成功"); } @@ -749,12 +786,12 @@ public class TsFilesController { @Log(module = "实验数据管理", value = "获取文件url!") @PostMapping("/obtainUrl") @ApiOperation("获取文件url") - public ResponseResult obtainUrl(String id, String type,String taskId) { + public ResponseResult obtainUrl(String id, String type, String taskId) { if (StrUtil.isBlank(id) && StrUtil.isBlank(type) && StrUtil.isBlank(type)) { return ResponseResult.error("参数为空"); } //查询本地树和minio树 - FileItemResult fileItemResult = tsFilesService.obtainUrl(id, type,taskId); + FileItemResult fileItemResult = tsFilesService.obtainUrl(id, type, taskId); return ResponseResult.successData(fileItemResult); } } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java index c3281fb..62321cc 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsTaskController.java @@ -44,6 +44,7 @@ public class TsTaskController { /********************************** * 用途说明: 分页查询试验数据管理-试验任务管理 * 参数说明 + * keyword 全属性搜索 * taskName 任务名称 * startDate (开始日期) * endDate (结束日期) @@ -62,42 +63,29 @@ public class TsTaskController { @GetMapping("/page") @ApiOperation("分页查询试验数据管理试验任务管理") @PreAuthorize("@el.check('select:tsTask')") - public ResponseResult getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType, String carrierName, String deviceCode, String testDescribe, String sensorDescribe, Page page, String attributeContentJson) { - // 双重解码处理 - if (attributeContentJson != null) { - try { - // 第二次解码:%5B → [ - attributeContentJson = URLDecoder.decode(attributeContentJson, StandardCharsets.UTF_8.name()); - } catch (UnsupportedEncodingException e) { - return ResponseResult.error("参数解码错误"); - } + public ResponseResult getTsTaskPage(String keyword, + String startDate, + String endDate, + String fieldName, + Page page, + String attributeContentJson) { + //字段名称 + List fieldNameData = new ArrayList<>(); + if (StrUtil.isNotEmpty(fieldName)) { + String[] splitIds = fieldName.split(","); + // 数组转集合 + fieldNameData = Arrays.asList(splitIds); } - - // 将JSON字符串转换为List> - List> attributeContent = null; - if (StringUtils.isNotEmpty(attributeContentJson)) { - try { - // 使用 TypeReference 指定精确类型 - attributeContent = JSON.parseObject( - attributeContentJson, - new TypeReference>>() { - } - ); - - // 遍历并移除空值字段 - if (attributeContent != null) { - for (Map item : attributeContent) { - // 遍历 Map,移除值为空的键值对 - item.entrySet().removeIf(entry -> entry.getValue() == null || entry.getValue().isEmpty()); - } - } - } catch (Exception e) { - return ResponseResult.error("属性参数格式错误"); - } + //自定义属性 + List attributeContent = new ArrayList<>(); + if (StrUtil.isNotEmpty(attributeContentJson)) { + String[] splitIds = attributeContentJson.split(","); + // 数组转集合 + attributeContent = Arrays.asList(splitIds); } //分页查询 - Page sdProjectPage = tsTaskService.getTsTaskPage(taskName, startDate, endDate, taskPlace, taskPerson, taskCode, taskType, carrierName, deviceCode, testDescribe, sensorDescribe, page, attributeContent); + Page sdProjectPage = tsTaskService.getTsTaskPage(keyword, startDate, endDate, fieldNameData, page, attributeContent); return ResponseResult.successData(sdProjectPage); } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java index 15807da..310cfc6 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java @@ -252,4 +252,8 @@ public interface ITsFilesService extends IService { IPage getCachedTsFilesPage(String taskId, String nodeId, int currentPage,String id); int countFiles(String id); + + void decompressionFolderAsync(String id, String decompressionPath, String parentId, String path, String taskId) throws IOException; + + Object decompressionFolderData(String taskId); } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsTaskService.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsTaskService.java index acc5f83..ed9a1ef 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsTaskService.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsTaskService.java @@ -36,7 +36,8 @@ public interface ITsTaskService extends IService { * pageNum 当前页 * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 ***********************************/ - Page getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType,String carrierName,String deviceCode,String testDescribe,String sensorDescribe, Page page,List> attributeContent); + //Page getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType,String carrierName,String deviceCode,String testDescribe,String sensorDescribe, Page page,List> attributeContent); + Page getTsTaskPage(String keyword, String startDate, String endDate, List fieldNameData, Page page, List attributeContent); /*********************************** * 用途说明:新增试验数据管理-试验任务管理 @@ -68,4 +69,5 @@ public interface ITsTaskService extends IService { Object confirmDeleteTask(List dataset); List listTsTask(); + } diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java index 45e3cbe..6971af4 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java @@ -34,6 +34,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.opencsv.CSVReader; import com.opencsv.CSVWriter; import com.opencsv.exceptions.CsvValidationException; +import com.yfd.platform.component.ExtractTaskStatus; import com.yfd.platform.component.ServerSendEventServer; import com.yfd.platform.component.TaskStatusHolder; import com.yfd.platform.component.WebSocketServer; @@ -134,6 +135,8 @@ public class TsFilesServiceImpl extends ServiceImpl impl @Autowired private TaskStatusHolder taskStatusHolder; + @Autowired + private ExtractTaskStatus extractTaskStatus; @Autowired private RedisTemplate redisTemplate; @@ -330,6 +333,7 @@ public class TsFilesServiceImpl extends ServiceImpl impl } + private boolean isValidPage(IPage page) { return page.getRecords() != null && !page.getRecords().isEmpty() @@ -1124,17 +1128,6 @@ public class TsFilesServiceImpl extends ServiceImpl impl updateWrapper1.eq(TsFiles::getParentId, folderId).set(TsFiles::getWorkPath, ""); int updatedCount = tsFilesMapper.update(null, updateWrapper1); LOGGER.info("本地更新操作:将 {} 条记录的 work_path 设置为空", updatedCount); - - - // 批量删除备份路径和工作路径为空的数据 条件是父ID等于folderId - LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); - deleteWrapper.eq(TsFiles::getParentId, folderId) - .and(wrapper -> wrapper.isNull(TsFiles::getBackupPath).or().eq(TsFiles::getBackupPath, "")) - .and(wrapper -> wrapper.isNull(TsFiles::getWorkPath).or().eq(TsFiles::getWorkPath, "")); - int deletedCounts = tsFilesMapper.delete(deleteWrapper); - LOGGER.info("本地删除操作:成功删除了 {} 条 work_path 和 backup_path 都为空的记录", deletedCounts); - deletedCount += deletedCounts; - } else { //删除minio的时候判断本地路径是否为空 如果为空直接删除 如果不为空把BackupPath修改成空 //如果是minio 先先吧所有的backup_path修改成空 然后删除所有work_path和backup_path为空的数据 @@ -1142,16 +1135,6 @@ public class TsFilesServiceImpl extends ServiceImpl impl updateWrapper1.eq(TsFiles::getParentId, folderId).set(TsFiles::getBackupPath, ""); int updatedCount = tsFilesMapper.update(null, updateWrapper1); LOGGER.info("minio更新操作:将 {} 条记录的 work_path 设置为空", updatedCount); - - - // 批量删除备份路径和工作路径为空的数据 条件是父ID等于folderId - LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); - deleteWrapper.eq(TsFiles::getParentId, folderId) - .and(wrapper -> wrapper.isNull(TsFiles::getBackupPath).or().eq(TsFiles::getBackupPath, "")) - .and(wrapper -> wrapper.isNull(TsFiles::getWorkPath).or().eq(TsFiles::getWorkPath, "")); - int deletedCounts = tsFilesMapper.delete(deleteWrapper); - LOGGER.info("minio删除操作:成功删除了 {} 条 work_path 和 backup_path 都为空的记录", deletedCounts); - deletedCount += deletedCounts; } @@ -1166,7 +1149,14 @@ public class TsFilesServiceImpl extends ServiceImpl impl } } - + // 批量删除备份路径和工作路径为空的数据 条件是父ID等于folderId + LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); + deleteWrapper.eq(TsFiles::getParentId, folderId) + .and(wrapper -> wrapper.isNull(TsFiles::getBackupPath).or().eq(TsFiles::getBackupPath, "")) + .and(wrapper -> wrapper.isNull(TsFiles::getWorkPath).or().eq(TsFiles::getWorkPath, "")); + int deletedCounts = tsFilesMapper.delete(deleteWrapper); + LOGGER.info("minio删除操作:成功删除了 {} 条 work_path 和 backup_path 都为空的记录", deletedCounts); + deletedCount += deletedCounts; // 递归删除子文件 // for (TsFiles subFile : subFiles) { // // 递归调用删除子文件的逻辑 @@ -1897,6 +1887,63 @@ public class TsFilesServiceImpl extends ServiceImpl impl /*************************************解压缩*******************************************/ + @Override + public void decompressionFolderAsync(String id, String decompressionPath, String parentId, String path, String taskId) throws IOException { + String tsfilesName = null; + try { + TsTask tsTask = tsTaskMapper.selectById(taskId); + TableNameContextHolder.setTaskCode(tsTask.getTaskCode()); + TsFiles tsFiles = tsFilesMapper.selectById(id); + tsfilesName = tsFiles.getFileName(); + // 执行实际备份逻辑 + this.decompressionFolder(id, decompressionPath, parentId, path, taskId); + } finally { + // 生成唯一Key + String extractKey = taskStatusHolder.extractKey(taskId); + // 无论成功失败都标记完成 + taskStatusHolder.finishTask(extractKey); + extractTaskStatus.removeTask(id); + WebSocketServer.sendMessageTo(tsfilesName + "解压缩完成", "id_extract_" + taskId); + TableNameContextHolder.clear(); + } + } + + @Override + public Object decompressionFolderData(String taskId) { + try { + StringBuilder decompressionFolder = new StringBuilder(); + TsTask tsTask = tsTaskMapper.selectById(taskId); + TableNameContextHolder.setTaskCode(tsTask.getTaskCode()); + // 如果id为空 + Set keys = extractTaskStatus.getKeysByValue(taskId); + if (keys != null && !keys.isEmpty()) { + for (String key : keys) { + TsFiles tsFiles = tsFilesMapper.selectById(key); + if (tsFiles != null) { // 确保 tsFiles 不为 null + if (decompressionFolder.length() > 0) { + decompressionFolder.append(";"); // 添加分号分隔 + } + decompressionFolder.append(tsFiles.getFileName()); + } + } + // 如果拼接了文件名,返回结果 + if (decompressionFolder.length() > 0) { + return (decompressionFolder+"任务正在进行中!"); + } + } + + + // 如果 decompressionFolder 为空 + if (decompressionFolder.length() == 0) { + decompressionFolder.append("没有解压任务!"); + } + return decompressionFolder; + }finally { + TableNameContextHolder.clear(); + } + } + + /********************************** * 用途说明: 解压缩接口 * 参数说明 id 要解压的文件id diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsTaskServiceImpl.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsTaskServiceImpl.java index 2773ead..0b09e9f 100644 --- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsTaskServiceImpl.java +++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsTaskServiceImpl.java @@ -26,6 +26,10 @@ import com.yfd.platform.modules.storage.model.enums.FileTypeEnum; import com.yfd.platform.modules.storage.model.request.BatchDeleteRequest; import com.yfd.platform.modules.storage.model.request.NewFolderRequest; import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.mapper.SysDictionaryMapper; import com.yfd.platform.utils.StringUtils; import com.yfd.platform.utils.TableNameContextHolder; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -82,114 +86,110 @@ public class TsTaskServiceImpl extends ServiceImpl impleme @Resource private StorageSourceMapper storageSourceMapper; + @Resource + private SysDictionaryMapper sysDictionaryMapper; + @Resource + private SysDictionaryItemsMapper sysDictionaryItemsMapper; @Autowired private DataSource dataSource; private static final String INITIAL_CODE = "00001"; private static final int MAX_CODE_VALUE = 99999; - /********************************** - * 用途说明: 分页查询试验数据管理-试验任务管理 - * 参数说明 - * taskName 任务名称 - * startDate (开始日期) - * endDate (结束日期) - * taskPlace 任务地点 - * taskPerson 任务人员 - * taskCode 任务编号 - * taskType 任务类型 - * carrierName 载体名称 - * deviceCode 设备代号_编号 - * testDescribe 试验描述 - * sensorDescribe 传感器描述 - * pageNum 当前页 - * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 - ***********************************/ + +// * keyword 全属性搜索 +// * taskName 任务名称 +// * startDate (开始日期) +// * endDate (结束日期) +// * taskPlace 任务地点 +// * taskPerson 任务人员 +// * taskCode 任务编号 +// * taskType 任务类型 +// * carrierName 载体名称 +// * deviceCode 设备代号_编号 +// * testDescribe 试验描述 +// * sensorDescribe 传感器描述 +// * pageNum 当前页 @Override - public Page getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType, String carrierName, String deviceCode, String testDescribe, String sensorDescribe, Page page,List> attributeContent) { + public Page getTsTaskPage(String keyword, String startDate, String endDate, List fieldNameData, Page page, List attributeContent) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + Set fieldSet = new HashSet<>(fieldNameData); + final List matchedTypeIds; - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - //如果任务名称 taskName 不为空 - if (StringUtils.isNotEmpty(taskName)) { - queryWrapper.like(TsTask::getTaskName, taskName); + // === 处理任务类型字典查询 === + if (fieldSet.contains("task_type")) { + QueryWrapper dictWrapper = new QueryWrapper<>(); + dictWrapper.eq("dictcode", "taskType").select("id"); + SysDictionary sysDictionary = sysDictionaryMapper.selectOne(dictWrapper); + + if (sysDictionary != null) { + QueryWrapper itemsWrapper = new QueryWrapper<>(); + itemsWrapper.select("itemcode") + .eq("dictid", sysDictionary.getId()) + .like("dictname", keyword); + matchedTypeIds = sysDictionaryItemsMapper.selectList(itemsWrapper) + .stream() + .map(SysDictionaryItems::getItemCode) + .collect(Collectors.toList()); + } else { + matchedTypeIds = Collections.emptyList(); + } + } else { + matchedTypeIds = Collections.emptyList(); } - //如果任务地点 taskPlace 不为空 - if (StringUtils.isNotEmpty(taskPlace)) { - queryWrapper.like(TsTask::getTaskPlace, taskPlace); - } + // === 关键字搜索条件(包含自定义属性)=== + if (StringUtils.isNotBlank(keyword)) { + queryWrapper.and(wrapper -> { + boolean hasCondition = false; - //如果任务人员 taskPerson 不为空 - if (StringUtils.isNotEmpty(taskPerson)) { - queryWrapper.like(TsTask::getTaskPerson, taskPerson); - } + // 1. 普通字段查询 + for (String field : fieldSet) { + if (FIELD_MAPPING.containsKey(field)) { + wrapper.or().like(true, FIELD_MAPPING.get(field), keyword); + hasCondition = true; + } + } - //如果任务编号 taskCode 不为空 - if (StringUtils.isNotEmpty(taskCode)) { - queryWrapper.like(TsTask::getTaskCode, taskCode); - } + // 2. 任务类型字典查询 + if (!matchedTypeIds.isEmpty()) { + wrapper.or().in(true, "task_type", matchedTypeIds); + hasCondition = true; + } - //如果任务类型 taskType 不为空 - if (StringUtils.isNotEmpty(taskType)) { - queryWrapper.eq(TsTask::getTaskType, taskType); - } - //如果载体名称 carrierName 不为空 - if (StringUtils.isNotEmpty(carrierName)) { - queryWrapper.like(TsTask::getCarrierName, carrierName); - } - //如果设备代号_编号 deviceCode 不为空 - if (StringUtils.isNotEmpty(deviceCode)) { - queryWrapper.like(TsTask::getDeviceCode, deviceCode); - } - //如果试验描述 testDescribe 不为空 - if (StringUtils.isNotEmpty(testDescribe)) { - queryWrapper.like(TsTask::getTestDescribe, testDescribe); - } - //如果传感器描述 sensorDescribe 不为空 - if (StringUtils.isNotEmpty(sensorDescribe)) { - queryWrapper.like(TsTask::getSensorDescribe, sensorDescribe); - } + // 3. 存储空间字段查询 + if (fieldSet.contains("local_storage_id")) { + wrapper.or().apply(true, + "local_storage_id IN (SELECT id FROM fi_storage_source WHERE name LIKE CONCAT('%', {0}, '%'))", + keyword + ); + hasCondition = true; + } + if (fieldSet.contains("backup_storage_id")) { + wrapper.or().apply(true, + "backup_storage_id IN (SELECT id FROM fi_storage_source WHERE name LIKE CONCAT('%', {0}, '%'))", + keyword + ); + hasCondition = true; + } - //开始时间startDate - DateTime parseStartDate = DateUtil.parse(startDate); - //时间endDate不为空 - DateTime parseEndDate = DateUtil.parse(endDate); - //开始时间和结束时间不为空 查询条件>=开始时间 <结束时间 - if (parseStartDate != null && parseEndDate != null) { - queryWrapper.ge(TsTask::getTaskStartdate, parseStartDate).lt(TsTask::getTaskEnddate, parseEndDate); - } - queryWrapper.orderByDesc(TsTask::getTaskStartdate); - - - // 处理属性过滤条件 - MySQL 5.7+ 兼容版本 - if (attributeContent != null && !attributeContent.isEmpty()) { - for (Map attr : attributeContent) { - for (Map.Entry entry : attr.entrySet()) { - String code = entry.getKey(); - String value = entry.getValue(); - - if (StringUtils.isEmpty(value)) { - // 检查属性存在 - queryWrapper.apply( - "EXISTS (SELECT 1 FROM JSON_TABLE(task_props, '$[*]' " + - "COLUMNS (code VARCHAR(50) PATH '$.code') AS jt " + - "WHERE jt.code = {0})", - code - ); - } else { + // 4. 自定义属性字段查询(关键修改点) + if (attributeContent != null && !attributeContent.isEmpty()) { + // 处理每个自定义属性的搜索 + for (String attrCode : attributeContent) { // 转义特殊字符 - String safeValue = value.replace("'", "''") + String safeValue = keyword.replace("'", "''") .replace("%", "\\%") .replace("_", "\\_"); - // 使用 JSON_EXTRACT 实现兼容查询 - queryWrapper.apply( + // 添加自定义属性搜索条件 + wrapper.or().apply(true, "EXISTS (SELECT 1 FROM (" + " SELECT " + " JSON_UNQUOTE(JSON_EXTRACT(t.obj, '$.code')) AS code, " + - " JSON_UNQUOTE(JSON_EXTRACT(t.obj, '$.data')) AS data " + + " COALESCE(JSON_UNQUOTE(JSON_EXTRACT(t.obj, '$.data')), '') AS data " + " FROM (" + " SELECT JSON_EXTRACT(task_props, CONCAT('$[', idx.idx, ']')) AS obj " + " FROM (SELECT 0 AS idx UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) idx" + @@ -197,16 +197,49 @@ public class TsTaskServiceImpl extends ServiceImpl impleme " ) t" + ") jt " + "WHERE jt.code = {0} AND jt.data LIKE CONCAT('%', {1}, '%'))", - code, value + attrCode, safeValue ); + hasCondition = true; } } + + if (!hasCondition) { + wrapper.apply(true, "1 = 0"); + } + }); + } + + // === 时间范围处理 === + if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) { + DateTime parseStartDate = DateUtil.parse(startDate); + DateTime parseEndDate = DateUtil.parse(endDate); + + if (parseStartDate != null && parseEndDate != null) { + queryWrapper.ge(true, "task_startdate", parseStartDate) + .lt(true, "task_enddate", parseEndDate); } } - //分页查询 - Page tsTaskPage = tsTaskMapper.selectPage(page, queryWrapper); - return tsTaskPage; + + // 添加排序 + queryWrapper.orderByDesc(true, "task_startdate"); + + return tsTaskMapper.selectPage(page, queryWrapper); } + // 字段映射(避免硬编码) + private static final Map FIELD_MAPPING = new HashMap<>(); + static { + FIELD_MAPPING.put("task_code", "task_code"); + FIELD_MAPPING.put("task_name", "task_name"); + FIELD_MAPPING.put("task_place", "task_place"); + FIELD_MAPPING.put("task_person", "task_person"); + FIELD_MAPPING.put("carrier_name", "carrier_name"); + FIELD_MAPPING.put("device_code", "device_code"); + FIELD_MAPPING.put("test_describe", "test_describe"); + FIELD_MAPPING.put("sensor_describe", "sensor_describe"); + } + + + /*********************************** * 用途说明:新增试验数据管理-试验任务管理