Jelajahi Sumber

feat:流水线构建应用,以及对应的创建构建任务、查询状态的接口

shanshan 2 tahun lalu
induk
melakukan
a285d2b7e9
20 mengubah file dengan 542 tambahan dan 9 penghapusan
  1. 5 0
      pom.xml
  2. 5 0
      src/main/java/cn/seecoder/fdroidrepository/DataObject/ApplicationProperties.java
  3. 5 1
      src/main/java/cn/seecoder/fdroidrepository/DataObject/Enum/BuildTaskStatusEnum.java
  4. 8 0
      src/main/java/cn/seecoder/fdroidrepository/DataObject/PO/BuildTaskPO.java
  5. 11 0
      src/main/java/cn/seecoder/fdroidrepository/Mapper/AppInfoMapper.java
  6. 0 4
      src/main/java/cn/seecoder/fdroidrepository/Mapper/AppMapper.java
  7. 19 0
      src/main/java/cn/seecoder/fdroidrepository/Mapper/BuildTaskMapper.java
  8. 150 0
      src/main/java/cn/seecoder/fdroidrepository/Mapper/GeneralInsertUpdateSqlProvider.java
  9. 16 0
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/Config/CommonValueTable.java
  10. 4 0
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/Config/DockerConfig.java
  11. 1 1
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/Context.java
  12. 20 0
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/ExecuteResult.java
  13. 49 0
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/BuildHandler.java
  14. 29 1
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/CleanHandler.java
  15. 38 1
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/GitPullingHandler.java
  16. 42 0
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/GradleHandler.java
  17. 10 1
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/PipelineFactory.java
  18. 4 0
      src/main/java/cn/seecoder/fdroidrepository/Pipeline/WaitLock.java
  19. 7 0
      src/main/java/cn/seecoder/fdroidrepository/Service/AppService.java
  20. 119 0
      src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/AppServiceImpl.java

+ 5 - 0
pom.xml

@@ -60,6 +60,11 @@
             <artifactId>docker-java-transport-httpclient5</artifactId>
             <version>3.3.0</version>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.jgit</groupId>
+            <artifactId>org.eclipse.jgit</artifactId>
+            <version>5.9.0.202009080501-r</version>
+        </dependency>
 
     </dependencies>
 

+ 5 - 0
src/main/java/cn/seecoder/fdroidrepository/DataObject/ApplicationProperties.java

@@ -1,6 +1,8 @@
 package cn.seecoder.fdroidrepository.DataObject;
 
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
 import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
@@ -10,7 +12,10 @@ import lombok.NoArgsConstructor;
 @Data
 @NoArgsConstructor
 @AllArgsConstructor
+@Builder
 public class ApplicationProperties {
     private String repoUrl;
     private String branch;
+    private String appName;
+    private Integer appId;
 }

+ 5 - 1
src/main/java/cn/seecoder/fdroidrepository/DataObject/Enum/BuildTaskStatusEnum.java

