添加了试验任务导出、导入、强制删除(本地存储、备份存储)

This commit is contained in:
wanxiaoli 2025-11-04 16:27:24 +08:00
parent 2a8996130c
commit 03f5145c83
14 changed files with 1074 additions and 122 deletions

View File

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

View File

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

View File

@ -13,4 +13,10 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface TsTaskMapper extends BaseMapper<TsTask> {
/**
* 查询最大的任务编号
* @return 最大任务编号
*/
String selectMaxTaskCode();
}

View File

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

View File

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

View File

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

View File

@ -6275,6 +6275,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);
}
}

View File

@ -682,7 +682,10 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
} else {
deleteFailCount++;
}
} catch (Exception e) {
}catch (NullPointerException e) {
LOGGER.error("删除节点时发生空指针异常,文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
deleteFailCount++;
}catch (Exception e) {
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
deleteFailCount++;
}
@ -1132,4 +1135,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);
}
}

View File

@ -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;
@ -533,98 +553,97 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
Boolean value = false;
try {
//循环所有的ID
for (String taskId : dataset) {
//删除项目
TsTask tsTask = tsTaskMapper.selectById(taskId);
TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
// 删除节点表
LambdaQueryWrapper<TsNodes> deleteWrapper = new LambdaQueryWrapper<>();
deleteWrapper.eq(TsNodes::getTaskId, taskId);
tsNodesMapper.delete(deleteWrapper);
try {
//获取试验任务及任务编码
TsTask tsTask = tsTaskMapper.selectById(taskId);
TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
// 删除文件表
LambdaQueryWrapper<TsFiles> deleteWrapperFiles = new LambdaQueryWrapper<>();
deleteWrapperFiles.eq(TsFiles::getTaskId, taskId);
tsFilesMapper.delete(deleteWrapperFiles);
//todo 删除文件表数据
// tsFilesMapper.deleteSdFilesBytaskId(taskId);
// 删除节点表
LambdaQueryWrapper<TsNodes> deleteWrapper = new LambdaQueryWrapper<>();
deleteWrapper.eq(TsNodes::getTaskId, taskId);
tsNodesMapper.delete(deleteWrapper);
// 删除文件表
LambdaQueryWrapper<TsFiles> deleteWrapperFiles = new LambdaQueryWrapper<>();
deleteWrapperFiles.eq(TsFiles::getTaskId, taskId);
tsFilesMapper.delete(deleteWrapperFiles);
// String path = "/" + tsTask.getTaskName() + "/";
// //调用删除节点 根据任务ID
// Boolean deleteTsnodes = tsNodesService.deleteTsNodesByTaskId(taskId, path);
// //如果删除成功 接着删除节点表数据
// if (deleteTsnodes) {
// LOGGER.info("tsNodes表结删除改成功");
// value = true;
// } else {
// LOGGER.error("tsNodes表结构删除失败");
// value = false;
// }
// 删除本地存储空间中的文件夹
StorageSource localStorageSource = getStorageConfig(tsTask.getLocalStorageId());
deleteStorageFolder(localStorageSource, tsTask);
StorageSource storageSource = getStorageConfig(tsTask.getLocalStorageId());
// 删除 local 中的文件夹 项目文件夹
List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
deleteItemData.setName(tsTask.getTaskName());
deleteItemData.setPassword("");
deleteItemData.setPath("/");
deleteItemData.setType(FileTypeEnum.FOLDER);
deleteItemList.add(deleteItemData);
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);
for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
boolean flag = false;
try {
if (deleteItem.getType() == FileTypeEnum.FILE) {
flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
} else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
}
if (flag) {
deleteSuccessCount++;
} else {
deleteFailCount++;
}
} catch (Exception e) {
LOGGER.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
deleteFailCount++;
// 如果有备份存储空间也删除备份存储空间中的文件夹
if (tsTask.getBackupStorageId() != null && tsTask.getBackupStorageId() > 0) {
StorageSource backupStorageSource = getStorageConfig(tsTask.getBackupStorageId());
deleteStorageFolder(backupStorageSource, tsTask);
}
}
if (deleteSuccessCount >= 1) {
// 删除当前项目
// 删除当前试验任务
int deleteCount = tsTaskMapper.deleteById(taskId);
if (deleteCount == 1) {
LOGGER.info("tstask表结删除成功");
LOGGER.info("tstask表结构删除成功");
value = true;
} else {
LOGGER.error("tstask表结构删除失败");
value = false;
}
} else {
value = false;
} finally {
// 每次处理完一个任务后清理上下文
TableNameContextHolder.clear();
}
}
return value;
} catch (Exception e) {
} finally {
TableNameContextHolder.clear();
LOGGER.error("删除试验任务时发生异常", e);
return false;
}
}
// 这个方法会递归删除试验任务文件夹及其下的所有子文件夹和文件当调用 fileService.deleteFolder() 方法时存储服务实现会自动处理递归删除逻辑确保整个文件夹树都被清除
//这是文件存储系统的基本特性不论是本地文件系统还是MinIO等云存储服务在删除文件夹时都会删除其包含的所有内容
private void deleteStorageFolder(StorageSource storageSource, TsTask tsTask) {
try {
List<BatchDeleteRequest.DeleteItem> deleteItemList = new ArrayList<>();
BatchDeleteRequest.DeleteItem deleteItemData = new BatchDeleteRequest.DeleteItem();
deleteItemData.setName(tsTask.getTaskName());
deleteItemData.setPassword("");
deleteItemData.setPath("/");
deleteItemData.setType(FileTypeEnum.FOLDER);
deleteItemList.add(deleteItemData);
BatchDeleteRequest batchDeleteRequest = new BatchDeleteRequest();
batchDeleteRequest.setDeleteItems(deleteItemList);
batchDeleteRequest.setStorageKey(storageSource.getKey());
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageKey(batchDeleteRequest.getStorageKey());
int deleteSuccessCount = 0;
for (BatchDeleteRequest.DeleteItem deleteItem : deleteItemList) {
boolean flag = false;
try {
if (deleteItem.getType() == FileTypeEnum.FILE) {
flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
} else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
}
if (flag) {
deleteSuccessCount++;
LOGGER.info("删除成功 - 存储类型: {}, 类型: {}, 路径: {}, 名称: {}",
storageSource.getKey(), deleteItem.getType(), deleteItem.getPath(), deleteItem.getName());
} else {
LOGGER.warn("删除失败 - 存储类型: {}, 类型: {}, 路径: {}, 名称: {}",
storageSource.getKey(), deleteItem.getType(), deleteItem.getPath(), deleteItem.getName());
}
} catch (Exception e) {
LOGGER.error("删除文件/文件夹失败, 存储类型: {}, 文件路径: {}, 文件名称: {}",
storageSource.getKey(), deleteItem.getPath(), deleteItem.getName(), e);
}
}
} catch (Exception e) {
LOGGER.error("删除存储空间文件夹时发生异常, 存储类型: {}", storageSource.getKey(), e);
}
return value;
}
/**********************************
@ -645,11 +664,11 @@ public class TsTaskServiceImpl extends ServiceImpl<TsTaskMapper, TsTask> impleme
for (TsTask tsTask : tsTasks) {
TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
// 查询是否有备份路径或非空路径的文件
// 查询 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);
@ -719,4 +738,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";
}
}
}

View File

@ -135,7 +135,28 @@ public class LocalServiceImpl extends AbstractProxyTransferService<LocalParam> {
checkNameSecurity(name);
String fullPath = StringUtils.concat(param.getFilePath(), path, name);
return FileUtil.del(fullPath);
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,61 @@ public class LocalServiceImpl extends AbstractProxyTransferService<LocalParam> {
checkPathSecurity(path);
checkNameSecurity(name);
return deleteFile(path, name);
String fullPath = StringUtils.concat(param.getFilePath(), path, name);
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);
}
}
}
}

View File

@ -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 =

View File

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

View File

@ -1,41 +1,50 @@
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:
name: Project-plateform
datasource:
type: com.alibaba.druid.pool.DruidDataSource
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
mvc:
pathmatch:
matching-strategy: ant_path_matcher
servlet:
multipart:
max-file-size: 30MB
max-request-size: 100MB
#应用名称
application:
name: Project-plateform
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
master:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://db-container:3306/filemanagedb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
username: root
password: pwd@FileMgr
mvc:
pathmatch:
matching-strategy: ant_path_matcher
servlet:
multipart:
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
name: logs/projectname.log
level:
com.genersoft.iot: debug
com.genersoft.iot.vmp.storager.dao: info
com.genersoft.iot.vmp.gb28181: info
file:
path: /opt/filemgr/logs/
name: logs/projectname.log
level:
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 +78,7 @@ ip:
file-space: #项目文档空间
system: D:\file\system\ #单独上传的文件
system: /data/local-data/ #单独上传的文件
file-system:
preview:

View File

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