🔨 上传任务使用用户连接配置.

This commit is contained in:
lijiahangmax
2024-12-14 12:41:06 +08:00
parent 885446dc58
commit 985091f42b
23 changed files with 266 additions and 147 deletions

View File

@@ -119,6 +119,8 @@ public interface ErrorMessage {
String AUTH_ERROR = "认证失败";
String FILE_UPLOAD_ERROR = "文件上传失败";
String SCRIPT_UPLOAD_ERROR = "脚本上传失败";
String EXEC_ERROR = "执行失败";

View File

@@ -37,7 +37,7 @@ import java.util.function.Function;
*/
public class Valid extends cn.orionsec.kit.lang.utils.Valid {
private static final Validator VALIDATOR = SpringHolder.getBean(Validator.class);
private static final Validator validator = SpringHolder.getBean(Validator.class);
public static <T> T notNull(T object) {
return notNull(object, ErrorMessage.PARAM_MISSING);
@@ -123,7 +123,7 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
public static <T> T valid(T obj, Class<?>... groups) {
notNull(obj, ErrorMessage.PARAM_MISSING);
// 验证对象
Set<ConstraintViolation<T>> set = VALIDATOR.validate(obj, groups);
Set<ConstraintViolation<T>> set = validator.validate(obj, groups);
if (!set.isEmpty()) {
throw new ConstraintViolationException(set);
}

View File

@@ -20,7 +20,9 @@ import org.apache.ibatis.annotations.Mapper;
import org.dromara.visor.framework.mybatis.core.mapper.IMapper;
import org.dromara.visor.framework.mybatis.core.query.Conditions;
import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO;
import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum;
import java.util.Date;
import java.util.List;
/**
@@ -58,35 +60,39 @@ public interface UploadTaskFileDAO extends IMapper<UploadTaskFileDO> {
}
/**
* 通过 taskId hostId 更新状态
* 通过 taskId hostId 更新为失败
*
* @param taskId taskId
* @param hostId hostId
* @param status status
* @param taskId taskId
* @param hostId hostId
* @param errorMessage errorMessage
* @return effect
*/
default int updateStatusByTaskHostId(Long taskId, Long hostId, String status) {
default int updateToFailedByTaskHostId(Long taskId, Long hostId, String errorMessage) {
// 条件
LambdaQueryWrapper<UploadTaskFileDO> wrapper = this.wrapper()
.eq(UploadTaskFileDO::getTaskId, taskId)
.eq(UploadTaskFileDO::getHostId, hostId);
// 修改值
UploadTaskFileDO update = new UploadTaskFileDO();
update.setStatus(status);
update.setStatus(UploadTaskFileStatusEnum.FAILED.name());
update.setErrorMessage(errorMessage);
update.setStartTime(new Date());
update.setEndTime(new Date());
// 更新
return this.update(update, wrapper);
}
/**
* 通过 id 更新状态
* 通过 id 更新为取消
*
* @param idList idList
* @param status status
* @return effect
*/
default int updateStatusByIdList(List<Long> idList, String status) {
default int updateToCanceledByIdList(List<Long> idList) {
UploadTaskFileDO update = new UploadTaskFileDO();
update.setStatus(status);
update.setStatus(UploadTaskFileStatusEnum.CANCELED.name());
update.setStartTime(new Date());
update.setEndTime(new Date());
// 更新
return this.update(update, Conditions.in(UploadTaskFileDO::getId, idList));
}

View File

@@ -75,6 +75,10 @@ public class UploadTaskFileDO extends BaseDO {
@TableField("status")
private String status;
@Schema(description = "错误信息")
@TableField("error_message")
private String errorMessage;
@Schema(description = "开始时间")
@TableField("start_time")
private Date startTime;

View File

@@ -67,6 +67,9 @@ public class UploadTaskFileVO implements Serializable {
@Schema(description = "状态")
private String status;
@Schema(description = "错误信息")
private String errorMessage;
@Schema(description = "开始时间")
private Date startTime;

View File

@@ -27,7 +27,7 @@ import lombok.Getter;
*/
@Getter
@AllArgsConstructor
public enum HostSshOsTypeEnum {
public enum HostOsTypeEnum {
/**
* linux
@@ -43,11 +43,11 @@ public enum HostSshOsTypeEnum {
private final String scriptSuffix;
public static HostSshOsTypeEnum of(String type) {
public static HostOsTypeEnum of(String type) {
if (type == null) {
return LINUX;
}
for (HostSshOsTypeEnum value : values()) {
for (HostOsTypeEnum value : values()) {
if (value.name().equals(type)) {
return value;
}

View File

@@ -38,7 +38,7 @@ import org.springframework.web.socket.WebSocketSession;
@Slf4j
public class ExecLogTracker implements IExecLogTracker {
private static final AppTrackerConfig TRACKER_CONFIG = SpringHolder.getBean(AppTrackerConfig.class);
private static final AppTrackerConfig trackerConfig = SpringHolder.getBean(AppTrackerConfig.class);
private final WebSocketSession session;
@@ -69,9 +69,9 @@ public class ExecLogTracker implements IExecLogTracker {
try {
this.tracker = new DelayTrackerListener(absolutePath, this);
tracker.charset(config.getCharset());
tracker.delayMillis(TRACKER_CONFIG.getDelay());
tracker.offset(FileOffsetMode.LINE, TRACKER_CONFIG.getOffset());
tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, TRACKER_CONFIG.getWaitTimes());
tracker.delayMillis(trackerConfig.getDelay());
tracker.offset(FileOffsetMode.LINE, trackerConfig.getOffset());
tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, trackerConfig.getWaitTimes());
// 开始监听文件
tracker.run();
} catch (Exception e) {

View File

@@ -47,7 +47,7 @@ import java.util.Map;
@Slf4j
public abstract class TransferSession implements ITransferSession {
protected static final AppSftpConfig SFTP_CONFIG = SpringHolder.getBean(AppSftpConfig.class);
protected static final AppSftpConfig sftpConfig = SpringHolder.getBean(AppSftpConfig.class);
protected final TerminalConnectDTO connectInfo;

View File

@@ -55,7 +55,7 @@ public class UploadSession extends TransferSession {
// 检查连接
this.init();
// 检查文件是否存在
SftpUtils.checkUploadFilePresent(SFTP_CONFIG, executor, path);
SftpUtils.checkUploadFilePresent(sftpConfig, executor, path);
// 打开输出流
this.outputStream = executor.openOutputStream(path);
// 响应结果

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2023 - present Jiahang Li (visor.orionsec.cn ljh1553488six@139.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.asset.handler.host.upload.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 文件上传配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/5/8 14:35
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FileUploadConfigDTO {
/**
* taskId
*/
private Long taskId;
/**
* hostId
*/
private Long hostId;
/**
* userId
*/
private Long userId;
/**
* 上传路径
*/
private String remotePath;
/**
* 文件
*/
private List<FileUploadFileItemDTO> files;
}

View File

@@ -44,9 +44,9 @@ public class FileUploadFileItemDTO {
private String fileId;
/**
* 远程路径
* 文件路径
*/
private String remotePath;
private String filePath;
/**
* 当前大小

View File

@@ -31,6 +31,7 @@ import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO;
import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum;
import org.dromara.visor.module.asset.enums.UploadTaskStatusEnum;
import org.dromara.visor.module.asset.handler.host.upload.manager.FileUploadTaskManager;
import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadConfigDTO;
import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
import org.dromara.visor.module.asset.handler.host.upload.uploader.FileUploader;
import org.dromara.visor.module.asset.handler.host.upload.uploader.IFileUploader;
@@ -102,6 +103,8 @@ public class FileUploadTask implements IFileUploadTask {
} catch (Exception e) {
log.error("FileUploadTask.run error id: {}", id, e);
} finally {
// 移除任务
fileUploadTaskManager.removeTask(id);
// 修改状态
if (canceled) {
this.updateStatus(UploadTaskStatusEnum.CANCELED);
@@ -110,8 +113,6 @@ public class FileUploadTask implements IFileUploadTask {
}
// 检查是否发送消息
this.checkSendMessage();
// 移除任务
fileUploadTaskManager.removeTask(id);
// 释放资源
this.close();
}
@@ -156,7 +157,7 @@ public class FileUploadTask implements IFileUploadTask {
.map(s -> FileUploadFileItemDTO.builder()
.id(s.getId())
.fileId(s.getFileId())
.remotePath(s.getRealFilePath())
.filePath(s.getFilePath())
.status(UploadTaskFileStatusEnum.WAITING.name())
.current(0L)
.build())
@@ -165,7 +166,14 @@ public class FileUploadTask implements IFileUploadTask {
return;
}
// 添加到上传器
uploaderList.add(new FileUploader(id, k, files));
FileUploadConfigDTO config = FileUploadConfigDTO.builder()
.taskId(id)
.hostId(k)
.userId(record.getUserId())
.remotePath(record.getRemotePath())
.files(files)
.build();
uploaderList.add(new FileUploader(config));
});
}

View File

@@ -15,6 +15,9 @@
*/
package org.dromara.visor.module.asset.handler.host.upload.uploader;
import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.lang.utils.collect.Maps;
import cn.orionsec.kit.lang.utils.io.Files1;
import cn.orionsec.kit.lang.utils.io.Streams;
import cn.orionsec.kit.net.host.SessionStore;
import cn.orionsec.kit.net.host.sftp.SftpExecutor;
@@ -22,14 +25,19 @@ import cn.orionsec.kit.spring.SpringHolder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.framework.common.constant.Const;
import org.dromara.visor.framework.common.constant.ErrorMessage;
import org.dromara.visor.framework.common.constant.ExtraFieldConst;
import org.dromara.visor.framework.common.enums.EndpointDefine;
import org.dromara.visor.framework.common.file.FileClient;
import org.dromara.visor.framework.common.utils.PathUtils;
import org.dromara.visor.module.asset.dao.UploadTaskFileDAO;
import org.dromara.visor.module.asset.define.config.AppSftpConfig;
import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO;
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
import org.dromara.visor.module.asset.enums.HostOsTypeEnum;
import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum;
import org.dromara.visor.module.asset.handler.host.jsch.SessionStores;
import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadConfigDTO;
import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
import org.dromara.visor.module.asset.service.TerminalService;
import org.dromara.visor.module.asset.utils.SftpUtils;
@@ -38,6 +46,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -54,10 +63,12 @@ public class FileUploader implements IFileUploader {
private static final UploadTaskFileDAO uploadTaskFileDAO = SpringHolder.getBean(UploadTaskFileDAO.class);
private static final AppSftpConfig SFTP_CONFIG = SpringHolder.getBean(AppSftpConfig.class);
private static final AppSftpConfig sftpConfig = SpringHolder.getBean(AppSftpConfig.class);
private static final FileClient localFileClient = SpringHolder.getBean("localFileClient");
private TerminalConnectDTO connectInfo;
private SessionStore sessionStore;
private SftpExecutor executor;
@@ -66,6 +77,8 @@ public class FileUploader implements IFileUploader {
private final Long hostId;
private final FileUploadConfigDTO config;
@Getter
private final List<FileUploadFileItemDTO> files;
@@ -77,10 +90,11 @@ public class FileUploader implements IFileUploader {
private volatile boolean closed;
public FileUploader(Long taskId, Long hostId, List<FileUploadFileItemDTO> files) {
this.taskId = taskId;
this.hostId = hostId;
this.files = files;
public FileUploader(FileUploadConfigDTO config) {
this.taskId = config.getTaskId();
this.hostId = config.getHostId();
this.files = config.getFiles();
this.config = config;
}
@Override
@@ -91,6 +105,8 @@ public class FileUploader implements IFileUploader {
if (!run) {
return;
}
// 计算实际上传路径
this.calcRemotePath();
// 上传文件
for (FileUploadFileItemDTO file : files) {
if (closed) {
@@ -116,7 +132,7 @@ public class FileUploader implements IFileUploader {
log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId);
try {
// 打开会话
TerminalConnectDTO connectInfo = terminalService.getTerminalConnectInfo(hostId);
this.connectInfo = terminalService.getTerminalConnectInfo(hostId, config.getUserId());
this.sessionStore = SessionStores.openSessionStore(connectInfo);
this.executor = sessionStore.getSftpExecutor(connectInfo.getFileNameCharset());
executor.connect();
@@ -125,12 +141,32 @@ public class FileUploader implements IFileUploader {
} catch (Exception e) {
log.error("HostFileUploader.initSession error taskId: {}, hostId: {}", taskId, hostId, e);
// 修改状态
uploadTaskFileDAO.updateStatusByTaskHostId(taskId, hostId, UploadTaskFileStatusEnum.FAILED.name());
uploadTaskFileDAO.updateToFailedByTaskHostId(taskId, hostId, ErrorMessage.getErrorMessage(e, ErrorMessage.CONNECT_ERROR));
files.forEach(s -> s.setStatus(UploadTaskFileStatusEnum.FAILED.name()));
return false;
}
}
/**
* 计算实际上传路径
*/
public void calcRemotePath() {
// 计算上传目录
String remotePath = config.getRemotePath();
boolean containsEnv = remotePath.contains(Const.DOLLAR);
if (containsEnv) {
// 替换占位符
String username = connectInfo.getUsername();
String home = PathUtils.getHomePath(HostOsTypeEnum.isWindows(connectInfo.getOsType()), username);
// 替换环境变量路径
Map<String, String> env = Maps.newMap(4);
env.put(ExtraFieldConst.USERNAME, username);
env.put(ExtraFieldConst.HOME, home);
// 设置主机上传路径
config.setRemotePath(Files1.getPath(Strings.format(remotePath, env)));
}
}
/**
* 上传文件
*
@@ -138,18 +174,18 @@ public class FileUploader implements IFileUploader {
*/
private void uploadFile(FileUploadFileItemDTO file) {
log.info("HostFileUploader.uploadFile start taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId());
// 修改状态
this.updateStatus(file, UploadTaskFileStatusEnum.UPLOADING);
String path = Files1.getPath(config.getRemotePath(), file.getFilePath());
try {
// 修改为上传中
this.updateToUploading(file, path);
// 获取本地文件路径
String endpoint = EndpointDefine.UPLOAD_SWAP.format(taskId);
String localPath = localFileClient.getReturnPath(endpoint + Const.SLASH + file.getFileId());
// 检查文件是否存在
String remotePath = file.getRemotePath();
SftpUtils.checkUploadFilePresent(SFTP_CONFIG, executor, remotePath);
SftpUtils.checkUploadFilePresent(sftpConfig, executor, path);
// 打开输出流
this.inputStream = localFileClient.getContentInputStream(localPath);
this.outputStream = executor.openOutputStream(remotePath);
this.outputStream = executor.openOutputStream(path);
// 传输
byte[] buffer = new byte[executor.getBufferSize()];
int read;
@@ -159,26 +195,26 @@ public class FileUploader implements IFileUploader {
}
outputStream.flush();
// 修改状态
this.updateStatus(file, UploadTaskFileStatusEnum.FINISHED);
this.updateToFinished(file);
log.info("HostFileUploader.uploadFile finish taskId: {}, hostId: {}, id: {}", taskId, hostId, file.getId());
} catch (Exception e) {
log.info("HostFileUploader.uploadFile error taskId: {}, hostId: {}, id: {}, canceled: {}", taskId, hostId, file.getId(), canceled);
log.error("HostFileUploader.uploadFile error taskId: {}, hostId: {}, id: {}, canceled: {}", taskId, hostId, file.getId(), canceled, e);
// 修改状态
if (canceled) {
this.updateStatus(file, UploadTaskFileStatusEnum.CANCELED);
this.updateToCanceled(file);
} else {
this.updateStatus(file, UploadTaskFileStatusEnum.FAILED);
this.updateToFailed(file, ErrorMessage.getErrorMessage(e, ErrorMessage.FILE_UPLOAD_ERROR));
}
} finally {
// 释放文件
this.resetFile();
this.releaseFileResource();
}
}
/**
* 释放文件
* 释放文件资源
*/
private void resetFile() {
private void releaseFileResource() {
Streams.close(outputStream);
Streams.close(inputStream);
}
@@ -199,34 +235,70 @@ public class FileUploader implements IFileUploader {
return;
}
// 修改状态
uploadTaskFileDAO.updateStatusByIdList(idList, UploadTaskFileStatusEnum.CANCELED.name());
uploadTaskFileDAO.updateToCanceledByIdList(idList);
}
/**
* 更新状态
* 修改为上传中
*
* @param file file
* @param realFilePath realFilePath
*/
private void updateToUploading(FileUploadFileItemDTO file, String realFilePath) {
UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.UPLOADING);
record.setRealFilePath(realFilePath);
record.setStartTime(new Date());
uploadTaskFileDAO.updateById(record);
}
/**
* 修改为完成
*
* @param file file
*/
private void updateToFinished(FileUploadFileItemDTO file) {
UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.FINISHED);
record.setEndTime(new Date());
uploadTaskFileDAO.updateById(record);
}
/**
* 修改为失败
*
* @param file file
* @param errorMessage errorMessage
*/
private void updateToFailed(FileUploadFileItemDTO file, String errorMessage) {
UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.FAILED);
record.setErrorMessage(errorMessage);
record.setEndTime(new Date());
uploadTaskFileDAO.updateById(record);
}
/**
* 修改为取消
*
* @param file file
*/
private void updateToCanceled(FileUploadFileItemDTO file) {
UploadTaskFileDO record = this.getUpdateRecord(file, UploadTaskFileStatusEnum.CANCELED);
record.setEndTime(new Date());
uploadTaskFileDAO.updateById(record);
}
/**
* 获取修改记录
*
* @param file file
* @param status status
* @return record
*/
private void updateStatus(FileUploadFileItemDTO file, UploadTaskFileStatusEnum status) {
private UploadTaskFileDO getUpdateRecord(FileUploadFileItemDTO file, UploadTaskFileStatusEnum status) {
file.setStatus(status.name());
UploadTaskFileDO update = new UploadTaskFileDO();
update.setId(file.getId());
update.setStatus(status.name());
if (UploadTaskFileStatusEnum.UPLOADING.equals(status)) {
// 上传中
update.setStartTime(new Date());
} else if (UploadTaskFileStatusEnum.FINISHED.equals(status)) {
// 已完成
update.setEndTime(new Date());
} else if (UploadTaskFileStatusEnum.FAILED.equals(status)) {
// 已失败
update.setEndTime(new Date());
} else if (UploadTaskFileStatusEnum.CANCELED.equals(status)) {
// 已失败
update.setEndTime(new Date());
}
uploadTaskFileDAO.updateById(update);
return update;
}
@Override
@@ -247,9 +319,9 @@ public class FileUploader implements IFileUploader {
return;
}
this.closed = true;
// 释放资源
Streams.close(outputStream);
Streams.close(inputStream);
// 释放文件资源
this.releaseFileResource();
// 释放连接资源
Streams.close(executor);
Streams.close(sessionStore);
}

View File

@@ -382,12 +382,12 @@ public class ExecCommandServiceImpl implements ExecCommandService {
* @return scriptPath
*/
private String buildScriptPath(String username, String osType, Long logId, Long hostId) {
HostSshOsTypeEnum os = HostSshOsTypeEnum.of(osType);
HostOsTypeEnum os = HostOsTypeEnum.of(osType);
String name = FileConst.EXEC
+ "/" + logId
+ "/" + hostId
+ os.getScriptSuffix();
return PathUtils.buildAppPath(HostSshOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name);
return PathUtils.buildAppPath(HostOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name);
}
}

View File

@@ -16,7 +16,6 @@
package org.dromara.visor.module.asset.service.impl;
import cn.orionsec.kit.lang.annotation.Keep;
import cn.orionsec.kit.lang.define.collect.MultiHashMap;
import cn.orionsec.kit.lang.define.wrapper.DataGrid;
import cn.orionsec.kit.lang.utils.Arrays1;
import cn.orionsec.kit.lang.utils.Booleans;
@@ -32,11 +31,9 @@ import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
import org.dromara.visor.framework.common.constant.Const;
import org.dromara.visor.framework.common.constant.ErrorMessage;
import org.dromara.visor.framework.common.constant.ExtraFieldConst;
import org.dromara.visor.framework.common.enums.EndpointDefine;
import org.dromara.visor.framework.common.file.FileClient;
import org.dromara.visor.framework.common.security.LoginUser;
import org.dromara.visor.framework.common.utils.PathUtils;
import org.dromara.visor.framework.common.utils.SqlUtils;
import org.dromara.visor.framework.common.utils.Valid;
import org.dromara.visor.framework.mybatis.core.query.Conditions;
@@ -53,15 +50,16 @@ import org.dromara.visor.module.asset.entity.domain.UploadTaskFileDO;
import org.dromara.visor.module.asset.entity.dto.UploadTaskExtraDTO;
import org.dromara.visor.module.asset.entity.request.upload.*;
import org.dromara.visor.module.asset.entity.vo.*;
import org.dromara.visor.module.asset.enums.*;
import org.dromara.visor.module.asset.handler.host.config.model.HostSshConfigModel;
import org.dromara.visor.module.asset.enums.HostStatusEnum;
import org.dromara.visor.module.asset.enums.HostTypeEnum;
import org.dromara.visor.module.asset.enums.UploadTaskFileStatusEnum;
import org.dromara.visor.module.asset.enums.UploadTaskStatusEnum;
import org.dromara.visor.module.asset.handler.host.upload.FileUploadTasks;
import org.dromara.visor.module.asset.handler.host.upload.manager.FileUploadTaskManager;
import org.dromara.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
import org.dromara.visor.module.asset.handler.host.upload.task.IFileUploadTask;
import org.dromara.visor.module.asset.handler.host.upload.uploader.IFileUploader;
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
import org.dromara.visor.module.asset.service.HostConfigService;
import org.dromara.visor.module.asset.service.UploadTaskFileService;
import org.dromara.visor.module.asset.service.UploadTaskService;
import org.dromara.visor.module.infra.api.FileUploadApi;
@@ -102,9 +100,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
@Resource
private AssetAuthorizedDataService assetAuthorizedDataService;
@Resource
private HostConfigService hostConfigService;
@Resource
private FileUploadTaskManager fileUploadTaskManager;
@@ -126,8 +121,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
this.checkHostPermission(hostIdList);
// 查询主机信息
List<HostDO> hosts = this.getUploadTaskHosts(hostIdList);
// 计算文件路径
MultiHashMap<Long, String, String> realRemoteFilePathMap = this.setFileRealRemotePath(request, hosts);
// 转换
UploadTaskDO record = UploadTaskConvert.MAPPER.to(request);
record.setUserId(user.getId());
@@ -153,7 +146,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
.hostId(hostId)
.fileId(s.getFileId())
.filePath(s.getFilePath())
.realFilePath(realRemoteFilePathMap.get(hostId, s.getFileId()))
.fileSize(s.getFileSize())
.status(UploadTaskFileStatusEnum.WAITING.name())
.build())
@@ -204,8 +196,10 @@ public class UploadTaskServiceImpl implements UploadTaskService {
// 查询任务
List<UploadTaskStatusVO> tasks = uploadTaskDAO.of()
.createWrapper()
.select(UploadTaskDO::getId, UploadTaskDO::getStatus,
UploadTaskDO::getStartTime, UploadTaskDO::getEndTime)
.select(UploadTaskDO::getId,
UploadTaskDO::getStatus,
UploadTaskDO::getStartTime,
UploadTaskDO::getEndTime)
.in(UploadTaskDO::getId, idList)
.then()
.list(UploadTaskConvert.MAPPER::toStatus);
@@ -215,9 +209,14 @@ public class UploadTaskServiceImpl implements UploadTaskService {
// 查询任务文件
Map<Long, List<UploadTaskFileVO>> taskFilesMap = uploadTaskFileDAO.of()
.createWrapper()
.select(UploadTaskFileDO::getId, UploadTaskFileDO::getTaskId, UploadTaskFileDO::getHostId,
UploadTaskFileDO::getStatus, UploadTaskFileDO::getFileSize,
UploadTaskFileDO::getStartTime, UploadTaskFileDO::getEndTime)
.select(UploadTaskFileDO::getId,
UploadTaskFileDO::getTaskId,
UploadTaskFileDO::getHostId,
UploadTaskFileDO::getStatus,
UploadTaskFileDO::getFileSize,
UploadTaskFileDO::getErrorMessage,
UploadTaskFileDO::getStartTime,
UploadTaskFileDO::getEndTime)
.in(UploadTaskFileDO::getTaskId, idList)
.then()
.stream()
@@ -377,53 +376,6 @@ public class UploadTaskServiceImpl implements UploadTaskService {
return hosts;
}
/**
* 设置文件实际路径
*
* @param request request
* @param hosts hosts
* @return realRemoteFilePathMap
*/
public MultiHashMap<Long, String, String> setFileRealRemotePath(UploadTaskCreateRequest request,
List<HostDO> hosts) {
MultiHashMap<Long, String, String> realRemoteFilePathMap = MultiHashMap.create();
// 计算上传目录
String remotePath = request.getRemotePath();
List<UploadTaskFileRequest> files = request.getFiles();
boolean containsEnv = remotePath.contains(Const.DOLLAR);
if (containsEnv) {
// 获取主机配置信息
Map<Long, HostSshConfigModel> hostConfigMap = hostConfigService.buildHostConfigMap(hosts, HostTypeEnum.SSH);
for (HostDO host : hosts) {
Long id = host.getId();
// 替换占位符
String username = Optional.ofNullable(id)
.map(hostConfigMap::get)
.map(HostSshConfigModel::getUsername)
.orElse(Const.EMPTY);
String home = PathUtils.getHomePath(HostSshOsTypeEnum.isWindows(host.getOsType()), username);
// 替换环境变量路径
Map<String, String> env = Maps.newMap(4);
env.put(ExtraFieldConst.USERNAME, username);
env.put(ExtraFieldConst.HOME, home);
// 设置主机上传路径
String realRemotePath = Files1.getPath(Strings.format(remotePath, env));
for (UploadTaskFileRequest file : files) {
realRemoteFilePathMap.put(id, file.getFileId(), Files1.getPath(realRemotePath, file.getFilePath()));
}
}
} else {
// 无占位符
for (UploadTaskFileRequest file : files) {
String path = Files1.getPath(remotePath, file.getFilePath());
for (HostDO host : hosts) {
realRemoteFilePathMap.put(host.getId(), file.getFileId(), path);
}
}
}
return realRemoteFilePathMap;
}
/**
* 检查文件完整性
*
@@ -448,7 +400,7 @@ public class UploadTaskServiceImpl implements UploadTaskService {
return;
}
// 修改任务状态
uploadTaskFileDAO.updateStatusByIdList(cancelIdList, UploadTaskFileStatusEnum.CANCELED.name());
uploadTaskFileDAO.updateToCanceledByIdList(cancelIdList);
}
/**

View File

@@ -12,6 +12,7 @@
<result column="real_file_path" property="realFilePath"/>
<result column="file_size" property="fileSize"/>
<result column="status" property="status"/>
<result column="error_message" property="errorMessage"/>
<result column="start_time" property="startTime"/>
<result column="end_time" property="endTime"/>
<result column="create_time" property="createTime"/>
@@ -23,7 +24,7 @@
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, task_id, host_id, file_id, file_path, real_file_path, file_size, status, start_time, end_time, create_time, update_time, creator, updater, deleted
id, task_id, host_id, file_id, file_path, real_file_path, file_size, status, error_message, start_time, end_time, create_time, update_time, creator, updater, deleted
</sql>
</mapper>

View File

@@ -89,6 +89,7 @@ export interface UploadTaskFile {
filePath: string;
fileSize: number;
status: string;
errorMessage: string;
startTime: number;
endTime: number;
current: number;

View File

@@ -10,15 +10,15 @@ const fieldConfig = {
label: 'id',
dataIndex: 'id',
slotName: 'id',
}, {
label: '类型',
dataIndex: 'type',
slotName: 'type',
}, {
label: '用户名',
dataIndex: 'username',
slotName: 'username',
ellipsis: true,
}, {
label: '类型',
dataIndex: 'type',
slotName: 'type',
}, {
label: '主机密钥',
dataIndex: 'keyId',

View File

@@ -15,15 +15,15 @@ const columns = [
slotName: 'name',
ellipsis: true,
tooltip: true
}, {
title: '用户名',
dataIndex: 'username',
slotName: 'username',
}, {
title: '类型',
dataIndex: 'type',
slotName: 'type',
width: 138,
}, {
title: '用户名',
dataIndex: 'username',
slotName: 'username',
}, {
title: '主机密钥',
dataIndex: 'keyId',

View File

@@ -31,7 +31,11 @@
</div>
<!-- 进度 -->
<a-tooltip position="left"
:content="file.fileSize ? ((file.current || 0) / file.fileSize * 100).toFixed(2) + '%' : '0%'"
:content="(
file.status === UploadTaskFileStatus.FAILED
? (file.errorMessage || '')
: (file.fileSize ? ((file.current || 0) / file.fileSize * 100).toFixed(2) + '%' : '0%')
)"
mini>
<a-progress type="circle"
size="mini"

View File

@@ -240,8 +240,9 @@
for (let file of host.files) {
const fileStatus = taskStatusData.files.find(s => s.id === file.id);
if (fileStatus) {
file.status = fileStatus.status;
file.current = fileStatus.current;
file.status = fileStatus.status;
file.errorMessage = fileStatus.errorMessage;
}
}
}

View File

@@ -46,9 +46,9 @@
<a-descriptions-item label="脚本执行">
{{ record.scriptExec === EnabledStatus.ENABLED ? '是' : '否' }}
</a-descriptions-item>
<!-- 创建时间 -->
<a-descriptions-item label="创建时间">
{{ dateFormat(new Date(record.createTime)) }}
<!-- 执行用户 -->
<a-descriptions-item label="执行用户">
{{ record.execUsername }}
</a-descriptions-item>
<!-- 修改时间 -->
<a-descriptions-item label="修改时间">

View File

@@ -12,6 +12,8 @@
:on-before-ok="handlerOk"
@close="handleClose">
<a-spin class="full" :loading="loading">
<!-- 提示 -->
<a-alert class="mb16">修改后将使用此用户的权限以及主机配置执行此任务</a-alert>
<a-form :model="formModel"
label-align="right"
:auto-label-width="true">