ludc
2024-02-24 156e136377680ac2dd5ad89735b7273db6f6d1d5
Source/UBCS/ubcs-service/ubcs-deploy/src/main/java/com/vci/ubcs/deploy/service/impl/DeployAppsServiceImpl.java
@@ -5,45 +5,98 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.shaded.com.google.protobuf.ServiceException;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.vci.ubcs.deploy.entity.CmdConfig;
import com.vci.ubcs.deploy.entity.DeployApps;
import com.vci.ubcs.deploy.entity.Instance;
import com.vci.ubcs.deploy.enumpack.CmdConfigEnum;
import com.vci.ubcs.deploy.mapper.DeployAppsMapper;
import com.vci.ubcs.deploy.service.IDeployAppsService;
import com.vci.ubcs.deploy.vo.DeployAppsVO;
import com.vci.ubcs.starter.util.HttpUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.UrlUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
/**
 * @author ludc
 * @date 2024/1/7 19:54
 */
@Service
public class DeployAppsServiceImpl extends ServiceImpl<DeployAppsMapper, DeployApps> implements IDeployAppsService {
@RequiredArgsConstructor
@Slf4j
public class DeployAppsServiceImpl extends ServiceImpl<DeployAppsMapper, DeployApps> implements IDeployAppsService, EnvironmentAware {
   @Autowired
   private DeployAppsMapper deployAppsMapper;
   private final DeployAppsMapper deployAppsMapper;
   @Value("${password-free.pwd-free-addr:localhost}")
   private String pwdFreeAddr;
   // 通过服务注册中心获取网关的端口号
   /**
    * 通过服务注册中心获取网关的端口号
    */
   @Autowired
   private DiscoveryClient discoveryClient;
   /**
    * 各个服务存放的的父路径
    */
   @Value("${local-log.parent-path:/data1/ubcs/ubcs-server}")
   private String PARENTPATH;
   /**
    * 日志文件的具体位置
    */
   @Value("${local-log.log-path:/target/log}")
   private String LOGPATH;
   /**
    * 根据操作系统生成分隔符
    */
   private String SEPARATOR = "/";
   /**
    * 根据当前运行的环境,对配置的日志路径格式进行调整
    * @param environment
    */
   @Override
   public void setEnvironment(Environment environment) {
      String os = environment.getProperty("os.name").toLowerCase();
      if (!os.contains("win")) {
         this.SEPARATOR = "/";
      }else{
         this.SEPARATOR = "\\";
      }
   }
   @Override
   public List<DeployAppsVO> getApplications(ServletRequest servletRequest) throws ServiceException {
@@ -80,12 +133,252 @@
               deployAppsVO.setStatusTimestamp(jsonObject.get("statusTimestamp").toString());
               deployAppsVOList.add(deployAppsVO);
            }
         }
      }catch (Exception e){
         throw new ServiceException("调用ubcs-admin获取服务信息失败,原因:"+e.getMessage());
      }
      // 再查询库中已经存在的服务配置信息,进行是否已经启动的判断
      List<DeployApps> deployApps = deployAppsMapper.selectList(null);
      // 库中未配置,直接返回正在运行的服务信息
      if(deployApps.isEmpty()){
      return deployAppsVOList;
      }
      // 筛选出不在运行的并生成默认的服务信息
      List<DeployAppsVO> deployAppsVOS1 = deployApps.stream()
         .filter(deployApp -> deployAppsVOList.stream()
            .noneMatch(deployAppVO -> deployApp.getServerName().equals(deployAppVO.getName()) &&
               deployApp.getServerName().equals(deployAppVO.getName())))
         .map(deployApp -> {
            DeployAppsVO deployAppsVO2 = new DeployAppsVO(deployApp.getServerName(), "DOWN", "", "", 0);
            if(deployApp.getServerName().equals("web")){
               deployAppsVO2.setStatus("UP");
               deployAppsVO2.setPort("8080");
               deployAppsVO2.setServiceNum(1);
            }
            return deployAppsVO2;
         })
         .collect(Collectors.toList());
      deployAppsVOList.addAll(deployAppsVOS1);
      return deployAppsVOList;
   }
   @Override
   public DeployApps saveOrGetServiceConfInfo(DeployAppsVO deployAppsVO) throws ServiceException {
      if(deployAppsVO.getName().isEmpty()){
         throw new ServiceException("缺少必传参数name");
      }
      DeployApps deployApps = deployAppsMapper.selectOne(Wrappers.<DeployApps>query().lambda().eq(DeployApps::getServerName, deployAppsVO.getName()));
      if(Func.isNotEmpty(deployApps)){
         return deployApps;
      }
      // 未从库中查询到,需要生成服务信息保存默认信息到库中
      DeployApps defaultDeployApps = new DeployApps();
      defaultDeployApps.setLogPath(PARENTPATH + "\\" + deployAppsVO.getName().replace("-","_") + LOGPATH);
      defaultDeployApps.setServerName(deployAppsVO.getName());
      defaultDeployApps.setStartCmd(CmdConfigEnum.START_CMD.getValue() + deployAppsVO.getName());
      defaultDeployApps.setStopCmd(CmdConfigEnum.STOP_CMD.getValue() + deployAppsVO.getName());
      defaultDeployApps.setRestartCmd(CmdConfigEnum.RESTART_CMD.getValue() + deployAppsVO.getName());
      defaultDeployApps.setServerPath(PARENTPATH + "\\" + deployAppsVO.getName().replace("-","_"));
      int eft = deployAppsMapper.insert(defaultDeployApps);
      if (!SqlHelper.retBool(eft)) {
         throw new ServiceException("生成默认服务信息到库中时失败!");
      }
      return defaultDeployApps;
   }
   /**
    * 修改或保存
    * @param deployAppsVO
    * @return
    * @throws ServiceException
    */
   @Override
   public boolean saveOrUpdateServiceInfo(DeployAppsVO deployAppsVO) throws ServiceException {
      boolean b = this.saveOrUpdate(deployAppsVO);
      return b;
   }
   /**
    * 新增服务信息
    * @param deployApps
    * @return
    * @throws ServiceException
    */
   @Override
   public boolean addSave(DeployApps deployApps) throws ServiceException {
      if (Func.isBlank(deployApps.getServerName()) || Func.isBlank(deployApps.getServerPath())) {
         throw new ServiceException("必传参数[服务名称,服务存放路径]不能为空");
      }
      return SqlHelper.retBool(deployAppsMapper.insert(deployApps));
   }
   /**
    * 更新文件上传
    * @param files
    * @param serverName
    * @return
    */
   @Override
   public R importClassify(MultipartFile[] files, String serverName) throws ServiceException {
      // 根据服务名查看到服务相关信息
      List<DeployApps> deployAppsDB = deployAppsMapper.selectList(Wrappers.<DeployApps>query().lambda().eq(DeployApps::getServerName, serverName));
      if(deployAppsDB.isEmpty()){
         return R.fail("No configuration information related to "+ serverName +" service found");
      }
      // 遍历MultipartFile数组,逐个处理文件
      try {
         for (MultipartFile file : files) {
            // 配置了备份文件路径,先备份再替换
            if(Func.isNotEmpty(deployAppsDB.get(0).getFileBack())){
               File backFile = new File(deployAppsDB.get(0).getFileBack());
               // 路径不存在就创建
               if (!backFile.exists()) {
                  backFile.mkdirs();
               }
               String backName = "";
               String fileType = "file";
               // 是压缩文件,因为只会存在两种情况,文件名是压缩文件,或者文件(.jar类型的文件)
               if (file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) {
                  backName = file.getOriginalFilename().replace(".zip","_"+Func.formatDate(new Date()));
                  fileType = "zip";
               }else{
                  backName = file.getOriginalFilename().replace(".","_"+Func.formatDate(new Date())+".");
                  fileType = "file";
               }
               File source = new File(deployAppsDB.get(0).getServerPath() + this.SEPARATOR + file.getOriginalFilename().replace(".zip", ""));
               File destination = new File(deployAppsDB.get(0).getFileBack() + this.SEPARATOR + backName);
               copyFolder(source, destination);
            }
            Path filePath = Paths.get(deployAppsDB.get(0).getServerPath(), file.getOriginalFilename());
            Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
            // 检查文件类型,如果是压缩文件则解压缩
            if (file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) {
               //sourcePath压缩包文件路径
               try (ZipFile zipFile = new ZipFile(new File(deployAppsDB.get(0).getServerPath()+ this.SEPARATOR +file.getOriginalFilename()))) {
                  Enumeration enumeration = zipFile.entries();
                  while (enumeration.hasMoreElements()) {
                     //依次获取压缩包内的文件实体对象
                     ZipEntry entry = (ZipEntry) enumeration.nextElement();
                     String name = entry.getName();
                     if (entry.isDirectory()) {
                        continue;
                     }
                     try (BufferedInputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) {
                        // 需要判断文件所在的目录是否存在,处理压缩包里面有文件夹的情况
                        String outName = deployAppsDB.get(0).getServerPath() + this.SEPARATOR + name;
                        File outFile = new File(outName);
                        File tempFile = new File(outName.substring(0, outName.lastIndexOf("/")));
                        if (!tempFile.exists()) {
                           tempFile.mkdirs();
                        }
                        try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outFile))) {
                           int len;
                           byte[] buffer = new byte[1024];
                           while ((len = inputStream.read(buffer)) > 0) {
                              outputStream.write(buffer, 0, len);
                           }
                        }
                     }
                  }
               } catch (Exception e) {
                  e.printStackTrace();
               }
               File file1 = new File(deployAppsDB.get(0).getServerPath() + SEPARATOR + file.getOriginalFilename());
               // 压缩文件上传成功之后,删除解压文件
               file1.delete();
            }
         }
         String output = excute(deployAppsDB.get(0),"UP");
         return R.success(output.toString());
      } catch (IOException e) {
         e.printStackTrace();
         log.error(e.getMessage());
         return R.fail("Failed to upload files");
      }
   }
   /**
    * 执行命令
    * @param deployAppsVO
    * @return
    */
   @Override
   public R cmdExecute(DeployAppsVO deployAppsVO) throws ServiceException {
      String excuteRes = "";
      try {
         List<DeployApps> deployAppsDB = deployAppsMapper.selectList(Wrappers.<DeployApps>query().lambda().eq(DeployApps::getServerName, deployAppsVO.getName()));
         if(deployAppsDB.isEmpty()){
            return R.fail("命令执行出错,库中未找到"+ deployAppsVO.getName() +"服务相关配置:" );
         }
         excuteRes = excute(deployAppsDB.get(0),deployAppsVO.getStatus());
         return R.success("命令执行结束:"+excuteRes);
      }catch (Exception e){
         throw new ServiceException(e.getMessage());
      }
   }
   /**
    * 执行命令
    * @param deployApps
    * @return
    * @throws ServiceException
    */
   private String excute(DeployApps deployApps,String type) throws ServiceException {
      // 处理上传文件的逻辑
      StringBuilder output = new StringBuilder();
      try {
         String cmd = "";
         if(type.equalsIgnoreCase("UP")){
            cmd = deployApps.getRestartCmd();
         }else {
            cmd = deployApps.getStartCmd();
         }
         if(Func.isEmpty(cmd)){
            return "The executed command is empty";
         }
         // 执行Linux命令
         Process process = Runtime.getRuntime().exec(cmd);
         // 读取命令执行结果
         BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
         String line;
         while ((line = reader.readLine()) != null) {
            output.append(line).append("\n");
         }
         // 等待命令执行完成
         int exitCode = process.waitFor();
         log.info("命令执行结果:" + output.toString());
         return output.toString();
      }catch (IOException | InterruptedException e){
         e.printStackTrace();
         log.error("命令执行出错,原因:" + e.getMessage());
         throw new ServiceException("Command execution failed"+e.getMessage());
      }
   }
   /**
    * 文件备份操作
    * @param source 源文件
    * @param destination 文件备份路径
    * @throws IOException
    */
   private void copyFolder(File source, File destination) throws IOException {
      // 文件存在才需要备份
      if(source.exists()){
         if (source.isDirectory()) {
            if (!destination.exists()) {
               destination.mkdir();
            }
            String[] files = source.list();
            for (String file : files) {
               File srcFile = new File(source, file);
               File destFile = new File(destination, file);
               copyFolder(srcFile, destFile);
            }
         } else {
            Files.copy(source.toPath(), destination.toPath());
         }
      }
   }
   /**
@@ -93,7 +386,7 @@
    * @param serviceId
    * @return
    */
   public String getGatewayPort(String serviceId) {
   private String getGatewayPort(String serviceId) {
      List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
      if (!instances.isEmpty()) {
         ServiceInstance gatewayInstance = instances.get(0);
@@ -101,6 +394,5 @@
      }
      return "8080";
   }
}