Sfoglia il codice sorgente

添加 html 模板

reghao 4 anni fa
parent
commit
8ee17f09ac
23 ha cambiato i file con 569 aggiunte e 235 eliminazioni
  1. 7 3
      dagent/src/main/resources/application-test.yml
  2. 21 21
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/StatusController.java
  3. 75 0
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/view/AppConfigPageController.java
  4. 30 55
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/view/AppPageController.java
  5. 36 0
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/view/BuildConfigPageController.java
  6. 6 5
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/AppBuilding.java
  7. 17 22
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/AppDeploying.java
  8. 30 3
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/AppRunning.java
  9. 2 0
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/log/BuildLog.java
  10. 2 0
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/log/DeployLog.java
  11. 5 3
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/repository/AppDeployingRepository.java
  12. 12 0
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/repository/AppRunningRepository.java
  13. 16 1
      dmaster/src/main/java/cn/reghao/autodop/dmaster/app/service/BuildDeployDispatcher.java
  14. 0 4
      dmaster/src/main/java/cn/reghao/autodop/dmaster/common/orm/BaseEntity.java
  15. 44 20
      dmaster/src/main/java/cn/reghao/autodop/dmaster/utils/dispatcher/AppOpsDispatcher.java
  16. 1 1
      dmaster/src/main/resources/templates/app/build.html
  17. 28 55
      dmaster/src/main/resources/templates/app/config/app/index.html
  18. 87 0
      dmaster/src/main/resources/templates/app/config/proj/index.html
  19. 12 30
      dmaster/src/main/resources/templates/app/deploy.html
  20. 76 6
      dmaster/src/main/resources/templates/app/log.html
  21. 3 5
      dmaster/src/main/resources/templates/app/status.html
  22. 56 0
      dmaster/src/test/java/cn/reghao/autodop/dmaster/app/entity/AppDeployingTest.java
  23. 3 1
      scripts/build.sh

+ 7 - 3
dagent/src/main/resources/application-test.yml

@@ -1,4 +1,8 @@
+#mosquitto:
+#  broker: tcp://192.168.0.220:1883
+#  username: test
+#  password: Test@123456
 mosquitto:
-  broker: tcp://192.168.0.220:1883
-  username: test
-  password: Test@123456
+  broker: tcp://localhost:1883
+  username: dev
+  password: Dev@123456

+ 21 - 21
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/StatusController.java

@@ -8,6 +8,7 @@ import cn.reghao.autodop.dmaster.common.db.PageList;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
@@ -29,6 +30,26 @@ public class StatusController {
         this.statusService = statusService;
     }
 
+    @ApiOperation(value = "重启应用")
+    @PostMapping(value = "/restart/{appId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<String> restart(@PathVariable("appId") String appId) {
+        return ResponseEntity.ok().body(WebBody.success(appId + " 已重启"));
+    }
+
+    @ApiOperation(value = "停止应用")
+    @PostMapping(value = "/stop/{appId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<String> stop(@PathVariable("appId") String appId) {
+        Map<String, AppStatus> map = statusService.stop(appId);
+        return ResponseEntity.ok().body(WebBody.success(appId + " 已停止"));
+    }
+
+    @ApiOperation(value = "启动应用")
+    @PostMapping(value = "/start/{appId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<String> start(@PathVariable("appId") String appId) {
+        Map<String, AppStatus> map = statusService.start(appId);
+        return ResponseEntity.ok().body(WebBody.success(appId + " 已启动"));
+    }
+
     @ApiOperation(value = "刷新已部署应用列表")
     @PostMapping("/refresh}")
     public ResponseEntity<String> refreshDeployedApps(@RequestParam("env") String env) {
@@ -69,25 +90,4 @@ public class StatusController {
         Map<String, AppStatus> map = statusService.appStatus(appId);
         return ResponseEntity.ok().body(WebBody.success(map));
     }
-
-    @ApiOperation(value = "重启应用")
-    @PostMapping("/restart/{appId}")
-    public ResponseEntity<String> restart(@PathVariable("appId") String appId) {
-        Map<String, AppStatus> map = statusService.restart(appId);
-        return ResponseEntity.ok().body(WebBody.success(map));
-    }
-
-    @ApiOperation(value = "停止应用")
-    @PostMapping("/stop/{appId}")
-    public ResponseEntity<String> stop(@PathVariable("appId") String appId) {
-        Map<String, AppStatus> map = statusService.stop(appId);
-        return ResponseEntity.ok().body(WebBody.success(map));
-    }
-
-    @ApiOperation(value = "启动应用")
-    @PostMapping("/start/{appId}")
-    public ResponseEntity<String> start(@PathVariable("appId") String appId) {
-        Map<String, AppStatus> map = statusService.start(appId);
-        return ResponseEntity.ok().body(WebBody.success(map));
-    }
 }

+ 75 - 0
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/view/AppConfigPageController.java

@@ -0,0 +1,75 @@
+package cn.reghao.autodop.dmaster.app.controller.view;
+
+import cn.reghao.autodop.dmaster.app.constant.EnvType;
+import cn.reghao.autodop.dmaster.app.entity.config.AppOrchestration;
+import cn.reghao.autodop.dmaster.app.repository.config.AppOrchestrationRepository;
+import cn.reghao.autodop.dmaster.common.db.PageList;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * @author reghao
+ * @date 2019-08-30 18:49:15
+ */
+@Slf4j
+@Api(tags = "应用配置页面接口")
+@Controller
+@RequestMapping("/app/config")
+public class AppConfigPageController {
+    private AppOrchestrationRepository appRepository;
+
+    public AppConfigPageController(AppOrchestrationRepository appRepository) {
+        this.appRepository = appRepository;
+    }
+
+    @ApiOperation(value = "应用配置页面")
+    @GetMapping("/app")
+    public String appConfigPage(Model model) {
+        String env = EnvType.test.name();
+        int page = 1;
+        int size = 10;
+        PageRequest pageRequest =
+                PageRequest.of(page-1, size, Sort.by(Sort.Direction.DESC, "updateTime"));
+
+        Page<AppOrchestration> appPage = appRepository.findByEnableTrueAndEnv(env, pageRequest);
+        PageList<AppOrchestration> pageList = PageList.pageList(appPage);
+
+        model.addAttribute("page", appPage);
+        model.addAttribute("list", pageList.getList());
+        return "/app/config/app/index";
+    }
+
+    @ApiOperation(value = "应用配置详情页面")
+    @GetMapping("/app/detail/{appId}")
+    public String appConfigPage(@PathVariable("appId") String appId, Model model) {
+        AppOrchestration app = appRepository.findByAppId(appId);
+        model.addAttribute("app", app);
+        return "/app/config/app/detail";
+    }
+
+    @ApiOperation(value = "项目配置页面")
+    @GetMapping("/proj")
+    public String projConfigPage(Model model) {
+        String env = EnvType.test.name();
+        int page = 1;
+        int size = 10;
+        PageRequest pageRequest =
+                PageRequest.of(page-1, size, Sort.by(Sort.Direction.DESC, "updateTime"));
+
+        Page<AppOrchestration> appPage = appRepository.findByEnableTrueAndEnv(env, pageRequest);
+        PageList<AppOrchestration> pageList = PageList.pageList(appPage);
+
+        model.addAttribute("page", appPage);
+        model.addAttribute("list", pageList.getList());
+        return "/app/config/proj/index";
+    }
+}

