Browse Source

Merge branch 'system-zjy' of uskycloud/usky-cloud into master

gez 6 days ago
parent
commit
04bd8fe3da

+ 6 - 0
base-modules/service-file/pom.xml

@@ -89,6 +89,12 @@
             <scope>runtime</scope>
             <scope>runtime</scope>
         </dependency>
         </dependency>
 
 
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.36</version>
+        </dependency>
+
     </dependencies>
     </dependencies>
 
 
     <build>
     <build>

+ 16 - 0
base-modules/service-file/src/main/java/com/ruoyi/file/config/MybatisPlusConfig.java

@@ -0,0 +1,16 @@
+package com.ruoyi.file.config;
+
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MybatisPlusConfig {
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
+        return interceptor;
+    }
+}

+ 127 - 0
base-modules/service-file/src/main/java/com/ruoyi/file/controller/FileUpdateInfoController.java

@@ -0,0 +1,127 @@
+package com.ruoyi.file.controller;
+
+import com.ruoyi.file.domain.FileUpdateInfo;
+import com.ruoyi.file.domain.FileWithId;
+import com.ruoyi.file.service.FileUpdateInfoService;
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.bean.CommonPage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/update")
+public class FileUpdateInfoController {
+
+    @Autowired
+    private FileUpdateInfoService fileUpdateInfoService;
+
+    //增加服务信息
+    @PostMapping("/add")
+    public ApiResult<String> addFileToDatabase(@RequestBody FileUpdateInfo fileUpdateInfo) {
+        try {
+            fileUpdateInfoService.addFileToDatabase(fileUpdateInfo);
+            return ApiResult.success("文件信息录入成功");
+        } catch (Exception e) {
+            return ApiResult.error("文件信息录入失败: " + e.getMessage());
+        }
+    }
+
+    //检查服务版本
+    @PostMapping("/checkById")
+    public ApiResult<String> checkFileUpdate(@RequestParam Long id) {
+        try {
+            boolean isUpdateRequired = fileUpdateInfoService.checkFileUpdate(id);
+            String result = isUpdateRequired ? "待更新" : "无需更新";
+            return ApiResult.success(result);
+        } catch (Exception e) {
+            return ApiResult.error("检查文件更新失败:远程仓库未找到对应服务");
+        }
+    }
+
+    //下载更新服务
+    @PostMapping("/reloadById")
+    public ApiResult<String> performFileUpdate(@RequestParam Long id) {
+        try {
+            fileUpdateInfoService.performFileUpdate(id);
+            return ApiResult.success("文件更新成功");
+        } catch (Exception e) {
+            return ApiResult.error("文件更新失败: " + e.getMessage());
+        }
+    }
+
+    //查询服务信息
+    @GetMapping("/query")
+    public ApiResult<CommonPage<FileUpdateInfo>> queryFiles(
+            @RequestParam(required = false) String fileName,
+            @RequestParam(required = false, defaultValue = "1") int current,
+            @RequestParam(required = false, defaultValue = "10") int size) {
+        try {
+            CommonPage<FileUpdateInfo> commonPage;
+            if (fileName == null || fileName.trim().isEmpty()) {
+                commonPage = fileUpdateInfoService.getAllFiles(current, size);
+            } else {
+                commonPage = fileUpdateInfoService.getFilesByFileNameContaining(fileName, current, size);
+            }
+            return ApiResult.success(commonPage);
+        } catch (Exception e) {
+            return ApiResult.error("查询文件信息失败: " + e.getMessage());
+        }
+    }
+
+    //删除服务信息
+    @DeleteMapping("/deleteById")
+    public ApiResult<String> deleteFileById(@RequestParam Long id) {
+        try {
+            fileUpdateInfoService.deleteFileById(id);
+            return ApiResult.success("文件信息删除成功");
+        } catch (Exception e) {
+            return ApiResult.error("删除文件信息失败: " + e.getMessage());
+        }
+    }
+
+    //控制服务运行状态(启动start、停止stop、重启restart)
+    @PostMapping("/controlById")
+    public ApiResult<String> controlServiceById(@RequestParam Long id, @RequestParam String action) {
+        try {
+            FileUpdateInfo fileUpdateInfo = fileUpdateInfoService.getFileById(id);
+            if (fileUpdateInfo == null) {
+                return ApiResult.error("未找到对应的文件信息,ID: " + id);
+            }
+            String fileName = fileUpdateInfo.getFileName();
+
+            // 在控制服务之前,先检测当前服务状态
+            int currentStatus = fileUpdateInfoService.getFileStatus(fileName);
+            if ((action.equals("start") && currentStatus == 1) || (action.equals("stop") && currentStatus == 2)) {
+                return ApiResult.error("服务已经是目标状态,无需重复操作");
+            }
+
+            fileUpdateInfoService.controlApplication(fileName, action);
+
+            // 根据 action 返回不同的成功消息
+            if ("start".equalsIgnoreCase(action)) {
+                return ApiResult.success("启动成功");
+            } else if ("stop".equalsIgnoreCase(action)) {
+                return ApiResult.success("停止成功");
+            } else if ("restart".equalsIgnoreCase(action)) {
+                return ApiResult.success("重启成功");
+            } else {
+                return ApiResult.success(action + "命令已发送");
+            }
+        } catch (Exception e) {
+            return ApiResult.error("控制服务失败: " + e.getMessage());
+        }
+    }
+
+    // 扫描服务所在目录下的文件
+    @GetMapping("/scan")
+    public ApiResult<CommonPage<FileWithId>> scanFilesInServiceDir(
+            @RequestParam(required = false, defaultValue = "1") int current,
+            @RequestParam(required = false, defaultValue = "10") int size) {
+        try {
+            CommonPage<FileWithId> fileNamesPage = fileUpdateInfoService.scanFilesInServiceDir(current, size);
+            return ApiResult.success(fileNamesPage);
+        } catch (Exception e) {
+            return ApiResult.error("扫描文件失败: " + e.getMessage());
+        }
+    }
+}

