解压缩异步以及全属性查询

This commit is contained in:
lilin 2025-07-15 16:37:53 +08:00
parent d3c8bde081
commit 32b34fa755
8 changed files with 332 additions and 166 deletions

View File

@ -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<String, String> extractTaskStatusMap = new ConcurrentHashMap<>();
// 存入方法
public void putTaskStatus(String key, String value) {
extractTaskStatusMap.put(key, value);
}
// 通过value获取key的方法
public Set<String> getKeysByValue(String value) {
Set<String> 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);
}
}

View File

@ -2,7 +2,10 @@ package com.yfd.platform.component;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@Component @Component
@ -14,6 +17,10 @@ public class TaskStatusHolder {
public String generateKey(String taskId, String nodeId) { public String generateKey(String taskId, String nodeId) {
return taskId + ":" + nodeId; return taskId + ":" + nodeId;
} }
// 生成唯一解压缩的KeytaskId + id
public String extractKey(String id) {
return id;
}
// 生成专项扫描唯一Keyproject_id // 生成专项扫描唯一Keyproject_id
public String specialGenerateKey(String id) { public String specialGenerateKey(String id) {
@ -47,4 +54,17 @@ public class TaskStatusHolder {
public String getValue(String key) { public String getValue(String key) {
return taskStatusMap.get(key); return taskStatusMap.get(key);
} }
// 通过value获取所有对应的key
public Set<String> findKeysByValue(String value) {
Set<String> result = new HashSet<>();
for (Map.Entry<String, String> entry : taskStatusMap.entrySet()) {
if (entry.getValue().equals(value)) {
result.add(entry.getKey());
}
}
return result;
}
} }

View File

@ -7,11 +7,14 @@ import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.annotation.Log; import com.yfd.platform.annotation.Log;
import com.yfd.platform.component.ExtractTaskStatus;
import com.yfd.platform.component.TaskStatusHolder; import com.yfd.platform.component.TaskStatusHolder;
import com.yfd.platform.config.ResponseResult; import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.experimentalData.domain.*; import com.yfd.platform.modules.experimentalData.domain.*;
import com.yfd.platform.modules.experimentalData.service.ITsFilesService; 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.modules.storage.model.result.FileItemResult;
import com.yfd.platform.utils.TableNameContextHolder;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@ -19,9 +22,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -44,6 +45,9 @@ public class TsFilesController {
@Autowired @Autowired
private TaskStatusHolder taskStatusHolder; private TaskStatusHolder taskStatusHolder;
@Resource
private ExtractTaskStatus extractTaskStatus;
/********************************** /**********************************
* 用途说明: 分页查询试验数据管理-文档内容 * 用途说明: 分页查询试验数据管理-文档内容
* 参数说明 * 参数说明
@ -66,7 +70,7 @@ public class TsFilesController {
//分页查询 //分页查询
int currentPage = (int) page.getCurrent(); int currentPage = (int) page.getCurrent();
// 先尝试从缓存获取 如果搜索条件为空 从Redis获取 // 先尝试从缓存获取 如果搜索条件为空 从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)) { if (!StrUtil.isEmpty(id)) {
IPage<TsFiles> cachedPage = tsFilesService.getCachedTsFilesPage(taskId, nodeId, currentPage, id); IPage<TsFiles> cachedPage = tsFilesService.getCachedTsFilesPage(taskId, nodeId, currentPage, id);
if (cachedPage != null) { if (cachedPage != null) {
@ -167,12 +171,12 @@ public class TsFilesController {
@PostMapping("/deleteTsFilesById") @PostMapping("/deleteTsFilesById")
@ApiOperation("根据ID删除试验数据管理文档内容") @ApiOperation("根据ID删除试验数据管理文档内容")
@PreAuthorize("@el.check('del:tsFiles')") @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)) { if (StrUtil.isBlank(id) || StrUtil.isBlank(taskId)) {
return ResponseResult.error("参数为空"); return ResponseResult.error("参数为空");
} }
List<String> dataset = Arrays.asList(id); List<String> 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") @PostMapping("/deleteTsFilesByIds")
@ApiOperation("批量删除试验数据管理文档内容") @ApiOperation("批量删除试验数据管理文档内容")
@PreAuthorize("@el.check('del:tsFiles')") @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)) { if (StrUtil.isBlank(ids) || StrUtil.isBlank(taskId)) {
return ResponseResult.error("参数为空"); return ResponseResult.error("参数为空");
} }
String[] splitIds = ids.split(","); String[] splitIds = ids.split(",");
// 数组转集合 // 数组转集合
List<String> dataset = Arrays.asList(splitIds); List<String> 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 = "压缩文件夹接口!") @Log(module = "实验数据管理", value = "压缩文件夹接口!")
@PostMapping("/compress") @PostMapping("/compress")
@ApiOperation("压缩文件夹接口") @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 { try {
if (StrUtil.isBlank(ids) && StrUtil.isBlank(compressedFormat) && StrUtil.isBlank(compressedName) && StrUtil.isBlank(compressedPath) && StrUtil.isBlank(path) && StrUtil.isBlank(taskId)) { if (StrUtil.isBlank(ids) && StrUtil.isBlank(compressedFormat) && StrUtil.isBlank(compressedName) && StrUtil.isBlank(compressedPath) && StrUtil.isBlank(path) && StrUtil.isBlank(taskId)) {
return ResponseResult.error("参数为空"); 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) { } catch (Exception e) {
System.out.print("压缩异常原因" + e); System.out.print("压缩异常原因" + e);
return ResponseResult.error("压缩失败"); return ResponseResult.error("压缩失败");
@ -235,18 +239,51 @@ public class TsFilesController {
@Log(module = "实验数据管理", value = "解压缩接口!") @Log(module = "实验数据管理", value = "解压缩接口!")
@PostMapping("/decompression") @PostMapping("/decompression")
@ApiOperation("解压缩接口") @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 { try {
if (StrUtil.isBlank(id)|| StrUtil.isBlank(taskId)) { if (StrUtil.isBlank(id) || StrUtil.isBlank(taskId)) {
return ResponseResult.error("参数为空"); 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) { } catch (Exception e) {
System.out.print("解压缩异常原因" + e); System.out.print("解压缩异常原因" + e);
return ResponseResult.error("解压缩失败"); 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 新路径 * 参数说明 newPath 新路径
@ -617,7 +654,7 @@ public class TsFilesController {
@Log(module = "实验数据管理", value = "文件自动备份!") @Log(module = "实验数据管理", value = "文件自动备份!")
@PostMapping("/automaticFileBackupByIds") @PostMapping("/automaticFileBackupByIds")
@ApiOperation("自动备份本地文件到备份空间通过ID") @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)) { if (StrUtil.isEmpty(id)) {
return ResponseResult.error("参数为空"); return ResponseResult.error("参数为空");
} }
@ -636,7 +673,7 @@ public class TsFilesController {
// 原子性启动新任务 // 原子性启动新任务
if (taskStatusHolder.startTaskIfAbsent(asyncKey)) { if (taskStatusHolder.startTaskIfAbsent(asyncKey)) {
// 直接异步执行并推送结果 // 直接异步执行并推送结果
tsFilesService.automaticFileBackupAsyncByIds(dataset,taskId,nodeId); tsFilesService.automaticFileBackupAsyncByIds(dataset, taskId, nodeId);
return ResponseResult.success("任务开始处理!"); return ResponseResult.success("任务开始处理!");
} else { } else {
return ResponseResult.success("任务已由其他请求启动"); return ResponseResult.success("任务已由其他请求启动");
@ -654,12 +691,12 @@ public class TsFilesController {
@Log(module = "实验数据管理", value = "实时获取轨迹数据!") @Log(module = "实验数据管理", value = "实时获取轨迹数据!")
@PostMapping("/startSimpleNavi") @PostMapping("/startSimpleNavi")
@ApiOperation("实时获取轨迹数据") @ApiOperation("实时获取轨迹数据")
public ResponseResult startSimpleNavi(String id, int samTimes, String token,String taskId) { public ResponseResult startSimpleNavi(String id, int samTimes, String token, String taskId) {
try { try {
// 使用线程池异步执行任务 // 使用线程池异步执行任务
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
try { try {
tsFilesService.batchSendNaviOutDataJob(id, samTimes, token,taskId); tsFilesService.batchSendNaviOutDataJob(id, samTimes, token, taskId);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -694,7 +731,7 @@ public class TsFilesController {
@Log(module = "实验数据管理", value = "查询文件内容!") @Log(module = "实验数据管理", value = "查询文件内容!")
@GetMapping("/api/files/content") @GetMapping("/api/files/content")
@ApiOperation("查询文件内容") @ApiOperation("查询文件内容")
public ResponseResult getFileContent(@RequestParam String id,String taskId) { public ResponseResult getFileContent(@RequestParam String id, String taskId) {
try { try {
if (StrUtil.isBlank(id) && StrUtil.isBlank(taskId)) { if (StrUtil.isBlank(id) && StrUtil.isBlank(taskId)) {
return ResponseResult.error("参数为空"); return ResponseResult.error("参数为空");
@ -715,7 +752,7 @@ public class TsFilesController {
@Log(module = "实验数据管理", value = "保存文件内容!") @Log(module = "实验数据管理", value = "保存文件内容!")
@PostMapping("/save/files/content") @PostMapping("/save/files/content")
@ApiOperation("保存文件内容") @ApiOperation("保存文件内容")
public ResponseResult saveFileContent(String id, String content,String taskId) { public ResponseResult saveFileContent(String id, String content, String taskId) {
try { try {
if (StrUtil.isBlank(id) && StrUtil.isBlank(content) && StrUtil.isBlank(taskId)) { if (StrUtil.isBlank(id) && StrUtil.isBlank(content) && StrUtil.isBlank(taskId)) {
return ResponseResult.error("参数为空"); return ResponseResult.error("参数为空");
@ -737,7 +774,7 @@ public class TsFilesController {
@PostMapping("/batchModify") @PostMapping("/batchModify")
@ApiOperation("批量修改文件中多行多列的内容") @ApiOperation("批量修改文件中多行多列的内容")
public ResponseResult batchModifyFile(@RequestBody BatchModifyRequest request) throws IOException { 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("文件保存成功"); return ResponseResult.success("文件保存成功");
} }
@ -749,12 +786,12 @@ public class TsFilesController {
@Log(module = "实验数据管理", value = "获取文件url") @Log(module = "实验数据管理", value = "获取文件url")
@PostMapping("/obtainUrl") @PostMapping("/obtainUrl")
@ApiOperation("获取文件url") @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)) { if (StrUtil.isBlank(id) && StrUtil.isBlank(type) && StrUtil.isBlank(type)) {
return ResponseResult.error("参数为空"); return ResponseResult.error("参数为空");
} }
//查询本地树和minio树 //查询本地树和minio树
FileItemResult fileItemResult = tsFilesService.obtainUrl(id, type,taskId); FileItemResult fileItemResult = tsFilesService.obtainUrl(id, type, taskId);
return ResponseResult.successData(fileItemResult); return ResponseResult.successData(fileItemResult);
} }
} }

View File

@ -44,6 +44,7 @@ public class TsTaskController {
/********************************** /**********************************
* 用途说明: 分页查询试验数据管理-试验任务管理 * 用途说明: 分页查询试验数据管理-试验任务管理
* 参数说明 * 参数说明
* keyword 全属性搜索
* taskName 任务名称 * taskName 任务名称
* startDate (开始日期) * startDate (开始日期)
* endDate 结束日期 * endDate 结束日期
@ -62,42 +63,29 @@ public class TsTaskController {
@GetMapping("/page") @GetMapping("/page")
@ApiOperation("分页查询试验数据管理试验任务管理") @ApiOperation("分页查询试验数据管理试验任务管理")
@PreAuthorize("@el.check('select:tsTask')") @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<TsTask> page, String attributeContentJson) { public ResponseResult getTsTaskPage(String keyword,
// 双重解码处理 String startDate,
if (attributeContentJson != null) { String endDate,
try { String fieldName,
// 第二次解码%5B [ Page<TsTask> page,
attributeContentJson = URLDecoder.decode(attributeContentJson, StandardCharsets.UTF_8.name()); String attributeContentJson) {
} catch (UnsupportedEncodingException e) { //字段名称
return ResponseResult.error("参数解码错误"); List<String> fieldNameData = new ArrayList<>();
} if (StrUtil.isNotEmpty(fieldName)) {
String[] splitIds = fieldName.split(",");
// 数组转集合
fieldNameData = Arrays.asList(splitIds);
} }
//自定义属性
// 将JSON字符串转换为List<Map<String, String>> List<String> attributeContent = new ArrayList<>();
List<Map<String, String>> attributeContent = null; if (StrUtil.isNotEmpty(attributeContentJson)) {
if (StringUtils.isNotEmpty(attributeContentJson)) { String[] splitIds = attributeContentJson.split(",");
try { // 数组转集合
// 使用 TypeReference 指定精确类型 attributeContent = Arrays.asList(splitIds);
attributeContent = JSON.parseObject(
attributeContentJson,
new TypeReference<List<Map<String, String>>>() {
}
);
// 遍历并移除空值字段
if (attributeContent != null) {
for (Map<String, String> item : attributeContent) {
// 遍历 Map移除值为空的键值对
item.entrySet().removeIf(entry -> entry.getValue() == null || entry.getValue().isEmpty());
}
}
} catch (Exception e) {
return ResponseResult.error("属性参数格式错误");
}
} }
//分页查询 //分页查询
Page<TsTask> sdProjectPage = tsTaskService.getTsTaskPage(taskName, startDate, endDate, taskPlace, taskPerson, taskCode, taskType, carrierName, deviceCode, testDescribe, sensorDescribe, page, attributeContent); Page<TsTask> sdProjectPage = tsTaskService.getTsTaskPage(keyword, startDate, endDate, fieldNameData, page, attributeContent);
return ResponseResult.successData(sdProjectPage); return ResponseResult.successData(sdProjectPage);
} }

View File

@ -252,4 +252,8 @@ public interface ITsFilesService extends IService<TsFiles> {
IPage<TsFiles> getCachedTsFilesPage(String taskId, String nodeId, int currentPage,String id); IPage<TsFiles> getCachedTsFilesPage(String taskId, String nodeId, int currentPage,String id);
int countFiles(String id); int countFiles(String id);
void decompressionFolderAsync(String id, String decompressionPath, String parentId, String path, String taskId) throws IOException;
Object decompressionFolderData(String taskId);
} }

View File

@ -36,7 +36,8 @@ public interface ITsTaskService extends IService<TsTask> {
* pageNum 当前页 * pageNum 当前页
* 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果
***********************************/ ***********************************/
Page<TsTask> getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType,String carrierName,String deviceCode,String testDescribe,String sensorDescribe, Page<TsTask> page,List<Map<String, String>> attributeContent); //Page<TsTask> getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType,String carrierName,String deviceCode,String testDescribe,String sensorDescribe, Page<TsTask> page,List<Map<String, String>> attributeContent);
Page<TsTask> getTsTaskPage(String keyword, String startDate, String endDate, List<String> fieldNameData, Page<TsTask> page, List<String> attributeContent);
/*********************************** /***********************************
* 用途说明新增试验数据管理-试验任务管理 * 用途说明新增试验数据管理-试验任务管理
@ -68,4 +69,5 @@ public interface ITsTaskService extends IService<TsTask> {
Object confirmDeleteTask(List<String> dataset); Object confirmDeleteTask(List<String> dataset);
List<TsTask> listTsTask(); List<TsTask> listTsTask();
} }

View File

@ -34,6 +34,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.opencsv.CSVReader; import com.opencsv.CSVReader;
import com.opencsv.CSVWriter; import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvValidationException; import com.opencsv.exceptions.CsvValidationException;
import com.yfd.platform.component.ExtractTaskStatus;
import com.yfd.platform.component.ServerSendEventServer; import com.yfd.platform.component.ServerSendEventServer;
import com.yfd.platform.component.TaskStatusHolder; import com.yfd.platform.component.TaskStatusHolder;
import com.yfd.platform.component.WebSocketServer; import com.yfd.platform.component.WebSocketServer;
@ -134,6 +135,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
@Autowired @Autowired
private TaskStatusHolder taskStatusHolder; private TaskStatusHolder taskStatusHolder;
@Autowired
private ExtractTaskStatus extractTaskStatus;
@Autowired @Autowired
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@ -330,6 +333,7 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
} }
private boolean isValidPage(IPage<TsFiles> page) { private boolean isValidPage(IPage<TsFiles> page) {
return page.getRecords() != null return page.getRecords() != null
&& !page.getRecords().isEmpty() && !page.getRecords().isEmpty()
@ -1124,17 +1128,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
updateWrapper1.eq(TsFiles::getParentId, folderId).set(TsFiles::getWorkPath, ""); updateWrapper1.eq(TsFiles::getParentId, folderId).set(TsFiles::getWorkPath, "");
int updatedCount = tsFilesMapper.update(null, updateWrapper1); int updatedCount = tsFilesMapper.update(null, updateWrapper1);
LOGGER.info("本地更新操作:将 {} 条记录的 work_path 设置为空", updatedCount); LOGGER.info("本地更新操作:将 {} 条记录的 work_path 设置为空", updatedCount);
// 批量删除备份路径和工作路径为空的数据 条件是父ID等于folderId
LambdaQueryWrapper<TsFiles> 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 { } else {
//删除minio的时候判断本地路径是否为空 如果为空直接删除 如果不为空把BackupPath修改成空 //删除minio的时候判断本地路径是否为空 如果为空直接删除 如果不为空把BackupPath修改成空
//如果是minio 先先吧所有的backup_path修改成空 然后删除所有work_path和backup_path为空的数据 //如果是minio 先先吧所有的backup_path修改成空 然后删除所有work_path和backup_path为空的数据
@ -1142,16 +1135,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
updateWrapper1.eq(TsFiles::getParentId, folderId).set(TsFiles::getBackupPath, ""); updateWrapper1.eq(TsFiles::getParentId, folderId).set(TsFiles::getBackupPath, "");
int updatedCount = tsFilesMapper.update(null, updateWrapper1); int updatedCount = tsFilesMapper.update(null, updateWrapper1);
LOGGER.info("minio更新操作将 {} 条记录的 work_path 设置为空", updatedCount); LOGGER.info("minio更新操作将 {} 条记录的 work_path 设置为空", updatedCount);
// 批量删除备份路径和工作路径为空的数据 条件是父ID等于folderId
LambdaQueryWrapper<TsFiles> 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<TsFilesMapper, TsFiles> impl
} }
} }
// 批量删除备份路径和工作路径为空的数据 条件是父ID等于folderId
LambdaQueryWrapper<TsFiles> 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) { // for (TsFiles subFile : subFiles) {
// // 递归调用删除子文件的逻辑 // // 递归调用删除子文件的逻辑
@ -1897,6 +1887,63 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> 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<String> 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 * 参数说明 id 要解压的文件id

View File

@ -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.BatchDeleteRequest;
import com.yfd.platform.modules.storage.model.request.NewFolderRequest; import com.yfd.platform.modules.storage.model.request.NewFolderRequest;
import com.yfd.platform.modules.storage.service.base.AbstractBaseFileService; 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.StringUtils;
import com.yfd.platform.utils.TableNameContextHolder; import com.yfd.platform.utils.TableNameContextHolder;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
@ -82,114 +86,110 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
@Resource @Resource
private StorageSourceMapper storageSourceMapper; private StorageSourceMapper storageSourceMapper;
@Resource
private SysDictionaryMapper sysDictionaryMapper;
@Resource
private SysDictionaryItemsMapper sysDictionaryItemsMapper;
@Autowired @Autowired
private DataSource dataSource; private DataSource dataSource;
private static final String INITIAL_CODE = "00001"; private static final String INITIAL_CODE = "00001";
private static final int MAX_CODE_VALUE = 99999; private static final int MAX_CODE_VALUE = 99999;
/**********************************
* 用途说明: 分页查询试验数据管理-试验任务管理 // * keyword 全属性搜索
* 参数说明 // * taskName 任务名称
* taskName 任务名称 // * startDate (开始日期)
* startDate (开始日期) // * endDate 结束日期
* endDate 结束日期 // * taskPlace 任务地点
* taskPlace 任务地点 // * taskPerson 任务人员
* taskPerson 任务人员 // * taskCode 任务编号
* taskCode 任务编号 // * taskType 任务类型
* taskType 任务类型 // * carrierName 载体名称
* carrierName 载体名称 // * deviceCode 设备代号_编号
* deviceCode 设备代号_编号 // * testDescribe 试验描述
* testDescribe 试验描述 // * sensorDescribe 传感器描述
* sensorDescribe 传感器描述 // * pageNum 当前页
* pageNum 当前页
* 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果
***********************************/
@Override @Override
public Page<TsTask> getTsTaskPage(String taskName, String startDate, String endDate, String taskPlace, String taskPerson, String taskCode, String taskType, String carrierName, String deviceCode, String testDescribe, String sensorDescribe, Page<TsTask> page,List<Map<String, String>> attributeContent) { public Page<TsTask> getTsTaskPage(String keyword, String startDate, String endDate, List<String> fieldNameData, Page<TsTask> page, List<String> attributeContent) {
QueryWrapper<TsTask> queryWrapper = new QueryWrapper<>();
Set<String> fieldSet = new HashSet<>(fieldNameData);
final List<String> matchedTypeIds;
LambdaQueryWrapper<TsTask> queryWrapper = new LambdaQueryWrapper<>(); // === 处理任务类型字典查询 ===
//如果任务名称 taskName 不为空 if (fieldSet.contains("task_type")) {
if (StringUtils.isNotEmpty(taskName)) { QueryWrapper<SysDictionary> dictWrapper = new QueryWrapper<>();
queryWrapper.like(TsTask::getTaskName, taskName); dictWrapper.eq("dictcode", "taskType").select("id");
SysDictionary sysDictionary = sysDictionaryMapper.selectOne(dictWrapper);
if (sysDictionary != null) {
QueryWrapper<SysDictionaryItems> 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)) { if (StringUtils.isNotBlank(keyword)) {
queryWrapper.like(TsTask::getTaskPlace, taskPlace); queryWrapper.and(wrapper -> {
} boolean hasCondition = false;
//如果任务人员 taskPerson 不为空 // 1. 普通字段查询
if (StringUtils.isNotEmpty(taskPerson)) { for (String field : fieldSet) {
queryWrapper.like(TsTask::getTaskPerson, taskPerson); if (FIELD_MAPPING.containsKey(field)) {
} wrapper.or().like(true, FIELD_MAPPING.get(field), keyword);
hasCondition = true;
}
}
//如果任务编号 taskCode 不为空 // 2. 任务类型字典查询
if (StringUtils.isNotEmpty(taskCode)) { if (!matchedTypeIds.isEmpty()) {
queryWrapper.like(TsTask::getTaskCode, taskCode); wrapper.or().in(true, "task_type", matchedTypeIds);
} hasCondition = true;
}
//如果任务类型 taskType 不为空 // 3. 存储空间字段查询
if (StringUtils.isNotEmpty(taskType)) { if (fieldSet.contains("local_storage_id")) {
queryWrapper.eq(TsTask::getTaskType, taskType); wrapper.or().apply(true,
} "local_storage_id IN (SELECT id FROM fi_storage_source WHERE name LIKE CONCAT('%', {0}, '%'))",
//如果载体名称 carrierName 不为空 keyword
if (StringUtils.isNotEmpty(carrierName)) { );
queryWrapper.like(TsTask::getCarrierName, carrierName); hasCondition = true;
} }
//如果设备代号_编号 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);
}
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 // 4. 自定义属性字段查询关键修改点
DateTime parseStartDate = DateUtil.parse(startDate); if (attributeContent != null && !attributeContent.isEmpty()) {
//时间endDate不为空 // 处理每个自定义属性的搜索
DateTime parseEndDate = DateUtil.parse(endDate); for (String attrCode : attributeContent) {
//开始时间和结束时间不为空 查询条件>=开始时间 <结束时间
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<String, String> attr : attributeContent) {
for (Map.Entry<String, String> 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 {
// 转义特殊字符 // 转义特殊字符
String safeValue = value.replace("'", "''") String safeValue = keyword.replace("'", "''")
.replace("%", "\\%") .replace("%", "\\%")
.replace("_", "\\_"); .replace("_", "\\_");
// 使用 JSON_EXTRACT 实现兼容查询 // 添加自定义属性搜索条件
queryWrapper.apply( wrapper.or().apply(true,
"EXISTS (SELECT 1 FROM (" + "EXISTS (SELECT 1 FROM (" +
" SELECT " + " SELECT " +
" JSON_UNQUOTE(JSON_EXTRACT(t.obj, '$.code')) AS code, " + " 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 (" + " FROM (" +
" SELECT JSON_EXTRACT(task_props, CONCAT('$[', idx.idx, ']')) AS obj " + " 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" + " FROM (SELECT 0 AS idx UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) idx" +
@ -197,16 +197,49 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
" ) t" + " ) t" +
") jt " + ") jt " +
"WHERE jt.code = {0} AND jt.data LIKE CONCAT('%', {1}, '%'))", "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<TsTask> tsTaskPage = tsTaskMapper.selectPage(page, queryWrapper); // 添加排序
return tsTaskPage; queryWrapper.orderByDesc(true, "task_startdate");
return tsTaskMapper.selectPage(page, queryWrapper);
} }
// 字段映射避免硬编码
private static final Map<String, String> 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");
}
/*********************************** /***********************************
* 用途说明新增试验数据管理-试验任务管理 * 用途说明新增试验数据管理-试验任务管理