feat(image): 添加定时任务检查虚拟机克隆进度并更新桌面镜像状态

- 在 ImageVirtualMachinesServiceImpl 中添加进度检查任务- 在 Application 中启用定时任务调度器
- 在 ClientOperateServiceImpl 中添加终端开机功能
- 新增 DeviceController 用于处理终端开机请求
- 更新 ExternalTorrentClient接口,添加新的远程调用方法
master
chenhao 2025-09-03 16:06:06 +08:00
parent 24deac28ae
commit 598acecab5
9 changed files with 105 additions and 6 deletions

View File

@ -9,6 +9,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -19,6 +20,7 @@ import java.net.UnknownHostException;
@SpringBootApplication @SpringBootApplication
@MapperScan(basePackages = "com.unisinsight.project.mapper") @MapperScan(basePackages = "com.unisinsight.project.mapper")
@EnableFeignClients(basePackages = "com.unisinsight.project.feign") @EnableFeignClients(basePackages = "com.unisinsight.project.feign")
@EnableScheduling
public class Application { public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class); private static final Logger logger = LoggerFactory.getLogger(Application.class);

View File

@ -3,6 +3,8 @@ package com.unisinsight.project.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -64,4 +66,17 @@ public class ThreadConfig {
new ThreadPoolExecutor.CallerRunsPolicy() new ThreadPoolExecutor.CallerRunsPolicy()
); );
} }
/**
* TaskScheduler
*/
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10); // 线程池大小
taskScheduler.setThreadNamePrefix("scheduled-task-"); // 线程名称前缀
taskScheduler.setWaitForTasksToCompleteOnShutdown(true); // 关闭时等待任务完成
taskScheduler.setAwaitTerminationSeconds(30); // 等待终止时间
return taskScheduler;
}
} }

View File

