This commit is contained in:
even 2025-06-05 15:28:43 +08:00
commit bfeacb12a2
13 changed files with 300 additions and 88 deletions

View File

@ -1,25 +1,21 @@
package cd.casic.ci.api;
import cd.casic.ci.process.dal.req.target.TargetManagerCreateReq;
import cd.casic.ci.process.dal.req.target.TargetManagerUpdateReq;
import cd.casic.ci.process.dal.req.target.TargetQueryReq;
import cd.casic.ci.process.dal.req.target.TargetUploadReq;
import cd.casic.ci.process.dal.req.target.*;
import cd.casic.ci.process.dal.resp.target.TargetManagerResp;
import cd.casic.ci.process.process.dataObject.base.BaseIdReq;
import cd.casic.ci.process.process.dataObject.target.TargetVersion;
import cd.casic.ci.process.process.service.target.TargetManagerService;
import cd.casic.ci.process.process.service.target.TargetVersionService;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.pojo.CommonResult;
import cd.casic.framework.commons.pojo.PageResult;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import org.jetbrains.annotations.NotNull;
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 org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@ -50,6 +46,15 @@ public class TargetController {
}
@PostMapping(value = "/createVersion")
@PermitAll
public CommonResult<Void> createVersion(@RequestBody @Valid TargetVersionCreateReq req) {
targetManagerService.createVersion(req);
return CommonResult.success();
}
@PostMapping(value = "/updateTarget")
@PermitAll
public CommonResult<Void> updateTarget(@RequestBody @Valid TargetManagerUpdateReq req) {
@ -68,6 +73,15 @@ public class TargetController {
}
@PostMapping(value = "/deleteVersion")
@PermitAll
public CommonResult<Void> deleteVersion(@RequestBody @Valid BaseIdReq req) {
targetManagerService.deleteVersion(req);
return CommonResult.success();
}
@PostMapping(path="/findTargetPage")
@PermitAll
public CommonResult<PageResult<TargetManagerResp>> findTargetPage(@RequestBody @NotNull @Valid TargetQueryReq query){
@ -99,8 +113,10 @@ public class TargetController {
@PostMapping(path="/fileUpload")
public CommonResult<String> fileUpload(@RequestBody @Valid TargetUploadReq req){
public CommonResult<String> fileUpload(@RequestParam("file") MultipartFile file) throws SftpUploadUtil.SftpUploadException {
TargetUploadReq req = new TargetUploadReq();
req.setFile(file);
String filePath = targetManagerService.fileUpload(req);
return CommonResult.success(filePath);

View File

@ -16,11 +16,6 @@ public class TargetManagerCreateReq {
*/
private String targetName;
/**
* 目标图标
*/
private String targetIcon;
/**
* 组织ID
*/

View File

@ -21,11 +21,6 @@ public class TargetManagerUpdateReq {
*/
private String targetName;
/**
* 目标图标
*/
private String targetIcon;
/**
* 组织ID
*/

View File

@ -29,6 +29,11 @@ public class TargetQueryReq extends PageParam {
@TableField("target_name")
private String targetName;
/**
* 目标类型
*/
private String targetType;
/**
* 创建人用户id
*/

View File

@ -1,8 +1,7 @@
package cd.casic.ci.process.dal.req.target;
import lombok.Data;
import java.io.File;
import org.springframework.web.multipart.MultipartFile;
/**
* @ClassName PipelineQueryReq
@ -13,11 +12,27 @@ import java.io.File;
@Data
public class TargetUploadReq{
private File file;
//远程服务器IP或主机名
private String remoteHost = "175.6.27.252";
//文件上传远程路径
private String remoteFilePath;
//远程服务器端口 (通常是 22) null <= 0 时使用默认端口 22
private Integer remotePort = 22;
//文件类型
private String fileType;
//远程服务器用户名
private String username = "roots";
//远程服务器密码 (如果使用密码认证)
private String password = "hnidc0327cn!@#xhh";
//SSH Key 文件路径 (如果使用密钥认证password 参数可以为 null)
private String sshKeyPath;
//文件输入流
private MultipartFile file;
//远程服务器存放文件的目录 (例如: /home/user/uploads/)
private String remoteDir = "/home/ops/ops-pro/file/";
//上传到远程服务器的文件名 ( null 或空字符串时使用本地文件名)
private String remoteFileName;
}

View File

@ -0,0 +1,54 @@
package cd.casic.ci.process.dal.req.target;
import lombok.Data;
/**
* @author HopeLi
* @version v1.0
* @ClassName TargetManagerReq
* @Date: 2025/5/17 15:36
* @Description:
*/
@Data
public class TargetVersionCreateReq {
/**
* 目标ID
*/
private String targetId;
/**
* 目标类型
*/
private String targetType;
/**
* 版本号
*/
private String version;
/**
* 描述
*/
private String targetDesc;
/**
* 文件大小单位字节
*/
private Double fileSize;
/**
* 文件名称
*/
private String fileName;
/**
* 文件类型例如 ziptar.gz
*/
private String fileType;
/**
* 文件地址路径
*/
private String filePath;
}

View File

@ -21,9 +21,9 @@ public class TargetManagerResp{
private String targetName;
/**
* 目标图标
* 目标类型
*/
private String targetIcon;
private String targetType;
/**
* 组织ID

View File

@ -23,9 +23,9 @@ public class TargetManager extends PipBaseElement {
private String targetName;
/**
* 目标图标
* 目标类型
*/
private String targetIcon;
private String targetType;
/**
* 组织ID

View File

@ -22,11 +22,6 @@ public class TargetVersion extends PipBaseElement {
*/
private String targetId;
/**
* 目标类型
*/
private String targetType;
/**
* 版本号
*/

View File

@ -1,13 +1,11 @@
package cd.casic.ci.process.process.service.target;
import cd.casic.ci.process.dal.req.target.TargetManagerCreateReq;
import cd.casic.ci.process.dal.req.target.TargetManagerUpdateReq;
import cd.casic.ci.process.dal.req.target.TargetQueryReq;
import cd.casic.ci.process.dal.req.target.TargetUploadReq;
import cd.casic.ci.process.dal.req.target.*;
import cd.casic.ci.process.dal.resp.target.TargetManagerResp;
import cd.casic.ci.process.process.dataObject.base.BaseIdReq;
import cd.casic.ci.process.process.dataObject.target.TargetManager;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.pojo.PageResult;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.Valid;
@ -32,5 +30,9 @@ public interface TargetManagerService extends IService<TargetManager> {
List<TargetManagerResp> findTargetList(@Valid TargetQueryReq query);
String fileUpload(@Valid TargetUploadReq req);
String fileUpload(TargetUploadReq req) throws SftpUploadUtil.SftpUploadException;
void createVersion(@Valid TargetVersionCreateReq req);
void deleteVersion(@Valid BaseIdReq req);
}

View File

@ -1,10 +1,7 @@
package cd.casic.ci.process.process.service.target.impl;
import cd.casic.ci.process.dal.req.target.TargetManagerCreateReq;
import cd.casic.ci.process.dal.req.target.TargetManagerUpdateReq;
import cd.casic.ci.process.dal.req.target.TargetQueryReq;
import cd.casic.ci.process.dal.req.target.TargetUploadReq;
import cd.casic.ci.process.dal.req.target.*;
import cd.casic.ci.process.dal.resp.target.TargetManagerResp;
import cd.casic.ci.process.process.converter.TargetConverter;
import cd.casic.ci.process.process.dal.pipeline.TargetManagerDao;
@ -13,16 +10,14 @@ import cd.casic.ci.process.process.dataObject.base.BaseIdReq;
import cd.casic.ci.process.process.dataObject.target.TargetManager;
import cd.casic.ci.process.process.dataObject.target.TargetVersion;
import cd.casic.ci.process.process.service.target.TargetManagerService;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import cd.casic.framework.commons.pojo.PageResult;
import cd.casic.framework.commons.util.object.BeanUtils;
import cd.casic.framework.security.dal.user.AdminUserDO;
import cd.casic.framework.tenant.core.service.AdminUserServiceImpl;
import cd.casic.module.infra.framework.file.core.client.ftp.FtpFileClient;
import cd.casic.module.infra.framework.file.core.client.ftp.FtpFileClientConfig;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.ftp.FtpMode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -32,13 +27,11 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ObjectUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author HopeLi
@ -103,6 +96,7 @@ public class TargetManagerServiceImpl extends ServiceImpl<TargetManagerDao, Targ
//新增一条版本信息
TargetVersion targetVersion = new TargetVersion();
BeanUtil.copyProperties(req,targetVersion);
targetVersion.setId(null);
targetVersion.setTargetId(targetManager.getId());
targetVersionService.save(targetVersion);
}else {
@ -149,6 +143,9 @@ public class TargetManagerServiceImpl extends ServiceImpl<TargetManagerDao, Targ
if (!ObjectUtils.isEmpty(query.getTargetName())){
wrapper.like("target_name",query.getTargetName());
}
if (!ObjectUtils.isEmpty(query.getTargetType())){
wrapper.like("target_type",query.getTargetType());
}
if (!ObjectUtils.isEmpty(query.getCreator())){
wrapper.eq("creator",query.getCreator());
}
@ -198,6 +195,9 @@ public class TargetManagerServiceImpl extends ServiceImpl<TargetManagerDao, Targ
if (!ObjectUtils.isEmpty(query.getTargetName())){
wrapper.like("target_name",query.getTargetName());
}
if (!ObjectUtils.isEmpty(query.getTargetType())){
wrapper.like("target_type",query.getTargetType());
}
if (!ObjectUtils.isEmpty(query.getCreator())){
wrapper.eq("creator",query.getCreator());
}
@ -213,30 +213,31 @@ public class TargetManagerServiceImpl extends ServiceImpl<TargetManagerDao, Targ
}
@Override
public String fileUpload(TargetUploadReq req) {
public String fileUpload(TargetUploadReq req) throws SftpUploadUtil.SftpUploadException {
String filePath = req.getRemoteDir() + UUID.randomUUID();
byte[] fileContent = null;
try {
fileContent = FileCopyUtils.copyToByteArray(new FileInputStream(req.getFile()));
} catch (IOException e) {
throw new RuntimeException(e);
}
SftpUploadUtil.uploadFileSftp(
req.getRemoteHost(),
req.getRemotePort(),
req.getUsername(),
req.getPassword(),
req.getSshKeyPath(),
req.getFile(),
filePath,
req.getRemoteFileName()
);
return filePath;
}
//TODO
FtpFileClientConfig config = new FtpFileClientConfig();
config.setDomain("http://127.0.0.1:48080");
config.setBasePath("/home/ops/ops-pro/file/identifier");
config.setHost("kanchai.club");
config.setPort(22);
config.setUsername("");
config.setPassword("");
config.setMode(FtpMode.Passive.name());
FtpFileClient client = new FtpFileClient(0L, config);
@Override
public void createVersion(TargetVersionCreateReq req) {
TargetVersion targetVersion = new TargetVersion();
BeanUtil.copyProperties(req,targetVersion);
targetVersionService.save(targetVersion);
}
String fileUrl = client.upload(fileContent, req.getRemoteFilePath(), req.getFileType());
System.out.println("文件上传成功,访问地址: " + fileUrl);
return fileUrl;
@Override
public void deleteVersion(BaseIdReq req) {
targetManagerDao.deleteById(req.getId());
}
}

View File

@ -2,11 +2,9 @@ package cd.casic.ci.process.util;
import cd.casic.framework.commons.exception.ServiceException;
import com.jcraft.jsch.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.*;
public class SftpUploadUtil {
@ -162,6 +160,154 @@ public class SftpUploadUtil {
}
}
/**
* 通过 SFTP 上传文件到远程服务器
*
* @param remoteHost 远程服务器IP或主机名
* @param remotePort 远程服务器端口 (通常是 22) null <= 0 时使用默认端口 22
* @param username 远程服务器用户名
* @param password 远程服务器密码 (如果使用密码认证)
* @param sshKeyPath SSH Key 文件路径 (如果使用密钥认证password 参数可以为 null)
* @param file 本地要上传的文件路径
* @param remoteDir 远程服务器存放文件的目录 (例如: /home/user/uploads/)
* @param remoteFileName 上传到远程服务器的文件名 ( null 或空字符串时使用本地文件名)
* @throws SftpUploadException 如果上传过程中发生任何错误
*/
public static void uploadFileSftp(
String remoteHost,
Integer remotePort,
String username,
String password,
String sshKeyPath,
MultipartFile file,
String remoteDir,
String remoteFileName) throws SftpUploadException {
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
InputStream fis = null;
try {
JSch jsch = new JSch();
// 1. 添加身份认证信息 (密码或密钥)
if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
// 使用 SSH Key 认证
File sshKeyFile = new File(sshKeyPath);
if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
}
jsch.addIdentity(sshKeyPath);
System.out.println("使用 SSH Key 认证: " + sshKeyPath);
} else if (password == null || password.trim().isEmpty()) {
// 如果没有提供密码或密钥路径则认证信息不全
throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
}
// 如果提供了密码将在 getSession 后设置因为 getSession 需要用户名主机和端口先建立连接意图
// 2. 获取 Session
int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
session = jsch.getSession(username, remoteHost, port);
System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);
// 如果使用密码认证且提供了密码
if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
session.setPassword(password);
System.out.println("使用密码认证.");
}
// 设置连接不进行主机密钥检查 (生产环境不推荐应该配置 known_hosts)
// 在实际应用中应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
session.setConfig(config);
// 3. 连接 Session
session.connect();
System.out.println("SFTP Session 连接成功.");
// 4. 打开 SFTP Channel
channel = session.openChannel("sftp");
channel.connect();
System.out.println("SFTP Channel 打开成功.");
channelSftp = (ChannelSftp) channel;
// 5. 检查并切换到远程目录
try {
channelSftp.cd(remoteDir);
System.out.println("已切换到远程目录: " + remoteDir);
} catch (SftpException e) {
// 如果远程目录不存在尝试创建
System.err.println("远程目录不存在: " + remoteDir + ",尝试创建...");
try {
// 尝试递归创建目录 (如果需要)
createRemoteDirRecursive(channelSftp, remoteDir);
channelSftp.cd(remoteDir); // 创建后再次切换
System.out.println("远程目录创建成功并已切换: " + remoteDir);
} catch (SftpException e2) {
// 创建目录失败
throw new SftpUploadException("远程目录创建失败: " + remoteDir, e2);
}
}
// 6. 获取本地文件流
fis = file.getInputStream();
System.out.println("本地文件流获取成功: " + file);
// 7. 确定远程文件名
String finalRemoteFileName = (remoteFileName != null && !remoteFileName.trim().isEmpty()) ?
remoteFileName : file.getOriginalFilename();
System.out.println("最终上传到远程的文件名为: " + finalRemoteFileName);
// 8. 上传文件
channelSftp.put(fis, finalRemoteFileName);
System.out.println("文件上传成功!");
} catch (JSchException e) {
throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
} catch (SftpException e) {
throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
} catch (FileNotFoundException e) {
throw new SftpUploadException("本地文件未找到: " + e.getMessage(), e);
} catch (IOException e) {
throw new SftpUploadException("文件流读写异常: " + e.getMessage(), e);
} catch (SftpUploadException e) {
// 重新抛出自定义异常
throw e;
} catch (Exception e) {
// 捕获其他未知异常
throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
} finally {
// 9. 关闭资源 (确保在任何情况下都关闭)
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.err.println("关闭本地文件流失败: " + e.getMessage());
e.printStackTrace(); // 打印堆栈以便调试
}
}
if (channelSftp != null) {
channelSftp.disconnect();
System.out.println("SFTP Channel 已断开.");
}
if (channel != null) {
channel.disconnect();
System.out.println("SFTP Channel 资源已释放.");
}
if (session != null) {
session.disconnect();
System.out.println("SFTP Session 已断开.");
}
}
}
/**
* 辅助方法递归创建远程目录
* @param channelSftp ChannelSftp 实例

View File

@ -2,7 +2,6 @@ package cd.casic.server;
import cd.casic.ci.process.dal.req.target.TargetManagerCreateReq;
import cd.casic.ci.process.dal.req.target.TargetManagerUpdateReq;
import cd.casic.ci.process.dal.req.target.TargetUploadReq;
import cd.casic.ci.process.process.converter.TargetConverter;
import cd.casic.ci.process.process.dal.pipeline.TargetManagerDao;
import cd.casic.ci.process.process.dal.pipeline.TargetVersionDao;
@ -18,10 +17,6 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
import java.io.File;
import java.io.IOException;
import static org.hibernate.validator.internal.util.Contracts.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.times;
@ -81,12 +76,5 @@ public class TargetTest {
verify(targetManagerDao, times(1)).deleteById("1");
verify(targetVersionDao, times(1)).deleteByIds(anyList());
}
@Test
void testFileUpload() throws IOException {
TargetUploadReq targetUploadReq = new TargetUploadReq();
targetUploadReq.setFile(new File("D:\\test\\test.txt"));
String fileUrl = targetManagerService.fileUpload(targetUploadReq);
assertNotNull(fileUrl);
}
}