+ 30 - 55
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/AppPageController.java → dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/view/AppPageController.java

@@ -1,17 +1,13 @@
-package cn.reghao.autodop.dmaster.app.controller;
+package cn.reghao.autodop.dmaster.app.controller.view;
 
-import cn.reghao.autodop.common.result.Result;
-import cn.reghao.autodop.common.result.ResultStatus;
-import cn.reghao.autodop.dmaster.app.constant.AppType;
 import cn.reghao.autodop.dmaster.app.constant.EnvType;
-import cn.reghao.autodop.dmaster.app.entity.config.AppOrchestration;
+import cn.reghao.autodop.dmaster.app.entity.AppDeploying;
+import cn.reghao.autodop.dmaster.app.entity.AppRunning;
 import cn.reghao.autodop.dmaster.app.entity.log.BuildLog;
-import cn.reghao.autodop.dmaster.app.entity.log.DeployLog;
 import cn.reghao.autodop.dmaster.app.repository.AppBuildingRepository;
 import cn.reghao.autodop.dmaster.app.repository.AppDeployingRepository;
-import cn.reghao.autodop.dmaster.app.repository.config.AppOrchestrationRepository;
+import cn.reghao.autodop.dmaster.app.repository.AppRunningRepository;
 import cn.reghao.autodop.dmaster.app.entity.AppBuilding;
-import cn.reghao.autodop.dmaster.app.entity.AppDeploying;
 import cn.reghao.autodop.dmaster.app.repository.log.BuildLogRepository;
 import cn.reghao.autodop.dmaster.app.repository.log.DeployLogRepository;
 import cn.reghao.autodop.dmaster.common.db.PageList;
@@ -25,11 +21,7 @@ import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
 
 /**
  * @author reghao
@@ -40,20 +32,20 @@ import java.util.stream.Collectors;
 @Controller
 @RequestMapping("/app")
 public class AppPageController {
-    private AppOrchestrationRepository appRepository;
     private AppBuildingRepository appBuildingRepository;
     private AppDeployingRepository appDeployingRepository;
+    private AppRunningRepository appRunningRepository;
     private BuildLogRepository buildLogRepository;
     private DeployLogRepository deployLogRepository;
 
-    public AppPageController(AppOrchestrationRepository appRepository,
-                             AppBuildingRepository appBuildingRepository,
+    public AppPageController(AppBuildingRepository appBuildingRepository,
                              AppDeployingRepository appDeployingRepository,
+                             AppRunningRepository appRunningRepository,
                              BuildLogRepository buildLogRepository,
                              DeployLogRepository deployLogRepository) {
-        this.appRepository = appRepository;
         this.appBuildingRepository = appBuildingRepository;
         this.appDeployingRepository = appDeployingRepository;
+        this.appRunningRepository = appRunningRepository;
         this.buildLogRepository = buildLogRepository;
         this.deployLogRepository = deployLogRepository;
     }
@@ -62,13 +54,11 @@ public class AppPageController {
     @GetMapping("/build")
     public String buildPage(Model model) {
         String env = EnvType.test.name();
-        String appType = AppType.npm.name();
         int page = 1;
         int size = 10;
         PageRequest pageRequest =
                 PageRequest.of(page-1, size, Sort.by(Sort.Direction.DESC, "updateTime"));
 
-        //Page<AppBuilding> appBuildings = appBuildingRepository.findAllByEnvAndAppType(env, appType, pageRequest);
         Page<AppBuilding> appBuildings = appBuildingRepository.findAllByEnv(env, pageRequest);
         PageList<AppBuilding> pageList = PageList.pageList(appBuildings);
 
@@ -78,34 +68,10 @@ public class AppPageController {
     }
 
     @ApiOperation(value = "部署页面")
-    @GetMapping("/deploy/{appId}")
-    public String deployPage(@PathVariable("appId") String appId, Model model) {
-        int page = 1;
-        int size = 10;
-        PageRequest pageRequest =
-                PageRequest.of(page-1, size, Sort.by(Sort.Direction.DESC, "updateTime"));
-
-        Page<BuildLog> buildLogPage = buildLogRepository.findByAppId(appId, pageRequest);
-        Map<String, List<DeployLog>> map = new HashMap<>();
-        for (BuildLog buildLog : buildLogPage.getContent()) {
-            if (buildLog.getResult().getCode() != ResultStatus.SUCCESS.getCode()) {
-                continue;
-            }
-
-            String buildLogId = buildLog.getId();
-            List<DeployLog> deployLogs = deployLogRepository.findByBuildLogId(buildLogId);
-            if (!deployLogs.isEmpty()) {
-                map.put(buildLogId, deployLogs);
-            }
-        }
-
-        AppOrchestration app = appRepository.findByAppId(appId);
-        List<AppDeploying> list = new ArrayList<>();
-        /*PageList<AppBuilding> pageList = PageList.emptyPageList(appPage);
-        pageList.setList(apps);
-        model.addAttribute("list", pageList.getList());
-        model.addAttribute("page", pageList);*/
-
+    @GetMapping("/deploy/{buildLogId}")
+    public String deployPage(@PathVariable("buildLogId") String buildLogId, Model model) {
+        List<AppDeploying> list = appDeployingRepository.findByBuildLogId(buildLogId);
+        model.addAttribute("list", list);
         return "/app/deploy";
     }
 
@@ -124,19 +90,28 @@ public class AppPageController {
         int size = 10;
         PageRequest pageRequest =
                 PageRequest.of(page-1, size, Sort.by(Sort.Direction.DESC, "updateTime"));
-        Page<AppDeploying> appDeployingPage = appDeployingRepository.findAll(pageRequest);
-        PageList<AppDeploying> pageList = PageList.pageList(appDeployingPage);
+        Page<AppRunning> appRunningPage = appRunningRepository.findAll(pageRequest);
+        PageList<AppRunning> pageList = PageList.pageList(appRunningPage);
 
-        model.addAttribute("page", appDeployingPage);
+        model.addAttribute("page", appRunningPage);
         model.addAttribute("list", pageList.getList());
         return "/app/status";
     }
 
-    @ApiOperation(value = "应用配置详情页面")
-    @GetMapping("/config/app/detail/{appId}")
-    public String appConfigPage(@PathVariable("appId") String appId, Model model) {
-        AppOrchestration app = appRepository.findByAppId(appId);
-        model.addAttribute("app", app);
-        return "/app/config/app/detail";
+    @ApiOperation(value = "构建历史页面")
+    @GetMapping("/log/build")
+    public String buildLogPage(Model model) {
+        String env = EnvType.test.name();
+        int page = 1;
+        int size = 10;
+        PageRequest pageRequest =
+                PageRequest.of(page-1, size, Sort.by(Sort.Direction.DESC, "updateTime"));
+
+        Page<AppBuilding> appBuildings = appBuildingRepository.findAllByEnv(env, pageRequest);
+        PageList<AppBuilding> pageList = PageList.pageList(appBuildings);
+
+        model.addAttribute("page", appBuildings);
+        model.addAttribute("list", pageList.getList());
+        return "/app/log";
     }
 }