@@ -3,8 +3,12 @@ package cn.seecoder.fdroidrepository.DataObject.Enum;
 
 public enum BuildTaskStatusEnum {
     NEW("New"),
-    FETCHING("Fetching"),
+    PULLING("Pulling"),
+    FINISHPULLING("FinishPulling"),
+    WAITING("Waiting"),
+    WRITTEN("Written"),
     BUILDING("Building"),
+    FINISHBUILDING("FinishBuilding"),
     SUCCESS("Success"),
     FAILED("Failed");
     private final String value;

+ 8 - 0
src/main/java/cn/seecoder/fdroidrepository/DataObject/PO/BuildTaskPO.java

@@ -2,7 +2,11 @@ package cn.seecoder.fdroidrepository.DataObject.PO;
 
 import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
 import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
 
 import java.util.Date;
 
@@ -10,6 +14,9 @@ import java.util.Date;
  * 构建任务
  */
 @Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
 public class BuildTaskPO {
     Integer id;
     Integer appId;
@@ -21,4 +28,5 @@ public class BuildTaskPO {
     String repoUrl;
     String branch;
     String message;
+    String result;
 }

+ 11 - 0
src/main/java/cn/seecoder/fdroidrepository/Mapper/AppInfoMapper.java

@@ -0,0 +1,11 @@
+package cn.seecoder.fdroidrepository.Mapper;
+
+import cn.seecoder.fdroidrepository.DataObject.PO.AppInfoPO;
+import org.apache.ibatis.annotations.Select;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface AppInfoMapper {
+    @Select("select * from app_info where id = #{id}")
+    AppInfoPO selectById(Integer id);
+}

+ 0 - 4
src/main/java/cn/seecoder/fdroidrepository/Mapper/AppMapper.java

@@ -1,4 +0,0 @@
-package cn.seecoder.fdroidrepository.Mapper;
-
-public class AppMapper {
-}

+ 19 - 0
src/main/java/cn/seecoder/fdroidrepository/Mapper/BuildTaskMapper.java

@@ -0,0 +1,19 @@
+package cn.seecoder.fdroidrepository.Mapper;
+
+import cn.seecoder.fdroidrepository.DataObject.PO.BuildTaskPO;
+import org.apache.ibatis.annotations.InsertProvider;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.UpdateProvider;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface BuildTaskMapper {
+    @InsertProvider(type = GeneralInsertUpdateSqlProvider.class, method = "insert")
+    int insert(BuildTaskPO buildTaskPO, String... ignoredCols);
+
+    @UpdateProvider(type = GeneralInsertUpdateSqlProvider.class, method = "updateById")
+    int updateById(BuildTaskPO buildTaskPO);
+
+    @Select("select * from build_task where id = #{taskId}")
+    BuildTaskPO selectById(Integer taskId);
+}

+ 150 - 0
src/main/java/cn/seecoder/fdroidrepository/Mapper/GeneralInsertUpdateSqlProvider.java

@@ -0,0 +1,150 @@
+package cn.seecoder.fdroidrepository.Mapper;
+
+import org.apache.ibatis.jdbc.SQL;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class GeneralInsertUpdateSqlProvider {
+
+    /**
+     * @param obj 插入对象, 用来获取字段和表名
+     * @param ignoredCols 插入时需要忽略的列名 列名格式是Underscore,如created_at
+     * @return sql
+     *  插入单个参数(除了ignoredCols以外的参数)时,mapper写方法就行,实例见 UserMapper.insert
+     *  插入时填入了ignoredCols参数(比如不要id、startTime等自增、默认值字段),实例见PipelineRecordMapper.insert
+     */
+    public static String insert(Object obj,String... ignoredCols){
+        Map<String, String> map;
+        try {
+            map = getFieldsMap(obj, true, false);
+            for (String col: ignoredCols){
+                map.remove(camelCase2Underscore(col));
+            }
+        } catch (IllegalArgumentException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        return getInsertSQL(obj, map);
+    }
+
+    private static String getInsertSQL(Object obj, Map<String, String> map){
+        return new SQL() {
+            {
+                INSERT_INTO(getTableName(obj));
+                for (String col : map.keySet()) {
+                    VALUES(col, map.get(col));
+                }
+            }
+        }.toString();
+    }
+
+    /**
+     * 更新obj所含的字段,但是不包括为null的字段
+     */
+    public static String updateById(Object obj) {
+        return updateById(obj, false);
+    }
+
+    /**
+     * 更新obj所含的字段,但是包括为null的字段
+     */
+    public static String updateWithNullById(Object obj) {
+        return updateById(obj, true);
+    }
+
+    /**
+     * 按传入的列名更新字段
+     */
+    public static String updateWithColsById(Object obj, String... cols) {
+        return updateById(obj, true, cols);
+    }
+
+    private static String updateById(Object obj, boolean includeNullValueField, String... cols) {
+        Map<String, String> map;
+        try {
+            map = getFieldsMap(obj, includeNullValueField, cols.length == 0);
+            map.remove("id");
+            //有可变长参数就保留可边长参数
+            if (cols.length!=0){
+                Set<String> colsSet = Arrays.stream(cols).collect(Collectors.toSet());
+                map = map.entrySet().stream()
+                        .filter(e -> colsSet.contains(e.getKey()))
+                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+            }
+        } catch (IllegalArgumentException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        return getUpdateSQL(obj,map);
+    }
+
+    private static String getUpdateSQL(Object obj, Map<String, String> map){
+        return new SQL() {
+            {
+                UPDATE(getTableName(obj));
+                for (String col : map.keySet()) {
+                    SET(col + "=" + map.get(col));
+                }
+                WHERE("id = #{id}");
+            }
+        }.toString();
+    }
+
+
+    /**
+     *
+     * @param obj 获取字段名的对象
+     * @param includeNullValue 是否要获取为null的字段名
+     * @param singleParam sql参数只有一个实体类的时候 设置为true,多个参数的时候设置false
+     * @return
+     * @throws IllegalArgumentException
+     * @throws IllegalAccessException
+     */
+    private static Map<String, String> getFieldsMap(Object obj, boolean includeNullValue, boolean singleParam)
+            throws IllegalArgumentException, IllegalAccessException {
+        HashMap<String, String> result = new HashMap<>();
+        Class<?> cls = obj.getClass();
+        Field[] fields = cls.getDeclaredFields(); // getDeclaredFields
+        for (Field f : fields) {
+            String col = f.getName();
+            String colName = col.substring(0, 1).toUpperCase() + col.substring(1);
+            f.setAccessible(true);
+            try {
+                cls.getMethod("get" + colName);
+                cls.getMethod("set" + colName, f.getType());
+            } catch (NoSuchMethodException | SecurityException e) {
+                continue;
+            }
+            /**
+             * 单参数的时候
+             * #{colname}可以直接取到实体的属性
+             * 多参数的时候
+             * #{param1.colname}来取实体的属性
+             * 注:实体类一定要放第一个参数
+             */
+            String paramPrefix =  singleParam? "#{" : "#{param1.";
+            if ((!"id".equals(col) && includeNullValue) || f.get(obj) != null) {
+                result.put(camelCase2Underscore(col), paramPrefix + col + "}");
+            }
+        }
+        return result;
+    }
+
+    public static String getTableName(Object obj) {
+        return camelCase2Underscore(obj.getClass().getSimpleName().replaceAll("PO",""));
+    }
+
+    public static String camelCase2Underscore(String s) {
+        StringBuffer buf = new StringBuffer();
+        for (String w : s.split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")) {
+            buf.append("_").append(w.toLowerCase());
+        }
+
+        return "`"+ buf.substring(1) + "`";
+    }
+
+}

+ 16 - 0
src/main/java/cn/seecoder/fdroidrepository/Pipeline/Config/CommonValueTable.java

@@ -0,0 +1,16 @@
+package cn.seecoder.fdroidrepository.Pipeline.Config;
+
+import cn.seecoder.fdroidrepository.Pipeline.ExecuteResult;
+import cn.seecoder.fdroidrepository.Pipeline.WaitLock;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * CommonValueTable 用于保存流水线任务执行过程中的数据
+ */
+public class CommonValueTable {
+    // LockTable 保存了 taskId 对应的WaitLock信息,用于等待用户修改Gradle
+    public static ConcurrentHashMap<Integer, WaitLock> LockTable = new ConcurrentHashMap<>();
+    // ResultTable 保存了 taskId 对应的执行结果的信息,方便过程中进行查询
+    public static ConcurrentHashMap<Integer, ExecuteResult> ResultTable = new ConcurrentHashMap<>();
+}

+ 4 - 0
src/main/java/cn/seecoder/fdroidrepository/Pipeline/Config/DockerConfig.java

@@ -24,4 +24,8 @@ public class DockerConfig {
             .responseTimeout(Duration.ofSeconds(45))
             .build();
     public static DockerClient dockerClient = DockerClientImpl.getInstance(standardConfig, dockerHttpClient);
+
+    public static DockerClient getDockerClient() {
+        return dockerClient;
+    }
 }

+ 1 - 1
src/main/java/cn/seecoder/fdroidrepository/Pipeline/Context.java

@@ -14,7 +14,7 @@ import java.util.Map;
 @AllArgsConstructor
 @NoArgsConstructor
 public class Context {
-    private Integer pipelineId;
+    private Integer taskId;
     private String projectName;
     private ApplicationProperties applicationProperties;
 

+ 20 - 0
src/main/java/cn/seecoder/fdroidrepository/Pipeline/ExecuteResult.java

@@ -0,0 +1,20 @@
+package cn.seecoder.fdroidrepository.Pipeline;
+
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ExecuteResult {
+    private BuildTaskStatusEnum status;
+    // 保存执行结果
+    private String result;
+    // 保存错误信息
+    private String message;
+    private Date updateTime;
+}

+ 49 - 0
src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/BuildHandler.java

@@ -1,11 +1,60 @@
 package cn.seecoder.fdroidrepository.Pipeline.Handlers;
 
+import cn.seecoder.fdroidrepository.DataObject.ApplicationProperties;
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
+import cn.seecoder.fdroidrepository.Pipeline.Config.CommonValueTable;
+import cn.seecoder.fdroidrepository.Pipeline.Config.DockerConfig;
 import cn.seecoder.fdroidrepository.Pipeline.Context;
+import cn.seecoder.fdroidrepository.Pipeline.ExecuteResult;
 import cn.seecoder.fdroidrepository.Pipeline.PipelineException;
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.command.CreateContainerCmd;
+import com.github.dockerjava.api.command.CreateContainerResponse;
+import com.github.dockerjava.api.model.Bind;
+import org.apache.tomcat.jni.Proc;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Date;
 
 public class BuildHandler extends AbstractHandler{
+    private static final String ANDROID_PATH = "/root/bin:/opt/android-sdk";
+    private static final String DIR = "/fdroid/code/";
+    private static final String GRADLE = "gradle assemble";
+    private static final String MOVE = "cp *.apk /fdroid/repo";
     @Override
     public void process(Context context) throws PipelineException {
+        ApplicationProperties app = context.getApplicationProperties();
+        app.setStatusEnum(BuildTaskStatusEnum.BUILDING);
+        try {
+            ProcessBuilder processBuilder = new ProcessBuilder(GRADLE, MOVE);
+            processBuilder.directory(new File(DIR + app.getAppName()));
+            Process process = processBuilder.start();
+            // 获取输出结果
+            InputStream inputStream = process.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+            String line;
+            StringBuilder resultMsg = new StringBuilder();
+            while ((line = reader.readLine()) != null) {
+                resultMsg.append(line);
+            }
+            ExecuteResult result = CommonValueTable.ResultTable.get(context.getTaskId());
+            synchronized (result) {
+                result.setMessage(resultMsg.toString());
+                result.setStatus(BuildTaskStatusEnum.FINISHBUILDING);
+                result.setUpdateTime(new Date(System.currentTimeMillis()));
+            }
+        } catch (Exception exception) {
+            ExecuteResult result = CommonValueTable.ResultTable.get(context.getTaskId());
+            synchronized (result) {
+                result.setMessage("执行Gradle脚本出现问题" + exception.getMessage());
+                result.setStatus(BuildTaskStatusEnum.FAILED);
+                result.setUpdateTime(new Date(System.currentTimeMillis()));
+            }
+            throw new PipelineException(400, "执行Gradle脚本出现问题");
+        }
 
     }
 }

+ 29 - 1
src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/CleanHandler.java

@@ -1,11 +1,39 @@
 package cn.seecoder.fdroidrepository.Pipeline.Handlers;
 
+import cn.seecoder.fdroidrepository.DataObject.ApplicationProperties;
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
+import cn.seecoder.fdroidrepository.Pipeline.Config.CommonValueTable;
 import cn.seecoder.fdroidrepository.Pipeline.Context;
+import cn.seecoder.fdroidrepository.Pipeline.ExecuteResult;
 import cn.seecoder.fdroidrepository.Pipeline.PipelineException;
 
+import java.io.File;
+import java.util.Date;
+
 public class CleanHandler extends AbstractHandler{
+    private static final String filePath = "/fdroid/code/";
     @Override
     public void process(Context context) throws PipelineException {
-
+        // 进行文件夹删除,清理git拉取的代码文件以及构建过程中出现的文件
+        ApplicationProperties app = context.getApplicationProperties();
+        try {
+            File codeFile = new File(filePath + app.getAppName());
+            if (codeFile.exists()) {
+                codeFile.delete();
+            }
+            ExecuteResult result = CommonValueTable.ResultTable.get(context.getTaskId());
+            synchronized (result) {
+                result.setStatus(BuildTaskStatusEnum.SUCCESS);
+                result.setUpdateTime(new Date(System.currentTimeMillis()));
+            }
+        } catch (Exception exception) {
+            ExecuteResult result = CommonValueTable.ResultTable.get(context.getTaskId());
+            synchronized (result) {
+                result.setStatus(BuildTaskStatusEnum.FAILED);
+                app.setMessage("清理文件发生异常," + exception.getMessage());
+                result.setUpdateTime(new Date(System.currentTimeMillis()));
+            }
+            throw new PipelineException(400, "清理文件发生异常");
+        }
     }
 }

+ 38 - 1
src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/GitPullingHandler.java

@@ -1,11 +1,48 @@
 package cn.seecoder.fdroidrepository.Pipeline.Handlers;
 
+import cn.seecoder.fdroidrepository.DataObject.ApplicationProperties;
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
+import cn.seecoder.fdroidrepository.Pipeline.Config.CommonValueTable;
 import cn.seecoder.fdroidrepository.Pipeline.Context;
+import cn.seecoder.fdroidrepository.Pipeline.ExecuteResult;
 import cn.seecoder.fdroidrepository.Pipeline.PipelineException;
+import org.eclipse.jgit.api.CloneCommand;
+import org.eclipse.jgit.api.Git;
+
+import java.io.File;
+import java.util.Date;
 
 public class GitPullingHandler extends AbstractHandler{
+    private static final String localPath = "/fdroid/code/";
     @Override
     public void process(Context context) throws PipelineException {
-
+        // 拉取Git远程仓库
+        ApplicationProperties app = context.getApplicationProperties();
+        try {
+            ExecuteResult result = CommonValueTable.ResultTable.get(context.getTaskId());
+            synchronized (result) {
+                result.setStatus(BuildTaskStatusEnum.PULLING);
+                result.setUpdateTime(new Date(System.currentTimeMillis()));
+            }
+            app.setStatusEnum(BuildTaskStatusEnum.PULLING);
+            CloneCommand cloneCommand = Git.cloneRepository()
+                    .setURI(app.getRepoUrl())
+                    .setBranch(app.getBranch())
+                    .setDirectory(new File(localPath + app.getAppName()));
+            cloneCommand.call();
+            synchronized (result) {
+                result.setStatus(BuildTaskStatusEnum.FINISHPULLING);
+                result.setUpdateTime(new Date(System.currentTimeMillis()));
+            }
+        } catch (Exception exception) {
+            ExecuteResult result = CommonValueTable.ResultTable.get(context.getTaskId());
+            synchronized (result) {
+                result.setStatus(BuildTaskStatusEnum.FAILED);
+                result.setMessage("拉取Git远程仓库代码出现错误," + exception.getMessage());
+                result.setUpdateTime(new Date(System.currentTimeMillis()));
+            }
+            throw new PipelineException(500, "拉取Git远程仓库代码出现错误");
+        }
     }
+
 }

+ 42 - 0
src/main/java/cn/seecoder/fdroidrepository/Pipeline/Handlers/GradleHandler.java

@@ -1,11 +1,53 @@
 package cn.seecoder.fdroidrepository.Pipeline.Handlers;
 
+import cn.seecoder.fdroidrepository.DataObject.ApplicationProperties;
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
+import cn.seecoder.fdroidrepository.Pipeline.Config.CommonValueTable;
 import cn.seecoder.fdroidrepository.Pipeline.Context;
+import cn.seecoder.fdroidrepository.Pipeline.ExecuteResult;
 import cn.seecoder.fdroidrepository.Pipeline.PipelineException;
+import cn.seecoder.fdroidrepository.Pipeline.WaitLock;
+
+import java.util.Date;
 
 public class GradleHandler extends AbstractHandler{
     @Override
     public void process(Context context) throws PipelineException {
+        // 等待用户手动修改Gradle文件后才可以执行后续步骤
+        // 使用了wait-notify机制
+        ApplicationProperties app = context.getApplicationProperties();
+        Integer taskId = context.getTaskId();
+        ExecuteResult result = CommonValueTable.ResultTable.get(taskId);
+        synchronized (result) {
+            result.setStatus(BuildTaskStatusEnum.WAITING);
+            result.setUpdateTime(new Date(System.currentTimeMillis()));
+        }
+
+        // LockTable中有对应的流水线的锁对象,证明已经完成了gradle文件的修改
+        if (!CommonValueTable.LockTable.contains(taskId)) {
+            // 创建锁对象
+            // 调用wait方法,等待用户手动更改Gradle文件完成
+            WaitLock waitLock = new WaitLock();
+            CommonValueTable.LockTable.put(taskId, waitLock);
+            synchronized (waitLock) {
+                try {
+                    waitLock.wait();
+                } catch (InterruptedException exception) {
+                    synchronized (result) {
+                        result.setStatus(BuildTaskStatusEnum.FAILED);
+                        result.setMessage("Wait-notify机制出现问题," + exception.getMessage());
+                        result.setUpdateTime(new Date(System.currentTimeMillis()));
+                    }
+                    throw new PipelineException(400, "Wait-notify机制出现问题");
+                }
+            }
+        }
 
+        // 用户手动修改Gradle文件完成
+        synchronized (result) {
+            result.setStatus(BuildTaskStatusEnum.WRITTEN);
+            result.setUpdateTime(new Date(System.currentTimeMillis()));
+        }
+        CommonValueTable.LockTable.remove(taskId);
     }
 }

+ 10 - 1
src/main/java/cn/seecoder/fdroidrepository/Pipeline/PipelineFactory.java

@@ -1,7 +1,9 @@
 package cn.seecoder.fdroidrepository.Pipeline;
 
 import cn.seecoder.fdroidrepository.DataObject.ApplicationProperties;
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
 import cn.seecoder.fdroidrepository.DataObject.PO.BuildTaskPO;
+import cn.seecoder.fdroidrepository.Pipeline.Config.CommonValueTable;
 import cn.seecoder.fdroidrepository.Pipeline.Config.HandlerConfigTable;
 import cn.seecoder.fdroidrepository.Pipeline.Handlers.AbstractHandler;
 
@@ -22,7 +24,7 @@ public class PipelineFactory {
                                 BuildTaskPO buildTaskPO,
                                 ApplicationProperties applicationProperties) throws PipelineException {
         Context context = Context.builder()
-                .pipelineId(id)
+                .taskId(id)
                 .projectName(projectName)
                 .applicationProperties(applicationProperties)
                 .build();
@@ -40,6 +42,13 @@ public class PipelineFactory {
         Handler headHandler = new AbstractHandler() {
             @Override
             public void process(Context context) throws PipelineException {
+                // 防止后续处理出现问题,头节点需要检查是否存在对应的ExecuteResult
+                ExecuteResult result = CommonValueTable.ResultTable.get(context.getTaskId());
+                if (result == null) {
+                    result = new ExecuteResult();
+                    result.setStatus(BuildTaskStatusEnum.NEW);
+                    CommonValueTable.ResultTable.put(context.getTaskId(), result);
+                }
             }
         };
         Handler current = headHandler;

+ 4 - 0
src/main/java/cn/seecoder/fdroidrepository/Pipeline/WaitLock.java

@@ -0,0 +1,4 @@
+package cn.seecoder.fdroidrepository.Pipeline;
+
+public class WaitLock {
+}

+ 7 - 0
src/main/java/cn/seecoder/fdroidrepository/Service/AppService.java

@@ -1,5 +1,12 @@
 package cn.seecoder.fdroidrepository.Service;
 
 
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTypeEnum;
+import cn.seecoder.fdroidrepository.DataObject.PO.BuildTaskPO;
+
 public interface AppService {
+    Boolean rewriteGradleFile(Integer pipelineId, String fileName, String content);
+    BuildTaskPO checkStatus(Integer taskId);
+
+    Integer createBuildTask(Integer appId, Integer userId, String branch, BuildTypeEnum typeEnum);
 }

+ 119 - 0
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/AppServiceImpl.java

@@ -0,0 +1,119 @@
+package cn.seecoder.fdroidrepository.Service.ServiceImpl;
+
+import cn.seecoder.fdroidrepository.DataObject.ApplicationProperties;
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTaskStatusEnum;
+import cn.seecoder.fdroidrepository.DataObject.Enum.BuildTypeEnum;
+import cn.seecoder.fdroidrepository.DataObject.PO.AppInfoPO;
+import cn.seecoder.fdroidrepository.DataObject.PO.BuildTaskPO;
+import cn.seecoder.fdroidrepository.Mapper.AppInfoMapper;
+import cn.seecoder.fdroidrepository.Mapper.BuildTaskMapper;
+import cn.seecoder.fdroidrepository.Pipeline.*;
+import cn.seecoder.fdroidrepository.Pipeline.Config.CommonValueTable;
+import cn.seecoder.fdroidrepository.Service.AppService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.util.Date;
+import java.util.concurrent.*;
+
+@Service
+public class AppServiceImpl implements AppService {
+    private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(4, 8, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(512), new ThreadPoolExecutor.DiscardPolicy());
+    private final BuildTaskMapper buildTaskMapper;
+    private final AppInfoMapper appInfoMapper;
+
+    @Autowired
+    public AppServiceImpl(BuildTaskMapper buildTaskMapper, AppInfoMapper appInfoMapper) {
+        this.buildTaskMapper = buildTaskMapper;
+        this.appInfoMapper = appInfoMapper;
+    }
+
+    @Override
+    public Boolean rewriteGradleFile(Integer taskId, String fileName, String content) {
+        try {
+            File file = new File(fileName);
+            if (!file.exists()) {
+                file.createNewFile();
+            }
+            FileWriter fileWriter = new FileWriter(file.getName(), false);
+            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
+            bufferedWriter.write(content);
+            bufferedWriter.close();
+
+            // 修改完成Gradle文件,通知后续流程继续
+            if (CommonValueTable.LockTable.containsValue(taskId)) {
+                // 对应的LockTable已经创建
+                // 调用notify方法继续流水线执行
+                WaitLock waitLock = CommonValueTable.LockTable.get(taskId);
+                synchronized (waitLock) {
+                    waitLock.notify();
+                }
+            } else {
+                WaitLock waitLock = new WaitLock();
+                CommonValueTable.LockTable.put(taskId, waitLock);
+            }
+            return true;
+        } catch (Exception exception) {
+            return false;
+        }
+    }
+
+    @Override
+    public BuildTaskPO checkStatus(Integer taskId) {
+        ExecuteResult result = CommonValueTable.ResultTable.get(taskId);
+        BuildTaskPO buildTaskPO = this.buildTaskMapper.selectById(taskId);
+        if (result != null) {
+            // 根据对应的执行结果内容进行更新
+            buildTaskPO.setStatus(result.getStatus());
+            buildTaskPO.setUpdateTime(result.getUpdateTime());
+            buildTaskPO.setMessage(result.getMessage());
+            buildTaskPO.setResult(result.getResult());
+            buildTaskMapper.updateById(buildTaskPO);
+
+            // 流水线已经执行完成,删除其在Map中的内容,防止空间堆积
+            if(BuildTaskStatusEnum.SUCCESS.equals(result.getStatus()) || BuildTaskStatusEnum.FAILED.equals(result.getStatus())) {
+                CommonValueTable.ResultTable.remove(taskId);
+            }
+        }
+        return buildTaskPO;
+    }
+
+    @Override
+    public Integer createBuildTask(Integer appId, Integer userId, String branch, BuildTypeEnum typeEnum) {
+        AppInfoPO appInfoPO = appInfoMapper.selectById(appId);
+        if (appInfoPO == null) {
+            // AppInfo不存在
+        }
+        BuildTaskPO buildTaskPO = BuildTaskPO.builder()
+                .appId(appId)
+                .createUserId(userId)
+                .createTime(new Date(System.currentTimeMillis()))
+                .buildType(typeEnum)
+                .status(BuildTaskStatusEnum.NEW)
+                .build();
+        Integer taskId = buildTaskMapper.insert(buildTaskPO);
+        buildTaskPO.setId(taskId);
+        ApplicationProperties app = ApplicationProperties.builder()
+                .appId(appId)
+                .appName(appInfoPO.getAppName())
+                .repoUrl(appInfoPO.getRepoUrl())
+                .branch(branch)
+                .build();
+        THREAD_POOL_EXECUTOR.execute(new Runnable() {
+            @Override
+            public void run() {
+                Pipeline pipeline = null;
+                try {
+                    pipeline = PipelineFactory.init(taskId, appInfoPO.getAppName(), buildTaskPO, app);
+                    pipeline.start();
+                } catch (PipelineException e) {
+
+                }
+            }
+        });
+        return taskId;
+    }
+}