@ -40,8 +40,8 @@ public class ImageDesktop extends Model<ImageDesktop> {
@TableField(value = "desktop_type") @TableField(value = "desktop_type")
@ApiModelProperty("桌面类型1VHD 2VHDX 3QCOW2") @ApiModelProperty("桌面类型")
private Integer desktopType; private String desktopType;
/** /**
* id * id
**/ **/
@ -130,6 +130,9 @@ public class ImageDesktop extends Model<ImageDesktop> {
@TableField(value = "description") @TableField(value = "description")
@ApiModelProperty("描述") @ApiModelProperty("描述")
private String description; private String description;
@TableField(value = "file_size")
@ApiModelProperty("文件大小")
private String fileSize;
/** /**
* *
**/ **/

View File

@ -32,7 +32,7 @@ public class ImageDesktopReq {
**/ **/
@JsonProperty("desktop_type") @JsonProperty("desktop_type")
@ApiModelProperty("桌面类型1VHD 2VHDX 3QCOW2") @ApiModelProperty("桌面类型1VHD 2VHDX 3QCOW2")
private Integer desktopType; private String desktopType;
/** /**
* id * id
**/ **/

View File

@ -34,7 +34,7 @@ public class ImageDesktopRes implements Serializable {
**/ **/
@JsonProperty("desktop_type") @JsonProperty("desktop_type")
@ApiModelProperty("桌面类型1VHD 2VHDX 3QCOW2") @ApiModelProperty("桌面类型1VHD 2VHDX 3QCOW2")
private Integer desktopType; private String desktopType;
/** /**
* id * id
**/ **/
@ -108,5 +108,7 @@ public class ImageDesktopRes implements Serializable {
@ApiModelProperty("发布时间") @ApiModelProperty("发布时间")
private String publishTime; private String publishTime;
private ImageVirtualMachinesRes imageVirtualMachines;
} }

View File

@ -16,6 +16,7 @@ package com.unisinsight.project.feign;
*/ */
import com.unisinsight.project.config.FeignConfig; import com.unisinsight.project.config.FeignConfig;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -39,4 +40,12 @@ public interface ExternalTorrentClient {
@GetMapping("/vdi/progress") @GetMapping("/vdi/progress")
Double progress(@RequestParam("name") String name); Double progress(@RequestParam("name") String name);
@GetMapping("/vdi/start")
boolean startBt(@RequestParam("sourceFile") String sourceFile,
@RequestParam("torrentFile") String torrentFile);
@GetMapping("/start")
@ApiOperation(value = "终端开机")
boolean deviceStart(@RequestParam("macAddr") String macAddr);
} }

View File

@ -3,13 +3,19 @@ package com.unisinsight.project.service.impl;
import com.unisinsight.project.entity.enums.GrpcTypeEnum; import com.unisinsight.project.entity.enums.GrpcTypeEnum;
import com.unisinsight.project.entity.req.DeviceReq; import com.unisinsight.project.entity.req.DeviceReq;
import com.unisinsight.project.exception.BaseErrorCode;
import com.unisinsight.project.exception.Result; import com.unisinsight.project.exception.Result;
import com.unisinsight.project.feign.ExternalTorrentClient;
import com.unisinsight.project.grpc.generate.NotificationMessage; import com.unisinsight.project.grpc.generate.NotificationMessage;
import com.unisinsight.project.grpc.service.SendNotificationService; import com.unisinsight.project.grpc.service.SendNotificationService;
import com.unisinsight.project.service.ClientOperateService; import com.unisinsight.project.service.ClientOperateService;
import com.unisinsight.project.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException;
/** /**
* @author : ch * @author : ch
* @version : 1.0 * @version : 1.0
@ -24,14 +30,20 @@ import org.springframework.stereotype.Service;
* 2025/08/21 ch 1.0 Why & What is modified: <> * * 2025/08/21 ch 1.0 Why & What is modified: <> *
*/ */
@Service @Service
@Slf4j
public class ClientOperateServiceImpl implements ClientOperateService { public class ClientOperateServiceImpl implements ClientOperateService {
@Autowired @Autowired
private SendNotificationService notificationService; private SendNotificationService notificationService;
@Autowired
private ExternalTorrentClient externalTorrentClient;
@Override @Override
public Result<?> terminalStart(DeviceReq deviceReq) { public Result<?> terminalStart(DeviceReq deviceReq) {
//todo 待客户端确认消息内容后完善 if (StringUtil.isNotEmpty(deviceReq.getMacAddr())) {
return notificationService.sendNotification(deviceReq.getDeviceId(), NotificationMessage.newBuilder().setContent("终端开机").build()); return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "请输入正确的终端MAC地址");
}
boolean start = externalTorrentClient.deviceStart(deviceReq.getMacAddr());
return start ? Result.successResult() : Result.errorResultMessage(BaseErrorCode.INTERNAL_EXCEPTION, "终端启动失败");
} }
@Override @Override

View File

@ -1,5 +1,9 @@
package com.unisinsight.project.service.impl; package com.unisinsight.project.service.impl;
import cn.hutool.core.lang.func.Func;
import com.unisinsight.project.entity.dao.ImageVirtualMachines;
import com.unisinsight.project.entity.res.ImageVirtualMachinesRes;
import com.unisinsight.project.mapper.ImageVirtualMachinesMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
@ -24,6 +28,10 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j @Slf4j
/** /**
@ -36,6 +44,8 @@ import java.util.List;
public class ImageDesktopServiceImpl extends ServiceImpl<ImageDesktopMapper, ImageDesktop> implements ImageDesktopService { public class ImageDesktopServiceImpl extends ServiceImpl<ImageDesktopMapper, ImageDesktop> implements ImageDesktopService {
@Resource @Resource
private ImageDesktopMapper mapper; private ImageDesktopMapper mapper;
@Resource
private ImageVirtualMachinesMapper imageVirtualMachinesMapper;
@Override @Override
public Result<PageResult<ImageDesktopRes>> selectPage(ImageDesktopReq imageDesktopReq) { public Result<PageResult<ImageDesktopRes>> selectPage(ImageDesktopReq imageDesktopReq) {
@ -50,6 +60,15 @@ public class ImageDesktopServiceImpl extends ServiceImpl<ImageDesktopMapper, Ima
} else { } else {
PageResult<ImageDesktopRes> convert = PageResult.convertIPage(imageDesktopPage, ImageDesktopRes.class); PageResult<ImageDesktopRes> convert = PageResult.convertIPage(imageDesktopPage, ImageDesktopRes.class);
List<ImageDesktopRes> data = convert.getData(); List<ImageDesktopRes> data = convert.getData();
List<Integer> imageVirtualIdList = data.stream().map(ImageDesktopRes::getImageVirtualId).filter(Objects::nonNull).collect(Collectors.toList());
LambdaQueryWrapper<ImageVirtualMachines> imageVirtualMachinesLambdaQueryWrapper = new LambdaQueryWrapper<>();
imageVirtualMachinesLambdaQueryWrapper.in(ImageVirtualMachines::getId, imageVirtualIdList);
List<ImageVirtualMachines> imageVirtualMachines = imageVirtualMachinesMapper.selectList(imageVirtualMachinesLambdaQueryWrapper);
Map<Long, ImageVirtualMachines> machinesMap = imageVirtualMachines.stream().collect(Collectors.toMap(ImageVirtualMachines::getId, Function.identity(), (k1, k2) -> k1));
for (ImageDesktopRes datum : data) {
datum.setImageVirtualMachines(BeanUtil.copyProperties(machinesMap.get(Long.valueOf(datum.getImageVirtualId())), ImageVirtualMachinesRes.class));
}
convert.setData(data); convert.setData(data);
return Result.successResult(convert); return Result.successResult(convert);
} }

View File

@ -0,0 +1,37 @@
package com.unisinsight.torrent.controller;
import com.unisinsight.torrent.util.ImageVirtualUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
@RequestMapping("/vdi/device")
@Api("终端")
public class DeviceController {
@GetMapping("/start")
@ApiOperation(value = "终端开机")
public boolean start(@RequestParam("macAddr") String macAddr) {
try {
Process exec = Runtime.getRuntime().exec(" wakeonlan " + macAddr);
boolean b = exec.waitFor(2000, TimeUnit.MILLISECONDS);
return exec.exitValue() == 0;
} catch (IOException | InterruptedException e) {
log.error("调用开机命令失败,错误详情为:", e);
}
return false;
}
}