+ 36 - 0
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/controller/view/BuildConfigPageController.java

@@ -0,0 +1,36 @@
+package cn.reghao.autodop.dmaster.app.controller.view;
+
+import cn.reghao.autodop.dmaster.app.constant.EnvType;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * @author reghao
+ * @date 2019-08-30 18:49:15
+ */
+@Slf4j
+@Api(tags = "构建配置页面接口")
+@Controller
+@RequestMapping("/app/config/build")
+public class BuildConfigPageController {
+    public BuildConfigPageController() {
+    }
+
+    @ApiOperation(value = "构建页面")
+    @GetMapping("/repo")
+    public String buildPage(Model model) {
+        String env = EnvType.test.name();
+        int page = 1;
+        int size = 10;
+        PageRequest pageRequest =
+                PageRequest.of(page-1, size, Sort.by(Sort.Direction.DESC, "updateTime"));
+        return "/app/build";
+    }
+}

+ 6 - 5
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/AppBuilding.java

@@ -2,6 +2,7 @@ package cn.reghao.autodop.dmaster.app.entity;
 
 import cn.reghao.autodop.common.result.ResultStatus;
 import cn.reghao.autodop.common.utils.DateTimeConverter;
+import cn.reghao.autodop.dmaster.app.entity.config.AppOrchestration;
 import cn.reghao.autodop.dmaster.app.entity.log.BuildLog;
 import cn.reghao.autodop.dmaster.app.service.bd.tools.repo.CommitInfo;
 import cn.reghao.autodop.dmaster.common.orm.BaseEntity;