+ 7 - 8
base-modules/service-file/src/main/java/com/ruoyi/file/controller/FilesController.java

@@ -1,16 +1,15 @@
 package com.ruoyi.file.controller;
 package com.ruoyi.file.controller;
 
 
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.core.domain.R;
 import com.ruoyi.common.core.domain.R;
 import com.ruoyi.file.service.FileUploadResponse;
 import com.ruoyi.file.service.FileUploadResponse;
-import com.ruoyi.file.service.FilesUpload;
+import com.ruoyi.file.domain.FilesUpload;
 import com.ruoyi.file.service.FilesService;
 import com.ruoyi.file.service.FilesService;
+import com.usky.common.core.bean.ApiResult;
 import com.usky.common.core.bean.CommonPage;
 import com.usky.common.core.bean.CommonPage;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;
 
 
-import javax.servlet.http.HttpServletResponse;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 
 
 
 
@@ -22,20 +21,20 @@ public class FilesController {
     private FilesService filesService;
     private FilesService filesService;
 
 
     @PostMapping("/upload")
     @PostMapping("/upload")
-    public R<FileUploadResponse> upload(@RequestParam MultipartFile file) {
+    public ApiResult<FileUploadResponse> upload(@RequestParam MultipartFile file) {
         // 获取上传文件
         // 获取上传文件
         FileUploadResponse response = filesService.upload(file);
         FileUploadResponse response = filesService.upload(file);
-        return R.ok(response);
+        return ApiResult.success(response);
     }
     }
 
 
 
 
     @DeleteMapping("/delete/{id}")
     @DeleteMapping("/delete/{id}")
-    public R<Void> delete(@PathVariable Integer id) {
+    public ApiResult<String> delete(@PathVariable Integer id) {
         try {
         try {
             filesService.deleteFile(id);
             filesService.deleteFile(id);
-            return R.ok();
+            return ApiResult.success("删除成功");
         } catch (Exception e) {
         } catch (Exception e) {
-            return R.fail(e.getMessage()); // 删除失败,返回错误信息
+            return ApiResult.error("删除失败"); // 删除失败,返回错误信息
         }
         }
     }
     }
 
 

+ 82 - 0
base-modules/service-file/src/main/java/com/ruoyi/file/domain/FileUpdateInfo.java

@@ -0,0 +1,82 @@
+package com.ruoyi.file.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+
+import java.io.File;
+import java.net.URL;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("file_update_info")
+public class FileUpdateInfo {
+    private Long id;
+    private String fileName; // 文件名
+    private String fileMd5;
+    private String remoteMd5;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    private int updateStatus;    // 0表示无需更新,1表示待更新
+
+    // 新增版本号字段
+    @TableField(fill = FieldFill.INSERT)
+    private String version = "v1.0.0"; // 默认版本号
+
+    // 新增运行状态字段
+    private int fileStatus; // 0:未知,1:运行中,2:已停止
+
+    // 获取带版本号的文件名
+//    @JsonIgnore // 忽略该字段的序列化
+//    public String getVersionedFileName() {
+//        return fileName.substring(0, fileName.lastIndexOf('.')) + "-" + version + fileName.substring(fileName.lastIndexOf('.'));
+//    }
+
+    // 获取文件完整路径(基于 JAR 文件所在的目录)
+    @JsonIgnore // 忽略该字段的序列化
+    public String getFilePath() {
+        return getServiceDir() + File.separator + fileName;
+    }
+
+    // 动态获取 JAR 文件所在的目录
+    public static String getServiceDir() {
+        try {
+            // 获取当前类的 ClassLoader
+            ClassLoader classLoader = FileUpdateInfo.class.getClassLoader();
+            // 获取当前类的资源路径(例如 JAR 文件路径)
+            URL url = classLoader.getResource(FileUpdateInfo.class.getName().replace('.', '/') + ".class");
+            if (url != null && "jar".equals(url.getProtocol())) {
+                // 从 JAR 文件中获取路径
+                String jarPath = url.getPath();
+                // 去掉 JAR 文件内部路径部分
+                int bangIndex = jarPath.indexOf("!/");
+                if (bangIndex > 0) {
+                    jarPath = jarPath.substring(0, bangIndex);
+                }
+                // 去掉 "file:" 前缀
+                if (jarPath.startsWith("file:")) {
+                    jarPath = jarPath.substring(5);
+                }
+                // 获取 JAR 文件所在的目录
+                return new java.io.File(jarPath).getParent();
+            } else if (url != null && "file".equals(url.getProtocol())) {
+                // 如果是直接运行的文件路径
+                String filePath = url.getPath();
+                return new java.io.File(filePath).getParent();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return System.getProperty("user.dir"); // 如果无法获取 JAR 路径,退回到当前工作目录
+    }
+}

+ 14 - 0
base-modules/service-file/src/main/java/com/ruoyi/file/domain/FileWithId.java

@@ -0,0 +1,14 @@
+package com.ruoyi.file.domain;
+
+import lombok.Data;
+
+@Data
+public class FileWithId {
+    private Long id;
+    private String fileName;
+
+    public FileWithId(Long id, String fileName) {
+        this.id = id;
+        this.fileName = fileName;
+    }
+}

+ 1 - 1
base-modules/service-file/src/main/java/com/ruoyi/file/service/FilesUpload.java → base-modules/service-file/src/main/java/com/ruoyi/file/domain/FilesUpload.java

@@ -1,4 +1,4 @@
-package com.ruoyi.file.service;
+package com.ruoyi.file.domain;
 
 
 import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableField;

+ 9 - 0
base-modules/service-file/src/main/java/com/ruoyi/file/mapper/FileUpdateInfoMapper.java

@@ -0,0 +1,9 @@
+package com.ruoyi.file.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.file.domain.FileUpdateInfo;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface FileUpdateInfoMapper extends BaseMapper<FileUpdateInfo> {
+}

+ 1 - 1
base-modules/service-file/src/main/java/com/ruoyi/file/mapper/FilesMapper.java

@@ -1,7 +1,7 @@
 package com.ruoyi.file.mapper;
 package com.ruoyi.file.mapper;
 
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.ruoyi.file.service.FilesUpload;
+import com.ruoyi.file.domain.FilesUpload;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Mapper;
 
 
 @Mapper
 @Mapper

+ 50 - 0
base-modules/service-file/src/main/java/com/ruoyi/file/service/FileUpdateInfoService.java

@@ -0,0 +1,50 @@
+package com.ruoyi.file.service;
+
+import com.ruoyi.file.domain.FileUpdateInfo;
+import com.ruoyi.file.domain.FileWithId;
+import com.usky.common.core.bean.CommonPage;
+import org.springframework.web.bind.annotation.RequestParam;
+
+public interface FileUpdateInfoService {
+
+    /**
+     * 获取文件的运行状态
+     * @param fileName 文件名
+     * @return 状态码(1: 运行中,2: 已停止,0: 未知)
+     */
+    int getFileStatus(String fileName);
+
+    //添加服务信息
+    void addFileToDatabase(FileUpdateInfo fileUpdateInfo) throws Exception;
+
+    //检查服务更新
+    boolean checkFileUpdate(Long id) throws Exception;
+
+    //执行服务更新
+    void performFileUpdate(Long id) throws Exception;
+
+    // 获取所有服务信息(分页)
+    CommonPage<FileUpdateInfo> getAllFiles(
+            @RequestParam(required = false, defaultValue = "1") int current,
+            @RequestParam(required = false, defaultValue = "10") int size) throws Exception;
+
+    // 根据文件名模糊查询服务信息(分页)
+    CommonPage<FileUpdateInfo> getFilesByFileNameContaining(
+            @RequestParam(required = false) String fileName,
+            @RequestParam(required = false, defaultValue = "1") int current,
+            @RequestParam(required = false, defaultValue = "10") int size) throws Exception;
+
+    //根据id查询服务信息
+    FileUpdateInfo getFileById(Long id) throws Exception;
+
+    //删除服务信息
+    void deleteFileById(Long id) throws Exception;
+
+    //控制服务运行状态
+    void controlApplication(String fileName, String action) throws Exception;
+
+    // 扫描 JAR 文件所在的目录并获取文件列表
+    CommonPage<FileWithId> scanFilesInServiceDir(
+            @RequestParam(required = false, defaultValue = "1") int current,
+            @RequestParam(required = false, defaultValue = "10") int size) throws Exception;
+}

+ 1 - 2
base-modules/service-file/src/main/java/com/ruoyi/file/service/FilesService.java

@@ -1,11 +1,10 @@
 package com.ruoyi.file.service;
 package com.ruoyi.file.service;
 
 
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.file.domain.FilesUpload;
 import com.usky.common.core.bean.CommonPage;
 import com.usky.common.core.bean.CommonPage;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;
 
 
-import javax.servlet.http.HttpServletResponse;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 
 
 public interface FilesService extends IService<FilesUpload> {
 public interface FilesService extends IService<FilesUpload> {

+ 416 - 0
base-modules/service-file/src/main/java/com/ruoyi/file/service/impl/FileUpdateInfoServiceImpl.java

@@ -0,0 +1,416 @@
+package com.ruoyi.file.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.file.domain.FileUpdateInfo;
+import com.ruoyi.file.domain.FileWithId;
+import com.ruoyi.file.mapper.FileUpdateInfoMapper;
+import com.ruoyi.file.service.FileUpdateInfoService;
+import com.usky.common.core.bean.CommonPage;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class FileUpdateInfoServiceImpl implements FileUpdateInfoService {
+    private static final Logger logger = LoggerFactory.getLogger(FileUpdateInfoServiceImpl.class);
+
+    @Autowired
+    private FileUpdateInfoMapper fileUpdateInfoMapper;
+
+    @Value("${file.remote-url}")
+    private String remoteUrlBase;
+
+    // 从配置文件中读取脚本名称
+    @Value("${file.linuxrestart}")
+    private String linuxContralScript;
+
+    @Value("${file.windowsstop}")
+    private String windowsStopScript;
+
+    @Value("${file.windowsstart}")
+    private String windowsStartScript;
+
+    @Override
+    public void addFileToDatabase(FileUpdateInfo fileUpdateInfo) throws Exception {
+        // 假设前端只传入文件名
+        String fileName = fileUpdateInfo.getFileName();
+        if (fileName == null || fileName.trim().isEmpty()) {
+            throw new IllegalArgumentException("文件名不能为空");
+        }
+
+        // 设置文件路径
+        String filePath = FileUpdateInfo.getServiceDir() + File.separator + fileUpdateInfo.getFileName();
+
+        File file = new File(filePath);
+
+        if (file.exists()) {
+            // 如果文件存在,计算本地文件的 MD5 值
+            String fileMd5 = DigestUtils.md5Hex(new FileInputStream(file));
+            fileUpdateInfo.setFileMd5(fileMd5);
+            fileUpdateInfo.setUpdateStatus(0);
+        } else {
+            // 如果文件不存在,设置状态为“待更新”
+            fileUpdateInfo.setFileMd5(null);
+            fileUpdateInfo.setUpdateStatus(1);
+        }
+
+        fileUpdateInfo.setVersion("v1.0.0"); // 初始化版本号
+        fileUpdateInfo.setCreateTime(LocalDateTime.now());
+        fileUpdateInfo.setUpdateTime(null);
+        fileUpdateInfoMapper.insert(fileUpdateInfo);
+    }
+
+    //检查是否需要更新
+    @Override
+    public boolean checkFileUpdate(Long id) throws Exception {
+        FileUpdateInfo fileUpdateInfo = fileUpdateInfoMapper.selectById(id);
+        if (fileUpdateInfo == null) {
+            throw new FileNotFoundException("文件信息未找到:id=" + id);
+        }
+
+        String localFilePath = fileUpdateInfo.getFilePath(); // 使用新的 getFilePath 方法
+        File localFile = new File(localFilePath);
+
+        String remoteFileMd5 = getRemoteFileMd5(fileUpdateInfo.getFileName());
+
+        if (!localFile.exists()) {
+            // 如果本地文件不存在,设置状态为“待更新”
+            fileUpdateInfo.setRemoteMd5(remoteFileMd5);
+            fileUpdateInfo.setUpdateStatus(1); // 设置为待更新
+            fileUpdateInfoMapper.updateById(fileUpdateInfo);
+            return true; // 需要更新
+        } else {
+            // 如果本地文件存在,比较 MD5 值
+            String localFileMd5 = DigestUtils.md5Hex(new FileInputStream(localFile));
+            boolean isUpdateRequired = !localFileMd5.equals(remoteFileMd5);
+            fileUpdateInfo.setRemoteMd5(remoteFileMd5);
+            fileUpdateInfo.setUpdateStatus(isUpdateRequired ? 1 : 0);  // 0表示无需更新,1表示待更新
+            fileUpdateInfoMapper.updateById(fileUpdateInfo);
+            return isUpdateRequired;
+        }
+    }
+
+    //下载更新
+    @Override
+    public void performFileUpdate(Long id) throws Exception {
+        FileUpdateInfo fileUpdateInfo = fileUpdateInfoMapper.selectById(id);
+        if (fileUpdateInfo == null) {
+            throw new FileNotFoundException("文件信息未找到:id=" + id);
+        }
+
+        String localFilePath = fileUpdateInfo.getFilePath(); // 使用新的 getFilePath 方法
+        String fileName = fileUpdateInfo.getFileName();
+        String remoteUrl = remoteUrlBase + fileName;
+
+        try {
+            // 获取 JAR 文件所在的目录
+            String serviceDir = FileUpdateInfo.getServiceDir();
+            String backupDirPath = serviceDir + File.separator + "backup";
+            File backupDir = new File(backupDirPath);
+            if (!backupDir.exists()) {
+                boolean created = backupDir.mkdirs();
+                if (!created) {
+                    throw new IOException("无法创建备份目录: " + backupDirPath);
+                }
+            }
+
+            File originalFile = new File(localFilePath);
+            if (originalFile.exists()) {
+                String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+                String backupFilePath = backupDirPath + File.separator + fileName + "." + timestamp;
+                Files.move(originalFile.toPath(), Paths.get(backupFilePath), StandardCopyOption.REPLACE_EXISTING);
+                System.out.println("旧文件已备份到: " + backupFilePath);
+            }
+
+            downloadFileFromRemote(remoteUrl, localFilePath);
+
+            String newFileMd5 = DigestUtils.md5Hex(new FileInputStream(localFilePath));
+            fileUpdateInfo.setFileMd5(newFileMd5);
+            fileUpdateInfo.setCreateTime(fileUpdateInfo.getCreateTime());
+            fileUpdateInfo.setUpdateTime(LocalDateTime.now());
+            fileUpdateInfo.setUpdateStatus(0);
+
+            // 递增版本号
+            String currentVersion = fileUpdateInfo.getVersion();
+            String[] versionParts = currentVersion.substring(1).split("\\."); // 去掉 'v' 并拆分版本号
+            int major = Integer.parseInt(versionParts[0]);
+            int minor = Integer.parseInt(versionParts[1]);
+            int patch = Integer.parseInt(versionParts[2]) + 1; // 小版本号加 1
+            fileUpdateInfo.setVersion("v" + major + "." + minor + "." + patch);
+
+            fileUpdateInfoMapper.updateById(fileUpdateInfo);
+
+            // 下载完成后重启服务
+            controlApplication(fileName, "restart");
+
+        } catch (FileNotFoundException e) {
+            throw new Exception("文件未找到: " + localFilePath + ",原因: " + e.getMessage(), e);
+        } catch (IOException e) {
+            throw new Exception("文件写入失败: " + localFilePath + ",原因: " + e.getMessage(), e);
+        } catch (Exception e) {
+            throw new Exception("文件更新失败: " + localFilePath + ",原因: " + e.getMessage(), e);
+        }
+    }
+
+    private void downloadFileFromRemote(String remoteUrl, String localFilePath) throws Exception {
+        URL url = new URL(remoteUrl);
+        Path path = Paths.get(localFilePath);
+        try {
+            Files.copy(url.openStream(), path, StandardCopyOption.REPLACE_EXISTING);
+            System.out.println("文件下载成功: " + localFilePath);
+        } catch (IOException e) {
+            System.err.println("文件下载失败: " + localFilePath + ",原因: " + e.getMessage());
+            throw new Exception("文件下载失败: " + localFilePath, e);
+        }
+    }
+
+    private String getRemoteFileMd5(String fileName) throws Exception {
+        String remoteUrl = remoteUrlBase + fileName;
+        System.out.println("远程文件 URL: " + remoteUrl);
+        URL url = new URL(remoteUrl);
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestProperty("Cache-Control", "no-cache");
+        try (InputStream in = connection.getInputStream()) {
+            return DigestUtils.md5Hex(in);
+        }
+    }
+
+    @Override
+    public CommonPage<FileUpdateInfo> getAllFiles(int current, int size) throws Exception {
+        // 使用 MyBatis-Plus 的分页功能
+        Page<FileUpdateInfo> page = new Page<>(current, size);
+        IPage<FileUpdateInfo> result = fileUpdateInfoMapper.selectPage(page, null);
+
+        // 更新每个文件的运行状态
+        for (FileUpdateInfo fileUpdateInfo : result.getRecords()) {
+            checkServiceStatus(fileUpdateInfo);
+        }
+
+        // 将 MyBatis-Plus 的分页结果转换为 CommonPage
+        return new CommonPage<>(
+                result.getRecords(),
+                (int) result.getTotal(),
+                size,
+                current
+        );
+    }
+
+    @Override
+    public CommonPage<FileUpdateInfo> getFilesByFileNameContaining(String fileName, int current, int size) throws Exception {
+        // 使用 MyBatis-Plus 的分页功能
+        Page<FileUpdateInfo> page = new Page<>(current, size);
+        QueryWrapper<FileUpdateInfo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.like("file_name", fileName);
+        IPage<FileUpdateInfo> result = fileUpdateInfoMapper.selectPage(page, queryWrapper);
+
+        // 更新每个文件的运行状态
+        for (FileUpdateInfo fileUpdateInfo : result.getRecords()) {
+            checkServiceStatus(fileUpdateInfo);
+        }
+
+        // 将 MyBatis-Plus 的分页结果转换为 CommonPage
+        return new CommonPage<>(
+                result.getRecords(),
+                (int) result.getTotal(),
+                size,
+                current
+        );
+    }
+
+    @Override
+    public FileUpdateInfo getFileById(Long id) {
+        return fileUpdateInfoMapper.selectById(id);
+    }
+
+    @Override
+    public void deleteFileById(Long id) {
+        fileUpdateInfoMapper.deleteById(id);
+    }
+
+    @Override
+    public void controlApplication(String fileName, String action) throws Exception {
+        String osName = System.getProperty("os.name").toLowerCase();
+
+        if (osName.contains("linux") || osName.contains("unix")) {
+            // Linux 系统
+            String command = "sh " + linuxContralScript + " " + action + " " + fileName;
+            Runtime.getRuntime().exec(command);
+        } else if (osName.contains("windows")) {
+            // Windows 系统
+            String command;
+            switch (action.toLowerCase()) {
+                case "start":
+                    command = windowsStartScript + " " + fileName;
+                    break;
+                case "stop":
+                    command = windowsStopScript + " " + fileName;
+                    break;
+                case "restart":
+                    // Windows 系统没有单独的重启脚本,需要先停止再启动
+                    Runtime.getRuntime().exec(windowsStopScript + " " + fileName);
+                    Thread.sleep(3000); // 等待 3 秒,确保服务停止
+                    Runtime.getRuntime().exec(windowsStartScript + " " + fileName);
+                    return;
+                default:
+                    throw new IllegalArgumentException("不支持的操作类型: " + action);
+            }
+            Runtime.getRuntime().exec(command);
+        } else {
+            throw new Exception("不支持的操作系统: " + osName);
+        }
+
+        // 等待操作完成
+        long startTime = System.currentTimeMillis();
+        long timeout = 8000; // 最长等待时间 8 秒
+        boolean isActionCompleted = false;
+
+        while (System.currentTimeMillis() - startTime < timeout) {
+            int currentStatus = getFileStatus(fileName); // 获取当前服务状态
+            if (action.equals("start") && currentStatus == 1) {
+                isActionCompleted = true;
+                break; // 服务已启动
+            } else if (action.equals("stop") && currentStatus == 2) {
+                isActionCompleted = true;
+                break; // 服务已停止
+            } else if (action.equals("restart") && currentStatus == 1) {
+                isActionCompleted = true;
+                break; // 服务已重启
+            }
+
+            Thread.sleep(500); // 每隔 500 毫秒检测一次状态
+        }
+
+        if (!isActionCompleted) {
+            throw new Exception("操作超时,服务状态未达到预期");
+        }
+    }
+
+    //扫描服务所在目录下的文件
+    @Override
+    public CommonPage<FileWithId> scanFilesInServiceDir(int current, int size) throws Exception {
+        // 获取 JAR 文件所在的目录
+        String serviceDir = FileUpdateInfo.getServiceDir();
+        File dir = new File(serviceDir);
+
+        if (!dir.exists() || !dir.isDirectory()) {
+            throw new Exception("无法找到服务目录: " + serviceDir);
+        }
+
+        // 获取目录中的所有文件名
+        File[] files = dir.listFiles();
+        if (files == null) {
+            throw new Exception("无法读取目录内容: " + serviceDir);
+        }
+
+        // 提取文件名
+        List<String> fileNames = new ArrayList<>();
+        for (File file : files) {
+            if (file.isFile()) {
+                fileNames.add(file.getName());
+            }
+        }
+
+        // 查询数据库中已存在的文件名
+        List<String> existingFileNames = fileUpdateInfoMapper.selectList(null).stream()
+                .map(FileUpdateInfo::getFileName)
+                .collect(Collectors.toList());
+
+        // 从扫描结果中排除已存在的文件名
+        List<String> newFileNames = fileNames.stream()
+                .filter(fileName -> !existingFileNames.contains(fileName))
+                .collect(Collectors.toList());
+
+        // 对文件名列表进行分页处理
+        int total = newFileNames.size();
+        int start = (current - 1) * size;
+        int end = Math.min(start + size, total);
+
+        List<String> pageFileNames = newFileNames.subList(start, end);
+
+        // 创建包含 id 的文件信息列表
+        List<FileWithId> fileWithIds = new ArrayList<>();
+        for (int i = 0; i < pageFileNames.size(); i++) {
+            fileWithIds.add(new FileWithId((long) (start + i + 1), pageFileNames.get(i)));
+        }
+
+        // 返回分页结果
+        return new CommonPage<>(fileWithIds, total, size, current);
+    }
+
+
+    // 检测服务运行状态并更新到数据库
+    private void checkServiceStatus(FileUpdateInfo fileUpdateInfo) {
+        String fileName = fileUpdateInfo.getFileName();
+        int fileStatus = getFileStatus(fileName); // 调用方法获取文件运行状态
+        fileUpdateInfo.setFileStatus(fileStatus); // 更新运行状态
+        fileUpdateInfoMapper.updateById(fileUpdateInfo); // 更新数据库
+    }
+
+    // 获取文件运行状态
+    @Override
+    public int getFileStatus(String fileName) {
+        String osName = System.getProperty("os.name").toLowerCase();
+
+        if (osName.contains("linux") || osName.contains("unix")) {
+            // Linux 系统
+            String command = "pgrep -f " + fileName;
+            try {
+                Process process = Runtime.getRuntime().exec(command);
+                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String line;
+                boolean isRunning = false;
+                while ((line = reader.readLine()) != null) {
+                    if (line != null && !line.isEmpty()) {
+                        isRunning = true;
+                        break;
+                    }
+                }
+                return isRunning ? 1 : 2; // 1: 运行中,2: 已停止
+            } catch (IOException e) {
+                logger.error("Failed to execute command: {}", command, e);
+            }
+        } else if (osName.contains("windows")) {
+            // Windows 系统
+            String command = "sc query " + fileName;
+            try {
+                Process process = Runtime.getRuntime().exec(command);
+                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+                String line;
+                boolean isRunning = false;
+                while ((line = reader.readLine()) != null) {
+                    if (line.contains("RUNNING")) {
+                        isRunning = true;
+                        break;
+                    } else if (line.contains("STOPPED")) {
+                        break;
+                    }
+                }
+                return isRunning ? 1 : 2; // 1: 运行中,2: 已停止
+            } catch (IOException e) {
+                logger.error("Failed to execute command: {}", command, e);
+            }
+        } else {
+            logger.warn("Unsupported operating system: {}", osName);
+        }
+        return 0; // 未知
+    }
+}

+ 5 - 6
base-modules/service-file/src/main/java/com/ruoyi/file/service/FilesServiceImpl.java → base-modules/service-file/src/main/java/com/ruoyi/file/service/impl/FilesServiceImpl.java

@@ -1,21 +1,20 @@
-package com.ruoyi.file.service;
+package com.ruoyi.file.service.impl;
 
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.file.mapper.FilesMapper;
 import com.ruoyi.file.mapper.FilesMapper;
+import com.ruoyi.file.service.FileUploadResponse;
+import com.ruoyi.file.service.FilesService;
+import com.ruoyi.file.domain.FilesUpload;
 import com.usky.common.core.bean.CommonPage;
 import com.usky.common.core.bean.CommonPage;
 import com.usky.common.security.utils.SecurityUtils;
 import com.usky.common.security.utils.SecurityUtils;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;
 
 
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 import java.io.*;
-import java.net.URLEncoder;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
 
 
@@ -209,7 +208,7 @@ public class FilesServiceImpl extends ServiceImpl<FilesMapper, FilesUpload> impl
         // 将 MyBatis Plus 的 IPage 转换为 CommonPage
         // 将 MyBatis Plus 的 IPage 转换为 CommonPage
         CommonPage<FilesUpload> commonPage = new CommonPage<>();
         CommonPage<FilesUpload> commonPage = new CommonPage<>();
         commonPage.setRecords(iPage.getRecords());
         commonPage.setRecords(iPage.getRecords());
-        commonPage.setTotal(iPage.getRecords().size());
+        commonPage.setTotal(iPage.getTotal());
         commonPage.setSize(iPage.getSize());
         commonPage.setSize(iPage.getSize());
         commonPage.setCurrent(iPage.getCurrent());
         commonPage.setCurrent(iPage.getCurrent());