From f7a5e8c69c96e4352ee4977b95029da4cbc367be Mon Sep 17 00:00:00 2001 From: chenhao <852066789@qq.com> Date: Tue, 2 Sep 2025 20:56:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(image):=20=E6=B7=BB=E5=8A=A0=E8=99=9A?= =?UTF-8?q?=E6=8B=9F=E6=9C=BA=E5=85=8B=E9=9A=86=E5=8A=9F=E8=83=BD=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=9B=B8=E5=85=B3=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 ExternalTorrentClient 接口,用于调用第三方 API - 在 ImageVirtualMachinesServiceImpl 中实现虚拟机克隆功能 - 优化虚拟机启动、关闭、销毁和重启操作 - 在 application.yml 中添加外部 API 客户端配置 - 更新 ExternalApiClient 接口,增加获取虚拟机信息的方法 --- .../entity/req/ImageVirtualMachinesReq.java | 2 + .../project/feign/ExternalApiClient.java | 4 ++ .../project/feign/ExternalTorrentClient.java | 42 +++++++++++ .../service/impl/ImageToolServiceImpl.java | 4 ++ .../impl/ImageVirtualMachinesServiceImpl.java | 70 +++++++++++-------- nex-be/src/main/resources/application.yml | 6 +- 6 files changed, 97 insertions(+), 31 deletions(-) create mode 100644 nex-be/src/main/java/com/unisinsight/project/feign/ExternalTorrentClient.java diff --git a/nex-be/src/main/java/com/unisinsight/project/entity/req/ImageVirtualMachinesReq.java b/nex-be/src/main/java/com/unisinsight/project/entity/req/ImageVirtualMachinesReq.java index 8c9d901..f6c4f38 100644 --- a/nex-be/src/main/java/com/unisinsight/project/entity/req/ImageVirtualMachinesReq.java +++ b/nex-be/src/main/java/com/unisinsight/project/entity/req/ImageVirtualMachinesReq.java @@ -128,6 +128,8 @@ public class ImageVirtualMachinesReq { @JsonProperty("memory_total") @ApiModelProperty("内存大小(MB)") private Integer memoryTotal; + @JsonProperty("auto_mount_virtio") + private Boolean autoMountVirtio; /** * 系统盘大小(GB) **/ diff --git a/nex-be/src/main/java/com/unisinsight/project/feign/ExternalApiClient.java b/nex-be/src/main/java/com/unisinsight/project/feign/ExternalApiClient.java index 81b7efd..f6b6ad5 100644 --- a/nex-be/src/main/java/com/unisinsight/project/feign/ExternalApiClient.java +++ b/nex-be/src/main/java/com/unisinsight/project/feign/ExternalApiClient.java @@ -5,6 +5,7 @@ import com.unisinsight.project.entity.dto.ApiResponse; import com.unisinsight.project.entity.dto.Network; import com.unisinsight.project.entity.dto.NetworkData; import com.unisinsight.project.entity.dto.StoragePoolData; +import com.unisinsight.project.entity.dto.VmInfoDTO; import com.unisinsight.project.entity.req.*; import com.unisinsight.project.entity.res.ImageStatusRes; import org.springframework.cloud.openfeign.FeignClient; @@ -75,6 +76,9 @@ public interface ExternalApiClient { @PostMapping("/api/v1/vm/start") ApiResponse startImage(@RequestBody ImageOperationReq operationReq); + @GetMapping("/api/v1/vm/{vm_name}") + ApiResponse getVmInfo(@PathVariable("vm_name") String vmName); + @PostMapping("/api/v1/vm/shutdown") ApiResponse shutdownImage(@RequestBody ImageOperationReq operationReq); diff --git a/nex-be/src/main/java/com/unisinsight/project/feign/ExternalTorrentClient.java b/nex-be/src/main/java/com/unisinsight/project/feign/ExternalTorrentClient.java new file mode 100644 index 0000000..da289c6 --- /dev/null +++ b/nex-be/src/main/java/com/unisinsight/project/feign/ExternalTorrentClient.java @@ -0,0 +1,42 @@ +package com.unisinsight.project.feign; + + +/** + * @Author : ch + * @version : 1.0 + * @ClassName : ExternalTorrentClient + * @Description : + * @DATE : Created in 17:12 2025/9/2 + *
       Copyright: Copyright(c) 2025     
+ *
       Company :   	紫光汇智信息技术有限公司		           
+ * Modification History: + * Date Author Version Discription + * -------------------------------------------------------------------------- + * 2025/09/02 ch 1.0 Why & What is modified: <修改原因描述> * + */ + +import com.unisinsight.project.config.FeignConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * 第三方API客户端 + * @author ch + */ +@FeignClient( + name = "external-torrent-client", + url = "${external.torrent.url:http://localhost:8114}", + configuration = FeignConfig.class +) +public interface ExternalTorrentClient { + @GetMapping("/vdi/desk-image") + boolean start(@RequestParam("srcFile") String srcFile, + @RequestParam("detFile") String detFile, + @RequestParam("name") String name, + @RequestParam("type") String type); + + @GetMapping("/vdi/progress") + Double progress(@RequestParam("name") String name); + +} diff --git a/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageToolServiceImpl.java b/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageToolServiceImpl.java index 2578751..0bbd8bf 100644 --- a/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageToolServiceImpl.java +++ b/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageToolServiceImpl.java @@ -6,6 +6,7 @@ import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.unisinsight.project.entity.dao.Image; import com.unisinsight.project.entity.dao.ImageTool; import com.unisinsight.project.entity.req.DeleteIdReq; import com.unisinsight.project.entity.req.ImageToolReq; @@ -20,6 +21,7 @@ import com.unisinsight.project.service.ChunkedUploadService; import com.unisinsight.project.service.ImageToolService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -56,6 +58,8 @@ public class ImageToolServiceImpl extends ServiceImpl> selectPage(ImageToolReq imageToolReq) { Page page = new Page<>(imageToolReq.getPageNum(), imageToolReq.getPageSize()); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.like(StringUtils.isNotBlank(imageToolReq.getFileName()), ImageTool::getFileName, imageToolReq.getFileName()); + queryWrapper.eq(StringUtils.isNotBlank(imageToolReq.getFileType()), ImageTool::getFileType, imageToolReq.getFileType()); queryWrapper.orderByAsc(ImageTool::getId); Page imageToolPage = mapper.selectPage(page, queryWrapper); log.info("分页查询返回:{}", JSONUtil.toJsonStr(imageToolPage)); diff --git a/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageVirtualMachinesServiceImpl.java b/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageVirtualMachinesServiceImpl.java index 5951dbc..e849f66 100644 --- a/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageVirtualMachinesServiceImpl.java +++ b/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageVirtualMachinesServiceImpl.java @@ -9,25 +9,20 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.unisinsight.project.entity.dao.Image; +import com.unisinsight.project.entity.dao.ImageDesktop; import com.unisinsight.project.entity.dao.ImageTool; import com.unisinsight.project.entity.dao.ImageVirtualMachines; -import com.unisinsight.project.entity.dao.ImageDesktop; import com.unisinsight.project.entity.dto.ApiResponse; -import com.unisinsight.project.entity.req.DeleteIdReq; -import com.unisinsight.project.entity.req.ImageCloneToDesktopReq; -import com.unisinsight.project.entity.req.ImageCreateReq; -import com.unisinsight.project.entity.req.ImageDeleteReq; -import com.unisinsight.project.entity.req.ImageOperationReq; -import com.unisinsight.project.entity.req.ImageUpdateReq; -import com.unisinsight.project.entity.req.ImageVirtualMachinesReq; +import com.unisinsight.project.entity.dto.VmInfoDTO; +import com.unisinsight.project.entity.req.*; import com.unisinsight.project.entity.res.ImageStatusRes; import com.unisinsight.project.entity.res.ImageVirtualMachinesRes; import com.unisinsight.project.entity.res.PageResult; import com.unisinsight.project.exception.BaseErrorCode; import com.unisinsight.project.exception.Result; import com.unisinsight.project.feign.ExternalApiClient; +import com.unisinsight.project.feign.ExternalTorrentClient; import com.unisinsight.project.mapper.ImageVirtualMachinesMapper; - import com.unisinsight.project.service.ImageDesktopService; import com.unisinsight.project.service.ImageService; import com.unisinsight.project.service.ImageToolService; @@ -36,7 +31,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -59,6 +53,8 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl update(ImageVirtualMachinesReq imageVirtualMachinesReq) { //查询驱动信息 LambdaQueryWrapper imageToolLambdaQueryWrapper = new LambdaQueryWrapper<>(); - imageToolLambdaQueryWrapper.eq(ImageTool::getFileName, imageVirtualMachinesReq.getImageName()); + imageToolLambdaQueryWrapper.eq(ImageTool::getFileName, imageVirtualMachinesReq.getImageToolName()); ImageTool imageTool = imageToolService.getOne(imageToolLambdaQueryWrapper); // 调用镜像修改服务 ImageUpdateReq updateReq = ImageUpdateReq.builder() @@ -265,24 +261,33 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl cloneTemplate(ImageVirtualMachinesReq req) { -// todo 改为调用脚本方式执行 // 查询虚拟机信息 - ImageVirtualMachines imageVirtualMachines = machinesMapper.selectById(req.getId()); + LambdaQueryWrapper queryWrapper=new LambdaQueryWrapper<>(); + queryWrapper.eq(ImageVirtualMachines::getImageName, req.getImageName()); + ImageVirtualMachines imageVirtualMachines = getOne(queryWrapper); if (ObjectUtils.isEmpty(imageVirtualMachines)) { log.info("查询镜像虚拟机返回为空"); return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "虚拟机不存在"); } + + // 调用远程接口获取虚拟机详细信息 + ApiResponse vmInfoResponse = externalApiClient.getVmInfo(req.getImageName()); + if (vmInfoResponse == null || !"200".equals(vmInfoResponse.getCode()) || vmInfoResponse.getData() == null) { + log.error("获取虚拟机信息失败: {}", vmInfoResponse); + return Result.errorResult(BaseErrorCode.HTTP_REQUEST_FAILURE, "获取虚拟机信息失败"); + } + + // 从返回数据中提取需要的信息 + VmInfoDTO vmInfo = vmInfoResponse.getData(); - // 调用克隆虚拟机到桌面镜像服务 - ImageCloneToDesktopReq cloneReq = ImageCloneToDesktopReq.builder() - .vmName(imageVirtualMachines.getImageName()) - .desktopName(req.getImageName() + "_desktop") // 使用虚拟机名称加desktop作为桌面镜像名称 - .storagePath("/vms/iso") // 默认存储路径 - .description("从虚拟机克隆的桌面镜像") - .build(); + String diskPath = vmInfo.getDiskPath(); - ApiResponse response = externalApiClient.cloneTemplate(cloneReq); - if (response == null || !"200".equals(response.getCode())) { + // 根据虚拟机信息调用远程虚拟机信息 + boolean response = externalTorrentClient.start(diskPath, + diskPath.replaceAll(imageVirtualMachines.getImageName(),imageVirtualMachines.getImageName()+"_desktop"), + imageVirtualMachines.getImageName()+".json", "vhd"); + + if (!response) { return Result.errorResult(BaseErrorCode.HTTP_REQUEST_FAILURE, "克隆虚拟机到桌面镜像失败"); } @@ -291,9 +296,6 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl start(ImageVirtualMachinesReq req) { // 查询虚拟机信息 - ImageVirtualMachines imageVirtualMachines = machinesMapper.selectById(req.getId()); + LambdaQueryWrapper queryWrapper=new LambdaQueryWrapper<>(); + queryWrapper.eq(ImageVirtualMachines::getImageName, req.getImageName()); + ImageVirtualMachines imageVirtualMachines = getOne(queryWrapper); if (ObjectUtils.isEmpty(imageVirtualMachines)) { log.info("查询镜像虚拟机返回为空"); return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "虚拟机不存在"); @@ -332,7 +336,9 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl shutdown(ImageVirtualMachinesReq req) { // 查询虚拟机信息 - ImageVirtualMachines imageVirtualMachines = machinesMapper.selectById(req.getId()); + LambdaQueryWrapper queryWrapper=new LambdaQueryWrapper<>(); + queryWrapper.eq(ImageVirtualMachines::getImageName, req.getImageName()); + ImageVirtualMachines imageVirtualMachines = getOne(queryWrapper); if (ObjectUtils.isEmpty(imageVirtualMachines)) { log.info("查询镜像虚拟机返回为空"); return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "虚拟机不存在"); @@ -353,7 +359,9 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl destroy(ImageVirtualMachinesReq req) { // 查询虚拟机信息 - ImageVirtualMachines imageVirtualMachines = machinesMapper.selectById(req.getId()); + LambdaQueryWrapper queryWrapper=new LambdaQueryWrapper<>(); + queryWrapper.eq(ImageVirtualMachines::getImageName, req.getImageName()); + ImageVirtualMachines imageVirtualMachines = getOne(queryWrapper); if (ObjectUtils.isEmpty(imageVirtualMachines)) { log.info("查询镜像虚拟机返回为空"); return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "虚拟机不存在"); @@ -374,7 +382,9 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl reboot(ImageVirtualMachinesReq req) { // 查询虚拟机信息 - ImageVirtualMachines imageVirtualMachines = machinesMapper.selectById(req.getId()); + LambdaQueryWrapper queryWrapper=new LambdaQueryWrapper<>(); + queryWrapper.eq(ImageVirtualMachines::getImageName, req.getImageName()); + ImageVirtualMachines imageVirtualMachines = getOne(queryWrapper); if (ObjectUtils.isEmpty(imageVirtualMachines)) { log.info("查询镜像虚拟机返回为空"); return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "虚拟机不存在"); diff --git a/nex-be/src/main/resources/application.yml b/nex-be/src/main/resources/application.yml index 6f7d1a6..42aa3bf 100644 --- a/nex-be/src/main/resources/application.yml +++ b/nex-be/src/main/resources/application.yml @@ -58,6 +58,8 @@ feign: config: external-api-client: # 对应 FeignClient 的 name logger-level: full # 完整日志级别 + external-torrent-client: + logger-level: full default: # 全局默认配置 logger-level: basic @@ -68,4 +70,6 @@ logging: com.unisinsight.project.feign.ExternalApiClient: debug external: api: - url: http://10.100.51.178:8000 \ No newline at end of file + url: http://10.100.51.178:8000 + torrent: + url: http://10.100.51.178:8114 \ No newline at end of file