@@ -32,11 +33,11 @@ public class AppBuilding extends BaseEntity<Integer> {
     private LocalDateTime buildTime;
     private String buildBy;
 
-    public AppBuilding(String appId, String env, String appType, String branch) {
-        this.appId = appId;
-        this.env = env;
-        this.appType = appType;
-        this.branch = branch;
+    public AppBuilding(AppOrchestration app) {
+        this.appId = app.getAppId();
+        this.env = app.getEnv();
+        this.appType = app.getAppType();
+        this.branch = app.getBranch();
     }
 
     public static AppBuilding from(BuildLog buildLog) {

+ 17 - 22
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/AppDeploying.java

@@ -1,15 +1,12 @@
 package cn.reghao.autodop.dmaster.app.entity;
 
-import cn.reghao.autodop.common.dagent.app.api.data.AppStatus;
 import cn.reghao.autodop.dmaster.app.entity.log.BuildLog;
 import cn.reghao.autodop.dmaster.common.orm.BaseEntity;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
-import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import java.time.LocalDateTime;
-import java.util.List;
 
 /**
  * @author reghao
@@ -21,29 +18,27 @@ import java.util.List;
 public class AppDeploying extends BaseEntity<Integer> {
     private String buildLogId;
     private String appId;
-    private String env;
-    private String branch;
-    private String commitId;
-    /*@ElementCollection(targetClass = AppRunning.class)
-    private List<AppRunning> appRunnings;*/
+    private String machineId;
     private String machineIpv4;
-    private LocalDateTime startTime;
-    private Integer pid;
-    private Boolean isRunning;
-    private LocalDateTime lastCheck;
+    private Boolean isDeploy;
+    private LocalDateTime deployTime;
+    private String deployResult;
+    private String deployBy;
 
-    public static AppDeploying from(BuildLog buildLog, AppStatus appStatus) {
+    public static AppDeploying from(String appId, String machineId, String machineIpv4) {
+        AppDeploying appDeploying = new AppDeploying();
+        appDeploying.setAppId(appId);
+        appDeploying.setMachineId(machineId);
+        appDeploying.setMachineIpv4(machineIpv4);
+        appDeploying.setIsDeploy(false);
+        return appDeploying;
+    }
+
+    public static AppDeploying from(BuildLog buildLog) {
         AppDeploying appDeploying = new AppDeploying();
         appDeploying.setBuildLogId(buildLog.getId());
-        appDeploying.setAppId(appStatus.getAppId());
-        appDeploying.setEnv(buildLog.getEnv());
-        appDeploying.setBranch(buildLog.getBranch());
-        appDeploying.setCommitId(buildLog.getCommitInfo().getCommitId());
-        appDeploying.setMachineIpv4(appStatus.getMachineIpv4());
-        appDeploying.setStartTime(appStatus.getStartTime());
-        appDeploying.setPid(appStatus.getPid());
-        appDeploying.setIsRunning(appStatus.getIsRunning());
-        appDeploying.setLastCheck(LocalDateTime.now());
+        appDeploying.setAppId(buildLog.getAppId());
+        appDeploying.setIsDeploy(false);
         return appDeploying;
     }
 }

+ 30 - 3
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/AppRunning.java

@@ -1,20 +1,47 @@
 package cn.reghao.autodop.dmaster.app.entity;
 
+import cn.reghao.autodop.common.dagent.app.api.data.AppStatus;
+import cn.reghao.autodop.dmaster.app.entity.log.BuildLog;
+import cn.reghao.autodop.dmaster.common.orm.BaseEntity;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
-import javax.persistence.Embeddable;
+import javax.persistence.Entity;
 import java.time.LocalDateTime;
 
 /**
  * @author reghao
  * @date 2021-05-31 13:36:46
  */
-@Embeddable
 @Data
-public class AppRunning {
+@EqualsAndHashCode(callSuper = false)
+@Entity
+public class AppRunning extends BaseEntity<Integer> {
+    private String buildLogId;
+    private String appId;
+    private String env;
+    private String branch;
+    private String commitId;
     private String machineId;
     private String machineIpv4;
     private Boolean isRunning;
     private LocalDateTime startTime;
     private Integer pid;
+    private LocalDateTime lastCheck;
+
+    public static AppRunning from(BuildLog buildLog, AppStatus appStatus) {
+        AppRunning appRunning = new AppRunning();
+        appRunning.setBuildLogId(buildLog.getId());
+        appRunning.setAppId(appStatus.getAppId());
+        appRunning.setEnv(buildLog.getEnv());
+        appRunning.setBranch(buildLog.getBranch());
+        appRunning.setCommitId(buildLog.getCommitInfo().getCommitId());
+        appRunning.setMachineId(appStatus.getMachineId());
+        appRunning.setMachineIpv4(appStatus.getMachineIpv4());
+        appRunning.setStartTime(appStatus.getStartTime());
+        appRunning.setPid(appStatus.getPid());
+        appRunning.setIsRunning(appStatus.getIsRunning());
+        appRunning.setLastCheck(LocalDateTime.now());
+        return appRunning;
+    }
 }

+ 2 - 0
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/log/BuildLog.java

@@ -31,6 +31,7 @@ public class BuildLog extends BaseDocument {
     private CommitInfo commitInfo;
     private BuildTime buildTime;
     private Result result;
+    private String buildBy;
 
     public static BuildLog from(AppOrchestration app) {
         BuildLog buildLog = new BuildLog();
@@ -45,6 +46,7 @@ public class BuildLog extends BaseDocument {
 
         buildLog.setBuildTime(new BuildTime());
         buildLog.setResult(new Result());
+        buildLog.setBuildBy("某人");
         return buildLog;
     }
 }

+ 2 - 0
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/entity/log/DeployLog.java

@@ -25,6 +25,7 @@ public class DeployLog extends BaseDocument {
     private String machineIpv4;
     private LocalDateTime deployTime;
     private Result result;
+    private String deployBy;
 
     public static DeployLog from(DeployResult deployResult) {
         DeployLog deployLog = new DeployLog();
@@ -36,6 +37,7 @@ public class DeployLog extends BaseDocument {
             deployLog.setMachineIpv4(appStatus.getMachineIpv4());
             deployLog.setDeployTime(appStatus.getStartTime());
         }
+        deployLog.setDeployBy("某人");
         return deployLog;
     }
 }

+ 5 - 3
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/repository/AppDeployingRepository.java

@@ -1,14 +1,16 @@
 package cn.reghao.autodop.dmaster.app.repository;
 
 import cn.reghao.autodop.dmaster.app.entity.AppDeploying;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 
+import java.util.List;
+
 /**
  * @author reghao
  * @date 2021-05-24 15:20:24
  */
 public interface AppDeployingRepository extends JpaRepository<AppDeploying, Integer> {
-    AppDeploying findByAppId(String appId);
+    List<AppDeploying> findByAppId(String appId);
+    List<AppDeploying> findByBuildLogId(String buildLogId);
+    AppDeploying findByBuildLogIdAndMachineId(String buildLogId, String machineId);
 }

+ 12 - 0
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/repository/AppRunningRepository.java

@@ -0,0 +1,12 @@
+package cn.reghao.autodop.dmaster.app.repository;
+
+import cn.reghao.autodop.dmaster.app.entity.AppRunning;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author reghao
+ * @date 2021-05-24 15:20:24
+ */
+public interface AppRunningRepository extends JpaRepository<AppRunning, Integer> {
+    AppRunning findByAppId(String appId);
+}

+ 16 - 1
dmaster/src/main/java/cn/reghao/autodop/dmaster/app/service/BuildDeployDispatcher.java

@@ -3,11 +3,13 @@ package cn.reghao.autodop.dmaster.app.service;
 import cn.reghao.autodop.common.result.ResultStatus;
 import cn.reghao.autodop.dmaster.app.cache.BuildDeployCache;
 import cn.reghao.autodop.dmaster.app.entity.AppBuilding;
+import cn.reghao.autodop.dmaster.app.entity.AppDeploying;
 import cn.reghao.autodop.dmaster.app.entity.config.NotifyReceiver;
 import cn.reghao.autodop.dmaster.app.entity.config.deploy.DeployConfig;
 import cn.reghao.autodop.dmaster.app.entity.log.*;
 import cn.reghao.autodop.dmaster.app.interceptor.AppIntegrateReinitInterceptor;
 import cn.reghao.autodop.dmaster.app.repository.AppBuildingRepository;
+import cn.reghao.autodop.dmaster.app.repository.AppDeployingRepository;
 import cn.reghao.autodop.dmaster.app.repository.log.BuildLogRepository;
 import cn.reghao.autodop.dmaster.app.service.bd.AppIntegrate;
 import cn.reghao.autodop.dmaster.app.service.bd.AppDeployer;
@@ -35,6 +37,7 @@ import java.util.concurrent.*;
 @Service
 public class BuildDeployDispatcher {
     private AppBuildingRepository appBuildingRepository;
+    private AppDeployingRepository appDeployingRepository;
     private BuildLogRepository buildLogRepository;
     private AppIntegrateReinitInterceptor reinitInterceptor;
     // TODO 多线程访问的集合
@@ -47,12 +50,14 @@ public class BuildDeployDispatcher {
     private NotifyService notifyService;
 
     public BuildDeployDispatcher(AppBuildingRepository appBuildingRepository,
+                                 AppDeployingRepository appDeployingRepository,
                                  AppIntegrateReinitInterceptor reinitInterceptor,
                                  BuildDeployCache cache,
                                  BuildLogRepository buildLogRepository,
                                  NotifyService notify,
                                  AppDeployer appDeployer) {
         this.appBuildingRepository = appBuildingRepository;
+        this.appDeployingRepository = appDeployingRepository;
         this.reinitInterceptor = reinitInterceptor;
         this.cache = cache;
         this.buildLogRepository = buildLogRepository;
@@ -90,7 +95,8 @@ public class BuildDeployDispatcher {
         }
 
         BuildSupplier supplier = new BuildSupplier(appIntegrate);
-        final List<NotifyReceiver> notifyReceivers = appIntegrate.app().getNotifyReceivers();
+        AppOrchestration app = appIntegrate.app();
+        final List<NotifyReceiver> notifyReceivers = app.getNotifyReceivers();
         log.info("开始异步构建 {}...", appId);
         CompletableFuture.supplyAsync(supplier, threadPool)
                 .whenComplete((buildLog, throwable) -> {
@@ -105,6 +111,15 @@ public class BuildDeployDispatcher {
                         appBuilding.setUpdateTime(LocalDateTime.now());
                         appBuildingRepository.save(appBuilding);
                     }
+
+                    List<AppDeploying> entities = appDeployingRepository.findByAppId(appId);
+                    if (!entities.isEmpty()) {
+                        for (AppDeploying appDeploying : entities) {
+                            appDeploying.setBuildLogId(buildLog.getId());
+                        }
+                        appDeployingRepository.saveAll(entities);
+                    }
+
                     log.info("{} 构建完成...", appId);
                 })
                 .thenAccept(buildLog -> {

+ 0 - 4
dmaster/src/main/java/cn/reghao/autodop/dmaster/common/orm/BaseEntity.java

@@ -22,13 +22,9 @@ public class BaseEntity<T extends Serializable> implements Serializable {
     // 逻辑删除
     private Boolean isDelete;
 
-    /*@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")*/
     @CreationTimestamp
     @Column(updatable = false)
     protected LocalDateTime createTime;
-    /*@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")*/
     @CreationTimestamp
     protected LocalDateTime updateTime;
 

+ 44 - 20
dmaster/src/main/java/cn/reghao/autodop/dmaster/utils/dispatcher/AppOpsDispatcher.java

@@ -7,11 +7,13 @@ import cn.reghao.autodop.common.result.ResultStatus;
 import cn.reghao.autodop.common.utils.serializer.JsonConverter;
 import cn.reghao.autodop.dmaster.app.cache.BuildDeployCache;
 import cn.reghao.autodop.dmaster.app.entity.AppDeploying;
+import cn.reghao.autodop.dmaster.app.entity.AppRunning;
 import cn.reghao.autodop.dmaster.app.entity.config.AppOrchestration;
 import cn.reghao.autodop.dmaster.app.entity.config.NotifyReceiver;
 import cn.reghao.autodop.dmaster.app.entity.log.BuildLog;
 import cn.reghao.autodop.dmaster.app.entity.log.DeployLog;
 import cn.reghao.autodop.dmaster.app.repository.AppDeployingRepository;
+import cn.reghao.autodop.dmaster.app.repository.AppRunningRepository;
 import cn.reghao.autodop.dmaster.app.repository.log.BuildLogRepository;
 import cn.reghao.autodop.dmaster.app.repository.log.DeployLogRepository;
 import cn.reghao.autodop.dmaster.app.service.bd.DeployNotifyMsg;
@@ -35,17 +37,20 @@ import java.util.Optional;
 public class AppOpsDispatcher {
     private BuildLogRepository buildLogRepository;
     private AppDeployingRepository appDeployingRepository;
+    private AppRunningRepository appRunningRepository;
     private DeployLogRepository deployLogRepository;
     private BuildDeployCache cache;
     private NotifyService notifyService;
 
     public AppOpsDispatcher(BuildLogRepository buildLogRepository,
                             AppDeployingRepository appDeployingRepository,
+                            AppRunningRepository appRunningRepository,
                             DeployLogRepository deployLogRepository,
                             BuildDeployCache cache,
                             NotifyService notifyService) {
         this.buildLogRepository = buildLogRepository;
         this.appDeployingRepository = appDeployingRepository;
+        this.appRunningRepository = appRunningRepository;
         this.deployLogRepository = deployLogRepository;
         this.cache = cache;
         this.notifyService = notifyService;
@@ -63,26 +68,9 @@ public class AppOpsDispatcher {
                 Optional<BuildLog> optional = buildLogRepository.findById(buildId);
                 if (optional.isPresent()) {
                     BuildLog buildLog = optional.get();
-                    AppOrchestration app = cache.findByAppId(buildLog.getAppId());
-                    DingMsg dingMsg = new DeployNotifyMsg(buildLog, deployResult).dingMsg();
-                    List<NotifyReceiver> notifyReceivers = app.getNotifyReceivers();
-                    notifyReceivers.forEach(notifyReceiver -> notifyService.notify(notifyReceiver, dingMsg));
-                    log.info("应用 {} 部署成功...", buildLog.getAppId());
-
-                    if (deployResult.getResult().getCode() == ResultStatus.SUCCESS.getCode()) {
-                        AppStatus appStatus = deployResult.getAppStatus();
-                        AppDeploying appDeploying = AppDeploying.from(buildLog, appStatus);
-                        appDeploying.setLastCheck(LocalDateTime.now());
-
-                        String appId = appDeploying.getAppId();
-                        AppDeploying entity = appDeployingRepository.findByAppId(appId);
-                        if (entity != null) {
-                            appDeploying.setId(entity.getId());
-                            appDeploying.setIsDelete(entity.getIsDelete());
-                            appDeploying.setCreateTime(entity.getCreateTime());
-                        }
-                        appDeployingRepository.save(appDeploying);
-                    }
+                    deployNotify(buildLog, deployResult);
+                    updateDeployStatus(buildLog, deployResult);
+                    updateAppStatus(buildLog, deployResult);
                 }
                 break;
             case appStatusResult:
@@ -100,4 +88,40 @@ public class AppOpsDispatcher {
                 break;
         }
     }
+
+    private void deployNotify(BuildLog buildLog, DeployResult deployResult) {
+        AppOrchestration app = cache.findByAppId(buildLog.getAppId());
+        DingMsg dingMsg = new DeployNotifyMsg(buildLog, deployResult).dingMsg();
+        List<NotifyReceiver> notifyReceivers = app.getNotifyReceivers();
+        notifyReceivers.forEach(notifyReceiver -> notifyService.notify(notifyReceiver, dingMsg));
+    }
+
+    private void updateDeployStatus(BuildLog buildLog, DeployResult deployResult) {
+        String buildLogId = buildLog.getId();
+        String machineId = deployResult.getMachineId();
+        AppDeploying appDeploying = appDeployingRepository.findByBuildLogIdAndMachineId(buildLogId, machineId);
+        if (appDeploying != null) {
+            if (deployResult.getResult().getCode() == ResultStatus.SUCCESS.getCode()) {
+                appDeploying.setIsDeploy(true);
+                appDeploying.setDeployTime(deployResult.getAppStatus().getStartTime());
+            }
+            appDeploying.setDeployResult(deployResult.getResult().getMsg());
+            appDeployingRepository.save(appDeploying);
+        }
+    }
+
+    private void updateAppStatus(BuildLog buildLog, DeployResult deployResult) {
+        AppStatus appStatus = deployResult.getAppStatus();
+        AppRunning appRunning = AppRunning.from(buildLog, appStatus);
+        appRunning.setLastCheck(LocalDateTime.now());
+
+        String appId = appRunning.getAppId();
+        AppRunning entity = appRunningRepository.findByAppId(appId);
+        if (entity != null) {
+            appRunning.setId(entity.getId());
+            appRunning.setIsDelete(entity.getIsDelete());
+            appRunning.setCreateTime(entity.getCreateTime());
+        }
+        appRunningRepository.save(appRunning);
+    }
 }

+ 1 - 1
dmaster/src/main/resources/templates/app/build.html

@@ -70,7 +70,7 @@
                     <td>
                         <a class="ajax-post" th:href="@{'/api/app/bd/update?appId='+${item.appId}}">更新</a>
                         <a class="ajax-post" th:href="@{'/api/app/bd/build?appId='+${item.appId}}">构建</a>
-                        <a class="open-popup" data-title="应用部署" th:attr="data-url=@{'/app/deploy/' + ${item.appId}}"
+                        <a class="open-popup" data-title="应用部署" th:attr="data-url=@{'/app/deploy/' + ${item.buildLogId}}"
                            data-size="1000,600" href="#">部署</a>
                     </td>
                 </tr>

+ 28 - 55
dmaster/src/main/resources/templates/app/config/app/index.html

@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html xmlns:th="http://www.thymeleaf.org"
-      xmlns:mo="https://gitee.com/aun/Timo"
-      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+      xmlns:mo="https://gitee.com/aun/Timo">
 <head th:replace="/common/template :: header(~{::title},~{::link},~{::style})">
     <link rel="stylesheet" th:href="@{/lib/zTree_v3/css/zTreeStyle/zTreeStyle.css}" type="text/css">
 </head>
 <body class="timo-layout-page">
 <div class="layui-card">
     <div class="layui-card-header timo-card-header">
-        <span><i class="fa fa-bars"></i> 用户管理</span>
+        <span><i class="fa fa-bars"></i> 应用配置</span>
         <i class="layui-icon layui-icon-refresh refresh-btn"></i>
         <a th:href="@{/system/user/export}"><i class="fa fa-download"></i></a>
     </div>
@@ -16,16 +15,16 @@
         <div class="layui-row timo-card-screen put-row">
             <div class="pull-left layui-form-pane timo-search-box">
                 <div class="layui-inline">
-                    <label class="layui-form-label">状态</label>
+                    <label class="layui-form-label">环境</label>
                     <div class="layui-input-block timo-search-status">
                         <select class="timo-search-select" name="status" mo:dict="SEARCH_STATUS"
                                 mo-selected="${param.status}"></select>
                     </div>
                 </div>
                 <div class="layui-inline">
-                    <label class="layui-form-label">用户名</label>
+                    <label class="layui-form-label">应用 ID</label>
                     <div class="layui-input-block">
-                        <input type="text" name="username" th:value="${param.username}" placeholder="请输入用户名"
+                        <input type="text" name="username" th:value="${param.appId}" placeholder="请输入应用 ID"
                                autocomplete="off" class="layui-input">
                     </div>
                 </div>
@@ -36,20 +35,9 @@
                 </div>
             </div>
             <div class="pull-right screen-btn-group">
-                <div class="btn-group-left">
-                    <button class="layui-btn open-popup-param" data-title="修改密码" th:attr="data-url=@{/system/user/pwd}"
-                             data-size="456,242">
-                        <i class="fa fa-refresh"></i> 修改密码
-                    </button>
-                    <button class="layui-btn open-popup-param" data-type="radio" data-title="角色分配"
-                            th:attr="data-url=@{/system/user/role}"
-                            data-size="480,400">
-                        <i class="fa fa-user-secret"></i> 分配角色
-                    </button>
-                </div>
                 <div class="btn-group-right">
-                    <button class="layui-btn open-popup" data-title="添加用" th:attr="data-url=@{/system/user/add}"
-                             data-size="auto">
+                    <button class="layui-btn open-popup" data-title="添加应用" th:attr="data-url=@{/system/user/add}"
+                            data-size="auto">
                         <i class="fa fa-plus"></i> 添加
                     </button>
                     <div class="btn-group">
@@ -71,36 +59,34 @@
                         <label class="timo-checkbox"><input type="checkbox">
                             <i class="layui-icon layui-icon-ok"></i></label>
                     </th>
-                    <th class="sortable" data-field="username">用户名</th>
-                    <th class="sortable" data-field="nickname">用户昵称</th>
-                    <th class="sortable" data-field="sex">性别</th>
-                    <th class="sortable" data-field="phone">电话</th>
-                    <th class="sortable" data-field="email">邮箱</th>
-                    <th class="sortable" data-field="createDate">创建时间</th>
-                    <th>状态</th>
-                    <th>已授予的角色</th>
+                    <th class="sortable" data-field="appId">应用 ID</th>
+                    <th class="sortable" data-field="description">描述</th>
+                    <th class="sortable" data-field="branch">分支</th>
+                    <th class="sortable" data-field="dirname">应用目录</th>
+                    <th class="sortable" data-field="appType">应用类型</th>
+                    <th class="sortable" data-field="httpPort">HTTP 端口</th>
                     <th>操作</th>
                 </tr>
                 </thead>
                 <tbody>
                 <tr th:each="item:${list}">
-                    <td><label class="timo-checkbox"><input type="checkbox" th:value="${item.id}">
+                    <td><label class="timo-checkbox"><input type="checkbox" th:value="${item.appId}">
                         <i class="layui-icon layui-icon-ok"></i></label></td>
-                    <td th:text="${item.username}">用户名</td>
-                    <td th:text="${item.nickname}">用户昵称</td>
-                    <td th:text="${item.gender}">性别</td>
-                    <td th:text="${item.phone}">电话</td>
-                    <td th:text="${item.email}">邮箱</td>
-                    <td th:text="${item.createTime}">创建时间</td>
-                    <td th:text="正常">状态</td>
-                    <td th:text="查看">已授予的角色</td>
+                    <td th:text="${item.appId}">应用 ID</td>
+                    <td th:text="${item.description}">描述</td>
+                    <td th:text="${item.branch}">分支</td>
+                    <td th:text="${item.dirname}">应用目录</td>
+                    <td th:text="${item.appType}">应用类型</td>
+                    <td th:text="${item.runningConfig.httpPort}">HTTP 端口</td>
                     <td>
-                        <a class="open-popup" data-title="编辑用户" th:attr="data-url=@{'/system/user/edit/'+${item.id}}"
-                           data-size="600,570" href="#">编辑</a>
-                        <a class="open-popup" data-title="详细信息" th:attr="data-url=@{'/system/user/detail/'+${item.id}}"
-                           data-size="800,600" href="#">详细</a>
-                        <a class="ajax-get" th:attr="data-msg='您是否删除'+${item.nickname}"
-                           th:href="@{/system/user/status/delete(ids=${item.id})}">删除</a>
+                        <a class="open-popup" data-title="拷贝" th:attr="data-url=@{'/app/config/app/edit/'+${item.appId}}"
+                           data-size="480,320" href="#">拷贝</a>
+                        <a class="open-popup" data-title="应用详细信息" th:attr="data-url=@{'/app/config/app/detail/'+${item.appId}}"
+                           data-size="1000,600" href="#">详细</a>
+                        <a class="open-popup" data-title="编辑" th:attr="data-url=@{'/app/config/app/edit/'+${item.appId}}"
+                           data-size="1000,600" href="#">编辑</a>
+                        <a class="ajax-delete" th:attr="data-msg='您是否删除'+${item.appId}"
+                           th:href="@{/api/app/config/app(ids=${item.appId})}">删除</a>
                     </td>
                 </tr>
                 </tbody>
@@ -110,18 +96,5 @@
     </div>
 </div>
 <script th:replace="/common/template :: script"></script>
-<script type="text/javascript" th:src="@{/js/plugins/jquery-2.2.4.min.js}"></script>
-<script type="text/javascript" th:src="@{/lib/zTree_v3/js/jquery.ztree.core.min.js}"></script>
-<script type="text/javascript" th:src="@{/js/timoTree.js}"></script>
-<script type="text/javascript">
-    var local = window.localStorage;
-    // 树形菜单
-    $.fn.selectTree({
-        rootTree: '全部',
-        onSelected: function (treeNode) {
-            local.setItem('deptTitle', treeNode.name);
-        }
-    });
-</script>
 </body>
 </html>

+ 87 - 0
dmaster/src/main/resources/templates/app/config/proj/index.html

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org"
+      xmlns:mo="https://gitee.com/aun/Timo">
+<head th:replace="/common/template :: header(~{::title},~{::link},~{::style})"></head>
+<body class="timo-layout-page">
+<div class="layui-card">
+    <div class="layui-card-header timo-card-header">
+        <span><i class="fa fa-bars"></i> 项目配置</span>
+        <i class="layui-icon layui-icon-refresh refresh-btn"></i>
+    </div>
+    <div class="layui-card-body">
+        <div class="layui-row timo-card-screen put-row">
+            <div class="pull-left layui-form-pane timo-search-box">
+                <div class="layui-inline">
+                    <label class="layui-form-label">环境</label>
+                    <div class="layui-input-block timo-search-status">
+                        <select class="timo-search-select" name="status" mo:dict="SEARCH_STATUS"
+                                mo-selected="${param.env}"></select>
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <label class="layui-form-label">应用</label>
+                    <div class="layui-input-block">
+                        <input type="text" name="appId" th:value="${param.appId}" placeholder="请输入应用"
+                               autocomplete="off" class="layui-input">
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <button class="layui-btn timo-search-btn">
+                        <i class="fa fa-search"></i>
+                    </button>
+                </div>
+            </div>
+        </div>
+        <div class="timo-table-wrap">
+            <table class="layui-table timo-table">
+                <thead>
+                <tr>
+                    <th class="timo-table-checkbox">
+                        <label class="timo-checkbox"><input type="checkbox">
+                            <i class="layui-icon layui-icon-ok"></i></label>
+                    </th>
+                    <th class="sortable" data-field="appId">应用</th>
+                    <th class="sortable" data-field="branch">分支</th>
+                    <th class="sortable" data-field="commitId">版本</th>
+                    <th class="sortable" data-field="commitTime">提交时间</th>
+                    <th class="sortable" data-field="result">构建结果</th>
+                    <th class="sortable" data-field="buildTime">构建时间</th>
+                    <th class="sortable" data-field="buildBy">构建用户</th>
+                    <th>操作</th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr th:each="item:${list}">
+                    <td><label class="timo-checkbox"><input type="checkbox" th:value="${item.appId}">
+                        <i class="layui-icon layui-icon-ok"></i></label></td>
+                    <td>
+                        <a class="open-popup" data-title="应用配置详情" data-size="1000,600" href="#"
+                           th:text="${item.appId}" th:attr="data-url=@{'/app/config/app/detail/'+${item.appId}}"></a>
+                    </td>
+                    <td th:text="${item.branch}">分支</td>
+                    <td th:text="${item.appId}">版本</td>
+                    <td th:text="${item.appId}">提交时间</td>
+                    <td>
+                        <a class="open-popup" data-title="构建结果" data-size="800,600" href="#"
+                           th:text="${item.appId}" th:attr="data-url=@{'/app/log/build/'+${item.appId} + '/result'}"></a>
+                    </td>
+                    <td th:text="${item.appId}">构建时间</td>
+                    <td th:text="${item.appId}">构建用户</td>
+                    <td>
+                        <a class="ajax-post" th:href="@{'/api/app/bd/update?appId='+${item.appId}}">更新</a>
+                        <a class="ajax-post" th:href="@{'/api/app/bd/build?appId='+${item.appId}}">构建</a>
+                        <a class="open-popup" data-title="应用部署" th:attr="data-url=@{'/app/deploy/' + ${item.appId}}"
+                           data-size="1000,600" href="#">部署</a>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+        <div th:replace="/common/fragment :: page"></div>
+    </div>
+</div>
+<script th:replace="/common/template :: script"></script>
+<script type="text/javascript">
+</script>
+</body>
+</html>

+ 12 - 30
dmaster/src/main/resources/templates/app/deploy.html

@@ -8,47 +8,29 @@
             <table class="layui-table timo-table">
                 <thead>
                 <tr>
-                    <th class="timo-table-checkbox">
-                        <label class="timo-checkbox"><input type="checkbox">
-                            <i class="layui-icon layui-icon-ok"></i></label>
-                    </th>
-                    <th class="sortable" data-field="appId">应用</th>
-                    <th class="sortable" data-field="branch">分支</th>
-                    <th class="sortable" data-field="commitId">版本</th>
-                    <th class="sortable" data-field="commitTime">提交时间</th>
-                    <th class="sortable" data-field="result">构建结果</th>
-                    <th class="sortable" data-field="buildTime">构建时间</th>
-                    <th class="sortable" data-field="buildBy">构建用户</th>
+                    <th class="sortable" data-field="machineIpv4">机器地址</th>
+                    <th class="sortable" data-field="isDeploy">是否已部署</th>
+                    <th class="sortable" data-field="deployTime">部署时间</th>
+                    <th class="sortable" data-field="deployResult">部署结果</th>
+                    <th class="sortable" data-field="deployBy">部署用户</th>
                     <th>操作</th>
                 </tr>
                 </thead>
                 <tbody>
                 <tr th:each="item:${list}">
-                    <td><label class="timo-checkbox"><input type="checkbox" th:value="${item.appId}">
-                        <i class="layui-icon layui-icon-ok"></i></label></td>
+                    <td th:text="${item.machineIpv4}">机器地址</td>
+                    <td th:text="${item.isDeploy}">是否已部署</td>
+                    <td th:text="${item.deployTime}">部署时间</td>
+                    <td th:text="${item.deployResult}">部署结果</td>
+                    <td th:text="${item.deployBy}">部署用户</td>
                     <td>
-                        <a class="open-popup" data-title="应用配置详情" data-size="1200,600" href="#"
-                           th:text="${item.appId}" th:attr="data-url=@{'/app/config/app/detail/'+${item.appId}}"></a>
-                    </td>
-                    <td th:text="${item.branch}">分支</td>
-                    <td th:text="${item.commitId}">版本</td>
-                    <td th:text="${item.commitTime}">提交时间</td>
-                    <td th:text="${item.result}">构建结果</td>
-                    <td th:text="${item.buildTime}">构建时间</td>
-                    <td th:text="${item.buildBy}">构建用户</td>
-                    <td>
-                        <a class="open-popup" data-title="更新" th:attr="data-url=@{'/system/user/edit/'+${item.appId}}"
-                           data-size="600,570" href="#">更新</a>
-                        <a class="open-popup" data-title="构建" th:attr="data-url=@{'/system/user/detail/'+${item.appId}}"
-                           data-size="800,600" href="#">构建</a>
-                        <a class="open-popup" data-title="应用部署详情" th:attr="data-url=@{'/app/deploy/' + ${item.appId}}"
-                           data-size="800,600" href="#">部署</a>
+                        <a class="open-popup" data-title="部署" th:attr="data-url=@{'/system/user/edit/'+${item.buildLogId}}"
+                           data-size="600,570" href="#">部署</a>
                     </td>
                 </tr>
                 </tbody>
             </table>
         </div>
-        <!--<div th:replace="/common/fragment :: page"></div>-->
     </div>
 </div>
 <script th:replace="/common/template :: script"></script>

+ 76 - 6
dmaster/src/main/resources/templates/app/log.html

@@ -1,17 +1,87 @@
 <!DOCTYPE html>
 <html xmlns:th="http://www.thymeleaf.org"
       xmlns:mo="https://gitee.com/aun/Timo">
-<head th:replace="/common/template :: header(~{::title},~{::link},~{::style})">
-    <link rel="stylesheet" th:href="@{/lib/zTree_v3/css/zTreeStyle/zTreeStyle.css}" type="text/css">
-</head>
+<head th:replace="/common/template :: header(~{::title},~{::link},~{::style})"></head>
 <body class="timo-layout-page">
 <div class="layui-card">
-    <div class="layui-bg-gray" style="padding: 30px;">
-        <div class="layui-card-body">
-            <p th:text="${msg}"></p>
+    <div class="layui-card-header timo-card-header">
+        <span><i class="fa fa-bars"></i> 构建部署</span>
+        <i class="layui-icon layui-icon-refresh refresh-btn"></i>
+    </div>
+    <div class="layui-card-body">
+        <div class="layui-row timo-card-screen put-row">
+            <div class="pull-left layui-form-pane timo-search-box">
+                <div class="layui-inline">
+                    <label class="layui-form-label">环境</label>
+                    <div class="layui-input-block timo-search-status">
+                        <select class="timo-search-select" name="status" mo:dict="SEARCH_STATUS"
+                                mo-selected="${param.env}"></select>
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <label class="layui-form-label">应用</label>
+                    <div class="layui-input-block">
+                        <input type="text" name="appId" th:value="${param.appId}" placeholder="请输入应用"
+                               autocomplete="off" class="layui-input">
+                    </div>
+                </div>
+                <div class="layui-inline">
+                    <button class="layui-btn timo-search-btn">
+                        <i class="fa fa-search"></i>
+                    </button>
+                </div>
+            </div>
+        </div>
+        <div class="timo-table-wrap">
+            <table class="layui-table timo-table">
+                <thead>
+                <tr>
+                    <th class="timo-table-checkbox">
+                        <label class="timo-checkbox"><input type="checkbox">
+                            <i class="layui-icon layui-icon-ok"></i></label>
+                    </th>
+                    <th class="sortable" data-field="appId">应用</th>
+                    <th class="sortable" data-field="branch">分支</th>
+                    <th class="sortable" data-field="commitId">版本</th>
+                    <th class="sortable" data-field="commitTime">提交时间</th>
+                    <th class="sortable" data-field="result">构建结果</th>
+                    <th class="sortable" data-field="buildTime">构建时间</th>
+                    <th class="sortable" data-field="buildBy">构建用户</th>
+                    <th>操作</th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr th:each="item:${list}">
+                    <td><label class="timo-checkbox"><input type="checkbox" th:value="${item.appId}">
+                        <i class="layui-icon layui-icon-ok"></i></label></td>
+                    <td>
+                        <a class="open-popup" data-title="应用配置详情" data-size="1000,600" href="#"
+                           th:text="${item.appId}" th:attr="data-url=@{'/app/config/app/detail/'+${item.appId}}"></a>
+                    </td>
+                    <td th:text="${item.branch}">分支</td>
+                    <td th:text="${item.commitId}">版本</td>
+                    <td th:text="${item.commitTime}">提交时间</td>
+                    <td>
+                        <a class="open-popup" data-title="构建结果" data-size="800,600" href="#"
+                           th:text="${item.buildResult}" th:attr="data-url=@{'/app/log/build/'+${item.buildLogId} + '/result'}"></a>
+                    </td>
+                    <td th:text="${item.buildTime}">构建时间</td>
+                    <td th:text="${item.buildBy}">构建用户</td>
+                    <td>
+                        <a class="ajax-post" th:href="@{'/api/app/bd/update?appId='+${item.appId}}">更新</a>
+                        <a class="ajax-post" th:href="@{'/api/app/bd/build?appId='+${item.appId}}">构建</a>
+                        <a class="open-popup" data-title="应用部署" th:attr="data-url=@{'/app/deploy/' + ${item.buildLogId}}"
+                           data-size="1000,600" href="#">部署</a>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
         </div>
+        <div th:replace="/common/fragment :: page"></div>
     </div>
 </div>
 <script th:replace="/common/template :: script"></script>
+<script type="text/javascript">
+</script>
 </body>
 </html>

+ 3 - 5
dmaster/src/main/resources/templates/app/status.html

@@ -45,11 +45,9 @@
                 <td th:text="${item.isRunning}">运行状态</td>
                 <td th:text="${item.lastCheck}">上次检查时间</td>
                 <td>
-                    <a class="ajax-post" th:href="@{'/api/app/bd/update?appId='+${item.appId}}">重启</a>
-                    <a class="ajax-post" th:href="@{'/api/app/bd/build?appId='+${item.appId}}">停止</a>
-                    <a class="ajax-post" th:href="@{'/api/app/bd/build?appId='+${item.appId}}">启动</a>
-                    <!--<a class="open-popup" data-title="应用部署" th:attr="data-url=@{'/app/deploy/' + ${item.appId}}"
-                       data-size="1000,600" href="#">部署</a>-->
+                    <a class="ajax-post" th:href="@{'/api/app/status/restart/'+${item.appId}}">重启</a>
+                    <a class="ajax-post" th:href="@{'/api/app/status/stop/'+${item.appId}}">停止</a>
+                    <a class="ajax-post" th:href="@{'/api/app/status/start/'+${item.appId}}">启动</a>
                 </td>
             </tr>
             </tbody>

+ 56 - 0
dmaster/src/test/java/cn/reghao/autodop/dmaster/app/entity/AppDeployingTest.java

@@ -0,0 +1,56 @@
+package cn.reghao.autodop.dmaster.app.entity;
+
+import cn.reghao.autodop.dmaster.DmasterApplication;
+import cn.reghao.autodop.dmaster.app.entity.config.AppOrchestration;
+import cn.reghao.autodop.dmaster.app.entity.config.deploy.DeployConfig;
+import cn.reghao.autodop.dmaster.app.repository.AppDeployingRepository;
+import cn.reghao.autodop.dmaster.app.repository.config.AppOrchestrationRepository;
+import cn.reghao.autodop.dmaster.machine.entity.MachineInfo;
+import cn.reghao.autodop.dmaster.machine.entity.NetworkInfo;
+import cn.reghao.autodop.dmaster.machine.repository.MachineInfoRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@ActiveProfiles("dev")
+@SpringBootTest(classes = DmasterApplication.class)
+@RunWith(SpringRunner.class)
+class AppDeployingTest {
+    @Autowired
+    private AppOrchestrationRepository appOrchestrationRepository;
+    @Autowired
+    private MachineInfoRepository machineInfoRepository;
+    @Autowired
+    private AppDeployingRepository appDeployingRepository;
+
+    @Test
+    public void test() {
+        List<AppOrchestration> apps = appOrchestrationRepository.findAll();
+        List<AppDeploying> appDeployings = new ArrayList<>();
+        for (AppOrchestration app : apps) {
+            if (!app.getEnable() || app.getDeployConfigs().isEmpty()) {
+                continue;
+            }
+
+            for (DeployConfig deployConfig : app.getDeployConfigs()) {
+                String machineId = deployConfig.getMachineId();
+                MachineInfo machineInfo = machineInfoRepository.findByMachineId(machineId);
+                if (machineInfo != null && !machineInfo.getNetworkInfo().isEmpty()) {
+                    NetworkInfo networkInfo = machineInfo.getNetworkInfo().get(0);
+                    appDeployings.add(AppDeploying.from(app.getAppId(), machineId, networkInfo.getIpv4()));
+                }
+            }
+        }
+
+        log.info("");
+        //appDeployingRepository.saveAll(appDeployings);
+    }
+}

+ 3 - 1
scripts/build.sh

@@ -35,4 +35,6 @@ rm -rf ${app_name}
 cd ${proj_home}
 mvn clean
 
-#mvn clean package -am -Dmaven.test.skip=true -pl dmaster -Pdev
+#mvn clean package -am -Dmaven.test.skip=true -pl dmaster -Pdev
+#mvn clean package -am -Dmaven.test.skip=true -pl dmaster -Ptest
+#mvn clean package -am -Dmaven.test.skip=true -pl dagent -Ptest