Merge branch 'main' of http://121.37.111.42:3000/ThbTech/FileManage
This commit is contained in:
commit
9e889790fd
6
data/common_items.json
Normal file
6
data/common_items.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"taskLocation" : [ "浙江横店", "陕西宝鸡", "甘肃兰州", "标签1", "标签2", "标签3", "标签4" ],
|
||||
"aircraftName" : [ "载机名称一", "载机名称二", "载机名称三" ],
|
||||
"sensorDescription" : [ "电阻式传感器", "电容式传感器", "电容式传感器二" ],
|
||||
"taskLabel" : [ "试验标签一", "试验标签二", "试验标签三", "试验标签四", "试验标签五" ]
|
||||
}
|
||||
10
java/pom.xml
10
java/pom.xml
@ -12,7 +12,7 @@
|
||||
<groupId>com.yfd</groupId>
|
||||
<artifactId>filesmanagesystem</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<packaging>war</packaging>
|
||||
<name>filesmanagesystem</name>
|
||||
<description>文件管理系统</description>
|
||||
<properties>
|
||||
@ -26,6 +26,14 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- 配置Tomcat依赖作用域为provided,支持war包部署到外部Tomcat容器,
|
||||
同时不影响jar包的内嵌Tomcat运行方式 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 生成二维码 -->
|
||||
<dependency>
|
||||
|
||||
@ -0,0 +1,191 @@
|
||||
package com.yfd.platform.modules.experimentalData.controller;
|
||||
|
||||
import com.yfd.platform.modules.experimentalData.dto.ItemReq;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/common-items")
|
||||
public class CommonItemController {
|
||||
|
||||
private static final String FILE_NAME = "common_items.json";
|
||||
private static final String DATA_DIR = "data";
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
/** 查询 */
|
||||
@GetMapping
|
||||
public Map<String, Object> get() throws IOException {
|
||||
return success(readData());
|
||||
}
|
||||
|
||||
/** 新增 */
|
||||
@PostMapping
|
||||
public Map<String, Object> add(@RequestBody ItemReq req) throws IOException {
|
||||
validate(req);
|
||||
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
Map<String, List<String>> data = readData();
|
||||
List<String> list = data.computeIfAbsent(req.getType(), k -> new ArrayList<>());
|
||||
if (!list.contains(req.getLabel())) {
|
||||
list.add(req.getLabel());
|
||||
}
|
||||
writeData(data);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put(req.getType(), list);
|
||||
return success(result);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** 新增多个标签 */
|
||||
@PostMapping("/batch")
|
||||
public Map<String, Object> addBatch(@RequestBody ItemReq req) throws IOException {
|
||||
validateForBatch(req);
|
||||
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
Map<String, List<String>> data = readData();
|
||||
List<String> list = data.computeIfAbsent(req.getType(), k -> new ArrayList<>());
|
||||
|
||||
// 批量添加不重复的标签
|
||||
for (String label : req.getLabels()) {
|
||||
if (!list.contains(label)) {
|
||||
list.add(label);
|
||||
}
|
||||
}
|
||||
|
||||
writeData(data);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put(req.getType(), list);
|
||||
return success(result);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除 */
|
||||
@DeleteMapping
|
||||
public Map<String, Object> delete(@RequestBody ItemReq req) throws IOException {
|
||||
validate(req);
|
||||
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
Map<String, List<String>> data = readData();
|
||||
List<String> list = data.get(req.getType());
|
||||
if (list != null) {
|
||||
list.remove(req.getLabel());
|
||||
}
|
||||
|
||||
writeData(data);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put(req.getType(), list != null ? list : Collections.emptyList());
|
||||
return success(result);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* ================== 工具方法 ================== */
|
||||
|
||||
private void validate(ItemReq req) {
|
||||
if (req == null
|
||||
|| (!"taskLocation".equals(req.getType()) && !"aircraftName".equals(req.getType())
|
||||
&& !"sensorDescription".equals(req.getType()) && !"taskLabel".equals(req.getType()))
|
||||
|| req.getLabel() == null || req.getLabel().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("参数无效");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateForBatch(ItemReq req) {
|
||||
if (req == null
|
||||
|| (!"taskLocation".equals(req.getType()) && !"aircraftName".equals(req.getType())
|
||||
&& !"sensorDescription".equals(req.getType()) && !"taskLabel".equals(req.getType()))
|
||||
|| req.getLabels() == null || req.getLabels().isEmpty()) {
|
||||
throw new IllegalArgumentException("参数无效");
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, List<String>> readData() throws IOException {
|
||||
// 获取项目根目录下的data文件夹路径
|
||||
String userDir = System.getProperty("user.dir");
|
||||
Path dataDir = Paths.get(userDir, DATA_DIR);
|
||||
Path filePath = dataDir.resolve(FILE_NAME);
|
||||
|
||||
if (!Files.exists(filePath)) {
|
||||
// 如果项目根目录下没有文件,创建默认数据
|
||||
System.out.println("文件不存在,创建默认数据: " + filePath.toString());
|
||||
Files.createDirectories(dataDir);
|
||||
Map<String, List<String>> init = new HashMap<>();
|
||||
init.put("taskLocation", new ArrayList<>());
|
||||
init.put("aircraftName", new ArrayList<>());
|
||||
init.put("sensorDescription", new ArrayList<>());
|
||||
init.put("taskLabel", new ArrayList<>());
|
||||
writeData(init);
|
||||
return init;
|
||||
}
|
||||
|
||||
// 文件存在,读取数据
|
||||
try {
|
||||
System.out.println("正在读取文件: " + filePath.toString());
|
||||
Map<String, List<String>> data = objectMapper.readValue(
|
||||
filePath.toFile(),
|
||||
new TypeReference<Map<String, List<String>>>() {}
|
||||
);
|
||||
System.out.println("文件读取成功,数据大小: " + data.size());
|
||||
return data;
|
||||
} catch (IOException e) {
|
||||
// 如果读取失败,返回默认数据
|
||||
System.err.println("读取文件失败: " + e.getMessage());
|
||||
Map<String, List<String>> init = new HashMap<>();
|
||||
init.put("taskLocation", new ArrayList<>());
|
||||
init.put("aircraftName", new ArrayList<>());
|
||||
init.put("sensorDescription", new ArrayList<>());
|
||||
init.put("taskLabel", new ArrayList<>());
|
||||
return init;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeData(Map<String, List<String>> data) throws IOException {
|
||||
String userDir = System.getProperty("user.dir");
|
||||
Path dataDir = Paths.get(userDir, DATA_DIR);
|
||||
Path filePath = dataDir.resolve(FILE_NAME);
|
||||
|
||||
Files.createDirectories(dataDir);
|
||||
System.out.println("正在写入文件: " + filePath.toString());
|
||||
objectMapper.writerWithDefaultPrettyPrinter()
|
||||
.writeValue(filePath.toFile(), data);
|
||||
System.out.println("文件写入成功");
|
||||
}
|
||||
|
||||
private Map<String, Object> success(Object data) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("data", data);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -11,11 +11,16 @@ import com.yfd.platform.config.ResponseResult;
|
||||
import com.yfd.platform.modules.experimentalData.domain.TsTask;
|
||||
import com.yfd.platform.modules.experimentalData.service.ITsTaskService;
|
||||
import com.yfd.platform.utils.StringUtils;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
@ -37,6 +42,8 @@ import java.util.Map;
|
||||
@RequestMapping("/experimentalData/tstask")
|
||||
public class TsTaskController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
|
||||
|
||||
//试验任务服务类
|
||||
@Resource
|
||||
private ITsTaskService tsTaskService;
|
||||
@ -214,4 +221,125 @@ public class TsTaskController {
|
||||
List<TsTask> tsTasks = tsTaskService.listTsTask();
|
||||
return ResponseResult.successData(tsTasks);
|
||||
}
|
||||
|
||||
@Log(module = "试验数据管理", value = "导出试验任务SQL数据")
|
||||
@PostMapping("/exportTaskSql")
|
||||
@ApiOperation("导出试验任务SQL数据")
|
||||
@PreAuthorize("@el.check('export:tsTask')")
|
||||
public void exportTaskSql(@RequestParam String taskId, HttpServletResponse response) {
|
||||
if (StrUtil.isBlank(taskId)) {
|
||||
throw new RuntimeException("任务ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用服务层执行导出操作
|
||||
byte[] zipData = tsTaskService.exportTaskSqlAsZip(taskId);
|
||||
|
||||
// 添加日志记录ZIP数据大小
|
||||
LOGGER.info("生成的ZIP文件大小: {} 字节", zipData.length);
|
||||
|
||||
// 验证ZIP数据是否有效
|
||||
if (zipData.length == 0) {
|
||||
throw new RuntimeException("生成的ZIP数据为空");
|
||||
}
|
||||
|
||||
// 获取任务信息用于文件名
|
||||
TsTask task = tsTaskService.getById(taskId);
|
||||
String taskCode = "";
|
||||
|
||||
// 确保taskCode有值且符合5位数字格式
|
||||
if (task != null && StrUtil.isNotBlank(task.getTaskCode())) {
|
||||
taskCode = task.getTaskCode();
|
||||
// 验证taskCode格式是否正确(5位数字)
|
||||
if (!taskCode.matches("^\\d{5}$")) {
|
||||
// 如果不是5位数字格式,需要处理
|
||||
try {
|
||||
int codeValue = Integer.parseInt(taskCode);
|
||||
if (codeValue >= 1 && codeValue <= 99999) {
|
||||
// 格式化为5位数字字符串
|
||||
taskCode = String.format("%05d", codeValue);
|
||||
} else {
|
||||
throw new IllegalArgumentException("该任务编号超出有效范围: " + taskCode);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("无效的任务编号格式: " + taskCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置响应头信息
|
||||
String fileName = taskCode + "_exportdata.zip";
|
||||
response.setContentType("application/zip");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentLength(zipData.length);
|
||||
|
||||
// 写入响应流
|
||||
response.getOutputStream().write(zipData);
|
||||
response.getOutputStream().flush();
|
||||
|
||||
LOGGER.info("ZIP文件导出成功,文件名: {}, 大小: {} 字节", fileName, zipData.length);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("导出试验任务SQL数据失败", e);
|
||||
throw new RuntimeException("导出失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************
|
||||
* 用途说明: 导入ZIP文件中的试验任务SQL数据
|
||||
* 参数说明:
|
||||
* file ZIP文件
|
||||
* taskCode 任务编号
|
||||
* localStorageId 本地存储空间标识
|
||||
* backupStorageId 备份空间标识
|
||||
* 返回值说明: ResponseResult 返回导入结果
|
||||
***********************************/
|
||||
@Log(module = "试验数据管理", value = "导入试验任务SQL数据")
|
||||
@PostMapping("/importTaskSql")
|
||||
@ApiOperation("导入试验任务SQL数据")
|
||||
@PreAuthorize("@el.check('import:tsTask')")
|
||||
public ResponseResult importTaskSql(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam("taskCode") String taskCode,
|
||||
@RequestParam("localStorageId") int localStorageId,
|
||||
@RequestParam("backupStorageId") int backupStorageId) {
|
||||
if (file.isEmpty() || StrUtil.isBlank(taskCode)) {
|
||||
return ResponseResult.error("文件或任务编号不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用服务层执行导入操作
|
||||
boolean result = tsTaskService.importTaskSqlFromZip(file, taskCode, localStorageId, backupStorageId);
|
||||
|
||||
if (result) {
|
||||
return ResponseResult.success("导入成功");
|
||||
} else {
|
||||
return ResponseResult.error("导入失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//log.error("导入试验任务SQL数据失败", e);
|
||||
return ResponseResult.error("导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* 用途说明: 获取最大的任务编号+1
|
||||
* 参数说明: 无
|
||||
* 返回值说明: ResponseResult 返回最大任务编号+1
|
||||
***********************************/
|
||||
@Log(module = "试验数据管理", value = "获取最大任务编号")
|
||||
@GetMapping("/getMaxTaskCode")
|
||||
@ApiOperation("获取最大任务编号")
|
||||
// @PreAuthorize("@el.check('select:tsTask')")
|
||||
public ResponseResult getMaxTaskCode() {
|
||||
try {
|
||||
String maxTaskCode = tsTaskService.getMaxTaskCode();
|
||||
return ResponseResult.successData(maxTaskCode);
|
||||
} catch (Exception e) {
|
||||
//log.error("获取最大任务编号失败", e);
|
||||
return ResponseResult.error("获取最大任务编号失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package com.yfd.platform.modules.experimentalData.dto;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemReq {
|
||||
|
||||
private String type; // treatment | suggestion
|
||||
private String label; // 内容
|
||||
private List<String> labels; // 多个标签
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public List<String> getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
public void setLabels(List<String> labels) {
|
||||
this.labels = labels;
|
||||
}
|
||||
}
|
||||
@ -13,4 +13,10 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
*/
|
||||
public interface TsTaskMapper extends BaseMapper<TsTask> {
|
||||
|
||||
/**
|
||||
* 查询最大的任务编号
|
||||
* @return 最大任务编号
|
||||
*/
|
||||
String selectMaxTaskCode();
|
||||
|
||||
}
|
||||
|
||||
@ -264,4 +264,12 @@ public interface ITsFilesService extends IService<TsFiles> {
|
||||
* 返回值说明: com.yfd.platform.config.ResponseResult 返回文件集合列表
|
||||
***********************************/
|
||||
Object listTsFilesById(String id, String taskId, String nodeId);
|
||||
|
||||
/**
|
||||
* 根据任务ID获取文件列表
|
||||
* @param taskId 任务ID
|
||||
* @return 文件列表
|
||||
*/
|
||||
List<TsFiles> getByTaskId(String taskId);
|
||||
|
||||
}
|
||||
|
||||
@ -65,4 +65,12 @@ public interface ITsNodesService extends IService<TsNodes> {
|
||||
* 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败
|
||||
***********************************/
|
||||
Object confirmDeleteNodes(String id);
|
||||
|
||||
/**
|
||||
* 根据任务ID获取节点列表
|
||||
* @param taskId 任务ID
|
||||
* @return 节点列表
|
||||
*/
|
||||
List<TsNodes> getByTaskId(String taskId);
|
||||
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.yfd.platform.modules.experimentalData.service;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yfd.platform.modules.experimentalData.domain.TsTask;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@ -70,4 +71,27 @@ public interface ITsTaskService extends IService<TsTask> {
|
||||
|
||||
List<TsTask> listTsTask();
|
||||
|
||||
/**
|
||||
* 导出试验任务相关SQL数据为ZIP文件
|
||||
* @param taskId 任务ID
|
||||
* @return ZIP文件字节数组
|
||||
*/
|
||||
byte[] exportTaskSqlAsZip(String taskId) throws IOException;
|
||||
|
||||
/**
|
||||
* 从ZIP文件导入试验任务SQL数据
|
||||
* @param file ZIP文件
|
||||
* @param taskCode 任务编号
|
||||
* @param localStorageId 本地存储空间标识
|
||||
* @param backupStorageId 备份空间标识
|
||||
* @return 是否导入成功
|
||||
*/
|
||||
boolean importTaskSqlFromZip(MultipartFile file, String taskCode, int localStorageId, int backupStorageId) throws IOException;
|
||||
|
||||
/**
|
||||
* 获取最大任务编号+1
|
||||
* @return 最大任务编号+1
|
||||
*/
|
||||
String getMaxTaskCode();
|
||||
|
||||
}
|
||||
|
||||
@ -253,6 +253,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
FileItemResult fileItemResult = fileService.getFileItem(path);
|
||||
if (fileItemResult == null || fileItemResult.getName() == null) {
|
||||
LOGGER.error("{}文件没有上传到工作空间,请重新选择上传", fileNameData);
|
||||
// 跳过处理这个文件,避免空指针异常
|
||||
continue;
|
||||
}
|
||||
tsFiles.setUrl(fileItemResult.getUrl());
|
||||
//如果是压缩文件 类型就给zip
|
||||
@ -260,7 +262,12 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
if (isValid) {
|
||||
tsFiles.setType("ZIP");
|
||||
} else {
|
||||
// 添加空值检查,防止空指针异常
|
||||
if (fileItemResult.getType() != null) {
|
||||
tsFiles.setType(fileItemResult.getType().getValue());
|
||||
} else {
|
||||
tsFiles.setType("UNKNOWN");
|
||||
}
|
||||
}
|
||||
if (tsFiles.getUpdateTime() == null) {
|
||||
tsFiles.setUpdateTime(tsFiles.getUploadTime());
|
||||
@ -6275,6 +6282,12 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
return String.format("%s,%s,%s,%s", parts[0], parts[1], parts[2], parts[3]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TsFiles> getByTaskId(String taskId) {
|
||||
LambdaQueryWrapper<TsFiles> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TsFiles::getTaskId, taskId);
|
||||
return this.list(queryWrapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ import com.yfd.platform.system.domain.LoginUser;
|
||||
import com.yfd.platform.utils.StringUtils;
|
||||
import com.yfd.platform.utils.TableNameContextHolder;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import lombok.val;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
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 int BATCH_SIZE = 5000;
|
||||
|
||||
//试验任务节点表 Mapper
|
||||
@Resource
|
||||
private TsNodesMapper tsNodesMapper;
|
||||
@ -109,52 +113,177 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
|
||||
@Override
|
||||
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);
|
||||
|
||||
// 查找所有根节点(parentId为"00"的节点)
|
||||
List<Map<String, Object>> rootNodes = findRootNodes(allNodes, taskId);
|
||||
// 如果taskId为空,根据nodeName直接过滤
|
||||
if (StringUtils.isEmpty(taskId)) {
|
||||
if (StringUtils.isEmpty(nodeName)) {
|
||||
// 返回所有节点(需要按任务分组构建树)
|
||||
// return buildAllTaskTrees(allNodes);
|
||||
return result;
|
||||
} else {
|
||||
// 根据nodeName查询所有匹配节点,构建到根节点的路径
|
||||
return findNodesByName(allNodes, nodeName);
|
||||
}
|
||||
} else {
|
||||
// 原有逻辑:taskId不为空时按任务查询
|
||||
TsTask tsTask = tsTaskMapper.selectOne(new QueryWrapper<TsTask>().eq("id", taskId));
|
||||
if (tsTask == null) {
|
||||
throw new RuntimeException("任务不存在");
|
||||
}
|
||||
|
||||
// 根节点的基本路径:/项目名称/
|
||||
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("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);
|
||||
|
||||
// 如果 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()) {
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据节点名称查找所有匹配节点并构建树
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找节点到根节点的路径
|
||||
*/
|
||||
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;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 查询所有节点数据
|
||||
@ -682,6 +811,9 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
} else {
|
||||
deleteFailCount++;
|
||||
}
|
||||
}catch (NullPointerException e) {
|
||||
LOGGER.error("删除节点时发生空指针异常,文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
|
||||
deleteFailCount++;
|
||||
}catch (Exception e) {
|
||||
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
|
||||
deleteFailCount++;
|
||||
@ -723,6 +855,7 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
taskStatusHolder.finishTask(asyncKey);
|
||||
WebSocketServer.sendMessageTo("试验数据扫描接口完成", "taskId_" + id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**********************************
|
||||
@ -786,6 +919,15 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
return "扫描完成";
|
||||
} catch (Exception 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();
|
||||
} finally {
|
||||
TableNameContextHolder.clear();
|
||||
@ -975,12 +1117,18 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
// 批量插入文件表(忽略重复)
|
||||
if (!tsFilesToCreate.isEmpty()) {
|
||||
long startBatchInsertFiles = System.currentTimeMillis();
|
||||
int affectedRowsFiles = tsFilesMapper.batchInsertTsFiles(tsFilesToCreate);
|
||||
LOGGER.info("[批量插入试验任务文件表] 耗时 {} ms | 实际新增数量: {} 条",
|
||||
System.currentTimeMillis() - startBatchInsertFiles,
|
||||
affectedRowsFiles);
|
||||
//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 | 实际新增数量: {} 条",
|
||||
System.currentTimeMillis() - startBatchInsertFiles,
|
||||
affected);
|
||||
}
|
||||
// 记录开始时间
|
||||
long startTimeFiles = System.currentTimeMillis();
|
||||
// 执行更新操作 taskId, String nodeId
|
||||
@ -993,6 +1141,14 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
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/")
|
||||
* 若路径为空或非法,返回根路径 "/"
|
||||
@ -1132,4 +1288,12 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
return storageSourceMapper.selectOne(new LambdaQueryWrapper<StorageSource>().eq(StorageSource::getId, id)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TsNodes> getByTaskId(String taskId) {
|
||||
LambdaQueryWrapper<TsNodes> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TsNodes::getTaskId, taskId);
|
||||
return this.list(queryWrapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@ -36,11 +37,21 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
@ -48,7 +59,13 @@ import java.util.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.DataSource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -93,6 +110,9 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
|
||||
private SysDictionaryItemsMapper sysDictionaryItemsMapper;
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
|
||||
private static final String INITIAL_CODE = "00001";
|
||||
private static final int MAX_CODE_VALUE = 99999;
|
||||
@ -532,41 +552,95 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
|
||||
public boolean deleteTstaskByIds(List<String> dataset) {
|
||||
Boolean value = false;
|
||||
|
||||
LOGGER.info("开始批量删除试验任务,任务数量: {}", dataset.size());
|
||||
LOGGER.debug("待删除的任务ID列表: {}", dataset);
|
||||
|
||||
try {
|
||||
|
||||
|
||||
//循环所有的ID
|
||||
for (String taskId : dataset) {
|
||||
//删除项目
|
||||
try {
|
||||
LOGGER.info("开始处理任务删除,任务ID: {}", taskId);
|
||||
|
||||
//获取试验任务及任务编码
|
||||
TsTask tsTask = tsTaskMapper.selectById(taskId);
|
||||
if (tsTask == null) {
|
||||
LOGGER.warn("未找到指定的任务,任务ID: {}", taskId);
|
||||
continue;
|
||||
}
|
||||
|
||||
TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
|
||||
LOGGER.debug("设置任务编码上下文,任务编码: {}", tsTask.getTaskCode());
|
||||
|
||||
// 删除节点表
|
||||
LambdaQueryWrapper<TsNodes> deleteWrapper = new LambdaQueryWrapper<>();
|
||||
deleteWrapper.eq(TsNodes::getTaskId, taskId);
|
||||
tsNodesMapper.delete(deleteWrapper);
|
||||
int deletedNodes = tsNodesMapper.delete(deleteWrapper);
|
||||
LOGGER.info("删除任务相关的节点数据,任务ID: {}, 删除条数: {}", taskId, deletedNodes);
|
||||
|
||||
// 只有当任务文件表存在时才尝试删除文件数据
|
||||
if (tableExists("ts_files_" + tsTask.getTaskCode())) {
|
||||
// 删除文件表
|
||||
LambdaQueryWrapper<TsFiles> deleteWrapperFiles = new LambdaQueryWrapper<>();
|
||||
deleteWrapperFiles.eq(TsFiles::getTaskId, taskId);
|
||||
tsFilesMapper.delete(deleteWrapperFiles);
|
||||
//todo 删除文件表数据
|
||||
// tsFilesMapper.deleteSdFilesBytaskId(taskId);
|
||||
int deletedFiles = tsFilesMapper.delete(deleteWrapperFiles);
|
||||
LOGGER.info("删除任务相关的文件数据,任务ID: {}, 删除条数: {}", taskId, deletedFiles);
|
||||
} else {
|
||||
LOGGER.info("任务文件表不存在,跳过文件数据删除,任务ID: {}", taskId);
|
||||
}
|
||||
|
||||
// 删除本地存储空间中的文件夹
|
||||
StorageSource localStorageSource = getStorageConfig(tsTask.getLocalStorageId());
|
||||
LOGGER.info("开始删除本地存储空间中的文件夹,任务ID: {}, 本地存储ID: {}", taskId, tsTask.getLocalStorageId());
|
||||
deleteStorageFolder(localStorageSource, tsTask);
|
||||
|
||||
// 如果有备份存储空间,也删除备份存储空间中的文件夹
|
||||
if (tsTask.getBackupStorageId() != null && tsTask.getBackupStorageId() > 0) {
|
||||
StorageSource backupStorageSource = getStorageConfig(tsTask.getBackupStorageId());
|
||||
LOGGER.info("开始删除备份存储空间中的文件夹,任务ID: {}, 备份存储ID: {}", taskId, tsTask.getBackupStorageId());
|
||||
deleteStorageFolder(backupStorageSource, tsTask);
|
||||
}
|
||||
|
||||
// 删除当前试验任务
|
||||
int deleteCount = tsTaskMapper.deleteById(taskId);
|
||||
if (deleteCount == 1) {
|
||||
LOGGER.info("试验任务删除成功,任务ID: {}", taskId);
|
||||
value = true;
|
||||
} else {
|
||||
LOGGER.error("试验任务删除失败,任务ID: {}, 实际删除条数: {}", taskId, deleteCount);
|
||||
value = false;
|
||||
}
|
||||
} finally {
|
||||
// 每次处理完一个任务后清理上下文
|
||||
LOGGER.debug("清理任务上下文,任务ID: {}", taskId);
|
||||
TableNameContextHolder.clear();
|
||||
}
|
||||
}
|
||||
LOGGER.info("批量删除试验任务完成,任务数量: {}", dataset.size());
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("删除试验任务时发生异常,任务ID列表: {}", dataset, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// String path = "/" + tsTask.getTaskName() + "/";
|
||||
// //调用删除节点 根据任务ID
|
||||
// Boolean deleteTsnodes = tsNodesService.deleteTsNodesByTaskId(taskId, path);
|
||||
// //如果删除成功 接着删除节点表数据
|
||||
// if (deleteTsnodes) {
|
||||
// LOGGER.info("tsNodes表结删除改成功");
|
||||
// value = true;
|
||||
// } else {
|
||||
// LOGGER.error("tsNodes表结构删除失败");
|
||||
// value = false;
|
||||
// }
|
||||
// 这个方法会递归删除试验任务文件夹及其下的所有子文件夹和文件。当调用 fileService.deleteFolder() 方法时,存储服务实现会自动处理递归删除逻辑,确保整个文件夹树都被清除。
|
||||
//这是文件存储系统的基本特性,不论是本地文件系统还是MinIO等云存储服务,在删除文件夹时都会删除其包含的所有内容。
|
||||
private void deleteStorageFolder(StorageSource storageSource, TsTask tsTask) {
|
||||
LOGGER.info("==deleteStorageFolder begin====");
|
||||
if (storageSource == null) {
|
||||
LOGGER.warn("存储源配置为空,跳过删除操作,任务名称: {}", tsTask.getTaskName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (tsTask == null || tsTask.getTaskName() == null) {
|
||||
LOGGER.warn("任务信息不完整,跳过删除操作");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
LOGGER.info("准备删除存储文件夹,存储类型: {}, 任务名称: {}", storageSource.getKey(), tsTask.getTaskName());
|
||||
|
||||
StorageSource storageSource = getStorageConfig(tsTask.getLocalStorageId());
|
||||
// 删除 local 中的文件夹 项目文件夹
|
||||
List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
|
||||
BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
|
||||
deleteItemData.setName(tsTask.getTaskName());
|
||||
@ -578,53 +652,51 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
|
||||
BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
|
||||
batchDeleteRequest.setDeleteItems(deleteItemList);
|
||||
batchDeleteRequest.setStorageKey(storageSource.getKey());
|
||||
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
|
||||
int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItemList);
|
||||
|
||||
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
|
||||
if (fileService == null) {
|
||||
LOGGER.error("未能获取到文件服务实例,存储类型: {}", storageSource.getKey());
|
||||
return;
|
||||
}
|
||||
|
||||
int deleteSuccessCount = 0;
|
||||
for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
|
||||
boolean flag = false;
|
||||
try {
|
||||
if (deleteItem.getType() == FileTypeEnum.FILE) {
|
||||
LOGGER.info("删除文件,存储类型: {}, 路径: {}, 名称: {}",
|
||||
storageSource.getKey(), deleteItem.getPath(), deleteItem.getName());
|
||||
flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
|
||||
} else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
|
||||
LOGGER.info("删除文件夹,存储类型: {}, 路径: {}, 名称: {}",
|
||||
storageSource.getKey(), deleteItem.getPath(), deleteItem.getName());
|
||||
flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
|
||||
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
deleteSuccessCount++;
|
||||
LOGGER.info("删除成功 - 存储类型: {}, 类型: {}, 路径: {}, 名称: {}",
|
||||
storageSource.getKey(), deleteItem.getType(), deleteItem.getPath(), deleteItem.getName());
|
||||
} else {
|
||||
deleteFailCount++;
|
||||
LOGGER.warn("删除失败 - 存储类型: {}, 类型: {}, 路径: {}, 名称: {}",
|
||||
storageSource.getKey(), deleteItem.getType(), deleteItem.getPath(), deleteItem.getName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
|
||||
deleteFailCount++;
|
||||
LOGGER.error("删除文件/文件夹失败, 存储类型: {}, 文件路径: {}, 文件名称: {}",
|
||||
storageSource.getKey(), deleteItem.getPath(), deleteItem.getName(), e);
|
||||
// 继续处理其他项目,不中断整个删除过程
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteSuccessCount >= 1) {
|
||||
// 删除当前项目
|
||||
int deleteCount = tsTaskMapper.deleteById(taskId);
|
||||
if (deleteCount == 1) {
|
||||
LOGGER.info("tstask表结删除改成功");
|
||||
value = true;
|
||||
} else {
|
||||
LOGGER.error("tstask表结构删除失败");
|
||||
value = false;
|
||||
}
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return value;
|
||||
LOGGER.info("存储文件夹删除完成,存储类型: {}, 成功删除项数量: {}", storageSource.getKey(), deleteSuccessCount);
|
||||
} catch (Exception e) {
|
||||
|
||||
} finally {
|
||||
TableNameContextHolder.clear();
|
||||
LOGGER.error("删除存储空间文件夹时发生异常, 存储类型: {}", storageSource.getKey(), e);
|
||||
} catch (Error err) {
|
||||
// 捕获Error防止JVM退出
|
||||
LOGGER.error("删除存储空间文件夹时发生严重错误, 存储类型: {}", storageSource.getKey(), err);
|
||||
// 不重新抛出Error,防止Tomcat退出
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**********************************
|
||||
@ -643,13 +715,20 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
|
||||
boolean allDeletable = true;
|
||||
|
||||
for (TsTask tsTask : tsTasks) {
|
||||
// 检查对应的任务文件表是否存在
|
||||
if (!tableExists("ts_files_" + tsTask.getTaskCode())) {
|
||||
// 如果表不存在,可以安全删除
|
||||
continue;
|
||||
}
|
||||
|
||||
TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
|
||||
|
||||
// 查询是否有备份路径或非空路径的文件
|
||||
try {
|
||||
// 查询 backup_path 既不为 null 也不为空字符串的记录
|
||||
LambdaQueryWrapper<TsFiles> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TsFiles::getTaskId, tsTask.getId())
|
||||
.and(wrapper -> wrapper.isNotNull(TsFiles::getBackupPath)
|
||||
.or().ne(TsFiles::getBackupPath, ""));
|
||||
.isNotNull(TsFiles::getBackupPath)
|
||||
.ne(TsFiles::getBackupPath, "");
|
||||
|
||||
int count = tsFilesService.count(queryWrapper);
|
||||
|
||||
@ -657,6 +736,9 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
|
||||
allDeletable = false;
|
||||
break; // 一旦发现不可删除项,提前终止循环
|
||||
}
|
||||
} finally {
|
||||
TableNameContextHolder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
result.putOpt("status", allDeletable ? "1" : "0");
|
||||
@ -666,8 +748,6 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
|
||||
// 使用日志框架代替 printStackTrace
|
||||
LOGGER.error("确认删除实验任务时发生异常", e);
|
||||
return ResponseResult.error("查询失败");
|
||||
} finally {
|
||||
TableNameContextHolder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -719,4 +799,608 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
|
||||
return storageSourceMapper.selectOne(new LambdaQueryWrapper<StorageSource>().eq(StorageSource::getId, id)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportTaskSqlAsZip(String taskId) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try(ZipOutputStream zos = new ZipOutputStream(baos)) {
|
||||
// 生成SQL内容
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
|
||||
// 1. 添加ts_task表数据导出
|
||||
TsTask task = this.getById(taskId);
|
||||
String taskCode = "";
|
||||
|
||||
// 确保taskCode有值且符合5位数字格式
|
||||
if (task != null && StrUtil.isNotBlank(task.getTaskCode())) {
|
||||
taskCode = task.getTaskCode();
|
||||
// 验证taskCode格式是否正确(5位数字)
|
||||
if (!taskCode.matches("^\\d{5}$")) {
|
||||
// 如果不是5位数字格式,需要处理
|
||||
try {
|
||||
int codeValue = Integer.parseInt(taskCode);
|
||||
if (codeValue >= 1 && codeValue <= 99999) {
|
||||
// 格式化为5位数字字符串
|
||||
taskCode = String.format("%05d", codeValue);
|
||||
} else {
|
||||
throw new IllegalArgumentException("该任务编号超出有效范围: " + taskCode);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("无效的任务编号格式: " + taskCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (task != null) {
|
||||
sqlBuilder.append("-- 试验任务数据\n");
|
||||
sqlBuilder.append(generateInsertSql("ts_task", task)).append("\n\n");
|
||||
}
|
||||
|
||||
//2. 添加ts_nodes表相关记录导出(需要注入相应的service)
|
||||
List<TsNodes> nodes = tsNodesService.getByTaskId(taskId);
|
||||
if (nodes != null && !nodes.isEmpty()) {
|
||||
sqlBuilder.append("-- 节点数据\n");
|
||||
for (TsNodes node : nodes) {
|
||||
sqlBuilder.append(generateInsertSql("ts_nodes", node)).append("\n");
|
||||
}
|
||||
sqlBuilder.append("\n");
|
||||
}
|
||||
|
||||
// 3. 添加ts_files表结构和数据导出
|
||||
if (task != null) {
|
||||
String dynamicTableName = "ts_files_" + task.getTaskCode();
|
||||
// 设置动态表名上下文
|
||||
TableNameContextHolder.setTaskCode(task.getTaskCode());
|
||||
|
||||
try {
|
||||
// 查询该任务的所有文件数据
|
||||
List<TsFiles> files = tsFilesService.getByTaskId(taskId);
|
||||
if (files != null && !files.isEmpty()) {
|
||||
sqlBuilder.append("-- 文件表结构\n");
|
||||
sqlBuilder.append(generateCreateTableSql(dynamicTableName)).append("\n\n");
|
||||
|
||||
sqlBuilder.append("-- 文件数据\n");
|
||||
for (TsFiles file : files) {
|
||||
sqlBuilder.append(generateInsertSql(dynamicTableName, file)).append("\n");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// 清理表名上下文
|
||||
TableNameContextHolder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// 将SQL内容写入ZIP文件
|
||||
ZipEntry sqlEntry = new ZipEntry(taskCode+"_exportdata.sql");
|
||||
zos.putNextEntry(sqlEntry);
|
||||
zos.write(sqlBuilder.toString().getBytes(StandardCharsets.UTF_8));
|
||||
zos.closeEntry();
|
||||
|
||||
zos.finish();
|
||||
// zos.flush();
|
||||
|
||||
byte[] result = baos.toByteArray();
|
||||
LOGGER.info("生成ZIP数据大小: {} 字节", result.length);
|
||||
return result;
|
||||
} finally {
|
||||
//zos.close();
|
||||
baos.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 生成INSERT SQL语句的方法
|
||||
private String generateInsertSql(String tableName, Object entity) {
|
||||
if (entity == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> clazz = entity.getClass();
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
|
||||
StringBuilder columns = new StringBuilder();
|
||||
StringBuilder values = new StringBuilder();
|
||||
|
||||
for (Field field : fields) {
|
||||
// 跳过序列化相关字段
|
||||
if (field.getName().equals("serialVersionUID")) {
|
||||
continue;
|
||||
}
|
||||
// 跳过非数据库字段(可以根据实际需要调整条件)
|
||||
if (field.getName().equals("key")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 跳过 ts_nodes 表中不存在的字段
|
||||
if ("ts_nodes".equals(tableName) && "path".equals(field.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 跳过 ts_files 相关表中不存在的字段
|
||||
if (tableName.startsWith("ts_files_") &&
|
||||
("url".equals(field.getName()) ||
|
||||
"type".equals(field.getName()) ||
|
||||
"localOnlyFiles".equals(field.getName()) ||
|
||||
"minioOnlyFiles".equals(field.getName()) ||
|
||||
"md5mismatchedFiles".equals(field.getName()) ||
|
||||
"locatMd5".equals(field.getName()) ||
|
||||
"minioMd5".equals(field.getName()) ||
|
||||
"fileContent".equals(field.getName()) ||
|
||||
"md5MismatchedFiles".equals(field.getName()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
field.setAccessible(true);
|
||||
String columnName = camelToUnderscore(field.getName());
|
||||
Object value = field.get(entity);
|
||||
|
||||
columns.append(columnName).append(",");
|
||||
values.append(formatValue(value)).append(",");
|
||||
}
|
||||
|
||||
// 移除末尾逗号
|
||||
if (columns.length() > 0) {
|
||||
columns.deleteCharAt(columns.length() - 1);
|
||||
values.deleteCharAt(values.length() - 1);
|
||||
}
|
||||
|
||||
return "INSERT INTO " + tableName + " (" + columns + ") VALUES (" + values + ");";
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("生成INSERT SQL语句失败", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// 驼峰命名转下划线
|
||||
private String camelToUnderscore(String camelCase) {
|
||||
return camelCase.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
|
||||
}
|
||||
|
||||
// 格式化值
|
||||
private String formatValue(Object value) {
|
||||
if (value == null) {
|
||||
return "NULL";
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return "'" + value.toString().replace("'", "''") + "'";
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return "'" + sdf.format(value) + "'";
|
||||
}
|
||||
// 处理 java.time 类型
|
||||
if (value instanceof java.time.LocalDate) {
|
||||
return "'" + value.toString() + "'";
|
||||
}
|
||||
if (value instanceof java.time.LocalDateTime) {
|
||||
return "'" + ((java.time.LocalDateTime) value).format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "'";
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 生成CREATE TABLE SQL语句的方法
|
||||
private String generateCreateTableSql(String tableName) {
|
||||
// 使用 LIKE 语句创建与 ts_files 结构相同的新表
|
||||
return "CREATE TABLE IF NOT EXISTS `" + tableName + "` LIKE `ts_files`;";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ZIP文件中导入任务SQL数据
|
||||
*
|
||||
* @param file ZIP文件,包含SQL文件
|
||||
* @param taskCode 目标任务编号,用于替换SQL中的原始任务编号
|
||||
* @param localStorageId 本地存储ID
|
||||
* @param backupStorageId 备份存储ID
|
||||
* @return 导入成功返回true,失败返回false
|
||||
* @throws IOException 文件读取或解压过程中发生IO异常
|
||||
*/
|
||||
@Override
|
||||
public boolean importTaskSqlFromZip(MultipartFile file, String taskCode, int localStorageId, int backupStorageId) throws IOException {
|
||||
|
||||
// 1. 解压ZIP文件
|
||||
byte[] zipBytes = file.getBytes();
|
||||
//根据文件名称获取原始任务编号:00002_data.sql.zip
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String originalTaskCode = extractTaskCodeFromFileName(originalFilename);
|
||||
List<SqlFileContent> sqlFiles = extractSqlFromZip(zipBytes);
|
||||
|
||||
// 2. 替换 SQL 文件中的任务编号,存储空间编号。
|
||||
List<String> allSqlStatements = new ArrayList<>();
|
||||
for (SqlFileContent sqlFile : sqlFiles) {
|
||||
LOGGER.debug("原始SQL内容: {}", sqlFile.getContent().substring(0, Math.min(200, sqlFile.getContent().length())));
|
||||
String modifiedContent = replaceTaskCodeInSql(sqlFile.getContent(), originalTaskCode, taskCode, localStorageId, backupStorageId);
|
||||
LOGGER.debug("替换后SQL内容: {}", modifiedContent.substring(0, Math.min(200, modifiedContent.length())));
|
||||
allSqlStatements.addAll(parseSqlStatements(modifiedContent));
|
||||
}
|
||||
|
||||
// 3. 拆分 DDL 与 INSERT 语句,合并相同表的 INSERT,提高执行效率
|
||||
List<String> ddlSqlList = new ArrayList<>();
|
||||
List<String> dmlSqlList = new ArrayList<>();
|
||||
|
||||
for (String sql : allSqlStatements) {
|
||||
String upper = sql.trim().toUpperCase();
|
||||
if (upper.startsWith("CREATE") || upper.startsWith("DROP") || upper.startsWith("ALTER") || upper.startsWith("SET")) {
|
||||
ddlSqlList.add(sql);
|
||||
} else if (upper.startsWith("INSERT")) {
|
||||
dmlSqlList.add(sql);
|
||||
}
|
||||
}
|
||||
|
||||
// 先执行 DDL
|
||||
LOGGER.info("开始执行DDL语句,共 {} 条", ddlSqlList.size());
|
||||
executeSqlImport(ddlSqlList);
|
||||
|
||||
// 再合并并执行 DML
|
||||
List<String> mergedSqlStatements = mergeInsertStatements(dmlSqlList);
|
||||
LOGGER.info("开始导入数据,共 {} 条INSERT语句,合并后 {} 条", dmlSqlList.size(), mergedSqlStatements.size());
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 4. 执行 SQL 批量导入
|
||||
boolean result = executeSqlImport(mergedSqlStatements);
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
LOGGER.info("SQL导入完成,耗时 {} ms", (endTime - startTime));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件名中提取原始任务编号
|
||||
* @param fileName 文件名,如 "00002_data.sql.zip"
|
||||
* @return 任务编号,如 "00002"
|
||||
*/
|
||||
private String extractTaskCodeFromFileName(String fileName) {
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用正则表达式匹配5位数字开头的文件名
|
||||
Pattern pattern = Pattern.compile("^(\\d{5})_.*$");
|
||||
Matcher matcher = pattern.matcher(fileName);
|
||||
|
||||
if (matcher.matches()) {
|
||||
String taskCode = matcher.group(1);
|
||||
// 验证任务编号范围是否在00001-99999之间
|
||||
try {
|
||||
int codeValue = Integer.parseInt(taskCode);
|
||||
if (codeValue >= 1 && codeValue <= 99999) {
|
||||
return taskCode;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// 解析失败,返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 解压ZIP文件并提取SQL内容
|
||||
private List<SqlFileContent> extractSqlFromZip(byte[] zipBytes) throws IOException {
|
||||
List<SqlFileContent> sqlFiles = new ArrayList<>();
|
||||
try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
if (!entry.isDirectory() && entry.getName().endsWith(".sql")) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
while ((len = zis.read(buffer)) > 0) {
|
||||
baos.write(buffer, 0, len);
|
||||
}
|
||||
// 修改这一行,使用 Charset 的名称而不是 Charset 对象
|
||||
sqlFiles.add(new SqlFileContent(entry.getName(), baos.toString(StandardCharsets.UTF_8.name())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqlFiles;
|
||||
}
|
||||
|
||||
private String replaceTaskCodeInSql(String sqlContent, String originalTaskCode, String newTaskCode, int localStorageId, int backupStorageId) {
|
||||
if (StrUtil.isBlank(originalTaskCode)) {
|
||||
return sqlContent;
|
||||
}
|
||||
|
||||
// 同时替换 ts_task 表中的 task_code 值和存储空间ID值
|
||||
Pattern taskPattern = Pattern.compile(
|
||||
"(INSERT\\s+INTO\\s+`?ts_task`?\\s*\\([^)]*?task_code[^)]*?\\)\\s*VALUES\\s*\\([^)]*?)('"+Pattern.quote(originalTaskCode)+"')([^)]*)(?<!\\\\)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*\\)",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
Matcher taskMatcher = taskPattern.matcher(sqlContent);
|
||||
StringBuffer taskResult = new StringBuffer();
|
||||
|
||||
while (taskMatcher.find()) {
|
||||
String prefix = taskMatcher.group(1);
|
||||
String suffix = taskMatcher.group(3);
|
||||
String replacement = prefix + "'" + newTaskCode + "'" + suffix + "," + localStorageId + "," + backupStorageId + ")";
|
||||
taskMatcher.appendReplacement(taskResult, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
taskMatcher.appendTail(taskResult);
|
||||
|
||||
String result = taskResult.toString();
|
||||
|
||||
// 替换 ts_files_* 表名
|
||||
Pattern tableNamePattern = Pattern.compile(
|
||||
"INSERT\\s+INTO\\s+`?(ts_files_" + Pattern.quote(originalTaskCode) + ")`?",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
Matcher tableNameMatcher = tableNamePattern.matcher(result);
|
||||
StringBuffer tableNameResult = new StringBuffer();
|
||||
|
||||
while (tableNameMatcher.find()) {
|
||||
String replacement = "INSERT INTO `ts_files_" + newTaskCode + "`";
|
||||
tableNameMatcher.appendReplacement(tableNameResult, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
tableNameMatcher.appendTail(tableNameResult);
|
||||
|
||||
return tableNameResult.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// private String replaceTaskCodeInSql(String sqlContent, String originalTaskCode, String newTaskCode, int localStorageId, int backupStorageId) {
|
||||
// if (StrUtil.isBlank(originalTaskCode)) {
|
||||
// return sqlContent;
|
||||
// }
|
||||
//
|
||||
// // 1. 任务编号替换 - 使用精确匹配
|
||||
// sqlContent = sqlContent.replace("'" + originalTaskCode + "'", "'" + newTaskCode + "'");
|
||||
//
|
||||
// // 2. 表名替换
|
||||
// sqlContent = sqlContent.replace("ts_files_" + originalTaskCode, "ts_files_" + newTaskCode);
|
||||
//
|
||||
// // 3. 存储空间ID替换 - 针对INSERT语句进行精确替换
|
||||
// // 使用更复杂的正则表达式来匹配完整的INSERT语句并替换对应的值
|
||||
// sqlContent = replaceStorageIdsInInsertStatement(sqlContent, localStorageId, backupStorageId);
|
||||
//
|
||||
// return sqlContent;
|
||||
// }
|
||||
|
||||
private String replaceStorageIdsInInsertStatement(String sqlContent, int localStorageId, int backupStorageId) {
|
||||
// 只匹配 ts_task 表的 INSERT 语句,并替换最后两个字段值
|
||||
Pattern insertPattern = Pattern.compile(
|
||||
"(INSERT\\s+INTO\\s+`?ts_task`?\\s*\\([^)]*\\)\\s*VALUES\\s*\\([^)]*)(?<!\\\\)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*\\)",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
Matcher matcher = insertPattern.matcher(sqlContent);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String prefix = matcher.group(1);
|
||||
// 使用新的存储空间ID替换原有的值
|
||||
String modifiedInsert = prefix + "," + localStorageId + "," + backupStorageId + ")";
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(modifiedInsert));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
// 解析SQL语句
|
||||
private List<String> parseSqlStatements(String sqlContent) {
|
||||
List<String> sqlList = new ArrayList<>();
|
||||
String[] lines = sqlContent.split("\n");
|
||||
StringBuilder currentStatement = new StringBuilder();
|
||||
LOGGER.debug("开始解析SQL内容,总行数: {}", lines.length);
|
||||
for (String line : lines) {
|
||||
String trimmedLine = line.trim();
|
||||
|
||||
// 跳过纯注释行
|
||||
if (trimmedLine.startsWith("--")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 跳过空行
|
||||
if (trimmedLine.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 添加当前行到语句构建器
|
||||
currentStatement.append(trimmedLine).append(" ");
|
||||
|
||||
// 如果当前行以分号结尾,说明是一个完整语句
|
||||
if (trimmedLine.endsWith(";")) {
|
||||
String statement = currentStatement.toString().trim();
|
||||
if (!statement.isEmpty()) {
|
||||
sqlList.add(statement);
|
||||
LOGGER.debug("解析到SQL语句: {}", statement.length() > 200 ? statement.substring(0, 200) + "..." : statement);
|
||||
|
||||
}
|
||||
currentStatement.setLength(0); // 重置构建器
|
||||
}
|
||||
}
|
||||
|
||||
// 处理可能残留的未完成语句(虽然理论上不应该有)
|
||||
String remaining = currentStatement.toString().trim();
|
||||
if (!remaining.isEmpty() && remaining.endsWith(";")) {
|
||||
sqlList.add(remaining);
|
||||
LOGGER.debug("解析到残留SQL语句: {}", remaining.length() > 200 ? remaining.substring(0, 200) + "..." : remaining);
|
||||
|
||||
}
|
||||
|
||||
return sqlList;
|
||||
}
|
||||
|
||||
|
||||
// =========================== 高性能导入部分 ===========================
|
||||
// 批量执行: 使用batchExecute方法进行批量处理
|
||||
// INSERT语句合并: 通过mergeInsertStatements方法将多个INSERT语句合并为批量插入
|
||||
// 事务优化: 导入过程中关闭了外键检查和唯一性检查
|
||||
// 禁用自动提交: 使用SET AUTOCOMMIT=0来提高性能
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean executeSqlImport(List<String> sqlStatements) {
|
||||
int total = 0; // 在方法开始处声明 total 变量
|
||||
try {
|
||||
jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS=0;");
|
||||
jdbcTemplate.execute("SET UNIQUE_CHECKS=0;");
|
||||
jdbcTemplate.execute("SET AUTOCOMMIT=0;");
|
||||
|
||||
int batchSize = 2000;
|
||||
List<String> batch = new ArrayList<>(batchSize);
|
||||
|
||||
for (String sql : sqlStatements) {
|
||||
if (StringUtils.isBlank(sql)) continue;
|
||||
batch.add(sql);
|
||||
if (batch.size() >= batchSize) {
|
||||
batchExecute(batch);
|
||||
total += batch.size();
|
||||
LOGGER.info("已执行 {} 条SQL语句", total);
|
||||
batch.clear();
|
||||
}
|
||||
}
|
||||
if (!batch.isEmpty()) {
|
||||
batchExecute(batch);
|
||||
total += batch.size();
|
||||
}
|
||||
|
||||
jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS=1;");
|
||||
jdbcTemplate.execute("SET UNIQUE_CHECKS=1;");
|
||||
jdbcTemplate.execute("COMMIT;");
|
||||
|
||||
LOGGER.info("批量导入完成,共执行 {} 条SQL", total);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("SQL导入失败,已执行 {} 条SQL", total, e);
|
||||
try {
|
||||
jdbcTemplate.execute("ROLLBACK;");
|
||||
} catch (Exception rollbackEx) {
|
||||
LOGGER.error("回滚事务失败", rollbackEx);
|
||||
}
|
||||
throw new RuntimeException("SQL导入失败:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void batchExecute(List<String> sqlList) {
|
||||
try {
|
||||
jdbcTemplate.batchUpdate(sqlList.toArray(new String[0]));
|
||||
} catch (DataAccessException e) {
|
||||
LOGGER.warn("批量执行失败,分步重试:{}", e.getMessage());
|
||||
for (String sql : sqlList) {
|
||||
try {
|
||||
jdbcTemplate.update(sql);
|
||||
} catch (Exception ex) {
|
||||
LOGGER.warn("跳过失败SQL:{}", ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================== INSERT 合并优化 ===========================
|
||||
private List<String> mergeInsertStatements(List<String> sqlStatements) {
|
||||
Map<String, List<String>> groupedStatements = new LinkedHashMap<>();
|
||||
|
||||
Pattern pattern = Pattern.compile(
|
||||
"INSERT\\s+INTO\\s+`?(\\w+)`?\\s*\\(([^)]+)\\)\\s*VALUES\\s*\\((.*)\\)",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
for (String sql : sqlStatements) {
|
||||
Matcher matcher = pattern.matcher(sql);
|
||||
if (matcher.find()) {
|
||||
String table = matcher.group(1);
|
||||
String columns = matcher.group(2);
|
||||
String values = matcher.group(3);
|
||||
|
||||
// 使用表名+列名作为分组键
|
||||
String key = table + ":" + columns;
|
||||
|
||||
groupedStatements.computeIfAbsent(key, k -> new ArrayList<>()).add(values);
|
||||
} else {
|
||||
// 无法匹配的语句单独处理
|
||||
groupedStatements.computeIfAbsent("_others", k -> new ArrayList<>()).add(sql);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> mergedSql = new ArrayList<>();
|
||||
for (Map.Entry<String, List<String>> entry : groupedStatements.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
List<String> valuesList = entry.getValue();
|
||||
|
||||
if ("_others".equals(key)) {
|
||||
mergedSql.addAll(valuesList);
|
||||
} else {
|
||||
String[] parts = key.split(":", 2);
|
||||
String table = parts[0];
|
||||
String columns = parts[1];
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("INSERT INTO `").append(table).append("` (").append(columns).append(") VALUES ");
|
||||
|
||||
for (int i = 0; i < valuesList.size(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("(").append(valuesList.get(i)).append(")");
|
||||
}
|
||||
sb.append(";");
|
||||
|
||||
mergedSql.add(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return mergedSql;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =========================== 内部类定义 ===========================
|
||||
|
||||
private static class SqlFileContent {
|
||||
private final String name;
|
||||
private final String content;
|
||||
|
||||
public SqlFileContent(String name, String content) {
|
||||
this.name = name;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMaxTaskCode() {
|
||||
// 查询数据库中最大的任务编号
|
||||
String maxTaskCode = this.baseMapper.selectMaxTaskCode();
|
||||
if (StrUtil.isBlank(maxTaskCode)) {
|
||||
// 如果没有任务编号记录,返回默认值
|
||||
return "00001";
|
||||
}
|
||||
try {
|
||||
// 解析5位数字格式的任务编号并递增
|
||||
int currentNumber = Integer.parseInt(maxTaskCode);
|
||||
int nextNumber = currentNumber + 1;
|
||||
|
||||
// 确保不超过5位数的最大值
|
||||
if (nextNumber > 99999) {
|
||||
throw new IllegalStateException("任务编号已达到最大值99999");
|
||||
}
|
||||
|
||||
// 格式化为5位数字字符串
|
||||
return String.format("%05d", nextNumber);
|
||||
} catch (NumberFormatException e) {
|
||||
// 如果解析失败,返回默认值
|
||||
return "00001";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -135,7 +135,28 @@ public class LocalServiceImpl extends AbstractProxyTransferService<LocalParam> {
|
||||
checkNameSecurity(name);
|
||||
|
||||
String fullPath = StringUtils.concat(param.getFilePath(), path, name);
|
||||
try {
|
||||
// 检查文件是否存在
|
||||
File file = new File(fullPath);
|
||||
if (!file.exists()) {
|
||||
log.warn("文件不存在: {}", fullPath);
|
||||
return true; // 文件不存在也算删除成功
|
||||
}
|
||||
|
||||
// 检查并尝试修改文件权限
|
||||
if (!file.canWrite()) {
|
||||
log.info("文件无写权限,尝试修改权限: {}", fullPath);
|
||||
boolean permissionChanged = file.setWritable(true);
|
||||
if (!permissionChanged) {
|
||||
log.warn("无法修改文件写权限: {}", fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
return FileUtil.del(fullPath);
|
||||
} catch (Exception e) {
|
||||
log.error("删除文件失败: {}, 错误: {}", fullPath, e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -144,7 +165,64 @@ public class LocalServiceImpl extends AbstractProxyTransferService<LocalParam> {
|
||||
checkPathSecurity(path);
|
||||
checkNameSecurity(name);
|
||||
|
||||
return deleteFile(path, name);
|
||||
String fullPath = StringUtils.concatTrimStartSlashes(param.getFilePath(), name);
|
||||
log.info("param.getFilePath==========,{}", param.getFilePath());
|
||||
log.info("fullPath==========,{}", fullPath);
|
||||
|
||||
try {
|
||||
// 检查文件夹是否存在
|
||||
File folder = new File(fullPath);
|
||||
if (!folder.exists()) {
|
||||
log.warn("文件夹不存在: {}", fullPath);
|
||||
return true; // 文件夹不存在也算删除成功
|
||||
}
|
||||
|
||||
// 检查并尝试修改文件夹权限
|
||||
if (!folder.canWrite()) {
|
||||
log.info("文件夹无写权限,尝试修改权限: {}", fullPath);
|
||||
boolean permissionChanged = folder.setWritable(true);
|
||||
if (!permissionChanged) {
|
||||
log.warn("无法修改文件夹写权限: {}", fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 递归修改文件夹内所有文件的权限
|
||||
//setWritableRecursively(folder);
|
||||
|
||||
return FileUtil.del(fullPath);
|
||||
} catch (Exception e) {
|
||||
log.error("删除文件夹失败: {}, 错误: {}", fullPath, e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归设置文件夹及其内部所有文件的写权限
|
||||
* @param file 文件或文件夹
|
||||
*/
|
||||
private void setWritableRecursively(File file) {
|
||||
if (file.isFile()) {
|
||||
if (!file.canWrite()) {
|
||||
boolean success = file.setWritable(true);
|
||||
if (!success) {
|
||||
log.debug("无法设置文件写权限: {}", file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
} else if (file.isDirectory()) {
|
||||
if (!file.canWrite()) {
|
||||
boolean success = file.setWritable(true);
|
||||
if (!success) {
|
||||
log.debug("无法设置文件夹写权限: {}", file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
setWritableRecursively(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -18,7 +18,9 @@ import com.yfd.platform.system.service.IUserService;
|
||||
import com.yfd.platform.utils.FileUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
@ -65,20 +67,47 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
||||
***********************************/
|
||||
@Override
|
||||
public String getUsername() {
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||
LoginUser loginuser = (LoginUser) authentication.getPrincipal();
|
||||
String acountname =
|
||||
loginuser.getUser().getNickname();
|
||||
return acountname;
|
||||
//return "admin";
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication == null || !authentication.isAuthenticated() ||
|
||||
authentication instanceof AnonymousAuthenticationToken) {
|
||||
return "anonymous";
|
||||
}
|
||||
|
||||
if (authentication instanceof UsernamePasswordAuthenticationToken) {
|
||||
LoginUser loginuser = (LoginUser) authentication.getPrincipal();
|
||||
return loginuser.getUser().getNickname();
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, String> getNameInfo() {
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
|
||||
LoginUser loginuser = (LoginUser) authentication.getPrincipal();
|
||||
// 安全地获取认证信息
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
// 检查认证信息是否有效
|
||||
if (authentication == null || !authentication.isAuthenticated() ||
|
||||
authentication instanceof AnonymousAuthenticationToken) {
|
||||
// 返回默认值或抛出自定义异常
|
||||
Map<String, String> defaultMap = new HashMap<>();
|
||||
defaultMap.put("nickname", "Anonymous");
|
||||
defaultMap.put("username", "anonymous");
|
||||
return defaultMap;
|
||||
}
|
||||
|
||||
// 确保是 UsernamePasswordAuthenticationToken 类型
|
||||
if (!(authentication instanceof UsernamePasswordAuthenticationToken)) {
|
||||
Map<String, String> defaultMap = new HashMap<>();
|
||||
defaultMap.put("nickname", "Unknown");
|
||||
defaultMap.put("username", "unknown");
|
||||
return defaultMap;
|
||||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken usernameAuth = (UsernamePasswordAuthenticationToken) authentication;
|
||||
LoginUser loginuser = (LoginUser) usernameAuth.getPrincipal();
|
||||
String nickname = loginuser.getUser().getNickname();
|
||||
String username = loginuser.getUser().getUsername();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
@ -87,6 +116,7 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SysUser getUserInfo() {
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
|
||||
@ -20,6 +20,18 @@ spring:
|
||||
url: jdbc:mysql://43.138.168.68:3306/filemanagedb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: ylfw20230626@
|
||||
# url: jdbc:mysql://127.0.0.1:3307/filemanagedb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
||||
# username: root
|
||||
# password: 123456
|
||||
initial-size: 5
|
||||
min-idle: 5
|
||||
max-active: 20
|
||||
test-while-idle: true
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
validation-query: SELECT 1
|
||||
time-between-eviction-runs-millis: 60000
|
||||
min-evictable-idle-time-millis: 300000
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
server:
|
||||
port: 8087
|
||||
port: 8080
|
||||
tomcat:
|
||||
connection-timeout: 300000
|
||||
#密码加密传输,前端公钥加密,后端私钥解密
|
||||
rsa:
|
||||
private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
|
||||
spring:
|
||||
#应用名称
|
||||
application:
|
||||
@ -11,31 +14,41 @@ spring:
|
||||
druid:
|
||||
master:
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# url: jdbc:mysql://120.27.210.161:3306/testdb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
||||
# username: testdb
|
||||
# password: 27CTfsyJmZRESmsa
|
||||
url: jdbc:mysql://121.37.111.42:33306/filemanagedb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
||||
username: filemanagedb
|
||||
password: GAPchydbCKYFjjAa
|
||||
url: jdbc:mysql://db-container:3306/filemanagedb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: pwd@FileMgr
|
||||
initial-size: 5
|
||||
min-idle: 5
|
||||
max-active: 20
|
||||
validation-query: SELECT 1
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 30MB
|
||||
max-request-size: 100MB
|
||||
max-file-size: 50GB
|
||||
max-request-size: 50GB
|
||||
tomcat:
|
||||
max-swallow-size: -1
|
||||
connection-timeout: 86400000
|
||||
max-http-form-post-size: -1
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: MySecurePass123
|
||||
database: 0
|
||||
logging:
|
||||
file:
|
||||
path: /opt/filemgr/logs
|
||||
path: /opt/filemgr/logs/
|
||||
name: logs/projectname.log
|
||||
level:
|
||||
com.genersoft.iot: debug
|
||||
com.genersoft.iot: info
|
||||
com.genersoft.iot.vmp.storager.dao: info
|
||||
com.genersoft.iot.vmp.gb28181: info
|
||||
|
||||
# 在线文档: swagger-ui(生产环境建议关闭)
|
||||
swagger-ui:
|
||||
enabled: true
|
||||
enabled: false
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
@ -69,7 +82,7 @@ ip:
|
||||
|
||||
|
||||
file-space: #项目文档空间
|
||||
system: D:\file\system\ #单独上传的文件
|
||||
system: /data/local-data/ #单独上传的文件
|
||||
|
||||
file-system:
|
||||
preview:
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.yfd.platform.modules.experimentalData.mapper.TsTaskMapper">
|
||||
|
||||
<select id="selectMaxTaskCode" resultType="java.lang.String">
|
||||
SELECT task_code FROM ts_task
|
||||
ORDER BY task_code DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@ -78,3 +78,31 @@ export function importTaskSql(queryParams: any) {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
});
|
||||
}
|
||||
//
|
||||
export function commonItems() {
|
||||
return request({
|
||||
url: '/api/common-items',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
export function addCommonItems(queryParams: any) {
|
||||
return request({
|
||||
url: '/api/common-items',
|
||||
method: 'post',
|
||||
data: queryParams,
|
||||
});
|
||||
}
|
||||
export function delCommonItems(queryParams: any) {
|
||||
return request({
|
||||
url: '/api/common-items',
|
||||
method: 'delete',
|
||||
data: queryParams,
|
||||
});
|
||||
}
|
||||
export function batchCommonItems(queryParams: any) {
|
||||
return request({
|
||||
url: '/api/common-items/batch',
|
||||
method: 'post',
|
||||
data: queryParams,
|
||||
});
|
||||
}
|
||||
BIN
web/src/assets/MenuIcon/save.png
Normal file
BIN
web/src/assets/MenuIcon/save.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 280 B |
BIN
web/src/assets/MenuIcon/select.png
Normal file
BIN
web/src/assets/MenuIcon/select.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 560 B |
BIN
web/src/assets/MenuIcon/set.png
Normal file
BIN
web/src/assets/MenuIcon/set.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 665 B |
@ -1,6 +1,6 @@
|
||||
import minimatch from "minimatch";
|
||||
import useRouterData from "@/components/file/useRouterData";
|
||||
import { removeDuplicateSlashes } from "fast-glob/out/managers/patterns";
|
||||
// import { removeDuplicateSlashes } from "fast-glob/out/managers/patterns";
|
||||
import common from "@/components/file/common";
|
||||
import { useStorage } from '@vueuse/core';
|
||||
let { storageKey, currentPath } = useRouterData()
|
||||
@ -8,7 +8,9 @@ let { storageKey, currentPath } = useRouterData()
|
||||
const zfilePasswordCache = useStorage('zfile-pwd-cache', {});
|
||||
|
||||
export default function useFilePwd() {
|
||||
|
||||
function removeDuplicateSlashes(path) {
|
||||
return path.replace(/\\/g, '/').replace(/\/+/g, '/');
|
||||
}
|
||||
// 向缓存中写入当前路径密码
|
||||
let putPathPwd = (pattern, password) => {
|
||||
if (pattern) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
|
||||
import JSEncrypt from 'jsencrypt';
|
||||
// 密钥对生成 http://web.chacuo.net/netrsakeypair
|
||||
|
||||
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' +
|
||||
|
||||
22
web/src/views/testdata/datamanagement/index.vue
vendored
22
web/src/views/testdata/datamanagement/index.vue
vendored
@ -154,7 +154,7 @@ function getProject() {
|
||||
setupWebSocket()
|
||||
ws2 = new WebSocket(userStore.WebSocketUrl + '/websocket/' + "id_extract_" + projectId.value)
|
||||
setupWebSocket2()
|
||||
gettreedata()
|
||||
gettreedata('')
|
||||
tonstatus(false)
|
||||
})
|
||||
}
|
||||
@ -179,7 +179,7 @@ function tonstatus(ready: any) {
|
||||
tonloading.value = true
|
||||
buttonmsg.value = '处理中...'
|
||||
}
|
||||
gettreedata()
|
||||
gettreedata('')
|
||||
|
||||
})
|
||||
//解压
|
||||
@ -242,9 +242,13 @@ const treeForm = ref({
|
||||
//获取树数据
|
||||
const treeRef = ref();
|
||||
const filepath = ref('')
|
||||
function gettreedata() {
|
||||
function gettreedata(type:string) {
|
||||
treeloading.value = true
|
||||
if(type == '节点搜索' && projectId.value !== ''){
|
||||
treeForm.value.taskId = ''
|
||||
}else{
|
||||
treeForm.value.taskId = projectId.value
|
||||
}
|
||||
let keyar = projectArr.value.find(item => item.id === projectId.value);
|
||||
storageKey.value = keyar.key
|
||||
getTsNodesTree(treeForm.value).then((res: any) => {
|
||||
@ -252,6 +256,7 @@ function gettreedata() {
|
||||
treeloading.value = false
|
||||
if (treedata.value[0]) {
|
||||
pathid.value = treedata.value[0].nodeId
|
||||
projectId.value = treedata.value[0].taskId
|
||||
nodename.value = res.data[0].nodeName
|
||||
if (nodename.value == '根节点') {
|
||||
filepath.value = res.data[0].path
|
||||
@ -273,6 +278,7 @@ const pathid = ref()
|
||||
const nodename = ref('')
|
||||
function handleNodeClick(data: any, node: any) {
|
||||
pathid.value = data.nodeId
|
||||
projectId.value = data.taskId
|
||||
// filepath.value = data.path + data.nodeName + '/'
|
||||
creatform.value.parentId = ''
|
||||
patharr.value.length = 0
|
||||
@ -349,7 +355,7 @@ async function submitForm(formEl: any) {
|
||||
if (projectForme.value.nodeId) {
|
||||
updateTsNodes(projectForme.value).then((res: any) => {
|
||||
if (res.code == 0) {
|
||||
gettreedata()
|
||||
gettreedata('')
|
||||
ElMessage.success("修改成功")
|
||||
frame.value = false
|
||||
}
|
||||
@ -358,7 +364,7 @@ async function submitForm(formEl: any) {
|
||||
} else {
|
||||
addTsNodes(projectForme.value).then((res: any) => {
|
||||
if (res.code == 0) {
|
||||
gettreedata()
|
||||
gettreedata('')
|
||||
ElMessage.success("添加成功")
|
||||
frame.value = false
|
||||
}
|
||||
@ -393,7 +399,7 @@ function delSubItem(row: any) {
|
||||
loading.value = true
|
||||
deleteTsNodesById({ id: row.nodeId, path: row.path }).then((res: any) => {
|
||||
if (res.code == 0) {
|
||||
gettreedata()
|
||||
gettreedata('')
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '删除成功',
|
||||
@ -1863,12 +1869,12 @@ const configradio: any = ref(null)
|
||||
<div class="faulttemplate-box">
|
||||
<aside id="silderLeft">
|
||||
<div>
|
||||
<el-select v-model="projectId" placeholder="请选择项目" @change="gettreedata()">
|
||||
<el-select v-model="projectId" placeholder="请选择项目" @change="gettreedata('')">
|
||||
<el-option v-for="item in projectArr" :key="item.id" :label="item.taskName" :value="item.id" />
|
||||
</el-select>
|
||||
<div class="tree_sou">
|
||||
<el-input v-model="treeForm.nodeName" style="width:100%;margin:10px 9px 10px 0px;"
|
||||
placeholder="节点名称" clearable :suffix-icon="Search" @change="gettreedata()" />
|
||||
placeholder="节点名称" clearable :suffix-icon="Search" @change="gettreedata('节点搜索')" />
|
||||
<img src="@/assets/images/addNew.png" style="cursor: pointer;" title="新增子项目"
|
||||
@click="addSubItem({ nodeId: '00' })" alt="">
|
||||
<!-- <el-input v-model="treeForm.nodeName" style="width:100%;margin:10px 9px 10px 0px;"
|
||||
|
||||
473
web/src/views/testdata/testtask/index.vue
vendored
473
web/src/views/testdata/testtask/index.vue
vendored
@ -11,11 +11,12 @@ import Page from '@/components/Pagination/page.vue'
|
||||
import {
|
||||
tstaskPage, addtsTask, updatetsTask, exportTaskSql, deleteTsTaskById,
|
||||
deleteTsTaskByIds, confirmDeleteTask, selectTsNodesById, getMaxTaskCode,
|
||||
importTaskSql
|
||||
importTaskSql, commonItems, addCommonItems, delCommonItems, batchCommonItems
|
||||
} from "@/api/testtask";
|
||||
import { storagesBytype } from "@/api/storage";
|
||||
import { getDict } from '@/api/dict'
|
||||
import { getUserInfo } from '@/api/user';
|
||||
import { st } from "vue-router/dist/router-CWoNjPRp.mjs";
|
||||
///////
|
||||
let result2 = ref([])
|
||||
let result1 = ref([
|
||||
@ -143,6 +144,7 @@ const frame = ref(false)
|
||||
//新增项目弹框
|
||||
function addproject() {
|
||||
frame.value = true
|
||||
customList.value = []
|
||||
title.value = "新增试验任务"
|
||||
projectForme.value = {
|
||||
taskName: "",//任务名称
|
||||
@ -172,6 +174,11 @@ function addproject() {
|
||||
//修改项目弹框
|
||||
function editproject(row: any) {
|
||||
projectForme.value = JSON.parse(JSON.stringify(row))
|
||||
if( projectForme.value.custom1 !== null&& projectForme.value.custom1 !== undefined && projectForme.value.custom1 !== ''){
|
||||
customList.value = projectForme.value.custom1.split(',')
|
||||
}else{
|
||||
customList.value = []
|
||||
}
|
||||
formitemarr.value = projectForme.value.taskProps
|
||||
projectForme.value.taskDate = [projectForme.value.taskStartdate, projectForme.value.taskEnddate]
|
||||
title.value = "修改试验任务"
|
||||
@ -341,13 +348,14 @@ const projectForme: any = ref({
|
||||
carrierName: "",//载体名称
|
||||
deviceName: "",//设备名称
|
||||
taskCode: "",//任务编号
|
||||
deviceCode: ""//设备编号
|
||||
, taskType: "",
|
||||
deviceCode: "", //设备编号
|
||||
taskType: "",
|
||||
testDescribe: "",// 试验描述
|
||||
sensorDescribe: "",// 传感器描述
|
||||
taskProps: "",//信息
|
||||
localStorageId: '',
|
||||
backupStorageId: ''
|
||||
backupStorageId: '',
|
||||
custom1: '',//试验标签项
|
||||
})
|
||||
//表单确定
|
||||
async function submitForm(formEl: any) {
|
||||
@ -366,6 +374,12 @@ async function submitForm(formEl: any) {
|
||||
projectForme.value.taskStartdate = ''
|
||||
projectForme.value.taskEnddate = ''
|
||||
}
|
||||
//试验标签项
|
||||
if(customList.value.length>0){
|
||||
projectForme.value.custom1 = customList.value.join(',')
|
||||
}else{
|
||||
projectForme.value.custom1 = ''
|
||||
}
|
||||
if (projectForme.value.id) {
|
||||
updatetsTask(projectForme.value).then((res: any) => {
|
||||
if (res.code == 0) {
|
||||
@ -391,7 +405,9 @@ async function submitForm(formEl: any) {
|
||||
const moderules = ref({
|
||||
taskType: [{ required: true, message: "请选择任务类型", trigger: "change" }],
|
||||
taskDate: [{ required: true, message: "请选择任务时间", trigger: "change" }],
|
||||
taskPlace: [{ required: true, message: "请输入任务地点", trigger: "blur" }],
|
||||
taskPlace: [{ required: true, message: "请选择任务地点", trigger: "change" }],
|
||||
carrierName: [{ required: true, message: "请选择载机名称", trigger: "change" }],
|
||||
deviceCode: [{ required: true, message: "请输入设备代号_编号", trigger: "blur" }],
|
||||
localStorageId: [{ required: true, message: "请选择本地存储空间", trigger: "change" }],
|
||||
backupStorageId: [{ required: true, message: "请选择minio存储空间", trigger: "change" }],
|
||||
});
|
||||
@ -556,6 +572,317 @@ function importSubmit(formEl: any) {
|
||||
function importClose() {
|
||||
importDialog.value = false
|
||||
}
|
||||
//输入框单选
|
||||
const taskPlaceOptions = ref([])
|
||||
const taskPlaceLoading = ref(false)
|
||||
const selectDialog = ref(false)
|
||||
const selectData = ref([])
|
||||
const selectDataAll = ref([])
|
||||
const selectKeyword = ref('')
|
||||
const selectDialogLoading = ref(false)
|
||||
const selectData2 = ref([])
|
||||
const selectDataAll2 = ref([])
|
||||
const selectKeyword2 = ref('')
|
||||
// 载机名称
|
||||
const aircraftOptions = ref([])
|
||||
const aircraftLoading = ref(false)
|
||||
// 传感器描述
|
||||
const sensorDescribeOptions = ref([])
|
||||
const sensorDescribeLoading = ref(false)
|
||||
// 多选试验标签项
|
||||
const customList = ref([])
|
||||
const customMultpieOptions = ref([])
|
||||
const customMultpieLoading = ref(false)
|
||||
const iscustomMultpie = ref(false)
|
||||
const selectionList = ref([])
|
||||
const multipleTableRef = ref(null)
|
||||
|
||||
function remoteMethod(query) {
|
||||
let list = []
|
||||
commonItems().then((res: any) => {
|
||||
if (res.data.taskLocation) {
|
||||
list = res.data.taskLocation
|
||||
}
|
||||
if (query !== '') {
|
||||
taskPlaceLoading.value = true;
|
||||
setTimeout(() => {
|
||||
taskPlaceLoading.value = false;
|
||||
taskPlaceOptions.value = list.filter(item => item.includes(query));
|
||||
}, 200);
|
||||
} else {
|
||||
taskPlaceOptions.value = list
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
function remoteMethod2(query) {
|
||||
let list = []
|
||||
commonItems().then((res: any) => {
|
||||
if (res.data.aircraftName) {
|
||||
list = res.data.aircraftName
|
||||
}
|
||||
if (query !== '') {
|
||||
aircraftLoading.value = true;
|
||||
setTimeout(() => {
|
||||
aircraftLoading.value = false;
|
||||
aircraftOptions.value = list.filter(item => item.includes(query));
|
||||
}, 200);
|
||||
} else {
|
||||
aircraftOptions.value = list
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
function remoteMethod3(query) {
|
||||
let list = []
|
||||
commonItems().then((res: any) => {
|
||||
if (res.data.sensorDescription) {
|
||||
list = res.data.sensorDescription
|
||||
}
|
||||
if (query !== '') {
|
||||
sensorDescribeLoading.value = true;
|
||||
setTimeout(() => {
|
||||
sensorDescribeLoading.value = false;
|
||||
sensorDescribeOptions.value = list.filter(item => item.includes(query));
|
||||
}, 200);
|
||||
} else {
|
||||
sensorDescribeOptions.value = list
|
||||
}
|
||||
})
|
||||
}
|
||||
function remoteMethod4(query) {
|
||||
let list = []
|
||||
commonItems().then((res: any) => {
|
||||
if (res.data.taskLabel) {
|
||||
list = res.data.taskLabel
|
||||
}
|
||||
if (query !== '') {
|
||||
aircraftLoading.value = true;
|
||||
setTimeout(() => {
|
||||
customMultpieLoading.value = false;
|
||||
customMultpieOptions.value = list.filter(item => item.includes(query));
|
||||
}, 200);
|
||||
} else {
|
||||
customMultpieOptions.value = list
|
||||
}
|
||||
})
|
||||
}
|
||||
function saveSelect(type: string) {
|
||||
let value = ''
|
||||
if (type === 'taskLocation') {
|
||||
value = projectForme.value.taskPlace
|
||||
} else if (type === 'aircraftName') {
|
||||
value = projectForme.value.carrierName
|
||||
} else if (type === 'sensorDescription') {
|
||||
value = projectForme.value.sensorDescribe
|
||||
}
|
||||
const params = {
|
||||
label: value,
|
||||
type: type
|
||||
}
|
||||
addCommonItems(params).then((res: any) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('保存成功')
|
||||
} else {
|
||||
ElMessage.error('保存失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
function showSelectList(type: string) {
|
||||
querySelectList(type)
|
||||
iscustomMultpie.value = false
|
||||
selectDialog.value = true
|
||||
}
|
||||
function selectDataProcess(data: any, type: string) {
|
||||
let list = []
|
||||
data.forEach((item: any) => {
|
||||
list.push({
|
||||
name: item,
|
||||
type: type
|
||||
})
|
||||
})
|
||||
return list
|
||||
}
|
||||
function blurChange(type: string, e: any) {
|
||||
if (type == 'taskLocation' && e.target.value !== '') {
|
||||
projectForme.value.taskPlace = e.target.value
|
||||
} else if (type == 'aircraftName' && e.target.value !== '') {
|
||||
projectForme.value.carrierName = e.target.value
|
||||
} else if (type == 'sensorDescription' && e.target.value !== '') {
|
||||
projectForme.value.sensorDescribe = e.target.value
|
||||
} else if (type == 'taskLabel' && e.target.value !== '') {
|
||||
const issave = customList.value.indexOf(e.target.value) !== -1;
|
||||
if (!issave) {
|
||||
customList.value.push(e.target.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
function selectKeywordChange(type: string) {
|
||||
if (type == '1') {
|
||||
const list = selectDataAll.value
|
||||
if (selectKeyword.value !== '') {
|
||||
selectData.value = list.filter(item => item.name.includes(selectKeyword.value))
|
||||
} else {
|
||||
selectData.value = selectDataAll.value
|
||||
}
|
||||
} else {
|
||||
const list2 = selectDataAll2.value
|
||||
if (selectKeyword2.value !== '') {
|
||||
selectData2.value = list2.filter(item => item.name.includes(selectKeyword2.value))
|
||||
} else {
|
||||
selectData2.value = selectDataAll2.value
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
function selectClose() {
|
||||
selectDialog.value = false
|
||||
}
|
||||
function delSelect(row: any, type: string) {
|
||||
const params = {
|
||||
label: row.name,
|
||||
type: row.type
|
||||
}
|
||||
delCommonItems(params).then((res: any) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('删除成功')
|
||||
if (type == '1') {
|
||||
querySelectList(row.type)
|
||||
} else {
|
||||
customList.value.splice(customList.value.indexOf(row.name), 1)
|
||||
queryCustomMultpieList()
|
||||
}
|
||||
} else {
|
||||
ElMessage.error('删除失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
function submitSelect(row: any) {
|
||||
if (row.type === 'taskLocation') {
|
||||
projectForme.value.taskPlace = row.name
|
||||
} else if (row.type === 'aircraftName') {
|
||||
projectForme.value.carrierName = row.name
|
||||
} else if (row.type === 'sensorDescription') {
|
||||
projectForme.value.sensorDescribe = row.name
|
||||
} else if (row.type === 'sensorDescription') {
|
||||
projectForme.value.sensorDescribe = row.name
|
||||
}
|
||||
selectDialog.value = false
|
||||
}
|
||||
function querySelectList(type: string) {
|
||||
selectDialogLoading.value = true
|
||||
selectData.value = []
|
||||
selectDataAll.value = []
|
||||
commonItems().then((res: any) => {
|
||||
if (type === 'taskLocation') {
|
||||
if (res.data.taskLocation) {
|
||||
selectData.value = selectDataProcess(res.data.taskLocation, 'taskLocation')
|
||||
selectDataAll.value = selectDataProcess(res.data.taskLocation, 'taskLocation')
|
||||
}
|
||||
} else if ((type === 'aircraftName')) {
|
||||
if (res.data.aircraftName) {
|
||||
selectData.value = selectDataProcess(res.data.aircraftName, 'aircraftName')
|
||||
selectDataAll.value = selectDataProcess(res.data.aircraftName, 'aircraftName')
|
||||
}
|
||||
} else if ((type === 'sensorDescription')) {
|
||||
if (res.data.aircraftName) {
|
||||
selectData.value = selectDataProcess(res.data.sensorDescription, 'sensorDescription')
|
||||
selectDataAll.value = selectDataProcess(res.data.sensorDescription, 'sensorDescription')
|
||||
}
|
||||
}
|
||||
selectDialogLoading.value = false
|
||||
})
|
||||
}
|
||||
function queryCustomMultpieList() {
|
||||
customMultpieOptions.value = []
|
||||
commonItems().then((res: any) => {
|
||||
if (res.data.taskLabel) {
|
||||
customMultpieOptions.value = res.data.taskLabel
|
||||
}
|
||||
let list = []
|
||||
customMultpieOptions.value.forEach((item: any) => {
|
||||
list.push({
|
||||
name: item,
|
||||
type: 'taskLabel'
|
||||
})
|
||||
})
|
||||
selectData2.value = list
|
||||
selectDataAll2.value = list
|
||||
setTimeout(() => {
|
||||
if (customList.value.length > 0) {
|
||||
customList.value.forEach((item: any) => {
|
||||
multipleTableRef.value!.toggleRowSelection(
|
||||
{
|
||||
name: item,
|
||||
type: 'taskLabel',
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
function saveMultpieSelect() {
|
||||
console.log(customList.value)
|
||||
const params = {
|
||||
type: 'taskLabel',
|
||||
labels: customList.value
|
||||
}
|
||||
batchCommonItems(params).then((res: any) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('保存成功')
|
||||
customMultpieOptions.value = res.data.taskLabel
|
||||
} else {
|
||||
ElMessage.error('保存失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
function showMultpieSelectList() {
|
||||
commonItems().then((res: any) => {
|
||||
if (res.data.taskLabel) {
|
||||
customMultpieOptions.value = res.data.taskLabel
|
||||
}
|
||||
iscustomMultpie.value = true
|
||||
let list = []
|
||||
customMultpieOptions.value.forEach((item: any) => {
|
||||
list.push({
|
||||
name: item,
|
||||
type: 'taskLabel'
|
||||
})
|
||||
})
|
||||
selectData2.value = list
|
||||
selectDataAll2.value = list
|
||||
selectDialog.value = true
|
||||
setTimeout(() => {
|
||||
if (customList.value.length > 0) {
|
||||
customList.value.forEach((item: any) => {
|
||||
multipleTableRef.value!.toggleRowSelection(
|
||||
{
|
||||
name: item,
|
||||
type: 'taskLabel',
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
function saveCustomMultpie() {
|
||||
if (selectionList.value.length === 0) {
|
||||
ElMessage.warning('请选择试验标签项')
|
||||
return
|
||||
} else {
|
||||
const list = []
|
||||
selectionList.value.forEach((item: any) => {
|
||||
list.push(item.name)
|
||||
})
|
||||
customList.value = list
|
||||
}
|
||||
selectDialog.value = false
|
||||
}
|
||||
function customSelectionChange(val: any) {
|
||||
selectionList.value = val
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -679,7 +1006,7 @@ function importClose() {
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-for="item in result2" :prop="item.code" :label="item.name"
|
||||
<el-table-column v-for="item in result2" :key="item.code" :prop="item.code" :label="item.name"
|
||||
width="180"></el-table-column>
|
||||
</el-table>
|
||||
<Page :total="total" v-model:size="queryParams.size" v-model:current="queryParams.current"
|
||||
@ -708,27 +1035,59 @@ function importClose() {
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="任务时间" prop="taskDate" style="width: 50%;margin-left: 15px;">
|
||||
<el-date-picker :disabled="projectForme.id" v-model="projectForme.taskDate" type="daterange"
|
||||
range-separator="-" start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD"
|
||||
<el-date-picker :disabled="projectForme.modifiableStatus == '1' ? true : false"
|
||||
v-model="projectForme.taskDate" type="daterange" range-separator="-"
|
||||
start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div style="width: 100%;display: flex;justify-content: space-between;align-items: center;">
|
||||
<el-form-item label="任务地点" prop="taskPlace" style="width: 50%;">
|
||||
<el-input :disabled="projectForme.id" v-model="projectForme.taskPlace" maxlength="500"
|
||||
show-word-limit />
|
||||
<!-- <el-input :disabled="projectForme.id" v-model="projectForme.taskPlace" maxlength="500"
|
||||
show-word-limit /> -->
|
||||
<div style="display: flex;width: 100%;">
|
||||
<el-select :disabled="projectForme.modifiableStatus == '1' ? true : false"
|
||||
v-model="projectForme.taskPlace" filterable remote reserve-keyword
|
||||
style="width:100%" placeholder="请选择任务地点" :remote-method="remoteMethod"
|
||||
:loading="taskPlaceLoading" @blur="blurChange('taskLocation', $event)">
|
||||
<el-option v-for="item in taskPlaceOptions" :key="item" :label="item" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button :disabled="projectForme.modifiableStatus == '1' ? true : false" style="padding: 0px 5px;margin-left: 5px;"
|
||||
@click="saveSelect('taskLocation')"><img src="@/assets/MenuIcon/save.png" title="保存"
|
||||
alt=""></el-button>
|
||||
<el-button :disabled="projectForme.modifiableStatus == '1' ? true : false" style="padding: 0px 5px;margin-left: 5px;"
|
||||
@click="showSelectList('taskLocation')"><img src="@/assets/MenuIcon/set.png"
|
||||
title="" alt=""></el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="任务人员" style="width: 50%;margin-left: 15px;">
|
||||
<el-input v-model="projectForme.taskPerson" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div style="width: 100%;display: flex;justify-content: space-between;align-items: center;">
|
||||
<el-form-item label="载机名称" style="width: 50%;">
|
||||
<el-input :disabled="projectForme.id" v-model="projectForme.carrierName" maxlength="40"
|
||||
show-word-limit />
|
||||
<el-form-item label="载机名称" prop="carrierName" style="width: 50%;">
|
||||
<!-- <el-input :disabled="projectForme.id" v-model="projectForme.carrierName" maxlength="40"
|
||||
show-word-limit /> -->
|
||||
<div style="display: flex;width: 100%;">
|
||||
<el-select :disabled="projectForme.id" v-model="projectForme.carrierName" filterable
|
||||
remote reserve-keyword style="width:100%" placeholder="请选择载机名称"
|
||||
:remote-method="remoteMethod2" :loading="aircraftLoading"
|
||||
@blur="blurChange('aircraftName', $event)">
|
||||
<el-option v-for="item in aircraftOptions" :key="item" :label="item" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button :disabled="projectForme.id" style="padding: 0px 5px;margin-left: 5px;"
|
||||
@click="saveSelect('aircraftName')"><img src="@/assets/MenuIcon/save.png" title="保存"
|
||||
alt=""></el-button>
|
||||
<el-button :disabled="projectForme.id" style="padding: 0px 5px;margin-left: 5px;"
|
||||
@click="showSelectList('aircraftName')"><img src="@/assets/MenuIcon/set.png"
|
||||
title="" alt=""></el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备代号_编号" style="width: 50%;margin-left: 15px;">
|
||||
<el-form-item label="设备代号_编号" prop="deviceCode" style="width: 50%;margin-left: 15px;">
|
||||
<el-input :disabled="projectForme.id" v-model="projectForme.deviceCode" maxlength="40"
|
||||
show-word-limit />
|
||||
</el-form-item>
|
||||
@ -740,14 +1099,49 @@ function importClose() {
|
||||
<el-form-item label="试验描述">
|
||||
<el-input v-model="projectForme.testDescribe" :rows="7" type="textarea" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="传感器描述">
|
||||
<el-input v-model="projectForme.sensorDescribe" :rows="7" type="textarea" show-word-limit />
|
||||
<el-form-item label="试验标签项">
|
||||
<!-- <el-input v-model="projectForme.testDescribe" :rows="7" type="textarea" show-word-limit /> -->
|
||||
<div style="display: flex;width: 100%;">
|
||||
<!-- <el-select v-model="customList" multiple filterable allow-create default-first-option
|
||||
placeholder="请选择试验标签项">
|
||||
<el-option v-for="item in customMultpieOptions" :key="item" :label="item" :value="item">
|
||||
</el-option>
|
||||
</el-select> -->
|
||||
<el-select :disabled="projectForme.id" v-model="customList" filterable remote
|
||||
reserve-keyword style="width:100%" multiple placeholder="请选择试验标签项"
|
||||
:remote-method="remoteMethod4" :loading="customMultpieLoading"
|
||||
@blur="blurChange('taskLabel', $event)">
|
||||
<el-option v-for="item in customMultpieOptions" :key="item" :label="item" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button :disabled="projectForme.id" style="padding: 0px 5px;margin-left: 5px;" @click="saveMultpieSelect"><img
|
||||
src="@/assets/MenuIcon/save.png" title="保存" alt=""></el-button>
|
||||
<el-button :disabled="projectForme.id" style="padding: 0px 5px;margin-left: 5px;" @click="showMultpieSelectList"><img
|
||||
src="@/assets/MenuIcon/set.png" title="" alt=""></el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="传感器描述">
|
||||
<!-- <el-input v-model="projectForme.sensorDescribe" :rows="7" type="textarea" show-word-limit /> -->
|
||||
<div style="display: flex;width: 100%;">
|
||||
<el-select :disabled="projectForme.id" v-model="projectForme.sensorDescribe" filterable
|
||||
remote reserve-keyword style="width:100%" placeholder="请选择传感器描述"
|
||||
:remote-method="remoteMethod3" :loading="aircraftLoading"
|
||||
@blur="blurChange('sensorDescription', $event)">
|
||||
<el-option v-for="item in sensorDescribeOptions" :key="item" :label="item" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button :disabled="projectForme.id" style="padding: 0px 5px;margin-left: 5px;"
|
||||
@click="saveSelect('sensorDescription')"><img src="@/assets/MenuIcon/save.png"
|
||||
title="保存" alt=""></el-button>
|
||||
<el-button :disabled="projectForme.id" style="padding: 0px 5px;margin-left: 5px;"
|
||||
@click="showSelectList('sensorDescription')"><img src="@/assets/MenuIcon/set.png"
|
||||
title="" alt=""></el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="设备名称">
|
||||
<el-input v-model="projectForme.deviceName" maxlength="40" show-word-limit />
|
||||
</el-form-item> -->
|
||||
<el-form-item v-for="(item, index) in formitemarr" :label="item.name">
|
||||
<el-form-item v-for="(item, index) in formitemarr" :key="index" :label="item.name">
|
||||
<div style="width: 100%;display: flex;align-items: center;justify-content: space-between;">
|
||||
<el-input v-model="item.data" style="width: 92%;" />
|
||||
<el-button type="primary" @click="dellable(index)">删除</el-button>
|
||||
@ -842,6 +1236,49 @@ function importClose() {
|
||||
<el-button type="primary" @click="importSubmit(importFormRef)">导入</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog title="选择" :close-on-click-modal="false" v-model="selectDialog" width="30%" :before-close="selectClose">
|
||||
<div v-if="iscustomMultpie">
|
||||
<el-input v-model="selectKeyword2" placeholder="请输入内容" @input="selectKeywordChange('2')"></el-input>
|
||||
<el-table row-key="name" ref="multipleTableRef" :data="selectData2" border height="440"
|
||||
style="width: 100%;margin-top:10px;" @selection-change="customSelectionChange">
|
||||
<el-table-column type="selection" width="55">
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="名称">
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" width="60" align="center">
|
||||
<template #default="scope">
|
||||
<span
|
||||
style="display: flex;display: -webkit-flex;justify-content: space-around;-webkit-justify-content: space-around; ">
|
||||
<img src="@/assets/MenuIcon/lbcz_sc.png" alt="" title="删除"
|
||||
@click="delSelect(scope.row, '2')" style="cursor: pointer;">
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="width: 100%;display: flex;justify-content: end;margin-top: 10px;">
|
||||
<el-button type="primary" @click="saveCustomMultpie">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else v-loading="selectDialogLoading" style="height: 500px;">
|
||||
<el-input v-model="selectKeyword" placeholder="请输入内容" @input="selectKeywordChange('1')"></el-input>
|
||||
<el-table :data="selectData" border height="440" style="width: 100%;margin-top:10px;">
|
||||
<el-table-column prop="name" label="名称">
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<span
|
||||
style="display: flex;display: -webkit-flex;justify-content: space-around;-webkit-justify-content: space-around; ">
|
||||
<img src="@/assets/MenuIcon/select.png" alt="" title="选择"
|
||||
@click="submitSelect(scope.row)" style="cursor: pointer;">
|
||||
<img src="@/assets/MenuIcon/lbcz_sc.png" alt="" title="删除"
|
||||
@click="delSelect(scope.row, '1')" style="cursor: pointer;">
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user