Explorar el Código

Merge branch 'master' of http://gogs.seec.seecoder.cn/WengPuHong/seecoder-devcloud

zhaoxingrui hace 4 años
padre
commit
03c71c9da5

+ 15 - 0
api/src/main/java/cn/seecoder/api/k8s/model/LimitRange.java

@@ -49,6 +49,21 @@ public class LimitRange extends K8sAbstractObject<V1LimitRange>{
     public LimitRange(V1LimitRange v1LimitRange){
         super(v1LimitRange);
 
+        V1LimitRangeSpec spec = v1LimitRange.getSpec();
+        V1LimitRangeItem v1LimitRangeItem = spec.getLimits().get(0);
+        defaultCpu = v1LimitRangeItem.getDefault().get("cpu").toSuffixedString();
+        defaultMemory = v1LimitRangeItem.getDefault().get("memory").toSuffixedString();
+        defaultRequestCpu = v1LimitRangeItem.getDefaultRequest().get("cpu").toSuffixedString();
+        defaultRequestMemory = v1LimitRangeItem.getDefaultRequest().get("memory").toSuffixedString();
+        minCpu = v1LimitRangeItem.getMin().get("cpu").toSuffixedString();
+        minMemory = v1LimitRangeItem.getMin().get("memory").toSuffixedString();
+        maxCpu = v1LimitRangeItem.getMax().get("cpu").toSuffixedString();
+        maxMemory = v1LimitRangeItem.getMax().get("memory").toSuffixedString();
+        type = v1LimitRangeItem.getType();
+
+        V1ObjectMeta metadata = v1LimitRange.getMetadata();
+        name = metadata.getName();
+        namespace = metadata.getNamespace();
     }
 
     private V1LimitRangeSpec toV1LimitRangeSpec() {

+ 46 - 0
web/src/main/java/cn/seecoder/web/controller/devmanage/DevmanageController.java

@@ -0,0 +1,46 @@
+package cn.seecoder.web.controller.devmanage;
+
+import cn.seecoder.web.model.vo.Response;
+import cn.seecoder.web.model.vo.devmanage.DeploymentVO;
+import cn.seecoder.web.model.vo.user.UserVO;
+import cn.seecoder.web.service.devmanage.DevmanageService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/devmanage")
+public class DevmanageController {
+    private final DevmanageService devmanageService;
+
+    @GetMapping("/deployment")
+    public ResponseEntity<Response<List<DeploymentVO>>> getPagedDeployment(@RequestParam Integer pageNo, @RequestParam Integer pageSize) {
+        return ResponseEntity.ok().body(Response.buildSuccess(devmanageService.getPagedDeployments(pageNo, pageSize)));
+    }
+
+    @GetMapping("/deployment/search")
+    public ResponseEntity<Response<List<DeploymentVO>>> searchPagedDeployments(@RequestParam String keyword, @RequestParam Integer pageNo, @RequestParam Integer pageSize) {
+        return ResponseEntity.ok().body(Response.buildSuccess(devmanageService.searchPagedDeployments(keyword, pageNo, pageSize)));
+    }
+
+    @GetMapping("/owner/namespace")
+    public ResponseEntity<Response<UserVO>> getOwnerOfNamespace(@RequestParam String namespace) {
+        return ResponseEntity.ok().body(Response.buildSuccess(devmanageService.getOwnerOfNamespace(namespace)));
+    }
+
+    @GetMapping("/owner/deployment")
+    public ResponseEntity<Response<UserVO>> getOwnerOfDeployment(@RequestParam String deployName) {
+        return ResponseEntity.ok().body(Response.buildSuccess(devmanageService.getOwnerOfDeployment(deployName)));
+    }
+
+    @GetMapping("/pages")
+    public ResponseEntity<Response<Integer>> getTotalPages(@RequestParam Integer pageSize) {
+        return ResponseEntity.ok().body(Response.buildSuccess(devmanageService.getTotalPageCount(pageSize)));
+    }
+}

+ 24 - 0
web/src/main/java/cn/seecoder/web/dao/pipeline/DeploymentMapper.java

@@ -28,6 +28,30 @@ public interface DeploymentMapper {
     @Select("select * from deployment where pipeline_id = #{pipelineId}")
     DeploymentPO selectByPipelineId(Integer pipelineId);
 
+    @Select("select * from deployment where namespace like CONCAT('%', #{keyword}, '%') or " +
+            "deploy_name like CONCAT('%', #{keyword}, '%') " +
+            "limit #{startPos}, #{pageSize}")
+    List<DeploymentPO> searchDeployments(@Param("keyword") String keyword, @Param("startPos") Integer startPos, @Param("pageSize") Integer pageSize);
+
+    @Select("select * from deployment where namespace = #{namespace}")
+    DeploymentPO selectByNamespace(String namespace);
+
+    @Select("select * from deployment where deploy_name = #{deployName}")
+    DeploymentPO selectByDeployName(String deployName);
+
+    @Select("select * from deployment limit #{startPos}, #{pageSize}")
+    List<DeploymentPO> selectPagedDeployments(@Param("startPos") Integer startPos, @Param("pageSize") Integer pageSize);
+
+    /**
+     * !!For Scheduled Deployments Purge Only
+     * @return DeploymentPO List
+     */
+    @Select("select * from deployment")
+    List<DeploymentPO> selectAllDeployments();
+
+    @Select("select count(*) from deployment")
+    Integer selectTotalItemCount();
+
     @Delete("delete from deployment where pipeline_id = #{pipelineId}")
     void delete(Integer pipelineId);
 

+ 48 - 0
web/src/main/java/cn/seecoder/web/infrastructure/ScheduledTasks.java

@@ -0,0 +1,48 @@
+package cn.seecoder.web.infrastructure;
+
+import cn.seecoder.common.exceptions.AccessDeniedException;
+import cn.seecoder.web.dao.pipeline.DeploymentMapper;
+import cn.seecoder.web.dao.pipeline.PipelineMapper;
+import cn.seecoder.web.model.po.pipeline.DeploymentPO;
+import cn.seecoder.web.model.po.pipeline.PipelinePO;
+import cn.seecoder.web.service.pipeline.DeploymentService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+@Log4j2
+@RequiredArgsConstructor
+@Async
+@Component
+public class ScheduledTasks {
+    private final DeploymentService deploymentService;
+    private final DeploymentMapper deploymentMapper;
+    private final PipelineMapper pipelineMapper;
+
+    @Scheduled(cron = "0 0 1 * * ?")
+    public void purgeZombieDeployments() {
+        long ONE_DAY_MILLIS = 86400000L;
+        Timestamp expireLimit = new Timestamp(System.currentTimeMillis() - ONE_DAY_MILLIS);
+        List<DeploymentPO> deploymentPOList = deploymentMapper.selectAllDeployments();
+        for (DeploymentPO deploymentPO: deploymentPOList) {
+            Timestamp deployedTime = deploymentPO.getDeployedTime();
+            if (deployedTime.before(expireLimit)) {
+                PipelinePO pipelinePO = pipelineMapper.selectById(deploymentPO.getPipelineId());
+                try {
+                    deploymentService.delete(pipelinePO.getProjectId(), deploymentPO.getPipelineId());
+                } catch (AccessDeniedException e) {
+                    log.error(String.format("Scheduled Deployments Purge Failed for projectId: %s, pipelineId: %s, for %s",
+                            pipelinePO.getProjectId(),
+                            deploymentPO.getPipelineId(),
+                            e.getMessage()));
+                }
+            }
+        }
+    }
+}

+ 71 - 67
web/src/main/java/cn/seecoder/web/infrastructure/config/WebSecurityConfig.java

@@ -1,11 +1,10 @@
 package cn.seecoder.web.infrastructure.config;
 
 import cn.seecoder.web.infrastructure.security.JwtAuthenticationTokenFilter;
+import cn.seecoder.web.infrastructure.security.WebSecurityConstants;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.http.HttpMethod;
-import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -15,7 +14,10 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.CorsConfigurationSource;
 import org.springframework.web.cors.CorsUtils;
-import cn.seecoder.web.infrastructure.security.WebSecurityConstants;
+
+import static cn.seecoder.web.infrastructure.security.WebSecurityConstants.SEEC_AUTHORITY;
+import static cn.seecoder.web.infrastructure.security.WebSecurityConstants.SEEC_ROLE;
+import static org.springframework.http.HttpMethod.*;
 
 /**
  * @author PuHong Weng
@@ -53,83 +55,85 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
 
                 .authorizeRequests()
+                // Devmanage Controller
+                .antMatchers(GET, "/devmanage/**").hasAnyRole(SEEC_ROLE)
                 // API Test Controller
-                .antMatchers(HttpMethod.GET, "/test/list/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
-                .antMatchers(HttpMethod.GET, "/test/delete/{testId}").access("@authTools.checkTestOwnership(#testId)")
-                .antMatchers(HttpMethod.GET, "/test/execute/{testId}").access("@authTools.checkTestOwnership(#testId)")
-                .antMatchers(HttpMethod.GET, "/test/result/{testId}").access("@authTools.checkTestOwnership(#testId)")
-                .antMatchers(HttpMethod.GET, "/test/latest/{testId}").access("@authTools.checkTestOwnership(#testId)")
-                .antMatchers(HttpMethod.POST, "/test/create").authenticated()
+                .antMatchers(GET, "/test/list/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
+                .antMatchers(GET, "/test/delete/{testId}").access("@authTools.checkTestOwnership(#testId)")
+                .antMatchers(GET, "/test/execute/{testId}").access("@authTools.checkTestOwnership(#testId)")
+                .antMatchers(GET, "/test/result/{testId}").access("@authTools.checkTestOwnership(#testId)")
+                .antMatchers(GET, "/test/latest/{testId}").access("@authTools.checkTestOwnership(#testId)")
+                .antMatchers(POST, "/test/create").authenticated()
                 // Interface Test Controller
-                .antMatchers(HttpMethod.POST, "/interfaces/create").permitAll()
-                .antMatchers(HttpMethod.GET, "/interfaces/list/{projectId}").permitAll()
-                .antMatchers(HttpMethod.GET, "/interfaces/delete/{id}").permitAll()
-                .antMatchers(HttpMethod.GET, "/interfaces/execute/{id}/{username}/{type}").permitAll()
-                .antMatchers(HttpMethod.GET, "/interfaces/results/{id}").permitAll()
-                .antMatchers(HttpMethod.GET, "/interfaces/results/auto/{id}").permitAll()
+                .antMatchers(POST, "/interfaces/create").permitAll()
+                .antMatchers(GET, "/interfaces/list/{projectId}").permitAll()
+                .antMatchers(GET, "/interfaces/delete/{id}").permitAll()
+                .antMatchers(GET, "/interfaces/execute/{id}/{username}/{type}").permitAll()
+                .antMatchers(GET, "/interfaces/results/{id}").permitAll()
+                .antMatchers(GET, "/interfaces/results/auto/{id}").permitAll()
                 // Auto Test Controller
-                .antMatchers(HttpMethod.GET, "/auto_test/list").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/auto_test/list").access("@authTools.checkProjOwnershipParam(request)")
                 // Bug List Controller
-                .antMatchers(HttpMethod.GET, "/bug_list/list").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.POST, "/bug_list").authenticated()
-                .antMatchers(HttpMethod.PUT, "/bug_list").access("@authTools.checkBugOwnershipBody(request)")
+                .antMatchers(GET, "/bug_list/list").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(POST, "/bug_list").authenticated()
+                .antMatchers(PUT, "/bug_list").access("@authTools.checkBugOwnershipBody(request)")
                 // Func Test Controller
-                .antMatchers(HttpMethod.GET, "/func_test").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/func_test/taskList").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/func_test/steps").access("@authTools.checkTestCaseOwnershipParam(request)")
-                .antMatchers(HttpMethod.POST, "/func_test").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.POST, "/func_test/steps").access("@authTools.checkTestCaseOwnershipParam(request)")
-                .antMatchers(HttpMethod.PUT, "/func_test").access("@authTools.checkTestCaseOwnershipParam(request)")
-                .antMatchers(HttpMethod.PUT, "/func_test/steps").access("@authTools.checkTestStepOwnershipParam(request)")
-                .antMatchers(HttpMethod.PUT, "/func_test/finish").access("@authTools.checkTestCaseOwnershipParam(request)")
-                .antMatchers(HttpMethod.PUT, "/func_test/reopen").access("@authTools.checkTestCaseOwnershipParam(request)")
-                .antMatchers(HttpMethod.PUT, "/func_test/steps/state").access("@authTools.checkTestStepOwnershipParam(request)")
-                .antMatchers(HttpMethod.PUT, "/func_test/record/latest").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.DELETE, "/func_test/steps").access("@authTools.checkTestStepOwnershipParam(request)")
-                .antMatchers(HttpMethod.DELETE, "/func_test").access("@authTools.checkTestCaseOwnershipParam(request)")
+                .antMatchers(GET, "/func_test").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/func_test/taskList").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/func_test/steps").access("@authTools.checkTestCaseOwnershipParam(request)")
+                .antMatchers(POST, "/func_test").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(POST, "/func_test/steps").access("@authTools.checkTestCaseOwnershipParam(request)")
+                .antMatchers(PUT, "/func_test").access("@authTools.checkTestCaseOwnershipParam(request)")
+                .antMatchers(PUT, "/func_test/steps").access("@authTools.checkTestStepOwnershipParam(request)")
+                .antMatchers(PUT, "/func_test/finish").access("@authTools.checkTestCaseOwnershipParam(request)")
+                .antMatchers(PUT, "/func_test/reopen").access("@authTools.checkTestCaseOwnershipParam(request)")
+                .antMatchers(PUT, "/func_test/steps/state").access("@authTools.checkTestStepOwnershipParam(request)")
+                .antMatchers(PUT, "/func_test/record/latest").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(DELETE, "/func_test/steps").access("@authTools.checkTestStepOwnershipParam(request)")
+                .antMatchers(DELETE, "/func_test").access("@authTools.checkTestCaseOwnershipParam(request)")
                 // Commit Controller
-                .antMatchers(HttpMethod.GET, "/commits/tree").access("@authTools.checkTreeNodeOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/commits/bug_list").access("@authTools.checkBugOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/commits/list").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/commits/tree").access("@authTools.checkTreeNodeOwnershipParam(request)")
+                .antMatchers(GET, "/commits/bug_list").access("@authTools.checkBugOwnershipParam(request)")
+                .antMatchers(GET, "/commits/list").access("@authTools.checkProjOwnershipParam(request)")
                 // Deployment Controller
-                .antMatchers(HttpMethod.GET, "/deployments/list").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/deployments/log").access("@authTools.checkProjPipelineParam(request)")
-                .antMatchers(HttpMethod.POST, "/deployments").access("@authTools.checkProjPipelineParam(request)")
-                .antMatchers(HttpMethod.DELETE, "/deployments").permitAll()
+                .antMatchers(GET, "/deployments/list").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/deployments/log").access("@authTools.checkProjPipelineParam(request)")
+                .antMatchers(POST, "/deployments").access("@authTools.checkProjPipelineParam(request)")
+                .antMatchers(DELETE, "/deployments").permitAll()
                 // Pipeline Controller
-                .antMatchers(HttpMethod.GET, "/pipelines/list").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/pipelines").access("@authTools.checkProjPipelineParam(request)")
-                .antMatchers(HttpMethod.GET, "/pipelines/templates").authenticated()
-                .antMatchers(HttpMethod.GET, "/pipelines/record/list").access("@authTools.checkProjPipelineParam(request)")
-                .antMatchers(HttpMethod.GET, "/pipelines/record").access("@authTools.checkPipelineRecordOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/pipelines/record/details").access("@authTools.checkPipelineRecordOwnershipParam(request)")
-                .antMatchers(HttpMethod.POST, "/pipelines").authenticated()
-                .antMatchers(HttpMethod.PUT, "/pipelines/config").access("@authTools.checkProjPipelineBody(request)")
-                .antMatchers(HttpMethod.DELETE, "/pipelines").access("@authTools.checkProjPipelineParam(request)")
+                .antMatchers(GET, "/pipelines/list").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/pipelines").access("@authTools.checkProjPipelineParam(request)")
+                .antMatchers(GET, "/pipelines/templates").authenticated()
+                .antMatchers(GET, "/pipelines/record/list").access("@authTools.checkProjPipelineParam(request)")
+                .antMatchers(GET, "/pipelines/record").access("@authTools.checkPipelineRecordOwnershipParam(request)")
+                .antMatchers(GET, "/pipelines/record/details").access("@authTools.checkPipelineRecordOwnershipParam(request)")
+                .antMatchers(POST, "/pipelines").authenticated()
+                .antMatchers(PUT, "/pipelines/config").access("@authTools.checkProjPipelineBody(request)")
+                .antMatchers(DELETE, "/pipelines").access("@authTools.checkProjPipelineParam(request)")
                 // Stage Controller
-                .antMatchers(HttpMethod.GET, "/stages").authenticated()
+                .antMatchers(GET, "/stages").authenticated()
                 // Project Controller
-                .antMatchers(HttpMethod.GET, "/project/listByUser").authenticated()
-                .antMatchers(HttpMethod.GET, "/project/members").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/project/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
-                .antMatchers(HttpMethod.POST, "/project/create").authenticated()
-                .antMatchers(HttpMethod.POST, "/project/members").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.POST, "/project/relate/code").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.POST, "/project/relate").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.GET, "/project/relate/{projectId}").authenticated()
+                .antMatchers(GET, "/project/listByUser").authenticated()
+                .antMatchers(GET, "/project/members").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/project/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
+                .antMatchers(POST, "/project/create").authenticated()
+                .antMatchers(POST, "/project/members").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(POST, "/project/relate/code").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(POST, "/project/relate").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(GET, "/project/relate/{projectId}").authenticated()
                 // SQL Controller
-                .antMatchers(HttpMethod.GET, "/sql/instances").access("@authTools.checkProjOwnershipParam(request)")
-                .antMatchers(HttpMethod.POST, "/sql/exec").authenticated()
+                .antMatchers(GET, "/sql/instances").access("@authTools.checkProjOwnershipParam(request)")
+                .antMatchers(POST, "/sql/exec").authenticated()
                 // Tree Nodes Controller
-                .antMatchers(HttpMethod.GET, "/tree/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
-                .antMatchers(HttpMethod.GET, "/tree/node/type/task/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
-                .antMatchers(HttpMethod.POST, "/tree/node").authenticated()
-                .antMatchers(HttpMethod.POST, "/tree/subNode/{fatherId}").access("@authTools.checkTreeNodeOwnership(#fatherId)")
-                .antMatchers(HttpMethod.PUT, "/tree/node").access("@authTools.checkTreeNodeOwnershipBody(request)")
-                .antMatchers(HttpMethod.DELETE, "/tree/node/{nodeId}").access("@authTools.checkTreeNodeOwnership(#nodeId)")
+                .antMatchers(GET, "/tree/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
+                .antMatchers(GET, "/tree/node/type/task/{projectId}").access("@authTools.checkProjOwnership(#projectId)")
+                .antMatchers(POST, "/tree/node").authenticated()
+                .antMatchers(POST, "/tree/subNode/{fatherId}").access("@authTools.checkTreeNodeOwnership(#fatherId)")
+                .antMatchers(PUT, "/tree/node").access("@authTools.checkTreeNodeOwnershipBody(request)")
+                .antMatchers(DELETE, "/tree/node/{nodeId}").access("@authTools.checkTreeNodeOwnership(#nodeId)")
                 // User Controller
-                .antMatchers(HttpMethod.GET, "/users/self").authenticated()
-                .antMatchers(HttpMethod.POST, "/users/gitlab").authenticated()
+                .antMatchers(GET, "/users/self").authenticated()
+                .antMatchers(POST, "/users/gitlab").authenticated()
                 // Swagger
                 .antMatchers("/**/*swagger*/**").permitAll()
                 .antMatchers("/**/*api-docs*/**").permitAll()

+ 28 - 0
web/src/main/java/cn/seecoder/web/model/vo/devmanage/DeploymentVO.java

@@ -0,0 +1,28 @@
+package cn.seecoder.web.model.vo.devmanage;
+
+import cn.seecoder.web.model.po.pipeline.DeploymentPO;
+import lombok.Data;
+
+import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.util.TimeZone;
+
+@Data
+public class DeploymentVO {
+
+    private Integer id;
+    private Integer pipelineId;
+    private String namespace;
+    private String deployName;
+    private String deployedTime;
+
+    public DeploymentVO(DeploymentPO deploymentPO) {
+        this.id = deploymentPO.getId();
+        this.pipelineId = deploymentPO.getPipelineId();
+        this.namespace = deploymentPO.getNamespace();
+        this.deployName = deploymentPO.getDeployName();
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+        simpleDateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("+8")));
+        this.deployedTime = simpleDateFormat.format(deploymentPO.getDeployedTime());
+    }
+}

+ 14 - 0
web/src/main/java/cn/seecoder/web/service/devmanage/DevmanageService.java

@@ -0,0 +1,14 @@
+package cn.seecoder.web.service.devmanage;
+
+import cn.seecoder.web.model.vo.devmanage.DeploymentVO;
+import cn.seecoder.web.model.vo.user.UserVO;
+
+import java.util.List;
+
+public interface DevmanageService {
+    List<DeploymentVO> getPagedDeployments(Integer pageNo, Integer pageSize);
+    List<DeploymentVO> searchPagedDeployments(String keyword, Integer pageNo, Integer pageSize);
+    UserVO getOwnerOfNamespace(String namespace);
+    UserVO getOwnerOfDeployment(String deployName);
+    Integer getTotalPageCount(Integer pageSize);
+}

+ 94 - 0
web/src/main/java/cn/seecoder/web/service/impl/devmanage/DevmanageServiceImpl.java

@@ -0,0 +1,94 @@
+package cn.seecoder.web.service.impl.devmanage;
+
+import cn.seecoder.web.dao.pipeline.DeploymentMapper;
+import cn.seecoder.web.dao.pipeline.PipelineRecordMapper;
+import cn.seecoder.web.dao.user.UserMapper;
+import cn.seecoder.web.model.po.pipeline.DeploymentPO;
+import cn.seecoder.web.model.po.pipeline.PipelineRecordPO;
+import cn.seecoder.web.model.po.user.UserPO;
+import cn.seecoder.web.model.vo.devmanage.DeploymentVO;
+import cn.seecoder.web.model.vo.user.UserVO;
+import cn.seecoder.web.service.devmanage.DevmanageService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class DevmanageServiceImpl implements DevmanageService {
+    private final DeploymentMapper deploymentMapper;
+    private final PipelineRecordMapper pipelineRecordMapper;
+    private final UserMapper userMapper;
+    private static final int[] validPageSizes = new int[]{5, 10, 20};
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<DeploymentVO> getPagedDeployments(Integer pageNo, Integer pageSize) {
+        if (isInvalidPageSize(pageSize)) {
+            return new ArrayList<>();
+        }
+        List<DeploymentPO> deploymentPOList = deploymentMapper.selectPagedDeployments(pageSize * (pageNo - 1), pageSize);
+        List<DeploymentVO> deploymentVOList = new ArrayList<>();
+        for (DeploymentPO po: deploymentPOList) {
+            deploymentVOList.add(new DeploymentVO(po));
+        }
+        return deploymentVOList;
+    }
+
+    @Override
+    public List<DeploymentVO> searchPagedDeployments(String keyword, Integer pageNo, Integer pageSize) {
+        if (isInvalidPageSize(pageSize)) {
+            return new ArrayList<>();
+        }
+        List<DeploymentPO> deploymentPOList = deploymentMapper.searchDeployments(keyword, pageSize * (pageNo - 1), pageSize);
+        List<DeploymentVO> deploymentVOList = new ArrayList<>();
+        for (DeploymentPO po: deploymentPOList) {
+            deploymentVOList.add(new DeploymentVO(po));
+        }
+        return deploymentVOList;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public UserVO getOwnerOfNamespace(String namespace) {
+        DeploymentPO deploymentPO = deploymentMapper.selectByNamespace(namespace);
+        return new UserVO(getUserPO(deploymentPO));
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public UserVO getOwnerOfDeployment(String deployName) {
+        DeploymentPO deploymentPO = deploymentMapper.selectByDeployName(deployName);
+        return new UserVO(getUserPO(deploymentPO));
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public Integer getTotalPageCount(Integer pageSize) {
+        Integer totalItemCount = deploymentMapper.selectTotalItemCount();
+        return totalItemCount / pageSize + (totalItemCount % pageSize == 0 ? 0 : 1);
+    }
+
+    private boolean isInvalidPageSize(Integer pageSize) {
+        for (int validPageSize: validPageSizes) {
+            if (validPageSize == pageSize) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private UserPO getUserPO(DeploymentPO deploymentPO) {
+        Integer pipelineId = deploymentPO.getPipelineId();
+        List<PipelineRecordPO> pipelineRecordPOS = pipelineRecordMapper.selectByPipelineId(pipelineId);
+        if (pipelineRecordPOS.size() == 0) {
+            return null;
+        } else {
+            Integer userId = pipelineRecordPOS.get(0).getUserId();
+            return userMapper.select(userId);
+        }
+    }
+}