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 index 44b5a35..24914fe 100644 --- a/nex-be/src/main/java/com/unisinsight/project/feign/ExternalTorrentClient.java +++ b/nex-be/src/main/java/com/unisinsight/project/feign/ExternalTorrentClient.java @@ -18,6 +18,7 @@ package com.unisinsight.project.feign; import com.unisinsight.project.config.FeignConfig; import io.swagger.annotations.ApiOperation; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -44,8 +45,15 @@ public interface ExternalTorrentClient { boolean startBt(@RequestParam("sourceFile") String sourceFile, @RequestParam("torrentFile") String torrentFile); - @GetMapping("/start") + @GetMapping("/vdi/device/start") @ApiOperation(value = "终端开机") - boolean deviceStart(@RequestParam("macAddr") String macAddr); + boolean deviceStart(@RequestParam("macAddr") String macAddr); + + @GetMapping("/vdi/file/size") + @ApiOperation(value = "获取物理机文件大小") + Long getFileSize(@RequestParam("filePath") String filePath); + @DeleteMapping("/file") + @ApiOperation(value = "删除物理机文件") + boolean deleteFile(@RequestParam("filePath") String filePath); } diff --git a/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientOperateServiceImpl.java b/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientOperateServiceImpl.java index 3a26cba..05c5dc2 100644 --- a/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientOperateServiceImpl.java +++ b/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientOperateServiceImpl.java @@ -39,7 +39,7 @@ public class ClientOperateServiceImpl implements ClientOperateService { private ExternalTorrentClient externalTorrentClient; @Override public Result terminalStart(DeviceReq deviceReq) { - if (StringUtil.isNotEmpty(deviceReq.getMacAddr())) { + if (StringUtil.isEmpty(deviceReq.getMacAddr())) { return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "请输入正确的终端MAC地址"); } boolean start = externalTorrentClient.deviceStart(deviceReq.getMacAddr()); diff --git a/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientServiceImpl.java b/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientServiceImpl.java index aa49f75..f745739 100644 --- a/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientServiceImpl.java +++ b/nex-be/src/main/java/com/unisinsight/project/service/impl/ClientServiceImpl.java @@ -47,7 +47,7 @@ public class ClientServiceImpl implements ClientService { @Override public Result getImageList(String deviceId, String token) { -// todo 改为桌面镜像 需验证 +// 改为桌面镜像 HashMap hashMap = new HashMap<>(); List deviceImageMappings = deviceImageMappingMapper.selectList(new LambdaQueryWrapper().eq(DeviceImageMapping::getDeviceId, deviceId)); if (CollectionUtil.isEmpty(deviceImageMappings)) { @@ -58,12 +58,19 @@ public class ClientServiceImpl implements ClientService { if (CollectionUtil.isNotEmpty(imageIdList)) { List images = imageDesktopMapper.selectList(new LambdaQueryWrapper().in(ImageDesktop::getId, imageIdList)); log.info("用户登录查询镜像结果:{}", JSONUtil.toJsonStr(images)); - List imageRes = BeanUtil.copyToList(images, ImageDesktopRes.class); - List> collect = imageRes.stream().distinct().map(e -> { + + List> collect = images.stream().distinct().map(e -> { HashMap map = new HashMap<>(); + map.put("id",e.getId()); if (StringUtils.isNotBlank(e.getDesktopName())) { map.put("name", e.getDesktopName()); } + if (StringUtils.isNotBlank(e.getFileSize())) { + map.put("file_size", e.getFileSize()); + } + if (StringUtils.isNotBlank(e.getStoragePath())) { + map.put("storage_path", e.getStoragePath()); + } if (StringUtils.isNotBlank(e.getBtPath())) { if (e.getBtPath().contains("http://") || e.getBtPath().contains("https://")) { map.put("torrent", e.getBtPath()); diff --git a/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceImageMappingServiceImpl.java b/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceImageMappingServiceImpl.java index 994b96f..1670d0a 100644 --- a/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceImageMappingServiceImpl.java +++ b/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceImageMappingServiceImpl.java @@ -9,11 +9,13 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.unisinsight.project.entity.dao.DeviceImageMapping; import com.unisinsight.project.entity.dao.Image; +import com.unisinsight.project.entity.dao.ImageDesktop; import com.unisinsight.project.entity.dao.UserDeviceGroup; import com.unisinsight.project.entity.req.DeviceImageMappingReq; import com.unisinsight.project.entity.res.DeviceImageMappingRes; import com.unisinsight.project.exception.Result; import com.unisinsight.project.mapper.DeviceImageMappingMapper; +import com.unisinsight.project.mapper.ImageDesktopMapper; import com.unisinsight.project.mapper.ImageMapper; import com.unisinsight.project.mapper.UserDeviceGroupMapper; import com.unisinsight.project.service.DeviceImageMappingService; @@ -39,7 +41,7 @@ public class DeviceImageMappingServiceImpl extends ServiceImpl deviceImageMappingRes = BeanUtil.copyToList(deviceUserMappings, DeviceImageMappingRes.class); List imageIds = deviceImageMappingRes.stream().map(DeviceImageMappingRes::getImageId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); if (CollectionUtil.isNotEmpty(imageIds)) { - LambdaQueryWrapper imageLambdaQueryWrapper = new LambdaQueryWrapper().in(Image::getId, imageIds); - List images = imageMapper.selectList(imageLambdaQueryWrapper); + LambdaQueryWrapper imageLambdaQueryWrapper = new LambdaQueryWrapper().in(ImageDesktop::getId, imageIds); + List images = imageDesktopMapper.selectList(imageLambdaQueryWrapper); if (CollectionUtil.isNotEmpty(images)) { deviceImageMappingRes.forEach(deviceImage -> images.forEach(image -> { if (ObjectUtil.isNotEmpty(deviceImage.getImageId()) && image.getId().equals(deviceImage.getImageId())) { - deviceImage.setImageName(image.getImageName()); - deviceImage.setImageFileName(image.getImageFileName()); + deviceImage.setImageName(image.getDesktopName()); + deviceImage.setImageFileName(image.getDesktopName()+"."+image.getDesktopType()); } })); } @@ -77,7 +79,7 @@ public class DeviceImageMappingServiceImpl extends ServiceImpl userDeviceGroups = groupMapper.selectList(groupLambdaQueryWrapper); if (CollectionUtil.isNotEmpty(userDeviceGroups)) { deviceImageMappingRes.forEach(deviceImage -> userDeviceGroups.forEach(userDeviceGroup -> { - if (ObjectUtil.isNotEmpty(deviceImage.getImageId()) && userDeviceGroup.getId().equals(deviceImage.getImageId())) { + if (ObjectUtil.isNotEmpty(deviceImage.getDeviceGroupId()) && userDeviceGroup.getId().equals(deviceImage.getDeviceGroupId())) { deviceImage.setDeviceGroupName(userDeviceGroup.getName()); } })); diff --git a/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageDesktopServiceImpl.java b/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageDesktopServiceImpl.java index f295f90..2a0a0f0 100644 --- a/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageDesktopServiceImpl.java +++ b/nex-be/src/main/java/com/unisinsight/project/service/impl/ImageDesktopServiceImpl.java @@ -3,6 +3,7 @@ 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.feign.ExternalTorrentClient; import com.unisinsight.project.mapper.ImageVirtualMachinesMapper; import org.springframework.stereotype.Service; import cn.hutool.core.bean.BeanUtil; @@ -47,10 +48,13 @@ public class ImageDesktopServiceImpl extends ServiceImpl> selectPage(ImageDesktopReq imageDesktopReq) { Page page = new Page<>(imageDesktopReq.getPageNum(), imageDesktopReq.getPageSize()); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.like(StringUtils.isNotBlank(imageDesktopReq.getDesktopName()), ImageDesktop::getDesktopName, imageDesktopReq.getDesktopName()); queryWrapper.orderByAsc(ImageDesktop::getId); Page imageDesktopPage = mapper.selectPage(page, queryWrapper); log.info("分页查询桌面镜像返回:{}", JSONUtil.toJsonStr(imageDesktopPage)); @@ -123,8 +127,13 @@ public class ImageDesktopServiceImpl extends ServiceImpl delete(DeleteIdReq deleteIdReq) { + ImageDesktop imageDesktop = mapper.selectById(deleteIdReq.getId()); + boolean b = externalTorrentClient.deleteFile(imageDesktop.getStoragePath()); + if (!b) { + return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "删除文件失败"); + } int deleted = mapper.deleteById(deleteIdReq.getId()); - log.info("桌面镜像删除insert:{}", deleted); + log.info("桌面镜像删除:{}", deleted); if (deleted == 1) { return Result.successResult(); } else { 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 ab8d684..6abe3fc 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 @@ -3,9 +3,11 @@ package com.unisinsight.project.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.unisinsight.project.entity.dao.*; @@ -25,10 +27,15 @@ 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.scheduling.TaskScheduler; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; import java.util.function.Function; import java.util.stream.Collectors; @@ -53,8 +60,21 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl> scheduledTasks = new ConcurrentHashMap<>(); + + // 存储任务开始时间的Map,用于超时检查 + private final Map taskStartTimes = new ConcurrentHashMap<>(); @Override @@ -282,8 +302,10 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl existsScheduledFuture = scheduledTasks.get(taskKey); + if (existsScheduledFuture != null) { + // 中断正在执行的任务 + existsScheduledFuture.cancel(true); + scheduledTasks.remove(taskKey); + taskStartTimes.remove(taskKey); + } + // 记录任务开始时间 + taskStartTimes.put(taskKey, LocalDateTime.now()); + + // 启动定时任务,每30秒检查一次进度 + ScheduledFuture scheduledFuture = taskScheduler.scheduleAtFixedRate(() -> { + try { + checkProgressAndHandle(imageName, detFilePath, fileName, taskKey); + } catch (Exception e) { + log.error("检查进度任务执行异常: ", e); + } + }, timer * 60 * 1000); + + // 将任务存入Map + scheduledTasks.put(taskKey, scheduledFuture); + log.info("已启动进度检查任务: {}", taskKey); + } + + /** + * 检查进度并处理结果 + * + * @param imageName 虚拟机名称 + * @param fileName 文件名称 + * @param taskKey 任务标识 + */ + private void checkProgressAndHandle(String imageName, String detFilePath, String fileName, String taskKey) { + // 检查是否超时(12小时) + LocalDateTime startTime = taskStartTimes.get(taskKey); + if (startTime != null && LocalDateTime.now().isAfter(startTime.plusHours(12))) { + log.warn("任务{}已超时12小时,自动移除", taskKey); + cancelAndRemoveTask(taskKey); + return; + } + + try { + // 获取进度 + Double progress = externalTorrentClient.progress(imageName); + log.info("任务{}当前进度: {}", taskKey, progress); + + // 如果进度达到100%或以上,执行特有操作 + if (progress != null && progress >= 100.0) { + log.info("任务{}已完成,执行特有操作", taskKey); + // 执行你的特有操作,防止执行出错 清除任务失败 + try { + handleTaskCompletion(imageName, detFilePath, fileName); + } finally { + // 移除任务 + cancelAndRemoveTask(taskKey); + } + + } + } catch (Exception e) { + log.error("获取进度失败: ", e); + } + } + + /** + * 处理任务完成后的特有操作 + * + * @param imageName 虚拟机名称 + * @param fileName 文件名称 + */ + private void handleTaskCompletion(String imageName, String detFilePath, String fileName) { + // 在这里添加你的特有操作 + log.info("执行任务完成后的特有操作,虚拟机名称: {}, det文件路径: {}, 文件名称: {}", imageName, detFilePath, fileName); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(ImageDesktop::getDesktopName, fileName); + Long fileSize = externalTorrentClient.getFileSize(detFilePath); + log.info("det文件大小: {} 字节", fileSize); + updateWrapper.set(ImageDesktop::getPublishStatus, "1"); + updateWrapper.set(ImageDesktop::getFileSize, fileSize); + updateWrapper.set(ImageDesktop::getPublishTime, DateUtil.date()); + //转化bt + String fileNamewithSuffixes = detFilePath.substring(detFilePath.lastIndexOf(fileName)); + externalTorrentClient.startBt(detFilePath, "/var/lib/vdi/test/" + fileNamewithSuffixes + ".torrent"); + updateWrapper.set(ImageDesktop::getBtPath, torrentUrl + "/api/vdi/file/down/" + fileNamewithSuffixes + ".torrent"); + imageDesktopService.update(updateWrapper); + System.out.println("任务已完成,执行特有操作..."); + } + + /** + * 取消并移除任务 + * + * @param taskKey 任务标识 + */ + private void cancelAndRemoveTask(String taskKey) { + ScheduledFuture scheduledFuture = scheduledTasks.get(taskKey); + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + scheduledTasks.remove(taskKey); + } + taskStartTimes.remove(taskKey); + log.info("已取消并移除任务: {}", taskKey); + } + @Override public Result start(ImageVirtualMachinesReq req) { // 查询虚拟机信息 diff --git a/torrent-be/src/main/java/com/unisinsight/torrent/controller/DeskImageController.java b/torrent-be/src/main/java/com/unisinsight/torrent/controller/DeskImageController.java index 2767b28..852866d 100644 --- a/torrent-be/src/main/java/com/unisinsight/torrent/controller/DeskImageController.java +++ b/torrent-be/src/main/java/com/unisinsight/torrent/controller/DeskImageController.java @@ -4,10 +4,7 @@ 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 org.springframework.web.bind.annotation.*; import java.util.Map; @@ -71,4 +68,28 @@ public class DeskImageController { return 0.0; } } + + + @GetMapping("/file/size") + @ApiOperation(value = "获取物理机文件大小") + Long getFileSize(@RequestParam("filePath") String filePath) { + // 根据det文件路径获取文件 + java.io.File detFile = new java.io.File(filePath); + // 检查文件是否存在 + if (detFile.exists()) { + return detFile.length(); + } + return 0L; + } + @DeleteMapping("/file") + @ApiOperation(value = "删除物理机文件") + boolean deleteFile(@RequestParam("filePath") String filePath) { + // 根据det文件路径获取文件 + java.io.File detFile = new java.io.File(filePath); + // 检查文件是否存在 + if (detFile.exists()) { + return detFile.delete(); + } + return false; + } }