优化代码提交
This commit is contained in:
parent
fe39197754
commit
e0cc6d6e60
@ -247,34 +247,33 @@ public class TsNodesController {
|
||||
@PostMapping("/selectTsNodesById")
|
||||
@ApiOperation("查询可不可以修改项目")
|
||||
public ResponseResult selectTsNodesById(String taskId) {
|
||||
try {
|
||||
if (StrUtil.isBlank(taskId)) {
|
||||
return ResponseResult.error("参数为空");
|
||||
return ResponseResult.error("任务ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
TsTask tsTask = tsTaskService.getById(taskId);
|
||||
if (tsTask == null) {
|
||||
return ResponseResult.error("任务不存在");
|
||||
}
|
||||
|
||||
TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
|
||||
List<TsNodes> tsNodesList = tsNodesService.list(new QueryWrapper<TsNodes>().eq("task_id", taskId));
|
||||
// 如果节点不为空 就不能初始化了
|
||||
if (tsNodesList.size() > 0) {
|
||||
|
||||
// 1. 优先检查节点是否存在
|
||||
if (tsNodesService.count(new QueryWrapper<TsNodes>().eq("task_id", taskId)) > 0) {
|
||||
return ResponseResult.successData(false);
|
||||
} else {
|
||||
// 如果节点为空 就判断文件表
|
||||
List<TsFiles> filesList = tsFilesService.list(new QueryWrapper<TsFiles>().eq("task_id", taskId));
|
||||
if (filesList.size() > 0) {
|
||||
return ResponseResult.successData(false);
|
||||
} else {
|
||||
return ResponseResult.successData(true);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 再检查文件是否存在
|
||||
return ResponseResult.successData(
|
||||
tsFilesService.count(new QueryWrapper<TsFiles>().eq("task_id", taskId)) == 0
|
||||
);
|
||||
|
||||
} catch (Exception e) {
|
||||
// 捕获所有异常并记录日志
|
||||
// log.error("查询可不可以修改项目时发生异常: {}", e.getMessage(), e);
|
||||
return ResponseResult.error("系统异常,请稍后再试");
|
||||
}finally {
|
||||
} finally {
|
||||
TableNameContextHolder.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@ -85,7 +86,6 @@ import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.zip.*;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
|
||||
@ -127,9 +127,6 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
@Resource
|
||||
private TsTaskMapper tsTaskMapper;
|
||||
|
||||
@Resource
|
||||
private TsNodesMapper tsNodesMapper;
|
||||
|
||||
// 从数据库获取的压缩类型列表
|
||||
private List<String> compressSuffixes;
|
||||
private final Set<String> addedEntries = new HashSet<>();
|
||||
@ -3711,82 +3708,174 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
|
||||
***********************************/
|
||||
@Override
|
||||
public String automaticFileBackup(String taskId, String nodeId) {
|
||||
// 1. 获取任务信息
|
||||
TsTask tsTask = tsTaskMapper.selectById(taskId);
|
||||
TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
|
||||
try {
|
||||
final String taskCode = tsTask.getTaskCode();
|
||||
// 原子计数器用于多线程统计
|
||||
AtomicInteger fileCount = new AtomicInteger(0);
|
||||
AtomicInteger folderCount = new AtomicInteger(0);
|
||||
AtomicInteger backupsFileCount = new AtomicInteger(0);
|
||||
AtomicInteger backupsFolderCount = new AtomicInteger(0);
|
||||
|
||||
//首先查询节点下面所有 备份路径为空的数据
|
||||
try {
|
||||
// 1. 使用游标分页查询避免内存溢出
|
||||
int batchSize = 1000; // 每批处理量
|
||||
long totalProcessed = 0;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 2. 分批处理循环
|
||||
while (true) {
|
||||
// 5. 在查询前设置上下文
|
||||
TableNameContextHolder.setTaskCode(taskCode);
|
||||
QueryWrapper<TsFiles> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("id", "node_id", "task_id", "is_file", "parent_id", "file_name",
|
||||
"file_size", "work_path", "backup_path", "upload_time");
|
||||
queryWrapper.eq("node_id", nodeId);
|
||||
queryWrapper.eq("task_id", taskId);
|
||||
queryWrapper.and(wrapper -> wrapper.isNull("backup_path").or().eq("backup_path", ""));
|
||||
queryWrapper.isNotNull("work_path").ne("work_path", "");
|
||||
List<TsFiles> tsFilelist = tsFilesMapper.selectList(queryWrapper);
|
||||
int FileCount = 0, FolderCount = 0;
|
||||
int BackupsFileCount = 0, BackupsFolderCount = 0;
|
||||
"file_size", "work_path", "backup_path", "upload_time")
|
||||
.eq("node_id", nodeId)
|
||||
.eq("task_id", taskId)
|
||||
.and(wrapper -> wrapper.isNull("backup_path").or().eq("backup_path", ""))
|
||||
.isNotNull("work_path")
|
||||
.ne("work_path", "")
|
||||
.last("LIMIT " + batchSize + " OFFSET " + totalProcessed);
|
||||
|
||||
if (tsFilelist.isEmpty()) {
|
||||
return "本地有新增文件 " + FileCount + " 个, 文件夹 " + FolderCount + " 个, 已将 " + BackupsFileCount + " 个文件, " + BackupsFolderCount + " 个文件夹备份成功";
|
||||
List<TsFiles> batchList = tsFilesMapper.selectList(queryWrapper);
|
||||
if (batchList.isEmpty()) break;
|
||||
|
||||
// 3. 使用并行流处理批次
|
||||
batchList.parallelStream().forEach(tsFile -> {
|
||||
try {
|
||||
Parameter parameter = new Parameter();
|
||||
parameter.setTaskId(taskId);
|
||||
ParameterList paramItem = new ParameterList();
|
||||
paramItem.setName(tsFile.getFileName());
|
||||
paramItem.setPath(tsFile.getWorkPath());
|
||||
paramItem.setSize(tsFile.getFileSize());
|
||||
paramItem.setType(tsFile.getIsFile());
|
||||
|
||||
List<ParameterList> paramList = new ArrayList<>(1);
|
||||
paramList.add(paramItem);
|
||||
parameter.setParameterLists(paramList);
|
||||
|
||||
if ("FILE".equals(tsFile.getIsFile())) {
|
||||
fileCount.incrementAndGet();
|
||||
if (uploadToBackupData(parameter)) {
|
||||
backupsFileCount.incrementAndGet();
|
||||
}
|
||||
|
||||
// 在循环前记录开始时间
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (TsFiles tsFiles : tsFilelist) {
|
||||
ParameterList parameterList = new ParameterList();
|
||||
Parameter fileParameter = new Parameter();
|
||||
Parameter FolderParameter = new Parameter();
|
||||
List<ParameterList> fileParameterlist = new ArrayList<>();
|
||||
List<ParameterList> FolderParameterlist = new ArrayList<>();
|
||||
|
||||
if ("FILE".equals(tsFiles.getIsFile())) {
|
||||
parameterList.setName(tsFiles.getFileName());
|
||||
parameterList.setPath(tsFiles.getWorkPath());
|
||||
parameterList.setSize(tsFiles.getFileSize());
|
||||
parameterList.setType(tsFiles.getIsFile());
|
||||
fileParameterlist.add(parameterList);
|
||||
fileParameter.setParameterLists(fileParameterlist);
|
||||
fileParameter.setTaskId(taskId);
|
||||
Boolean value = uploadToBackupData(fileParameter);
|
||||
if (value) {
|
||||
BackupsFileCount++;
|
||||
}
|
||||
FileCount++;
|
||||
} else {
|
||||
parameterList.setName(tsFiles.getFileName());
|
||||
parameterList.setPath(tsFiles.getWorkPath());
|
||||
parameterList.setSize(tsFiles.getFileSize());
|
||||
parameterList.setType(tsFiles.getIsFile());
|
||||
FolderParameterlist.add(parameterList);
|
||||
FolderParameter.setParameterLists(FolderParameterlist);
|
||||
FolderParameter.setTaskId(taskId);
|
||||
Boolean value = uploadToBackupData(FolderParameter);
|
||||
if (value) {
|
||||
BackupsFolderCount++;
|
||||
}
|
||||
FolderCount++;
|
||||
folderCount.incrementAndGet();
|
||||
if (uploadToBackupData(parameter)) {
|
||||
backupsFolderCount.incrementAndGet();
|
||||
}
|
||||
}
|
||||
// 计算总耗时
|
||||
long endTime = System.currentTimeMillis();
|
||||
long totalTime = endTime - startTime;
|
||||
LOGGER.info("循环执行耗时: {} 毫秒 (约 {} 秒)", totalTime, totalTime / 1000.0);
|
||||
|
||||
//批量修改表结构在上下文中执行更新
|
||||
updateBackupPaths(taskId, nodeId);
|
||||
|
||||
return "本地有新增文件 " + FileCount + " 个, 新增文件夹 " + FolderCount + " 个, 已将 " + BackupsFileCount + " 个文件, " + BackupsFolderCount + " 个文件夹备份成功";
|
||||
} catch (Exception e) {
|
||||
// 日志记录异常信息
|
||||
LOGGER.error("文件处理失败: ID={}, 名称={}", tsFile.getId(), tsFile.getFileName(), e);
|
||||
}
|
||||
});
|
||||
|
||||
totalProcessed += batchList.size();
|
||||
LOGGER.debug("已处理: {}/{}", totalProcessed, batchList.size());
|
||||
}
|
||||
|
||||
// 4. 批量更新路径
|
||||
long updateStart = System.currentTimeMillis();
|
||||
updateBackupPaths(taskId, nodeId);
|
||||
LOGGER.info("批量更新耗时: {}ms", System.currentTimeMillis() - updateStart);
|
||||
|
||||
// 5. 性能监控
|
||||
long totalTime = System.currentTimeMillis() - startTime;
|
||||
LOGGER.info("总处理: {}文件/{}文件夹 | 成功: {}文件/{}文件夹 | 总耗时: {}秒",
|
||||
fileCount.get(), folderCount.get(),
|
||||
backupsFileCount.get(), backupsFolderCount.get(),
|
||||
totalTime/1000.0);
|
||||
|
||||
return String.format("本地有新增文件 %d 个, 文件夹 %d 个, 备份成功 %d 文件/%d 文件夹",
|
||||
fileCount.get(), folderCount.get(),
|
||||
backupsFileCount.get(), backupsFolderCount.get());
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("自动备份失败:任务ID={}, 节点ID={}", taskId, nodeId, e);
|
||||
// 返回错误信息
|
||||
return "自动备份失败:" + e.getMessage();
|
||||
} finally {
|
||||
TableNameContextHolder.clear();
|
||||
}
|
||||
|
||||
|
||||
// TsTask tsTask = tsTaskMapper.selectById(taskId);
|
||||
// TableNameContextHolder.setTaskCode(tsTask.getTaskCode());
|
||||
// try {
|
||||
//
|
||||
// //首先查询节点下面所有 备份路径为空的数据
|
||||
// QueryWrapper<TsFiles> queryWrapper = new QueryWrapper<>();
|
||||
// queryWrapper.select("id", "node_id", "task_id", "is_file", "parent_id", "file_name",
|
||||
// "file_size", "work_path", "backup_path", "upload_time");
|
||||
// queryWrapper.eq("node_id", nodeId);
|
||||
// queryWrapper.eq("task_id", taskId);
|
||||
// queryWrapper.and(wrapper -> wrapper.isNull("backup_path").or().eq("backup_path", ""));
|
||||
// queryWrapper.isNotNull("work_path").ne("work_path", "");
|
||||
// List<TsFiles> tsFilelist = tsFilesMapper.selectList(queryWrapper);
|
||||
// int FileCount = 0, FolderCount = 0;
|
||||
// int BackupsFileCount = 0, BackupsFolderCount = 0;
|
||||
//
|
||||
// if (tsFilelist.isEmpty()) {
|
||||
// return "本地有新增文件 " + FileCount + " 个, 文件夹 " + FolderCount + " 个, 已将 " + BackupsFileCount + " 个文件, " + BackupsFolderCount + " 个文件夹备份成功";
|
||||
// }
|
||||
//
|
||||
// // 在循环前记录开始时间
|
||||
// long startTime = System.currentTimeMillis();
|
||||
// for (TsFiles tsFiles : tsFilelist) {
|
||||
// ParameterList parameterList = new ParameterList();
|
||||
// Parameter fileParameter = new Parameter();
|
||||
// Parameter FolderParameter = new Parameter();
|
||||
// List<ParameterList> fileParameterlist = new ArrayList<>();
|
||||
// List<ParameterList> FolderParameterlist = new ArrayList<>();
|
||||
//
|
||||
// if ("FILE".equals(tsFiles.getIsFile())) {
|
||||
// parameterList.setName(tsFiles.getFileName());
|
||||
// parameterList.setPath(tsFiles.getWorkPath());
|
||||
// parameterList.setSize(tsFiles.getFileSize());
|
||||
// parameterList.setType(tsFiles.getIsFile());
|
||||
// fileParameterlist.add(parameterList);
|
||||
// fileParameter.setParameterLists(fileParameterlist);
|
||||
// fileParameter.setTaskId(taskId);
|
||||
// Boolean value = uploadToBackupData(fileParameter);
|
||||
// if (value) {
|
||||
// BackupsFileCount++;
|
||||
// }
|
||||
// FileCount++;
|
||||
// } else {
|
||||
// parameterList.setName(tsFiles.getFileName());
|
||||
// parameterList.setPath(tsFiles.getWorkPath());
|
||||
// parameterList.setSize(tsFiles.getFileSize());
|
||||
// parameterList.setType(tsFiles.getIsFile());
|
||||
// FolderParameterlist.add(parameterList);
|
||||
// FolderParameter.setParameterLists(FolderParameterlist);
|
||||
// FolderParameter.setTaskId(taskId);
|
||||
// Boolean value = uploadToBackupData(FolderParameter);
|
||||
// if (value) {
|
||||
// BackupsFolderCount++;
|
||||
// }
|
||||
// FolderCount++;
|
||||
// }
|
||||
// }
|
||||
// // 计算总耗时
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// long totalTime = endTime - startTime;
|
||||
// LOGGER.info("循环执行耗时: {} 毫秒 (约 {} 秒)", totalTime, totalTime / 1000.0);
|
||||
//
|
||||
// //批量修改表结构在上下文中执行更新
|
||||
// updateBackupPaths(taskId, nodeId);
|
||||
//
|
||||
// return "本地有新增文件 " + FileCount + " 个, 新增文件夹 " + FolderCount + " 个, 已将 " + BackupsFileCount + " 个文件, " + BackupsFolderCount + " 个文件夹备份成功";
|
||||
// } catch (Exception e) {
|
||||
// // 日志记录异常信息
|
||||
// LOGGER.error("自动备份失败:任务ID={}, 节点ID={}", taskId, nodeId, e);
|
||||
// // 返回错误信息
|
||||
// return "自动备份失败:" + e.getMessage();
|
||||
// } finally {
|
||||
// TableNameContextHolder.clear();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
// 辅助方法:更新备份路径
|
||||
private void updateBackupPaths(String taskId, String nodeId) {
|
||||
try {
|
||||
|
@ -629,40 +629,6 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
return nodesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除子节点
|
||||
*
|
||||
* @param parentId 父节点ID
|
||||
*/
|
||||
private void deleteChildren(String parentId, String taskId) {
|
||||
// 使用 QueryWrapper 查询当前节点的所有子节点
|
||||
QueryWrapper<TsNodes> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("parent_id", parentId); // parent_id = #{parentId}
|
||||
queryWrapper.eq("task_id", taskId); // parent_id = #{parentId}
|
||||
List<TsNodes> children = tsNodesMapper.selectList(queryWrapper);
|
||||
|
||||
// 递归删除每个子节点
|
||||
for (TsNodes child : children) {
|
||||
deleteChildren(child.getNodeId(), child.getTaskId()); // 递归删除子节点的子节点
|
||||
tsNodesMapper.deleteById(child.getNodeId()); // 删除当前子节点
|
||||
//批量文件的数据
|
||||
QueryWrapper<TsFiles> queryWrapper1 = new QueryWrapper<>();
|
||||
queryWrapper1.eq("node_id", child.getNodeId());
|
||||
queryWrapper1.eq("task_id", taskId);
|
||||
List<TsFiles> tsFiles = tsFilesMapper.selectList(queryWrapper1);
|
||||
|
||||
List<String> dataset = new ArrayList<>();
|
||||
if (tsFiles != null && !tsFiles.isEmpty()) {
|
||||
dataset = tsFiles.stream()
|
||||
.map(TsFiles::getId) // 假设 TsFiles 类中有 getId() 方法
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
//批量删除TsFiles表数据
|
||||
if (dataset.size() > 0) {
|
||||
tsFilesService.deleteTsFilesByIds(dataset, "local", taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************
|
||||
@ -895,76 +861,6 @@ public class TsNodesServiceImpl extends ServiceImpl<TsNodesMapper, TsNodes> impl
|
||||
}
|
||||
|
||||
|
||||
// //查询节点表中 有没有根节点的节点 如果有就直接使用 如果没有就新增
|
||||
// LambdaQueryWrapper<TsNodes> queryWrapper = new LambdaQueryWrapper<>();
|
||||
// queryWrapper.eq(TsNodes::getTaskId, taskId);
|
||||
// queryWrapper.eq(TsNodes::getParentId, TOP_LEVEL_PARENT_NODE);
|
||||
// queryWrapper.eq(TsNodes::getNodeName, "根节点");
|
||||
// TsNodes nodeData = tsNodesMapper.selectOne(queryWrapper);
|
||||
// if (nodeData == null) {
|
||||
// //新增一个根节点
|
||||
// TsNodes node = savetsNodes(taskId, TOP_LEVEL_PARENT_NODE, "根节点");
|
||||
//
|
||||
// String finalPath = item.getPath().replace(item.getName(), "");
|
||||
// //保存文件信息
|
||||
// TsFiles tsFiles1 = new TsFiles();
|
||||
// //任务
|
||||
// tsFiles1.setTaskId(taskId);
|
||||
// //节点
|
||||
// tsFiles1.setNodeId(node.getNodeId());
|
||||
// //文件 文件夹 区分
|
||||
// tsFiles1.setIsFile("FILE");
|
||||
// //上级ID
|
||||
// tsFiles1.setParentId("00");
|
||||
// //文件名称
|
||||
// tsFiles1.setFileName(item.getName());
|
||||
// //工作空间路径
|
||||
// tsFiles1.setWorkPath(ensurePathFormat(finalPath));
|
||||
// tsFiles1.setUploadTime(currentTime);
|
||||
// tsFiles1.setUploader(loginuser.getUsername());
|
||||
// long bytes = item.getSize();
|
||||
// // 转换为 KB 并保留两位小数
|
||||
// double fileSizeInKB = bytes / (1024.0);
|
||||
// String fileSizeFormatted = String.format("%.2f", fileSizeInKB); // 保留两位小数
|
||||
// tsFiles1.setFileSize(fileSizeFormatted);
|
||||
// tsFilesMapper.insert(tsFiles1);
|
||||
// } else {
|
||||
// //先通过名称 + 节点 + 任务 + 路径 查询 如果有就跳过 没有就继续新增
|
||||
// LambdaQueryWrapper<TsFiles> queryWrapperFiles = new LambdaQueryWrapper<>();
|
||||
// queryWrapperFiles.eq(TsFiles::getNodeId, nodeData.getNodeId());
|
||||
// queryWrapperFiles.eq(TsFiles::getTaskId, taskId);
|
||||
// queryWrapperFiles.eq(TsFiles::getWorkPath, item.getPath());
|
||||
// queryWrapperFiles.eq(TsFiles::getFileName, item.getName());
|
||||
// TsFiles tsFiles = tsFilesMapper.selectOne(queryWrapperFiles);
|
||||
// if (tsFiles == null) {
|
||||
// String finalPath = item.getPath().replace(item.getName(), "");
|
||||
// //保存文件信息
|
||||
// TsFiles tsFiles1 = new TsFiles();
|
||||
// //任务
|
||||
// tsFiles1.setTaskId(taskId);
|
||||
// //节点
|
||||
// tsFiles1.setNodeId(nodeData.getNodeId());
|
||||
// //文件 文件夹 区分
|
||||
// tsFiles1.setIsFile("FILE");
|
||||
// //上级ID
|
||||
// tsFiles1.setParentId("00");
|
||||
// //文件名称
|
||||
// tsFiles1.setFileName(item.getName());
|
||||
// //工作空间路径
|
||||
// tsFiles1.setWorkPath(ensurePathFormat(finalPath));
|
||||
// tsFiles1.setUploadTime(currentTime);
|
||||
// tsFiles1.setUploader(loginuser.getUsername());
|
||||
// long bytes = item.getSize();
|
||||
// // 转换为 KB 并保留两位小数
|
||||
// double fileSizeInKB = bytes / (1024.0);
|
||||
// String fileSizeFormatted = String.format("%.2f", fileSizeInKB); // 保留两位小数
|
||||
// tsFiles1.setFileSize(fileSizeFormatted);
|
||||
// tsFilesMapper.insert(tsFiles1);
|
||||
// } else {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -295,23 +295,18 @@ public class NodesController {
|
||||
@ApiOperation("查询可不可以修改项目")
|
||||
public ResponseResult selectNodesById(String projectId) {
|
||||
if (StrUtil.isBlank(projectId)) {
|
||||
return ResponseResult.error("参数为空");
|
||||
return ResponseResult.error("项目ID不能为空");
|
||||
}
|
||||
List<Nodes> nodesList = nodesService.list(new QueryWrapper<Nodes>().eq("project_id", projectId));
|
||||
//如果节点不为空 就不能初始化了 如果大于0是false 代表不能修改
|
||||
if (nodesList.size() > 0) {
|
||||
return ResponseResult.successData(false);
|
||||
} else {
|
||||
//如果节点为空 就判断文件表ew Qu
|
||||
List<Files> filesList = filesService.list(new QueryWrapper<Files>().eq("project_id", projectId));
|
||||
if (filesList.size() > 0) {
|
||||
return ResponseResult.successData(false);
|
||||
|
||||
} else {
|
||||
return ResponseResult.successData(true);
|
||||
// 优先检查节点是否存在 - 使用count避免加载全量数据
|
||||
if (nodesService.count(new QueryWrapper<Nodes>().eq("project_id", projectId)) > 0) {
|
||||
return ResponseResult.successData(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// 再检查文件是否存在
|
||||
return ResponseResult.successData(
|
||||
filesService.count(new QueryWrapper<Files>().eq("project_id", projectId)) == 0
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user