Bladeren bron

Merge branch 'feature-web-app-inspect-api' into 'Develop'

Feature web app inspect api



See merge request !1203

menduo 5 jaren geleden
bovenliggende
commit
13d4f61481
49 gewijzigde bestanden met toevoegingen van 4066 en 0 verwijderingen
  1. 48 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/ThirdPartToolJobStatusConstants.java
  2. 10 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/ThirdPartToolJobTypeConstants.java
  3. 28 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThirdPartToolJobDao.java
  4. 10 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/InspectTestType.java
  5. 18 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/ThirdPartToolJobDTO.java
  6. 30 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/ThirdPartToolJobVO.java
  7. 16 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/ThirdPartToolStartDTO.java
  8. 28 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startCommand/ThirdPartToolAppStartCommand.java
  9. 16 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startCommand/ThirdPartToolAppStartConfig.java
  10. 16 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startResponse/ThirdPartToolAppStartResponse.java
  11. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startResponse/ThirdPartToolAppStartResponseCode.java
  12. 16 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/statusResponse/ThirdPartToolAppStatusResponse.java
  13. 15 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/statusResponse/ThirdPartToolAppStatusResponseAppScore.java
  14. 15 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/statusResponse/ThirdPartToolAppStatusResponseCode.java
  15. 11 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/report/EnterpriseReportType.java
  16. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/report/ReportStartResponseStatusConstants.java
  17. 15 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/AuthConfigModel.java
  18. 18 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/AuthorizingAction.java
  19. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/RelatedFormInput.java
  20. 23 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartCommand.java
  21. 19 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfig.java
  22. 15 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigBrowser.java
  23. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModel.java
  24. 16 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModelFormInput.java
  25. 13 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModelFormInputIdentification.java
  26. 13 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModelFormInputInputValue.java
  27. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponse.java
  28. 9 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponseCode.java
  29. 15 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponseData.java
  30. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponseDataJob.java
  31. 14 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponse.java
  32. 11 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseCode.java
  33. 13 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseData.java
  34. 35 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseDataScoreModel.java
  35. 11 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseDataWebReport.java
  36. 77 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThirdPartToolJob.java
  37. 95 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThirdPartToolAppService.java
  38. 186 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThirdPartToolService.java
  39. 84 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThirdPartToolWebService.java
  40. 353 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolAppServiceImpl.java
  41. 445 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolServiceImpl.java
  42. 336 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolWebServiceImpl.java
  43. 246 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ThirdPartToolController.java
  44. 151 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ThirdPartToolLogic.java
  45. 542 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ThirdPartToolLogicImpl.java
  46. 146 0
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolAppServiceImplTest.java
  47. 239 0
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolServiceImplTest.java
  48. 137 0
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolWebServiceImplTest.java
  49. 428 0
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/logic/impl/ThirdPartToolLogicImplTest.java

+ 48 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/ThirdPartToolJobStatusConstants.java

@@ -0,0 +1,48 @@
+package cn.iselab.mooctest.site.common.constant;
+
+/**
+ * @author sunjh
+ * @date 2020/2/10 13:38
+ */
+public class ThirdPartToolJobStatusConstants {
+    /**
+     * 任务初始状态
+     */
+    public static final Integer CREATE = 0;
+    /**
+     * 任务开始后先进入排队状态
+     */
+    public static final Integer PENDING = 1;
+    /**
+     * 当服务器有空闲时,排队中的任务进入执行状态
+     */
+    public static final Integer RUNNING = 2;
+    /**
+     * 任务执行完成后进入运行完成状态
+     */
+    public static final Integer RUN_FINISH = 3;
+    /**
+     * 运行完成状态进入回写成绩状态
+     */
+    public static final Integer WRITE_SCORE = 4;
+    /**
+     * 回写玩成绩后进入写完成状态
+     */
+    public static final Integer WRITE_FINISH = 5;
+    /**
+     * 生成报告时先进入生成报告排队状态
+     */
+    public static final Integer GENERATE_REPORT_PENDING = 6;
+    /**
+     * 报告服务空闲时排队状态的任务进入生成报告运行状态
+     */
+    public static final Integer GENERATE_REPORTING = 7;
+    /**
+     * 报告生成成功状态
+     */
+    public static final Integer GENERATE_REPORT_FINISH = 8;
+    /**
+     * 失败状态
+     */
+    public static final Integer RUN_FAIL = 9;
+}

+ 10 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/ThirdPartToolJobTypeConstants.java

@@ -0,0 +1,10 @@
+package cn.iselab.mooctest.site.common.constant;
+
+/**
+ * @author sunjh
+ * @date 2020/2/10 16:27
+ */
+public class ThirdPartToolJobTypeConstants {
+    public final static String APP = "app";
+    public final static String WEB = "web";
+}

+ 28 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/ThirdPartToolJobDao.java

@@ -0,0 +1,28 @@
+package cn.iselab.mooctest.site.dao;
+
+import cn.iselab.mooctest.site.dao.adapter.UpdateAdapter;
+import cn.iselab.mooctest.site.models.ThirdPartToolJob;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.repository.query.Param;
+
+import javax.transaction.Transactional;
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/10 9:47
+ */
+@Transactional
+public interface ThirdPartToolJobDao extends UpdateAdapter<ThirdPartToolJob, Long>, PagingAndSortingRepository<ThirdPartToolJob, Long> {
+    List<ThirdPartToolJob> findByGroupId(Long groupId);
+
+    List<ThirdPartToolJob> findByStatusAndTypeOrderByStartTime(Integer status, String type);
+
+    ThirdPartToolJob findThirdPartToolJobByOwnerIdAndGroupId(Long userId, Long GroupId);
+
+    @Query("SELECT a FROM ThirdPartToolJob a, User u WHERE a.groupId = :examId AND a.ownerId = u.id and u.name like concat('%',:keyword,'%') ")
+    Page<ThirdPartToolJob> findByExamId(@Param("examId") Long examId, @Param("keyword") String keyword, Pageable pageable);
+}

+ 10 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/InspectTestType.java

@@ -0,0 +1,10 @@
+package cn.iselab.mooctest.site.data.inspect;
+
+/**
+ * @author sunjh
+ * @date 2020/2/16 14:47
+ */
+public class InspectTestType {
+    public static final Long APP_INSPECT = 18L;
+    public static final Long WEB_INSPECT = 19L;
+}

+ 18 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/ThirdPartToolJobDTO.java

@@ -0,0 +1,18 @@
+package cn.iselab.mooctest.site.data.inspect;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/10 20:17
+ */
+@Data
+public class ThirdPartToolJobDTO {
+    private String config;
+    private String url;
+    private String type;
+    private String name;
+    private String file;
+    private String information;
+    private Long examId;
+}

+ 30 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/ThirdPartToolJobVO.java

@@ -0,0 +1,30 @@
+package cn.iselab.mooctest.site.data.inspect;
+
+import lombok.Data;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/11 10:06
+ */
+@Data
+public class ThirdPartToolJobVO {
+    private Long userId;
+    private Long examId;
+    private String ownerName;
+    private Integer status;
+    private Object result;
+    private String score;
+    private Timestamp createTime;
+    private Timestamp startTime;
+    private String name;
+    private String url;
+    private String file;
+    private String school;
+    private String information;
+    private String type;
+    private Object config;
+    private String reportUrl;
+}

+ 16 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/ThirdPartToolStartDTO.java

@@ -0,0 +1,16 @@
+package cn.iselab.mooctest.site.data.inspect;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 15:09
+ */
+@Data
+public class ThirdPartToolStartDTO {
+    Long taskId;
+    String appPath;
+    Map<String, Object> config;
+}

+ 28 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startCommand/ThirdPartToolAppStartCommand.java

@@ -0,0 +1,28 @@
+package cn.iselab.mooctest.site.data.inspect.app.startCommand;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 19:30
+ */
+
+@Data
+public class ThirdPartToolAppStartCommand {
+    /**
+     * 即taskId
+     */
+    private String traceId;
+    /**
+     * 任务APK的下载链接
+     */
+    private String downloadUrl;
+    /**
+     * 任务的执行时间,单位:分钟;不必须,不传默认20分钟
+     */
+    private String limitTime;
+    /**
+     * 目前不需要,为以后根据业务需求进行扩展的保留字段
+     */
+    //private Object testRequirement;
+}

+ 16 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startCommand/ThirdPartToolAppStartConfig.java

@@ -0,0 +1,16 @@
+package cn.iselab.mooctest.site.data.inspect.app.startCommand;
+
+import cn.iselab.mooctest.site.data.inspect.web.startCommand.ThirdPartToolWebStartConfigBrowser;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 21:41
+ */
+@Data
+public class ThirdPartToolAppStartConfig {
+    private String limitTime;
+    //private ThirdPartToolWebStartConfigCrawlRulesModel crawlRulesModel;
+}

+ 16 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startResponse/ThirdPartToolAppStartResponse.java

@@ -0,0 +1,16 @@
+package cn.iselab.mooctest.site.data.inspect.app.startResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 19:34
+ */
+@Data
+public class ThirdPartToolAppStartResponse {
+    private String traceId;
+    private Long statusCode;
+    private String description;
+    private String downloadUrl;
+    private Object extraInfo;
+}

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/startResponse/ThirdPartToolAppStartResponseCode.java

@@ -0,0 +1,14 @@
+package cn.iselab.mooctest.site.data.inspect.app.startResponse;
+
+/**
+ * @author sunjh
+ * @date 2020/2/15 13:54
+ */
+public class ThirdPartToolAppStartResponseCode {
+    public final static Long CREATE = 0L;
+    public final static Long RUNNING = 1L;
+    public final static Long GENERATE_REPORT = 2L;
+    public final static Long FINISH = 3L;
+    public final static Long DOWNLOAD_FAIL = 4L;
+    public final static Long APK_UNZIP_FAIL = 5L;
+}

+ 16 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/statusResponse/ThirdPartToolAppStatusResponse.java

@@ -0,0 +1,16 @@
+package cn.iselab.mooctest.site.data.inspect.app.statusResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 19:38
+ */
+@Data
+public class ThirdPartToolAppStatusResponse {
+    private String traceId;
+    private Long statusCode;
+    private String description;
+    private String downloadUrl;
+    private Object extraInfo;
+}

+ 15 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/statusResponse/ThirdPartToolAppStatusResponseAppScore.java

@@ -0,0 +1,15 @@
+package cn.iselab.mooctest.site.data.inspect.app.statusResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/17 11:44
+ */
+@Data
+public class ThirdPartToolAppStatusResponseAppScore {
+    private String performance;
+    private String compatibility;
+    private String stability;
+    private String robustness;
+}

+ 15 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/app/statusResponse/ThirdPartToolAppStatusResponseCode.java

@@ -0,0 +1,15 @@
+package cn.iselab.mooctest.site.data.inspect.app.statusResponse;
+
+/**
+ * @author sunjh
+ * @date 2020/2/15 13:54
+ */
+public class ThirdPartToolAppStatusResponseCode {
+    public final static Long CREATE = 0L;
+    public final static Long RUNNING = 1L;
+    public final static Long GENERATE_REPORT = 2L;
+    public final static Long FINISH = 3L;
+    public final static Long DOWNLOAD_FAIL = 4L;
+    public final static Long APK_UNZIP_FAIL = 5L;
+    public final static Long RUN_FAIL = 6L;
+}

+ 11 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/report/EnterpriseReportType.java

@@ -0,0 +1,11 @@
+package cn.iselab.mooctest.site.data.inspect.report;
+
+/**
+ * @author sunjh
+ * @date 2020/2/27 17:33
+ */
+public class EnterpriseReportType {
+    public static final String WEB = "web-auto-test-report";
+    public static final String APP = "app-auto-test-report";
+    public static final String STATIC_SCAN = "static-scan-report";
+}

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/report/ReportStartResponseStatusConstants.java

@@ -0,0 +1,14 @@
+package cn.iselab.mooctest.site.data.inspect.report;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/27 17:03
+ */
+@Data
+public class ReportStartResponseStatusConstants {
+    public static final Long SUCCESS = 0L;
+    public static final Long FAIL = -1L;
+    public static final Long RUNNING = 1L;
+}

+ 15 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/AuthConfigModel.java

@@ -0,0 +1,15 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/29 23:46
+ */
+@Data
+public class AuthConfigModel {
+    private String authUrl;
+    private List<AuthorizingAction> authorizingActions;
+}

+ 18 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/AuthorizingAction.java

@@ -0,0 +1,18 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/29 23:46
+ */
+@Data
+public class AuthorizingAction {
+    private String eventType;
+    private String how;
+    private String target;
+    private List<RelatedFormInput> relatedFormInputs;
+    private String relatedFrame;
+}

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/RelatedFormInput.java

@@ -0,0 +1,14 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/29 23:46
+ */
+@Data
+public class RelatedFormInput {
+    private String how;
+    private String target;
+    private String type;
+    private String value;
+}

+ 23 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartCommand.java

@@ -0,0 +1,23 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import cn.iselab.mooctest.site.data.inspect.web.startCommand.ThirdPartToolWebStartConfigBrowser;
+import cn.iselab.mooctest.site.data.inspect.web.startCommand.ThirdPartToolWebStartConfigCrawlRulesModel;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 19:40
+ */
+@Data
+public class ThirdPartToolWebStartCommand {
+    private String url;
+    private Long maximumStates;
+    private Long maximumRuntime;
+    private Long maximumDepth;
+    private List<ThirdPartToolWebStartConfigBrowser> browsers;
+    private ThirdPartToolWebStartConfigCrawlRulesModel webAutoTestCrawlRulesModel;
+    private Object authConfigModel;
+    private String user;
+}

+ 19 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfig.java

@@ -0,0 +1,19 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 21:41
+ */
+@Data
+public class ThirdPartToolWebStartConfig {
+    private Long maximumStates;
+    private Long maximumRuntime;
+    private Long maximumDepth;
+    private List<ThirdPartToolWebStartConfigBrowser> browsers;
+    private AuthConfigModel authConfigModel;
+    //private ThirdPartToolWebStartConfigCrawlRulesModel crawlRulesModel;
+}

+ 15 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigBrowser.java

@@ -0,0 +1,15 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 21:43
+ */
+@Data
+public class ThirdPartToolWebStartConfigBrowser {
+    private String platform;
+    private String browserType;
+    private String version;
+    private Long numberOfBrowsers;
+}

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModel.java

@@ -0,0 +1,14 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 21:46
+ */
+@Data
+public class ThirdPartToolWebStartConfigCrawlRulesModel {
+    List<ThirdPartToolWebStartConfigCrawlRulesModelFormInput> formInputs;
+}

+ 16 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModelFormInput.java

@@ -0,0 +1,16 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 21:46
+ */
+@Data
+public class ThirdPartToolWebStartConfigCrawlRulesModelFormInput {
+    private ThirdPartToolWebStartConfigCrawlRulesModelFormInputIdentification identification;
+    private String inputType;
+    private List<ThirdPartToolWebStartConfigCrawlRulesModelFormInputInputValue> inputValues;
+}

+ 13 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModelFormInputIdentification.java

@@ -0,0 +1,13 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 21:47
+ */
+@Data
+public class ThirdPartToolWebStartConfigCrawlRulesModelFormInputIdentification {
+    private String how;
+    private String value;
+}

+ 13 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startCommand/ThirdPartToolWebStartConfigCrawlRulesModelFormInputInputValue.java

@@ -0,0 +1,13 @@
+package cn.iselab.mooctest.site.data.inspect.web.startCommand;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 21:47
+ */
+@Data
+public class ThirdPartToolWebStartConfigCrawlRulesModelFormInputInputValue {
+    private String value;
+    private Boolean checked;
+}

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponse.java

@@ -0,0 +1,14 @@
+package cn.iselab.mooctest.site.data.inspect.web.startResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 19:41
+ */
+@Data
+public class ThirdPartToolWebStartResponse {
+    private String message;
+    private Long code;
+    private ThirdPartToolWebStartResponseData data;
+}

+ 9 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponseCode.java

@@ -0,0 +1,9 @@
+package cn.iselab.mooctest.site.data.inspect.web.startResponse;
+
+/**
+ * @author sunjh
+ * @date 2020/2/15 0:00
+ */
+public class ThirdPartToolWebStartResponseCode {
+    public final static Long SUCCESS = 2000L;
+}

+ 15 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponseData.java

@@ -0,0 +1,15 @@
+package cn.iselab.mooctest.site.data.inspect.web.startResponse;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 23:29
+ */
+@Data
+public class ThirdPartToolWebStartResponseData {
+    private List<ThirdPartToolWebStartResponseDataJob> jobs;
+    private String sessionId;
+}

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/startResponse/ThirdPartToolWebStartResponseDataJob.java

@@ -0,0 +1,14 @@
+package cn.iselab.mooctest.site.data.inspect.web.startResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 23:28
+ */
+@Data
+public class ThirdPartToolWebStartResponseDataJob {
+    private String jobId;
+    private String reason;
+    private String status;
+}

+ 14 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponse.java

@@ -0,0 +1,14 @@
+package cn.iselab.mooctest.site.data.inspect.web.statusResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 19:41
+ */
+@Data
+public class ThirdPartToolWebStatusResponse {
+    private String message;
+    private Long code;
+    private ThirdPartToolWebStatusResponseData data;
+}

+ 11 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseCode.java

@@ -0,0 +1,11 @@
+package cn.iselab.mooctest.site.data.inspect.web.statusResponse;
+
+/**
+ * @author sunjh
+ * @date 2020/2/15 0:01
+ */
+public class ThirdPartToolWebStatusResponseCode {
+    public static final Long SUCCESS = 2003L;
+    public static final Long RUNNING = 2002L;
+    public static final Long FAIL = 5001L;
+}

+ 13 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseData.java

@@ -0,0 +1,13 @@
+package cn.iselab.mooctest.site.data.inspect.web.statusResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 23:49
+ */
+@Data
+public class ThirdPartToolWebStatusResponseData {
+    private Object webReport;
+    private ThirdPartToolWebStatusResponseDataScoreModel scoreModel;
+}

+ 35 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseDataScoreModel.java

@@ -0,0 +1,35 @@
+package cn.iselab.mooctest.site.data.inspect.web.statusResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 23:50
+ */
+@Data
+public class ThirdPartToolWebStatusResponseDataScoreModel {
+    /**
+     * 总分
+     */
+    private String totalScore;
+    /**
+     * 复杂度评分
+     */
+    private String complexity;
+    /**
+     * 鲁棒性评分
+     */
+    private String robustness;
+    /**
+     * 性能评分
+     */
+    private String performance;
+    /**
+     * 兼容性评分
+     */
+    private String compatibility;
+    /**
+     * 安全性评分
+     */
+    private String security;
+}

+ 11 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/inspect/web/statusResponse/ThirdPartToolWebStatusResponseDataWebReport.java

@@ -0,0 +1,11 @@
+package cn.iselab.mooctest.site.data.inspect.web.statusResponse;
+
+import lombok.Data;
+
+/**
+ * @author sunjh
+ * @date 2020/2/14 23:49
+ */
+@Data
+public class ThirdPartToolWebStatusResponseDataWebReport {
+}

+ 77 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/models/ThirdPartToolJob.java

@@ -0,0 +1,77 @@
+package cn.iselab.mooctest.site.models;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 16:00
+ */
+@Data
+@Entity
+@Table(name = "third_part_tool_job")
+public class ThirdPartToolJob {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(name = "name")
+    private String name;
+
+    @Column(name = "create_time")
+    private Timestamp createTime;
+
+    @Column(name = "start_time")
+    private Timestamp startTime;
+
+    @Column(name = "end_time")
+    private Timestamp endTime;
+
+    @Column(name = "information")
+    private String information;
+
+    @Column(name = "type")
+    private String type;
+
+    @Column(name = "owner_id")
+    private Long ownerId;
+
+    @Column(name = "status")
+    private Integer status;
+
+    @Column(name = "exam_id")
+    private Long groupId;
+
+    @Column(name = "config")
+    private String config;
+
+    @Column(name = "url")
+    private String url;
+
+    @Column(name = "file")
+    private String file;
+
+    @Column(name = "result")
+    private String result;
+
+    @Column(name = "trace_id")
+    private String traceId;
+
+    @Column(name = "extra")
+    private String extra;
+
+    @Column(name = "score")
+    private String score;
+
+    @Column(name = "report_url")
+    private String reportUrl;
+
+    @Column(name = "report_download_url")
+    private String reportDownloadUrl;
+
+    @Column(name = "report_trace_id")
+    private String reportTraceId;
+}

+ 95 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThirdPartToolAppService.java

@@ -0,0 +1,95 @@
+package cn.iselab.mooctest.site.service;
+
+import net.sf.json.JSONObject;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 15:19
+ */
+public interface ThirdPartToolAppService {
+    /**
+     * 获取正在运行中的app任务队列
+     *
+     * @return 正在运行中的app任务队列
+     * @throws Exception 异常
+     */
+    List<Long> getRunningThirdPartToolJobIdList() throws Exception;
+
+    /**
+     * 获取正在排队中的app任务队列
+     *
+     * @return 正在排队中的app任务队列
+     * @throws Exception 异常
+     */
+    List<Long> getPendingThirdPartToolJobIdList() throws Exception;
+
+    /**
+     * 获取运行结束的app任务队列
+     *
+     * @return 运行结束的app任务队列
+     * @throws Exception 异常
+     */
+    List<Long> getRunFinishThirdPartToolJobIdList() throws Exception;
+
+    List<Long> getWriteFinishThirdPartToolJobIdList() throws Exception;
+
+    List<Long> getGenReportPendingThirdPartToolJobIdList() throws Exception;
+
+    List<Long> getGenReportRunningThirdPartToolJobIdList() throws Exception;
+
+    Long runGenerateReport(Long jobId) throws Exception;
+
+    /**
+     * 判断当前运行中app队列是否已满
+     *
+     * @param appRunningJobIdList 当前运行中app队列
+     * @return false:未满;true:已满
+     * @throws Exception 异常
+     */
+    boolean isRunningQueueFull(List<Long> appRunningJobIdList) throws Exception;
+
+    /**
+     * 获取app任务的配置信息
+     *
+     * @param jobId 任务id
+     * @return app任务的配置信息
+     * @throws Exception 异常
+     */
+    Object getAppJobConfig(Long jobId) throws Exception;
+
+    /**
+     * 获取当前执行任务的运行信息
+     *
+     * @param jobId 任务id
+     * @return 当前执行任务的运行信息
+     * @throws Exception 异常
+     */
+    Long getJobStatus(Long jobId) throws Exception;
+
+    /**
+     * 启动任务
+     *
+     * @param jobId 任务id
+     * @throws Exception 异常
+     */
+    void runJob(Long jobId) throws Exception;
+
+    /**
+     * 回写成绩
+     *
+     * @param jobId 任务id
+     * @throws Exception 异常
+     */
+    void recordScore(Long jobId) throws Exception;
+
+    /**
+     * 根据app任务返回的json文件url获取信息
+     *
+     * @param url app任务返回的json文件url
+     * @return 成绩信息
+     * @throws Exception 异常
+     */
+    JSONObject readObjectByUrl(String url) throws Exception;
+}

+ 186 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThirdPartToolService.java

@@ -0,0 +1,186 @@
+package cn.iselab.mooctest.site.service;
+
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobDTO;
+import cn.iselab.mooctest.site.models.*;
+import cn.iselab.mooctest.site.web.data.ExamVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/10 21:26
+ */
+public interface ThirdPartToolService {
+    /**
+     * 创建一条任务
+     *
+     * @param thirdPartToolJobDTO 任务信息
+     * @param examId              作业id
+     * @param userId              用户id
+     * @return 创建的任务信息
+     * @throws Exception 异常
+     */
+    ThirdPartToolJob create(ThirdPartToolJobDTO thirdPartToolJobDTO, Long examId, Long userId) throws Exception;
+
+    /**
+     * 启动一个任务
+     *
+     * @param userId 用户id
+     * @param examId 作业id
+     * @throws Exception 异常
+     */
+    void startThirdPartToolJob(Long userId, Long examId) throws Exception;
+
+    /**
+     * 设置一条任务的状态为执行失败
+     *
+     * @param jobId 作业id
+     * @throws Exception 异常
+     */
+    void changeJobStatusToFail(Long jobId) throws Exception;
+
+    void changeJobStatusToFail(Long jobId,String message) throws Exception;
+
+    void changeJobStatusToRunning(Long jobId) throws Exception;
+
+    void changeJobStatusToWriteResult(Long jobId) throws Exception;
+
+    void changeJobStatusToWriteFinish(Long jobId) throws Exception;
+
+    void changeJobStatusToGenReportPending(Long jobId) throws Exception;
+
+    void changeJobStatusToGenReportRunning(Long jobId) throws Exception;
+
+    void changeJobStatusToGenReportFinish(Long jobId) throws Exception;
+
+    Long getGenerateStatus(Long taskId) throws Exception;
+
+    /**
+     * 设置一条任务的状态为执行成功
+     *
+     * @param jobId 作业id
+     * @throws Exception 异常
+     */
+    void changeJobStatusToRunFinish(Long jobId) throws Exception;
+
+    /**
+     * 批量启动一批任务
+     *
+     * @param examId 作业id
+     * @throws Exception 异常
+     */
+    void batchStartThirdPartToolJob(Long examId) throws Exception;
+
+    /**
+     * 停止一条任务
+     *
+     * @param userId 用户id
+     * @param examId 作业id
+     * @throws Exception 异常
+     */
+    void stopThirdPartToolJob(Long userId, Long examId) throws Exception;
+
+    /**
+     * 获取一个作业的所有提交信息
+     *
+     * @param examId 作业id
+     * @return 一个作业的所有提交信息
+     * @throws Exception 异常
+     */
+    List<ThirdPartToolJob> getAllThirdPartToolJobsByExamId(Long examId) throws Exception;
+
+    /**
+     * 获取一个作业的一个用户的提交信息
+     *
+     * @param examId 作业id
+     * @param userId 用户id
+     * @return 一个作业的一个用户的提交信息
+     * @throws Exception 异常
+     */
+    ThirdPartToolJob getThirdPartToolJobsByExamIdAndUserId(Long examId, Long userId) throws Exception;
+
+    /**
+     * 一键创建作业
+     *
+     * @param examVO   作业信息
+     * @param testType 作业testType
+     * @param userId   用户id
+     * @return 保存的作业信息
+     * @throws Exception 异常
+     */
+    ExamVO saveOrUpdateInspectExam(ExamVO examVO, Long testType, Long userId) throws Exception;
+
+    /**
+     * 弃用,设置成绩
+     *
+     * @param userId 用户id
+     * @param examId 作业id
+     * @throws Exception 异常
+     */
+    void submit(Long userId, Long examId) throws Exception;
+
+    /**
+     * 创建默认target
+     *
+     * @param userId 用户id
+     * @return 创建好的target
+     * @throws Exception 异常
+     */
+    Target createDefaultInspectTarget(Long userId) throws Exception;
+
+    /**
+     * 创建默认case
+     *
+     * @param userId   用户id
+     * @param targetId targetid
+     * @param caseType caseType
+     * @return 创建好的case
+     * @throws Exception 异常
+     */
+    CaseExtends createDefaultInspectCase(Long userId, Long targetId, Long caseType) throws Exception;
+
+    /**
+     * 创建默认paper
+     *
+     * @param userId 用户id
+     * @param caseId caseId
+     * @return 创建好的paper
+     * @throws Exception 异常
+     */
+    Paper createDefaultInspectPaper(Long userId, Long caseId) throws Exception;
+
+    /**
+     * 创建exam2Case
+     *
+     * @param examId  作业id
+     * @param paperId 试卷id
+     * @param caseId  caseId
+     * @return 创建好的exam2case
+     * @throws Exception 异常
+     */
+    Exam2Case createExam2Case(Long examId, Long paperId, Long caseId) throws Exception;
+
+    /**
+     * 创建exam2paper
+     *
+     * @param examId  作业id
+     * @param paperId 试卷id
+     * @return 创建完成的exam2paper
+     * @throws Exception 异常
+     */
+    Exam2Paper createExam2Paper(Long examId, Long paperId) throws Exception;
+
+    /**
+     * 创建paper2case
+     *
+     * @param paperId 试卷id
+     * @param caseId  caseId
+     * @return 创建完成的paper2case
+     * @throws Exception 异常
+     */
+    Paper2Case createPaper2Case(Long paperId, Long caseId) throws Exception;
+
+    Page<ThirdPartToolJob> getJobByExamIdPage(Long examId, String keyword, Pageable pageable);
+}

+ 84 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/ThirdPartToolWebService.java

@@ -0,0 +1,84 @@
+package cn.iselab.mooctest.site.service;
+
+import java.util.List;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 14:57
+ */
+public interface ThirdPartToolWebService {
+    /**
+     * 获取正在运行的web任务id的list
+     *
+     * @return 正在运行的web任务id的队列
+     * @throws Exception 异常
+     */
+    List<Long> getRunningThirdPartToolJobIdList() throws Exception;
+
+    /**
+     * 获取排队中的web任务id的list
+     *
+     * @return 排队中的web任务id的队列
+     * @throws Exception 异常
+     */
+    List<Long> getPendingThirdPartToolJobIdList() throws Exception;
+
+    /**
+     * 获取运行结束的web任务id的队列
+     *
+     * @return 运行结束的web任务id的队列
+     * @throws Exception 异常
+     */
+    List<Long> getRunFinishThirdPartToolJobList() throws Exception;
+
+    List<Long> getWriteFinishThirdPartToolJobIdList() throws Exception;
+
+    List<Long> getGenReportPendingThirdPartToolJobIdList() throws Exception;
+
+    List<Long> getGenReportRunningThirdPartToolJobIdList() throws Exception;
+
+    Long runGenerateReport(Long taskId) throws Exception;
+
+    /**
+     * 判断当前web任务运行队列是否已满
+     *
+     * @param webRunningJobIdList 当前运行中web任务队列
+     * @return true:队列已满;false:队列未满
+     * @throws Exception 异常
+     */
+    boolean isRunningQueueFull(List<Long> webRunningJobIdList) throws Exception;
+
+    /**
+     * 获取 web 任务配置信息
+     *
+     * @param jobId 任务id
+     * @return web任务配置信息
+     * @throws Exception 异常
+     */
+    Object getWebJobConfig(Long jobId) throws Exception;
+
+    /**
+     * 获取当前执行的任务的执行信息
+     *
+     * @param jobId 任务id
+     * @return 状态 ThirdPartToolWebStatusResponseCode
+     * @throws Exception 异常
+     */
+    Long getJobStatus(Long jobId) throws Exception;
+
+    /**
+     * 执行任务
+     *
+     * @param jobId 任务id
+     * @throws Exception 异常
+     */
+    void runJob(Long jobId) throws Exception;
+
+    /**
+     * 回写成绩
+     *
+     * @param jobId 任务id
+     * @throws Exception 异常
+     */
+    void recordScore(Long jobId) throws Exception;
+}

+ 353 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolAppServiceImpl.java

@@ -0,0 +1,353 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobStatusConstants;
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobTypeConstants;
+import cn.iselab.mooctest.site.dao.Exam2CaseDao;
+import cn.iselab.mooctest.site.dao.ExamDao;
+import cn.iselab.mooctest.site.dao.ThirdPartToolJobDao;
+import cn.iselab.mooctest.site.data.inspect.app.startCommand.ThirdPartToolAppStartCommand;
+import cn.iselab.mooctest.site.data.inspect.app.startCommand.ThirdPartToolAppStartConfig;
+import cn.iselab.mooctest.site.data.inspect.app.startResponse.ThirdPartToolAppStartResponse;
+import cn.iselab.mooctest.site.data.inspect.app.startResponse.ThirdPartToolAppStartResponseCode;
+import cn.iselab.mooctest.site.data.inspect.app.statusResponse.ThirdPartToolAppStatusResponse;
+import cn.iselab.mooctest.site.data.inspect.app.statusResponse.ThirdPartToolAppStatusResponseCode;
+import cn.iselab.mooctest.site.data.inspect.report.EnterpriseReportType;
+import cn.iselab.mooctest.site.data.inspect.report.ReportStartResponseStatusConstants;
+import cn.iselab.mooctest.site.models.Exam;
+import cn.iselab.mooctest.site.models.Exam2Case;
+import cn.iselab.mooctest.site.models.SubmitRecord;
+import cn.iselab.mooctest.site.models.ThirdPartToolJob;
+import cn.iselab.mooctest.site.service.SubmitRecordService;
+import cn.iselab.mooctest.site.service.ThirdPartToolAppService;
+import cn.iselab.mooctest.site.service.UserService;
+import com.google.gson.Gson;
+import net.sf.json.JSONObject;
+import org.json.JSONArray;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 15:19
+ */
+@Service
+public class ThirdPartToolAppServiceImpl implements ThirdPartToolAppService {
+
+    @Value("${thirdPartTool.reportService.url}")
+    private String reportServiceUrl;
+
+    @Value("${thirdPartTool.appAutoTestService.url}")
+    private String appAutoTestServiceUrl;
+
+    @Autowired
+    private ThirdPartToolJobDao thirdPartToolJobDao;
+
+    @Autowired
+    private SubmitRecordService submitRecordService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private Exam2CaseDao exam2CaseDao;
+
+    @Autowired
+    private ExamDao examDao;
+
+    @Override
+    public List<Long> getRunningThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> appRunningJobList = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.RUNNING, ThirdPartToolJobTypeConstants.APP);
+        List<Long> appRunningJobIdList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : appRunningJobList) {
+            appRunningJobIdList.add(thirdPartToolJob.getId());
+        }
+        return appRunningJobIdList;
+    }
+
+    @Override
+    public List<Long> getPendingThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> appPendingJobList = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.PENDING, ThirdPartToolJobTypeConstants.APP);
+        List<Long> appPendingJobIdList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : appPendingJobList) {
+            appPendingJobIdList.add(thirdPartToolJob.getId());
+        }
+        return appPendingJobIdList;
+    }
+
+    @Override
+    public List<Long> getRunFinishThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> appRunFinishJobList = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.RUN_FINISH, ThirdPartToolJobTypeConstants.APP);
+        List<Long> appRunFinishJobIdList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : appRunFinishJobList) {
+            appRunFinishJobIdList.add(thirdPartToolJob.getId());
+        }
+        return appRunFinishJobIdList;
+    }
+
+    @Override
+    public List<Long> getWriteFinishThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> list = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.WRITE_FINISH, ThirdPartToolJobTypeConstants.APP);
+        List<Long> idList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : list) {
+            idList.add(thirdPartToolJob.getId());
+        }
+        return idList;
+    }
+
+    @Override
+    public List<Long> getGenReportPendingThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> list = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.GENERATE_REPORT_PENDING, ThirdPartToolJobTypeConstants.APP);
+        List<Long> idList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : list) {
+            idList.add(thirdPartToolJob.getId());
+        }
+        return idList;
+    }
+
+    @Override
+    public List<Long> getGenReportRunningThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> list = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.GENERATE_REPORTING, ThirdPartToolJobTypeConstants.APP);
+        List<Long> idList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : list) {
+            idList.add(thirdPartToolJob.getId());
+        }
+        return idList;
+    }
+
+    @Override
+    public Long runGenerateReport(Long jobId) throws Exception {
+        RestTemplate rt = new RestTemplate();
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        JSONObject extra = JSONObject.fromObject(thirdPartToolJob.getExtra());
+        if (null == extra) {
+            throw new Exception("[jobs status:write result]fail to get result :" + jobId);
+        }
+        JSONObject result = readObjectByUrl(extra.get("downloadUrl").toString());
+        if (null == result) {
+            throw new Exception("[jobs status:write result]fail to get result :" + jobId);
+        }
+        HttpEntity<JSONObject> httpEntity = new HttpEntity<>(result, getHeader());
+        String url = getGenerateReportUrl(EnterpriseReportType.APP);
+        Map<String, Object> res;
+        try {
+            res = rt.postForObject(url, httpEntity, Map.class);
+        } catch (Exception e) {
+            throw new Exception("生成报告--启动当前job报告服务失败[taskId:" + jobId + "]");
+        }
+        if (null == res) {
+            throw new Exception("生成报告--启动当前job,结果为null[taskId:" + jobId + "]");
+        } else {
+            Long status = Long.parseLong(res.get("status").toString());
+            if (status.equals(ReportStartResponseStatusConstants.FAIL)) {
+                throw new Exception("生成报告--启动当前job报告服务失败[taskId:" + jobId + "]");
+            } else {
+                thirdPartToolJob.setReportTraceId(res.get("traceId").toString());
+                thirdPartToolJobDao.save(thirdPartToolJob);
+                return ReportStartResponseStatusConstants.SUCCESS;
+            }
+        }
+    }
+
+    @Override
+    public boolean isRunningQueueFull(List<Long> appRunningJobIdList) throws Exception {
+        if (null == appRunningJobIdList) {
+            appRunningJobIdList = new LinkedList<>();
+        }
+        if (appRunningJobIdList.size() >= 1) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Object getAppJobConfig(Long jobId) throws Exception {
+        return null;
+    }
+
+    @Override
+    public Long getJobStatus(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("[jobs status:running]fail to get job information:" + jobId);
+        }
+        RestTemplate rt = new RestTemplate();
+        String url = getAppAutoTestStatusUrl(thirdPartToolJob.getTraceId());
+        ThirdPartToolAppStatusResponse appDataJobStatus;
+        try {
+            appDataJobStatus = rt.getForObject(url, ThirdPartToolAppStatusResponse.class);
+        } catch (Exception e) {
+            throw new Exception("[jobs status:running]fail to get job status:" + jobId);
+        }
+        if (null == appDataJobStatus) {
+            throw new Exception("[jobs status:running]fail to get job status:" + jobId);
+        }
+        if (appDataJobStatus.getStatusCode().equals(ThirdPartToolAppStatusResponseCode.FINISH)) {
+            String resultJsonUrl = appDataJobStatus.getDownloadUrl();
+            JSONObject extra = new JSONObject();
+            extra.put("downloadUrl", resultJsonUrl);
+            thirdPartToolJob.setExtra(extra.toString());
+            thirdPartToolJobDao.save(thirdPartToolJob);
+        }
+        return appDataJobStatus.getStatusCode();
+    }
+
+    @Override
+    public void runJob(Long jobId) throws Exception {
+        Gson gson = new Gson();
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("[jobs status:pending]fail to run job :" + jobId);
+        }
+        if (null == thirdPartToolJob.getConfig()) {
+            throw new Exception("[jobs status:pending]fail to run job :" + jobId);
+        }
+        String config = thirdPartToolJob.getConfig();
+        if (null == config) {
+            throw new Exception("[jobs status:pending]fail to get this job's config :" + jobId);
+        }
+        ThirdPartToolAppStartConfig thirdPartToolAppStartConfig = gson.fromJson(config, ThirdPartToolAppStartConfig.class);
+
+        ThirdPartToolAppStartCommand thirdPartToolAppStartCommand = new ThirdPartToolAppStartCommand();
+        //TODO set command
+        thirdPartToolAppStartCommand.setDownloadUrl(thirdPartToolJob.getUrl());
+        thirdPartToolAppStartCommand.setLimitTime(thirdPartToolAppStartConfig.getLimitTime());
+        thirdPartToolAppStartCommand.setTraceId(null);
+
+        RestTemplate rt = new RestTemplate();
+        HttpEntity<ThirdPartToolAppStartCommand> httpEntity = new HttpEntity<>(thirdPartToolAppStartCommand, getHeader());
+        String url = getAppAutoTestStartUrl();
+        ThirdPartToolAppStartResponse thirdPartToolAppStartResponse;
+        try {
+            thirdPartToolAppStartResponse = rt.postForObject(url, httpEntity, ThirdPartToolAppStartResponse.class);
+        } catch (Exception e) {
+            throw new Exception("[jobs status:pending]fail to run job :" + jobId + ";" + e.getMessage());
+        }
+        if (null == thirdPartToolAppStartResponse) {
+            throw new Exception("[jobs status:pending]fail to run job :" + jobId);
+        }
+
+        if (thirdPartToolAppStartResponse.getStatusCode().equals(ThirdPartToolAppStartResponseCode.CREATE)) {
+            thirdPartToolJob.setTraceId(thirdPartToolAppStartResponse.getTraceId());
+            thirdPartToolJobDao.save(thirdPartToolJob);
+        } else {
+            throw new Exception("[jobs status:pending]fail to run job :" + jobId + ";jobstatus:" + thirdPartToolAppStartResponse.getStatusCode());
+        }
+    }
+
+    @Override
+    public void recordScore(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        JSONObject extra = JSONObject.fromObject(thirdPartToolJob.getExtra());
+        if (extra == null) {
+            throw new Exception("[jobs status:write result]fail to get result :" + jobId);
+        }
+        JSONObject result = readObjectByUrl(extra.get("downloadUrl").toString());
+        if (null == result) {
+            throw new Exception("[jobs status:write result]fail to get result :" + jobId);
+        }
+        String appScore = result.get("appScore").toString();
+        thirdPartToolJob.setResult(appScore);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+        createSubmitRecord(thirdPartToolJob.getGroupId(), thirdPartToolJob.getOwnerId(), JSONObject.fromObject(appScore));
+    }
+
+    @Override
+    public JSONObject readObjectByUrl(String url) throws Exception {
+        RestTemplate rt = new RestTemplate();
+        rt.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
+        String result = rt.getForObject(url, String.class);
+        JSONObject json = JSONObject.fromObject(result);
+        return json;
+    }
+
+    void createSubmitRecord(Long examId, Long userId, JSONObject score) throws Exception {
+        SubmitRecord submitRecord = submitRecordService.getAssignedTask(examId, userId);
+        if (null == submitRecord) {
+            submitRecord = new SubmitRecord();
+        }
+        Exam exam = examDao.findOne(examId);
+
+        submitRecord.setExamId(examId);
+        submitRecord.setOwnerId(exam.getOwnerId());
+        submitRecord.setDeleted(false);
+        submitRecord.setName("用户" + userId + "的作业提交");
+        submitRecord.setContent("无");
+        submitRecord.setParticipantId(userId);
+        submitRecord.setScore(Double.parseDouble(score.get("robustness").toString()));
+
+        JSONObject assignedResults = new JSONObject();
+        JSONObject caseDetail = new JSONObject();
+        List<Exam2Case> exam2CaseList = exam2CaseDao.findByExamId(examId);
+        caseDetail.put("id", exam2CaseList.get(0).getCaseId());
+        caseDetail.put("maxScore", score.get("robustness").toString());
+        JSONArray scores = new JSONArray();
+        scores.put(score.get("robustness").toString());
+        caseDetail.put("scores", scores);
+        caseDetail.put("name", "验收测试");
+        caseDetail.put("resultUrls", new JSONArray());
+        assignedResults.put(String.valueOf(examId), caseDetail);
+        JSONObject assignedResultObj = new JSONObject();
+        assignedResultObj.put("results", assignedResults);
+        submitRecord.setResult(assignedResultObj.toString());
+        submitRecordService.saveOrUpdateAssignedTask(submitRecord);
+    }
+
+    /*
+    @Override
+    public void startJob(ThirdPartToolStartDTO thirdPartToolStartDTO) throws Exception {
+        RestTemplate rt = new RestTemplate();
+        ThirdPartToolAppStartCommand thirdPartToolAppStartCommand = new ThirdPartToolAppStartCommand();
+        //TODO 添加参数
+        HttpEntity<ThirdPartToolAppStartCommand> httpEntity = new HttpEntity<>(thirdPartToolAppStartCommand, getHeader());
+        String url = getAppAutoTestStartUrl();
+        ThirdPartToolAppStartResponse thirdPartToolAppStartResponse = rt.postForObject(url, httpEntity, ThirdPartToolAppStartResponse.class);
+        //TODO 处理返回结果
+    }
+
+    @Override
+    public Map<String, Object> getJobStatus(Long taskId) throws Exception {
+        RestTemplate rt = new RestTemplate();
+        String url = getAppAutoTestStatusUrl(taskId);
+        try {
+            ThirdPartToolAppStatusResponse appAutoTestStatus = rt.getForObject(url, ThirdPartToolAppStatusResponse.class);
+            Map<String, Object> res = new HashMap<>();
+            res.put("appAutoTestStatus", appAutoTestStatus);
+            return res;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+    */
+
+    private String getAppAutoTestStartUrl() throws Exception {
+        String api = "/api/v1/runTest";
+        return appAutoTestServiceUrl + api;
+    }
+
+    private String getAppAutoTestStatusUrl(String traceId) throws Exception {
+        String api = "/api/v1/result?traceId={traceId}";
+        return appAutoTestServiceUrl + api.replace("{traceId}", "" + traceId);
+    }
+
+    private HttpHeaders getHeader() throws Exception {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return headers;
+    }
+
+    private String getGenerateReportUrl(String reportType) {
+        String api = "/api/report/generate/{reportType}".replace("{reportType}", reportType);
+        return reportServiceUrl + api;
+    }
+}

+ 445 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolServiceImpl.java

@@ -0,0 +1,445 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobStatusConstants;
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobTypeConstants;
+import cn.iselab.mooctest.site.dao.Exam2CaseDao;
+import cn.iselab.mooctest.site.dao.Exam2PaperDao;
+import cn.iselab.mooctest.site.dao.Paper2CaseDao;
+import cn.iselab.mooctest.site.dao.ThirdPartToolJobDao;
+import cn.iselab.mooctest.site.data.inspect.InspectTestType;
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobDTO;
+import cn.iselab.mooctest.site.data.inspect.report.ReportStartResponseStatusConstants;
+import cn.iselab.mooctest.site.models.*;
+import cn.iselab.mooctest.site.service.*;
+import cn.iselab.mooctest.site.service.instancePermission.CasePermissionService;
+import cn.iselab.mooctest.site.service.instancePermission.PaperPermissionService;
+import cn.iselab.mooctest.site.service.instancePermission.TargetPermissionService;
+import cn.iselab.mooctest.site.web.data.ExamVO;
+import cn.iselab.mooctest.site.web.data.forMongo.CaseGraphDTO;
+import cn.iselab.mooctest.site.web.data.forMongo.TargetGraphDTO;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author sunjh
+ * @date 2020/2/10 21:26
+ */
+@Service
+public class ThirdPartToolServiceImpl implements ThirdPartToolService {
+    @Autowired
+    private ThirdPartToolJobDao thirdPartToolJobDao;
+
+    @Autowired
+    private SubmitRecordService submitRecordService;
+
+    @Autowired
+    private ThirdPartToolWebService thirdPartToolWebService;
+
+    @Autowired
+    private ThirdPartToolAppService thirdPartToolAppService;
+    @Autowired
+    private TargetService targetService;
+
+    @Autowired
+    private TargetPermissionService targetPermissionService;
+
+    @Autowired
+    private TargetGraphService targetGraphService;
+
+    @Autowired
+    private CaseService caseService;
+
+    @Autowired
+    private CaseGraphService caseGraphService;
+
+    @Autowired
+    private CasePermissionService casePermissionService;
+
+    @Autowired
+    private PaperService paperService;
+
+    @Autowired
+    private PaperPermissionService paperPermissionService;
+
+    @Autowired
+    private Exam2CaseDao exam2CaseDao;
+
+    @Autowired
+    private Exam2PaperDao exam2PaperDao;
+
+    @Autowired
+    private Paper2CaseDao paper2CaseDao;
+
+    @Autowired
+    private AnswerWay2CaseBlockService answerWay2CaseBlockService;
+
+    @Value("${thirdPartTool.reportService.url}")
+    private String reportServiceUrl;
+
+    @Override
+    public ThirdPartToolJob create(ThirdPartToolJobDTO thirdPartToolJobDTO, Long examId, Long userId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findThirdPartToolJobByOwnerIdAndGroupId(userId, examId);
+        if (null == thirdPartToolJob) {
+            thirdPartToolJob = new ThirdPartToolJob();
+            thirdPartToolJob.setCreateTime(new Timestamp(System.currentTimeMillis()));
+            thirdPartToolJob.setOwnerId(userId);
+            thirdPartToolJob.setGroupId(examId);
+            thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.CREATE);
+            thirdPartToolJob.setType(thirdPartToolJobDTO.getType());
+        }
+        thirdPartToolJob.setName(thirdPartToolJobDTO.getName());
+        thirdPartToolJob.setConfig(thirdPartToolJobDTO.getConfig());
+        thirdPartToolJob.setInformation(thirdPartToolJobDTO.getInformation());
+        thirdPartToolJob.setFile(thirdPartToolJobDTO.getFile());
+        thirdPartToolJob.setUrl(thirdPartToolJobDTO.getUrl());
+        return thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void startThirdPartToolJob(Long userId, Long examId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findThirdPartToolJobByOwnerIdAndGroupId(userId, examId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("there is no job");
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.PENDING);
+        thirdPartToolJob.setStartTime(new Timestamp(System.currentTimeMillis()));
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+
+    @Override
+    public void changeJobStatusToFail(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.RUN_FAIL);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void changeJobStatusToFail(Long jobId, String message) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.RUN_FAIL);
+        thirdPartToolJob.setInformation(message);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void changeJobStatusToRunning(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.RUNNING);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void changeJobStatusToWriteResult(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.WRITE_SCORE);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void changeJobStatusToWriteFinish(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.WRITE_FINISH);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void changeJobStatusToGenReportPending(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.GENERATE_REPORT_PENDING);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void changeJobStatusToGenReportRunning(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.GENERATE_REPORTING);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void changeJobStatusToGenReportFinish(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.GENERATE_REPORT_FINISH);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public Long getGenerateStatus(Long jobId) throws Exception {
+        RestTemplate rt = new RestTemplate();
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        String url = getReportStateUrl(thirdPartToolJob.getReportTraceId());
+        Long status = ReportStartResponseStatusConstants.FAIL;
+        Map<String, Object> map;
+        try {
+            map = rt.getForObject(url, Map.class);
+        } catch (Exception e) {
+            throw new Exception("fail to get report information[jobId:" + jobId + "]:" + e.getMessage());
+        }
+        if (null == map) {
+            throw new Exception("fail to get report information[jobId:" + jobId + "]");
+        }
+        status = Long.parseLong(map.get("status").toString());
+        if (status.equals(ReportStartResponseStatusConstants.SUCCESS)) {
+            String reportUrl = (String) map.get("url");
+            String downloadUrl = (String) map.get("downloadUrl");
+            thirdPartToolJob.setReportUrl(reportUrl);
+            thirdPartToolJob.setReportDownloadUrl(downloadUrl);
+            thirdPartToolJobDao.save(thirdPartToolJob);
+
+        } else if (status.equals(ReportStartResponseStatusConstants.FAIL)) {
+            throw new Exception("fail to get report information[jobId:" + jobId + "]");
+        }
+        return status;
+    }
+
+    @Override
+    public void changeJobStatusToRunFinish(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("the job is not exist:" + jobId);
+        }
+        //submit(thirdPartToolJob.getOwnerId(), thirdPartToolJob.getGroupId());
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.RUN_FINISH);
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public void batchStartThirdPartToolJob(Long examId) throws Exception {
+        List<ThirdPartToolJob> thirdPartToolJobList = thirdPartToolJobDao.findByGroupId(examId);
+        if (null == thirdPartToolJobList || thirdPartToolJobList.isEmpty()) {
+            throw new Exception("the class is empty");
+        }
+        for (ThirdPartToolJob thirdPartToolJob : thirdPartToolJobList) {
+            if (thirdPartToolJob.getStatus().equals(ThirdPartToolJobStatusConstants.CREATE)
+                    || thirdPartToolJob.getStatus().equals(ThirdPartToolJobStatusConstants.RUN_FAIL)) {
+                thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.PENDING);
+                thirdPartToolJob.setStartTime(new Timestamp(System.currentTimeMillis()));
+            }
+        }
+        thirdPartToolJobDao.saveAll(thirdPartToolJobList);
+    }
+
+    @Override
+    public void stopThirdPartToolJob(Long userId, Long examId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findThirdPartToolJobByOwnerIdAndGroupId(userId, examId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("there is no job");
+        }
+        if (!thirdPartToolJob.getStatus().equals(ThirdPartToolJobStatusConstants.RUNNING) && !thirdPartToolJob.getStatus().equals(ThirdPartToolJobStatusConstants.PENDING)) {
+            throw new Exception("current state can not stop");
+        }
+        thirdPartToolJob.setStatus(ThirdPartToolJobStatusConstants.RUN_FAIL);
+        thirdPartToolJob.setEndTime(new Timestamp(System.currentTimeMillis()));
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    @Override
+    public List<ThirdPartToolJob> getAllThirdPartToolJobsByExamId(Long examId) throws Exception {
+        return thirdPartToolJobDao.findByGroupId(examId);
+    }
+
+    @Override
+    public ThirdPartToolJob getThirdPartToolJobsByExamIdAndUserId(Long examId, Long userId) throws Exception {
+//        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findThirdPartToolJobByOwnerIdAndGroupId(userId, examId);
+//        if (null == thirdPartToolJob) {
+//            throw new Exception("this job is not exist");
+//        }
+        return thirdPartToolJobDao.findThirdPartToolJobByOwnerIdAndGroupId(userId, examId);
+    }
+
+    @Override
+    public ExamVO saveOrUpdateInspectExam(ExamVO examVO, Long testType, Long userId) throws Exception {
+        return null;
+    }
+
+    @Override
+    public void submit(Long userId, Long examId) throws Exception {
+        SubmitRecord submitRecord = new SubmitRecord();
+        submitRecord.setExamId(examId);
+        submitRecord.setOwnerId(2L);
+        submitRecord.setDeleted(false);
+        submitRecord.setName("无");
+        submitRecord.setContent("无");
+        submitRecord.setParticipantId(userId);
+        submitRecord.setScore(59.0);
+
+        JSONObject assignedResults = new JSONObject();
+        JSONObject caseDetail = new JSONObject();
+        caseDetail.put("id", examId);
+        caseDetail.put("maxScore", 59L);
+        JSONArray scores = new JSONArray();
+        scores.put(59);
+        caseDetail.put("scores", scores);
+        caseDetail.put("name", "验收测试");
+        caseDetail.put("resultUrls", new JSONArray());
+        assignedResults.put(String.valueOf(examId), caseDetail);
+        JSONObject assignedResultObj = new JSONObject();
+        assignedResultObj.put("results", assignedResults);
+        submitRecord.setResult(assignedResultObj.toString());
+        submitRecordService.saveOrUpdateAssignedTask(submitRecord);
+    }
+
+    @Override
+    public Target createDefaultInspectTarget(Long userId) throws Exception {
+        Target target = new Target();
+        target.setName("作业验收");
+        target.setUrl("");
+        target.setTargetType(14L);
+        target.setVisible(false);
+        target.setStatus(1);
+        target.setCreateTime(new Timestamp(System.currentTimeMillis()));
+        target.setOwnerId(userId);
+        target.setMessage("作业验收");
+        //保存target表
+        Target savedTarget = targetService.createTarget(target);
+        //保存target_permission表
+        targetPermissionService.uploadApp(userId, savedTarget.getId());
+        //保存properties,暂不用
+        //Map<String, String> properties = new HashMap<>();
+        //targetService.saveTargetProperties(null, savedTarget.getId());
+        return savedTarget;
+    }
+
+    @Override
+    public CaseExtends createDefaultInspectCase(Long userId, Long targetId, Long caseType) throws Exception {
+        CaseExtends caseExtends = new CaseExtends();
+        caseExtends.setName("提交作业");
+        caseExtends.setDifficulty(3);
+        caseExtends.setDomain(0);
+        caseExtends.setDescription("");
+        //TODO 判断subjectId
+        if (caseType.equals(InspectTestType.APP_INSPECT)) {
+            caseExtends.setSubjectId(71L);
+        } else if (caseType.equals(InspectTestType.WEB_INSPECT)) {
+            caseExtends.setSubjectId(72L);
+        }
+
+        caseExtends.setCaseId("-1");
+        caseExtends.setVisible(false);
+        caseExtends.setCreateTime(new Timestamp(System.currentTimeMillis()));
+        caseExtends.setAppId(targetId);
+        caseExtends.setDeleted(false);
+        caseExtends.setOwnerId(userId);
+        short platform = 7;
+        caseExtends.setPlatform(platform);
+        JSONObject properties = new JSONObject();
+        properties.put("testType", caseType);
+        caseExtends.setProperties(properties.toString());
+        caseExtends.setAnswerWay(14L);
+
+        //保存到case_entity
+        CaseExtends savedCaseExtends = caseService.save(caseExtends);
+        //生成case_graph--
+        CaseGraphDTO caseGraphDTO = new CaseGraphDTO();
+        caseGraphDTO.setCaseId(savedCaseExtends.getId());
+        TargetGraphDTO targetGraphDTO = targetGraphService.getTargetGraph(savedCaseExtends.getAppId());
+        if (null != targetGraphDTO) {
+            caseGraphDTO.setEdges(targetGraphDTO.getEdges());
+            caseGraphDTO.setCategories(targetGraphDTO.getCategories());
+            caseGraphDTO.setNodes(targetGraphDTO.getNodes());
+            caseGraphService.saveCaseGraph(caseGraphDTO);
+        }
+        //设置case_permission
+        casePermissionService.createCase(userId, savedCaseExtends.getId());
+        if (savedCaseExtends.getVisible()) {
+            casePermissionService.publishCase(savedCaseExtends.getId());
+        }
+        //检查完整性--此处省略
+        return savedCaseExtends;
+    }
+
+    @Override
+    public Paper createDefaultInspectPaper(Long userId, Long caseId) throws Exception {
+        Paper paper = new Paper();
+        paper.setOwnerId(userId);
+        paper.setName("验收");
+        paper.setDescription("");
+        byte difficult = 0;
+        paper.setDifficult(difficult);
+        paper.setCreateTime(new Timestamp(System.currentTimeMillis()));
+        paper.setUpdateTime(new Timestamp(System.currentTimeMillis()));
+        paper.setIsPublic(false);
+        paper.setIsDeleted(false);
+        Paper savedPaper = paperService.createOrUpdate(paper);
+        //设置paper_permission表
+        paperPermissionService.createPaper(userId, savedPaper.getId());
+        paperPermissionService.publishPaper(savedPaper.getId());
+        return savedPaper;
+    }
+
+    @Override
+    public Exam2Case createExam2Case(Long examId, Long paperId, Long caseId) throws Exception {
+        Exam2Case exam2Case = new Exam2Case();
+        exam2Case.setExamId(examId);
+        exam2Case.setPaperId(paperId);
+        exam2Case.setCaseId(caseId);
+        exam2Case.setWeight(1);
+        exam2Case.setCaseBlock(answerWay2CaseBlockService.findByCaseId(caseId).getCaseBlock());
+        return exam2CaseDao.save(exam2Case);
+    }
+
+    @Override
+    public Exam2Paper createExam2Paper(Long examId, Long paperId) throws Exception {
+        Exam2Paper exam2Paper = new Exam2Paper();
+        exam2Paper.setExamId(examId);
+        exam2Paper.setPaperId(paperId);
+        return exam2PaperDao.save(exam2Paper);
+    }
+
+    @Override
+    public Paper2Case createPaper2Case(Long paperId, Long caseId) throws Exception {
+        Paper2Case paper2Case = new Paper2Case();
+        paper2Case.setPaperId(paperId);
+        paper2Case.setCaseId(caseId);
+        paper2Case.setWeight(1d);
+        paper2Case.setCaseIndex(0);
+        paper2Case.setCount(39);
+        paper2Case.setAutoSelect(true);
+        return paper2CaseDao.save(paper2Case);
+    }
+
+    @Override
+    public Page<ThirdPartToolJob> getJobByExamIdPage(Long examId, String keyword, Pageable pageable) {
+        return thirdPartToolJobDao.findByExamId(examId, keyword, pageable);
+    }
+
+    private String getReportStateUrl(String traceId) {
+        String api = "/api/report/status/{traceId}".replace("{traceId}", traceId);
+        return reportServiceUrl + api;
+    }
+}

+ 336 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolWebServiceImpl.java

@@ -0,0 +1,336 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobStatusConstants;
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobTypeConstants;
+import cn.iselab.mooctest.site.dao.Exam2CaseDao;
+import cn.iselab.mooctest.site.dao.ExamDao;
+import cn.iselab.mooctest.site.dao.ThirdPartToolJobDao;
+import cn.iselab.mooctest.site.data.UserDTOForMT;
+import cn.iselab.mooctest.site.data.inspect.report.EnterpriseReportType;
+import cn.iselab.mooctest.site.data.inspect.report.ReportStartResponseStatusConstants;
+import cn.iselab.mooctest.site.data.inspect.web.startCommand.ThirdPartToolWebStartCommand;
+import cn.iselab.mooctest.site.data.inspect.web.startCommand.ThirdPartToolWebStartConfig;
+import cn.iselab.mooctest.site.data.inspect.web.startResponse.ThirdPartToolWebStartResponse;
+import cn.iselab.mooctest.site.data.inspect.web.startResponse.ThirdPartToolWebStartResponseCode;
+import cn.iselab.mooctest.site.data.inspect.web.statusResponse.ThirdPartToolWebStatusResponse;
+import cn.iselab.mooctest.site.data.inspect.web.statusResponse.ThirdPartToolWebStatusResponseCode;
+import cn.iselab.mooctest.site.data.inspect.web.statusResponse.ThirdPartToolWebStatusResponseDataScoreModel;
+import cn.iselab.mooctest.site.models.Exam;
+import cn.iselab.mooctest.site.models.Exam2Case;
+import cn.iselab.mooctest.site.models.SubmitRecord;
+import cn.iselab.mooctest.site.models.ThirdPartToolJob;
+import cn.iselab.mooctest.site.service.SubmitRecordService;
+import cn.iselab.mooctest.site.service.ThirdPartToolWebService;
+import cn.iselab.mooctest.site.service.UserService;
+import com.google.gson.Gson;
+import org.json.JSONArray;
+import net.sf.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 14:58
+ */
+@Service
+public class ThirdPartToolWebServiceImpl implements ThirdPartToolWebService {
+    @Autowired
+    private ThirdPartToolJobDao thirdPartToolJobDao;
+
+    @Autowired
+    private SubmitRecordService submitRecordService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private Exam2CaseDao exam2CaseDao;
+
+    @Autowired
+    private ExamDao examDao;
+
+    @Value("${thirdPartTool.webAutoTestService.url}")
+    private String webAutoTestServiceUrl;
+
+    @Value("${thirdPartTool.reportService.url}")
+    private String reportServiceUrl;
+
+    /*
+    @Override
+    public void startJob(ThirdPartToolStartDTO thirdPartToolStartDTO) throws Exception {
+        RestTemplate rt = new RestTemplate();
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        UserDTOForMT user = userService.findByUserId(userId);
+        List<String> browsers = new LinkedList<>();
+        ThirdPartToolWebStartCommand thirdPartToolWebStartCommand = new ThirdPartToolWebStartCommand();
+        //TODO 设置参数
+        HttpEntity<ThirdPartToolWebStartCommand> httpEntity = new HttpEntity<>(thirdPartToolWebStartCommand, getHeader());
+        String url = getWebAutoTestStartUrl();
+        ThirdPartToolWebStartResponse thirdPartToolWebStartResponse = rt.postForObject(url, httpEntity, ThirdPartToolWebStartResponse.class);
+        //TODO 返回结果
+    }
+    */
+
+    @Override
+    public List<Long> getRunningThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> webRunningJobList = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.RUNNING, ThirdPartToolJobTypeConstants.WEB);
+        List<Long> webRunningJobIdList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : webRunningJobList) {
+            webRunningJobIdList.add(thirdPartToolJob.getId());
+        }
+        return webRunningJobIdList;
+    }
+
+    @Override
+    public List<Long> getPendingThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> webRunningJobList = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.PENDING, ThirdPartToolJobTypeConstants.WEB);
+        List<Long> webRunningJobIdList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : webRunningJobList) {
+            webRunningJobIdList.add(thirdPartToolJob.getId());
+        }
+        return webRunningJobIdList;
+    }
+
+    @Override
+    public List<Long> getRunFinishThirdPartToolJobList() throws Exception {
+        List<ThirdPartToolJob> webRunFinishJobList = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.RUN_FINISH, ThirdPartToolJobTypeConstants.WEB);
+        List<Long> webRunFinishJobIdList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : webRunFinishJobList) {
+            webRunFinishJobIdList.add(thirdPartToolJob.getId());
+        }
+        return webRunFinishJobIdList;
+    }
+
+    @Override
+    public List<Long> getWriteFinishThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> list = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.WRITE_FINISH, ThirdPartToolJobTypeConstants.WEB);
+        List<Long> idList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : list) {
+            idList.add(thirdPartToolJob.getId());
+        }
+        return idList;
+    }
+
+    @Override
+    public List<Long> getGenReportPendingThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> list = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.GENERATE_REPORT_PENDING, ThirdPartToolJobTypeConstants.WEB);
+        List<Long> idList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : list) {
+            idList.add(thirdPartToolJob.getId());
+        }
+        return idList;
+    }
+
+    @Override
+    public List<Long> getGenReportRunningThirdPartToolJobIdList() throws Exception {
+        List<ThirdPartToolJob> list = thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(ThirdPartToolJobStatusConstants.GENERATE_REPORTING, ThirdPartToolJobTypeConstants.WEB);
+        List<Long> idList = new LinkedList<>();
+        for (ThirdPartToolJob thirdPartToolJob : list) {
+            idList.add(thirdPartToolJob.getId());
+        }
+        return idList;
+    }
+
+    @Override
+    public Long runGenerateReport(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        RestTemplate rt = new RestTemplate();
+        String url = getWebAutoTestStatusUrl(thirdPartToolJob.getTraceId());
+        ThirdPartToolWebStatusResponse webDataJobStatus;
+        try {
+            webDataJobStatus = rt.getForObject(url, ThirdPartToolWebStatusResponse.class);
+        } catch (Exception e) {
+            throw new Exception("[jobs status:write result]fail to get result :" + jobId + ";" + e.getMessage());
+        }
+        if (null == webDataJobStatus) {
+            throw new Exception("[jobs status:write result]fail to get result :" + jobId);
+        }
+        if (webDataJobStatus.getCode().equals(ThirdPartToolWebStatusResponseCode.SUCCESS)) {
+            JSONObject report = JSONObject.fromObject(webDataJobStatus.getData().getWebReport());
+            HttpEntity<JSONObject> httpEntity = new HttpEntity<>(report, getHeader());
+            String generateReportUrl = getGenerateReportUrl(EnterpriseReportType.WEB);
+            Map<String, Object> res;
+            try {
+                res = rt.postForObject(generateReportUrl, httpEntity, Map.class);
+
+            } catch (Exception e) {
+                throw new Exception("fail to generate report[jobId:" + jobId + "]");
+            }
+            if (null == res) {
+                throw new Exception("fail to generate report[jobId:" + jobId + "]");
+            } else {
+                Long status = Long.parseLong(res.get("status").toString());
+                if (status.equals(ReportStartResponseStatusConstants.FAIL)) {
+                    throw new Exception("fail to generate report[jobId:" + jobId + "]");
+                } else {
+                    thirdPartToolJob.setReportTraceId(res.get("traceId").toString());
+                    thirdPartToolJobDao.save(thirdPartToolJob);
+                    return ReportStartResponseStatusConstants.SUCCESS;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isRunningQueueFull(List<Long> webRunningJobIdList) throws Exception {
+        if (null == webRunningJobIdList) {
+            webRunningJobIdList = new LinkedList<>();
+        }
+        if (webRunningJobIdList.size() >= 1) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Object getWebJobConfig(Long jobId) throws Exception {
+        return null;
+    }
+
+    @Override
+    public Long getJobStatus(Long jobId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        if (null == thirdPartToolJob) {
+            throw new Exception("[jobs status:running]fail to get job information:" + jobId);
+        }
+        RestTemplate rt = new RestTemplate();
+        String url = getWebAutoTestStatusUrl(thirdPartToolJob.getTraceId());
+        ThirdPartToolWebStatusResponse webDataJobStatus;
+        try {
+            webDataJobStatus = rt.getForObject(url, ThirdPartToolWebStatusResponse.class);
+        } catch (Exception e) {
+            throw new Exception("[jobs status:running]fail to get job status:" + jobId);
+        }
+        if (null == webDataJobStatus) {
+            throw new Exception("[jobs status:running]fail to get job status:" + jobId);
+        }
+        if (webDataJobStatus.getCode().equals(ThirdPartToolWebStatusResponseCode.SUCCESS)) {
+            Gson gson = new Gson();
+            String score = gson.toJson(webDataJobStatus.getData().getScoreModel());
+            thirdPartToolJob.setResult(score);
+            thirdPartToolJobDao.save(thirdPartToolJob);
+        }
+        return webDataJobStatus.getCode();
+    }
+
+    @Override
+    public void runJob(Long jobId) throws Exception {
+        Gson gson = new Gson();
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        String config = thirdPartToolJob.getConfig();
+        if (null == config) {
+            throw new Exception("[jobs status:pending]fail to get config:" + jobId);
+        }
+        ThirdPartToolWebStartConfig thirdPartToolWebStartConfig;
+        try {
+            thirdPartToolWebStartConfig = gson.fromJson(config, ThirdPartToolWebStartConfig.class);
+        } catch (Exception e) {
+            throw new Exception("[jobs status:pending]fail to get config:" + jobId);
+        }
+
+        ThirdPartToolWebStartCommand thirdPartToolWebStartCommand = new ThirdPartToolWebStartCommand();
+        thirdPartToolWebStartCommand.setMaximumStates(thirdPartToolWebStartConfig.getMaximumStates());
+        thirdPartToolWebStartCommand.setMaximumDepth(thirdPartToolWebStartConfig.getMaximumDepth());
+        thirdPartToolWebStartCommand.setMaximumRuntime(thirdPartToolWebStartConfig.getMaximumRuntime());
+        thirdPartToolWebStartCommand.setBrowsers(thirdPartToolWebStartConfig.getBrowsers());
+        thirdPartToolWebStartCommand.setAuthConfigModel(thirdPartToolWebStartConfig.getAuthConfigModel());
+        thirdPartToolWebStartCommand.setWebAutoTestCrawlRulesModel(null);
+        thirdPartToolWebStartCommand.setUrl(thirdPartToolJob.getUrl());
+
+        UserDTOForMT user = userService.findByUserId(thirdPartToolJob.getOwnerId());
+        thirdPartToolWebStartCommand.setUser(user.getUserName());
+
+        RestTemplate rt = new RestTemplate();
+        HttpEntity<ThirdPartToolWebStartCommand> httpEntity = new HttpEntity<>(thirdPartToolWebStartCommand, getHeader());
+        String url = getWebAutoTestStartUrl();
+        ThirdPartToolWebStartResponse thirdPartToolWebStartResponse;
+        try {
+            thirdPartToolWebStartResponse = rt.postForObject(url, httpEntity, ThirdPartToolWebStartResponse.class);
+        } catch (Exception e) {
+            throw new Exception("[jobs status:pending]fail to run job :" + jobId);
+        }
+
+        if (null == thirdPartToolWebStartResponse || !thirdPartToolWebStartResponse.getCode().equals(ThirdPartToolWebStartResponseCode.SUCCESS)) {
+            throw new Exception("[jobs status:pending]fail to run job :" + jobId);
+        } else {
+            thirdPartToolJob.setTraceId(thirdPartToolWebStartResponse.getData().getSessionId());
+            thirdPartToolJobDao.save(thirdPartToolJob);
+        }
+    }
+
+    @Override
+    public void recordScore(Long jobId) throws Exception {
+        Gson gson = new Gson();
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolJobDao.findOne(jobId);
+        ThirdPartToolWebStatusResponseDataScoreModel result;
+        try {
+            result = gson.fromJson(thirdPartToolJob.getResult(), ThirdPartToolWebStatusResponseDataScoreModel.class);
+        } catch (Exception e) {
+            throw new Exception("[jobs status:write result]fail to get result :" + jobId + ";" + e.getMessage());
+        }
+        createSubmitRecord(thirdPartToolJob.getGroupId(), thirdPartToolJob.getOwnerId(), JSONObject.fromObject(result));
+        thirdPartToolJobDao.save(thirdPartToolJob);
+    }
+
+    void createSubmitRecord(Long examId, Long userId, JSONObject score) throws Exception {
+        SubmitRecord submitRecord = submitRecordService.getAssignedTask(examId, userId);
+        if (null == submitRecord) {
+            submitRecord = new SubmitRecord();
+        }
+        Exam exam = examDao.findOne(examId);
+        submitRecord.setExamId(examId);
+        submitRecord.setOwnerId(exam.getOwnerId());
+        submitRecord.setDeleted(false);
+        submitRecord.setName("用户" + userId + "的作业提交");
+        submitRecord.setContent("无");
+        submitRecord.setParticipantId(userId);
+        submitRecord.setScore(Double.parseDouble(score.get("totalScore").toString()));
+
+        JSONObject assignedResults = new JSONObject();
+        JSONObject caseDetail = new JSONObject();
+        List<Exam2Case> exam2CaseList = exam2CaseDao.findByExamId(examId);
+        caseDetail.put("id", exam2CaseList.get(0).getCaseId());
+        caseDetail.put("maxScore", score.get("totalScore").toString());
+        JSONArray scores = new JSONArray();
+        scores.put(score.get("totalScore").toString());
+        caseDetail.put("scores", scores);
+        caseDetail.put("name", "验收测试");
+        caseDetail.put("resultUrls", new JSONArray());
+        assignedResults.put(String.valueOf(examId), caseDetail);
+        JSONObject assignedResultObj = new JSONObject();
+        assignedResultObj.put("results", assignedResults);
+        submitRecord.setResult(assignedResultObj.toString());
+        submitRecordService.saveOrUpdateAssignedTask(submitRecord);
+    }
+
+    private String getWebAutoTestStartUrl() throws Exception {
+        String api = "/api/v1/task/start";
+        return webAutoTestServiceUrl + api;
+    }
+
+    private String getWebAutoTestStatusUrl(String traceId) throws Exception {
+        String api = "/api/v1/task/result/{task_id}";
+        return webAutoTestServiceUrl + api.replace("{task_id}", traceId);
+    }
+
+    private HttpHeaders getHeader() throws Exception {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return headers;
+    }
+
+    private String getGenerateReportUrl(String reportType) {
+        String api = "/api/report/generate/{reportType}".replace("{reportType}", reportType);
+        return reportServiceUrl + api;
+    }
+}

+ 246 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ThirdPartToolController.java

@@ -0,0 +1,246 @@
+package cn.iselab.mooctest.site.web.ctrl;
+
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobDTO;
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobVO;
+import cn.iselab.mooctest.site.models.ThirdPartToolJob;
+import cn.iselab.mooctest.site.models.instancePermission.ExamPermission;
+import cn.iselab.mooctest.site.web.data.ExamVO;
+import cn.iselab.mooctest.site.web.logic.ThirdPartToolLogic;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.UnauthenticatedException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 14:55
+ */
+@RestController
+public class ThirdPartToolController extends BaseSearchController {
+    @Autowired
+    private ThirdPartToolLogic thirdPartToolLogic;
+
+    /**
+     * 定时任务,暂定每10秒执行一次
+     *
+     * @throws Exception 异常
+     */
+    @Scheduled(cron = "0/10 * * * * ?")
+    public void schedule() throws Exception {
+        thirdPartToolLogic.handleRunningJob();
+        thirdPartToolLogic.handlePendingJob();
+        thirdPartToolLogic.handleRunFinishJob();
+        thirdPartToolLogic.handleWriteResultFinishJob();
+        thirdPartToolLogic.handleGenReportRunningJob();
+        thirdPartToolLogic.handleGenReportPendingJob();
+    }
+
+    /**
+     * 新建third_part_tool任务,即学生的一次作业提交
+     * 已存在则更新
+     *
+     * @param thirdPartToolJobDTO 任务信息
+     * @return 创建完成或更新的任务
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/create", method = RequestMethod.POST)
+    public Map<String, Object> create(@RequestBody ThirdPartToolJobDTO thirdPartToolJobDTO) throws Exception {
+        Map<String, Object> map = new HashMap<>();
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolLogic.createThirdPartToolJob(thirdPartToolJobDTO, userId);
+        map.put("data", thirdPartToolJob);
+        return map;
+    }
+
+    /**
+     * 教师批量开始执行学生的任务
+     *
+     * @param examId 作业的id
+     * @return 默认返回
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/batch/start/{examId}", method = RequestMethod.GET)
+    public Map<String, Object> batchStart(@PathVariable(name = "examId") Long examId) throws Exception {
+        thirdPartToolLogic.batchStartThirdPartToolJob(examId);
+        Map<String, Object> map = new HashMap<>();
+        map.put("res", "success");
+        return map;
+    }
+
+    /**
+     * 教师单独执行验收某个学生的作业
+     *
+     * @param examId 作业的id
+     * @param userId 学生的用户id
+     * @return 默认返回
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/start/{examId}/{userId}", method = RequestMethod.GET)
+    public Map<String, Object> start(@PathVariable(name = "examId") Long examId, @PathVariable(name = "userId") Long userId) throws Exception {
+        Map<String, Object> map = new HashMap<>();
+        map.put("res", "success");
+        thirdPartToolLogic.startThirdPartToolJob(userId, examId);
+        return map;
+    }
+
+    /**
+     * 停止一个任务
+     * 暂未使用
+     *
+     * @param targetId 无效
+     * @return null
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/stop", method = RequestMethod.GET)
+    public Map<String, Object> stop(@RequestParam(name = "targetId") Long targetId) throws Exception {
+        return null;
+    }
+
+    /**
+     * 获取该作业的所有学生的提交信息
+     *
+     * @param examId 作业id
+     * @return 所有学生的提交信息
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/get/all/{examId}", method = RequestMethod.GET)
+    public List<ThirdPartToolJobVO> getAllJobsByExamId(@PathVariable(name = "examId") Long examId) throws Exception {
+        return thirdPartToolLogic.getAllThirdPartToolJobsByExamId(examId);
+    }
+
+    /**
+     * @param examId 作业id
+     * @param sortOrder 分页参数
+     * @param sortBy 分页参数
+     * @param keyword 分页参数
+     * @param request 分页参数
+     * @return 分页结果
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/get/all", method = RequestMethod.GET)
+    public Page<ThirdPartToolJobVO> getJobsPageByExamId(@RequestParam(name = "examId", required = false) Long examId,
+                                                        @RequestParam(name = "sortOrder", required = false, defaultValue = "desc") String sortOrder,
+                                                        @RequestParam(name = "sortBy", required = false, defaultValue = "score") String sortBy,
+                                                        @RequestParam(name = "keyword", required = false, defaultValue = "") String keyword,
+                                                        HttpServletRequest request) throws Exception {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        String examPermissionStr = userId.toString() + ":task:view:" + examId.toString();
+        if (!SecurityUtils.getSubject().isPermitted(new ExamPermission(examPermissionStr))) {
+            throw new UnauthenticatedException("forbidden");
+        }
+        Pageable pageable = getPageInfo(sortOrder, sortBy, request);
+        boolean hasScorePermission = SecurityUtils.getSubject().isPermitted(new ExamPermission(userId.toString() + ":task:score:" + examId.toString()));
+        boolean needFilter = !hasScorePermission;
+        return thirdPartToolLogic.getJobsPageByExamId(examId, keyword, needFilter, pageable);
+    }
+
+    /**
+     * 获取某个学生某次作业的提交信息
+     *
+     * @param examId 作业id
+     * @return 某个学生某次作业的提交信息
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/get/{examId}", method = RequestMethod.GET)
+    public ThirdPartToolJobVO getJobByExamId(@PathVariable(name = "examId") Long examId) throws Exception {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        return thirdPartToolLogic.getThirdPartToolJobByExamIdAndUserId(examId, userId);
+    }
+
+    /**
+     * 重启一个任务
+     *
+     * @param examId examId
+     * @return 默认
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/retry", method = RequestMethod.GET)
+    public Map<String, Object> retry(@PathVariable(name = "examId") Long examId) throws Exception {
+        return null;
+    }
+
+    /**
+     * 待重构
+     * 获取当前case的testType
+     * 目前web:19,app:18
+     *
+     * @param caseId caseId
+     * @return 目前web:19,app:18
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/case/type/{caseId}", method = RequestMethod.GET)
+    public Map<String, Object> getCaseTypeByCaseId(@PathVariable(name = "caseId") Long caseId) throws Exception {
+        Map<String, Object> map = new HashMap<>();
+        String testType = thirdPartToolLogic.getCaseTypeByCaseId(caseId);
+        map.put("testType", testType);
+        return map;
+    }
+
+    @RequestMapping(value = "api/third_part_tool/exam/type/{examId}", method = RequestMethod.GET)
+    public Map<String, Object> getExamTypeByExamId(@PathVariable(name = "examId") Long caseId) throws Exception {
+        Map<String, Object> map = new HashMap<>();
+        String testType = thirdPartToolLogic.getExamTypeByExamId(caseId);
+        map.put("testType", testType);
+        return map;
+    }
+
+    /**
+     * 一步创建exam
+     *
+     * @param examVO   exam信息
+     * @param testType 测试类型
+     * @return 创建好的exam信息
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/third_part_tool/exam/save", method = RequestMethod.POST)
+    public ExamVO createOrUpdateInspectExam(@RequestBody ExamVO examVO, @RequestParam("testType") Long testType) throws Exception {
+        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        return thirdPartToolLogic.saveOrUpdateInspectExam(examVO, testType, userId);
+    }
+
+    /**
+     * 测试接口,无实际作用
+     *
+     * @param url 测试
+     * @return 默认
+     * @throws Exception 异常
+     */
+    @RequestMapping(value = "api/common/third_part_tool/test", method = RequestMethod.POST)
+    public Map<String, Object> test(@RequestParam(name = "url") String url) throws Exception {
+        thirdPartToolLogic.test(url);
+        Map<String, Object> map = new HashMap<>();
+        map.put("url", url);
+        return map;
+    }
+
+    private Pageable getPageInfo(String sortOrder, String sortBy, HttpServletRequest request) {
+        String activePage = request.getHeader("activePage");
+        String rowsOnPage = request.getHeader("rowsOnPage");
+        if (activePage == null || rowsOnPage == null) {
+            throw new IllegalArgumentException("缺少分页信息");
+        }
+        Sort sort;
+        if (sortOrder.equals("desc")) {
+            sort = new Sort(Sort.Direction.DESC, sortBy);
+        } else {
+            sort = new Sort(Sort.Direction.ASC, sortBy);
+        }
+
+        return new PageRequest(Integer.parseInt(activePage) - 1, Integer.parseInt(rowsOnPage), sort);
+    }
+
+    @Override
+    public Page<?> search(String searchCondition) {
+        return null;
+    }
+}

+ 151 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ThirdPartToolLogic.java

@@ -0,0 +1,151 @@
+package cn.iselab.mooctest.site.web.logic;
+
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobDTO;
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobVO;
+import cn.iselab.mooctest.site.models.ThirdPartToolJob;
+import cn.iselab.mooctest.site.web.data.ExamVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 14:56
+ */
+public interface ThirdPartToolLogic {
+
+    /**
+     * 处理运行中的job
+     * @throws Exception 异常
+     */
+    void handleRunningJob() throws Exception;
+
+    /**
+     * 处理就绪队列中的job
+     * @throws Exception 异常
+     */
+    void handlePendingJob() throws Exception;
+
+    /**
+     * 处理运行结束的job
+     * @throws Exception 异常
+     */
+    void handleRunFinishJob() throws Exception;
+
+    /**
+     * 处理回写结果完成的job
+     * @throws Exception 异常
+     */
+    void handleWriteResultFinishJob() throws Exception;
+
+    /**
+     * 处理生成报告中的job
+     * @throws Exception 异常
+     */
+    void handleGenReportRunningJob() throws Exception;
+
+    /**
+     * 处理排队等待生成报告的job
+     * @throws Exception 异常
+     */
+    void handleGenReportPendingJob() throws Exception;
+    /**
+     * 创建或更新作业信息
+     *
+     * @param thirdPartToolJobDTO 作业信息
+     * @param userId              用户id
+     * @return 创建的作业信息
+     * @throws Exception 异常信息
+     */
+    ThirdPartToolJob createThirdPartToolJob(ThirdPartToolJobDTO thirdPartToolJobDTO, Long userId) throws Exception;
+
+    /**
+     * 单独启动验收一个学生的作业
+     *
+     * @param userId 学生的用户id
+     * @param examId 作业id
+     * @throws Exception 异常
+     */
+    void startThirdPartToolJob(Long userId, Long examId) throws Exception;
+
+    /**
+     * 批量启动验收一个学生的作业
+     *
+     * @param examId 作业id
+     * @throws Exception 异常
+     */
+    void batchStartThirdPartToolJob(Long examId) throws Exception;
+
+    /**
+     * 停止验收一个学生的作业
+     * 状态调整为失败
+     *
+     * @param userId 用户id
+     * @param examId 作业id
+     * @throws Exception 异常
+     */
+    void stopThirdPartToolJob(Long userId, Long examId) throws Exception;
+
+    /**
+     * 根据作业id获取该作业所有的提交信息
+     *
+     * @param examId 作业id
+     * @return 所有的提交信息
+     * @throws Exception 异常
+     */
+    List<ThirdPartToolJobVO> getAllThirdPartToolJobsByExamId(Long examId) throws Exception;
+
+    /**
+     * 根据作业id和用户id获取该学生提交的信息
+     *
+     * @param examId 作业id
+     * @param userId 用户id
+     * @return 提交信息
+     * @throws Exception 异常
+     */
+    ThirdPartToolJobVO getThirdPartToolJobByExamIdAndUserId(Long examId, Long userId) throws Exception;
+
+    /**
+     * 根据caseId获取case的testType
+     *
+     * @param caseId caseId
+     * @return testType
+     * @throws Exception 异常
+     */
+    String getCaseTypeByCaseId(Long caseId) throws Exception;
+
+    String getExamTypeByExamId(Long examId) throws Exception;
+
+    /**
+     * 测试
+     *
+     * @param url 测试用
+     * @return 测试用
+     * @throws Exception 异常
+     */
+    Map<String, Object> test(String url) throws Exception;
+
+    /**
+     * 快速创建exam(作业)
+     *
+     * @param exam     exam信息
+     * @param testType testType
+     * @param userId   用户id
+     * @return 创建好的作业信息
+     * @throws Exception 异常
+     */
+    ExamVO saveOrUpdateInspectExam(ExamVO exam, Long testType, Long userId) throws Exception;
+
+    /**
+     * 获取学生成绩的分页信息
+     *
+     * @param examId     examId
+     * @param keyword    keyword
+     * @param needFilter needFilter
+     * @param pageable   pageable
+     * @return 学生成绩的分页信息
+     */
+    Page<ThirdPartToolJobVO> getJobsPageByExamId(Long examId, String keyword, Boolean needFilter, Pageable pageable);
+}

+ 542 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ThirdPartToolLogicImpl.java

@@ -0,0 +1,542 @@
+package cn.iselab.mooctest.site.web.logic.impl;
+
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobTypeConstants;
+import cn.iselab.mooctest.site.data.UserDTOForMT;
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobDTO;
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobVO;
+import cn.iselab.mooctest.site.data.inspect.app.statusResponse.ThirdPartToolAppStatusResponseAppScore;
+import cn.iselab.mooctest.site.data.inspect.app.statusResponse.ThirdPartToolAppStatusResponseCode;
+import cn.iselab.mooctest.site.data.inspect.report.ReportStartResponseStatusConstants;
+import cn.iselab.mooctest.site.data.inspect.web.startCommand.ThirdPartToolWebStartConfig;
+import cn.iselab.mooctest.site.data.inspect.web.statusResponse.ThirdPartToolWebStatusResponseCode;
+import cn.iselab.mooctest.site.data.inspect.web.statusResponse.ThirdPartToolWebStatusResponseDataScoreModel;
+import cn.iselab.mooctest.site.models.*;
+import cn.iselab.mooctest.site.service.*;
+import cn.iselab.mooctest.site.web.data.ExamVO;
+import cn.iselab.mooctest.site.web.data.PaperVO;
+import cn.iselab.mooctest.site.web.logic.ExamLogic;
+import cn.iselab.mooctest.site.web.logic.ThirdPartToolLogic;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author sunjh
+ * @date 2020/2/7 14:57
+ */
+@Configuration      //1.主要用于标记配置类,兼备Component的效果。
+@EnableScheduling   // 2.开启定时任务
+@Service
+@Slf4j
+public class ThirdPartToolLogicImpl implements ThirdPartToolLogic {
+    @Autowired
+    private ThirdPartToolAppService thirdPartToolAppService;
+
+    @Autowired
+    private ThirdPartToolWebService thirdPartToolWebService;
+
+    @Autowired
+    private ThirdPartToolService thirdPartToolService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private CaseService caseService;
+
+    @Autowired
+    private Exam2CaseService exam2CaseService;
+
+    @Autowired
+    private ExamLogic examLogic;
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Override
+    public void handleRunningJob() throws Exception {
+        //获取进行中App任务队列
+        List<Long> appRunningJobIdList = thirdPartToolAppService.getRunningThirdPartToolJobIdList();
+        //获取进行中web任务队列
+        List<Long> webRunningJobIdList = thirdPartToolWebService.getRunningThirdPartToolJobIdList();
+        if (null != appRunningJobIdList) {
+            for (Long jobId : appRunningJobIdList) {
+                try {
+                    Long jobStatus = thirdPartToolAppService.getJobStatus(jobId);
+                    if (null == jobStatus) {
+                        throw new Exception("获取job当前状态信息失败[jobId:" + jobId + "]");
+                    }
+                    if (jobStatus.equals(ThirdPartToolAppStatusResponseCode.FINISH)) {
+                        thirdPartToolService.changeJobStatusToRunFinish(jobId);
+                    } else if (jobStatus.equals(ThirdPartToolAppStatusResponseCode.RUN_FAIL)
+                            || jobStatus.equals(ThirdPartToolAppStatusResponseCode.APK_UNZIP_FAIL)
+                            || jobStatus.equals(ThirdPartToolAppStatusResponseCode.DOWNLOAD_FAIL)) {
+                        thirdPartToolService.changeJobStatusToFail(jobId, "获取job当前状态信息失败[jobId:" + jobId + "]");
+                    }
+                } catch (Exception e) {
+                    log.info("当前job运行失败:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(jobId, "当前job运行失败:" + e.getMessage());
+                }
+            }
+        }
+
+        if (null != webRunningJobIdList) {
+            for (Long taskId : webRunningJobIdList) {
+                try {
+                    Long taskStatus = thirdPartToolWebService.getJobStatus(taskId);
+                    if (null == taskStatus) {
+                        throw new Exception("获取任务当前状态信息失败[taskId:" + taskId + "]");
+                    }
+                    if (taskStatus.equals(ThirdPartToolWebStatusResponseCode.SUCCESS)) {
+                        thirdPartToolService.changeJobStatusToRunFinish(taskId);
+                    } else if (taskStatus.equals(ThirdPartToolWebStatusResponseCode.FAIL)) {
+                        thirdPartToolService.changeJobStatusToFail(taskId, "获取任务当前状态信息失败[taskId:" + taskId + "]");
+                    }
+                } catch (Exception e) {
+                    log.info("当前任务运行失败:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "当前任务运行失败:" + e.getMessage());
+                }
+            }
+        }
+    }
+
+    @Override
+    public void handlePendingJob() throws Exception {
+        //获取进行中的app任务队列
+        List<Long> runningAppTaskIdList = thirdPartToolAppService.getRunningThirdPartToolJobIdList();
+        //获取排队中app任务队列
+        List<Long> pendingAppTaskIdList = thirdPartToolAppService.getPendingThirdPartToolJobIdList();
+
+        //获取进行中的web任务队列
+        List<Long> runningWebTaskIdList = thirdPartToolWebService.getRunningThirdPartToolJobIdList();
+        //获取排队中web任务队列
+        List<Long> pendingWebTaskIdList = thirdPartToolWebService.getPendingThirdPartToolJobIdList();
+
+        //TODO 判断运行中app队列是否已满,未满则启动排队中任务
+        while (!thirdPartToolAppService.isRunningQueueFull(runningAppTaskIdList) && null != pendingAppTaskIdList && !pendingAppTaskIdList.isEmpty()) {
+            Long tempAppTaskId = pendingAppTaskIdList.remove(0);
+            try {
+                thirdPartToolAppService.runJob(tempAppTaskId);
+                thirdPartToolService.changeJobStatusToRunning(tempAppTaskId);
+            } catch (Exception e) {
+                log.info(e.getMessage());
+                thirdPartToolService.changeJobStatusToFail(tempAppTaskId, e.getMessage());
+            }
+            runningAppTaskIdList.add(tempAppTaskId);
+        }
+
+        //TODO 判断运行中web队列是否已满,未满则启动排队中任务
+        while (!thirdPartToolWebService.isRunningQueueFull(runningWebTaskIdList) && null != pendingWebTaskIdList && !pendingWebTaskIdList.isEmpty()) {
+            Long tempWebTaskId = pendingWebTaskIdList.remove(0);
+            try {
+                thirdPartToolWebService.runJob(tempWebTaskId);
+                thirdPartToolService.changeJobStatusToRunning(tempWebTaskId);
+            } catch (Exception e) {
+                log.info(e.getMessage());
+                thirdPartToolService.changeJobStatusToFail(tempWebTaskId, e.getMessage());
+            }
+            runningWebTaskIdList.add(tempWebTaskId);
+        }
+    }
+
+    @Override
+    public void handleRunFinishJob() throws Exception {
+        //获取运行完成的app任务队列
+        List<Long> runFinishAppTaskIdList = thirdPartToolAppService.getRunFinishThirdPartToolJobIdList();
+        //TODO 获取运行完成的web任务队列
+        List<Long> runFinishWebTaskIdList = thirdPartToolWebService.getRunFinishThirdPartToolJobList();
+
+        //TODO app写数据
+        if (null != runFinishAppTaskIdList) {
+            for (Long taskId : runFinishAppTaskIdList) {
+                try {
+                    thirdPartToolService.changeJobStatusToWriteResult(taskId);
+                    thirdPartToolAppService.recordScore(taskId);
+                    thirdPartToolService.changeJobStatusToWriteFinish(taskId);
+                } catch (Exception e) {
+                    log.info("写结果--error[" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "写结果--error[" + taskId + "]:" + e.getMessage());
+                }
+
+            }
+        }
+
+        //TODO web写数据
+        if (null != runFinishWebTaskIdList) {
+            for (Long taskId : runFinishWebTaskIdList) {
+                try {
+                    thirdPartToolService.changeJobStatusToWriteResult(taskId);
+                    thirdPartToolWebService.recordScore(taskId);
+                    thirdPartToolService.changeJobStatusToWriteFinish(taskId);
+                } catch (Exception e) {
+                    log.info("写结果--error[" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "写结果--error[" + taskId + "]:" + e.getMessage());
+                }
+            }
+        }
+    }
+
+    @Override
+    public void handleWriteResultFinishJob() throws Exception {
+        List<Long> writeFinishAppTaskIdList = thirdPartToolAppService.getWriteFinishThirdPartToolJobIdList();
+        List<Long> writeFinishWebTaskIdList = thirdPartToolWebService.getWriteFinishThirdPartToolJobIdList();
+        if (null != writeFinishAppTaskIdList) {
+            for (Long taskId : writeFinishAppTaskIdList) {
+                try {
+                    thirdPartToolService.changeJobStatusToGenReportPending(taskId);
+                } catch (Exception e) {
+                    log.info("启动报告服务--error[taskId:" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "启动报告服务--error[taskId:" + taskId + "]:" + e.getMessage());
+                }
+            }
+        }
+
+        if (null != writeFinishWebTaskIdList) {
+            for (Long taskId : writeFinishWebTaskIdList) {
+                try {
+                    thirdPartToolService.changeJobStatusToGenReportPending(taskId);
+                } catch (Exception e) {
+                    log.info("启动报告服务--error[taskId:" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "启动报告服务--error[taskId:" + taskId + "]:" + e.getMessage());
+                }
+            }
+        }
+    }
+
+    @Override
+    public void handleGenReportPendingJob() throws Exception {
+        List<Long> generateReportPendingAppTaskIdList = thirdPartToolAppService.getGenReportPendingThirdPartToolJobIdList();
+        List<Long> generateReportPendingWebTaskIdList = thirdPartToolWebService.getGenReportPendingThirdPartToolJobIdList();
+
+        if (null != generateReportPendingAppTaskIdList && !generateReportPendingAppTaskIdList.isEmpty()) {
+            for (Long taskId : generateReportPendingAppTaskIdList) {
+                try {
+                    Long status = thirdPartToolAppService.runGenerateReport(taskId);
+                    if (status.equals(ReportStartResponseStatusConstants.SUCCESS)) {
+                        thirdPartToolService.changeJobStatusToGenReportRunning(taskId);
+                    } else {
+                        thirdPartToolService.changeJobStatusToFail(taskId, "启动报告服务失败[taskId:" + taskId + "]");
+                    }
+                } catch (Exception e) {
+                    log.info("启动报告失败[taskId:" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "启动报告服务失败[taskId:" + taskId + "]:" + e.getMessage());
+                }
+            }
+        }
+
+        if (null != generateReportPendingWebTaskIdList && !generateReportPendingWebTaskIdList.isEmpty()) {
+            for (Long taskId : generateReportPendingWebTaskIdList) {
+                try {
+                    Long status = thirdPartToolWebService.runGenerateReport(taskId);
+                    if (status.equals(ReportStartResponseStatusConstants.SUCCESS)) {
+                        thirdPartToolService.changeJobStatusToGenReportRunning(taskId);
+                    } else {
+                        thirdPartToolService.changeJobStatusToFail(taskId, "启动报告失败[taskId:" + taskId + "]:");
+                    }
+                } catch (Exception e) {
+                    log.info("启动报告失败[taskId:" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "启动报告服务失败[taskId:" + taskId + "]:" + e.getMessage());
+                }
+
+            }
+        }
+    }
+
+    @Override
+    public void handleGenReportRunningJob() throws Exception {
+        List<Long> generateReportRunningAppTaskIdList = thirdPartToolAppService.getGenReportRunningThirdPartToolJobIdList();
+        List<Long> generateReportRunningWebTaskIdList = thirdPartToolWebService.getGenReportRunningThirdPartToolJobIdList();
+
+        if (null != generateReportRunningAppTaskIdList && !generateReportRunningAppTaskIdList.isEmpty()) {
+            for (Long taskId : generateReportRunningAppTaskIdList) {
+                try {
+                    Long generateReportStatus = thirdPartToolService.getGenerateStatus(taskId);
+                    if (generateReportStatus.equals(ReportStartResponseStatusConstants.SUCCESS)) {
+                        thirdPartToolService.changeJobStatusToGenReportFinish(taskId);
+                    } else if (generateReportStatus.equals(ReportStartResponseStatusConstants.FAIL)) {
+                        thirdPartToolService.changeJobStatusToFail(taskId, "获取报告信息失败[taskId:" + taskId + "]");
+                    }
+                } catch (Exception e) {
+                    log.info("获取报告信息失败[taskId:" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "获取报告信息失败[taskId:" + taskId + "]:" + e.getMessage());
+                }
+
+            }
+        }
+
+        if (null != generateReportRunningWebTaskIdList && !generateReportRunningWebTaskIdList.isEmpty()) {
+            for (Long taskId : generateReportRunningWebTaskIdList) {
+                try {
+                    Long generateReportStatus = thirdPartToolService.getGenerateStatus(taskId);
+                    if (generateReportStatus.equals(ReportStartResponseStatusConstants.SUCCESS)) {
+                        thirdPartToolService.changeJobStatusToGenReportFinish(taskId);
+                    } else if (generateReportStatus.equals(ReportStartResponseStatusConstants.FAIL)) {
+                        thirdPartToolService.changeJobStatusToFail(taskId, "获取报告信息失败[taskId:" + taskId + "]");
+                    }
+                } catch (Exception e) {
+                    log.info("获取报告信息失败[taskId:" + taskId + "]:" + e.getMessage());
+                    thirdPartToolService.changeJobStatusToFail(taskId, "获取报告信息失败[taskId:" + taskId + "]:" + e.getMessage());
+                }
+            }
+        }
+    }
+
+    @Override
+    public ThirdPartToolJob createThirdPartToolJob(ThirdPartToolJobDTO thirdPartToolJobDTO, Long userId) throws Exception {
+        return thirdPartToolService.create(thirdPartToolJobDTO, thirdPartToolJobDTO.getExamId(), userId);
+    }
+
+    @Override
+    public void startThirdPartToolJob(Long userId, Long examId) throws Exception {
+        thirdPartToolService.startThirdPartToolJob(userId, examId);
+    }
+
+    @Override
+    public void batchStartThirdPartToolJob(Long examId) throws Exception {
+        thirdPartToolService.batchStartThirdPartToolJob(examId);
+    }
+
+    @Override
+    public void stopThirdPartToolJob(Long userId, Long examId) throws Exception {
+
+    }
+
+    @Override
+    public List<ThirdPartToolJobVO> getAllThirdPartToolJobsByExamId(Long examId) throws Exception {
+        List<ThirdPartToolJob> thirdPartToolJobList = thirdPartToolService.getAllThirdPartToolJobsByExamId(examId);
+        List<ThirdPartToolJobVO> thirdPartToolJobVOList = new LinkedList<>();
+        Gson gson = new Gson();
+        for (ThirdPartToolJob thirdPartToolJob : thirdPartToolJobList) {
+            UserDTOForMT user = userService.findByUserId(thirdPartToolJob.getOwnerId());
+            if (null == user) {
+                throw new Exception("this user is not exist:" + thirdPartToolJob.getOwnerId());
+            }
+            ThirdPartToolJobVO thirdPartToolJobVO = new ThirdPartToolJobVO();
+            thirdPartToolJobVO.setUserId(thirdPartToolJob.getOwnerId());
+            thirdPartToolJobVO.setExamId(thirdPartToolJob.getGroupId());
+            thirdPartToolJobVO.setOwnerName(user.getName());
+            thirdPartToolJobVO.setSchool(user.getSchool());
+            thirdPartToolJobVO.setName(thirdPartToolJob.getName());
+            thirdPartToolJobVO.setStatus(thirdPartToolJob.getStatus());
+            thirdPartToolJobVO.setCreateTime(thirdPartToolJob.getCreateTime());
+            thirdPartToolJobVO.setInformation(thirdPartToolJob.getInformation());
+            thirdPartToolJobVO.setType(thirdPartToolJob.getType());
+            thirdPartToolJobVO.setReportUrl(thirdPartToolJob.getReportUrl());
+            thirdPartToolJobVO.setFile(thirdPartToolJob.getFile());
+            if (thirdPartToolJob.getType().equals(ThirdPartToolJobTypeConstants.WEB)) {
+                ThirdPartToolWebStatusResponseDataScoreModel score = gson.fromJson(thirdPartToolJob.getResult(), ThirdPartToolWebStatusResponseDataScoreModel.class);
+                if (null != score) {
+                    JSONObject json = new JSONObject();
+                    json.put("compatibility", score.getCompatibility());
+                    json.put("complexity", score.getComplexity());
+                    json.put("performance", score.getPerformance());
+                    json.put("robustness", score.getRobustness());
+                    json.put("security", score.getSecurity());
+                    thirdPartToolJobVO.setScore(score.getTotalScore());
+                    thirdPartToolJobVO.setResult(json);
+                }
+            } else if (thirdPartToolJob.getType().equals(ThirdPartToolJobTypeConstants.APP)) {
+                ThirdPartToolAppStatusResponseAppScore score = gson.fromJson(thirdPartToolJob.getResult(), ThirdPartToolAppStatusResponseAppScore.class);
+                try {
+                    JSONObject config = JSONObject.fromObject(thirdPartToolJob.getConfig());
+                    thirdPartToolJobVO.setConfig(config);
+                } catch (Exception e) {
+                    thirdPartToolJobVO.setConfig(null);
+                }
+                if (null != score) {
+                    JSONObject json = new JSONObject();
+                    json.put("compatibility", score.getCompatibility());
+                    json.put("complexity", score.getStability());
+                    json.put("performance", score.getPerformance());
+                    json.put("robustness", score.getRobustness());
+                    json.put("security", 0);
+                    thirdPartToolJobVO.setScore(thirdPartToolJob.getScore() == null?"0":thirdPartToolJob.getScore());
+                    thirdPartToolJobVO.setResult(json);
+                }
+            }
+
+            thirdPartToolJobVOList.add(thirdPartToolJobVO);
+
+        }
+        return thirdPartToolJobVOList;
+    }
+
+    @Override
+    public ThirdPartToolJobVO getThirdPartToolJobByExamIdAndUserId(Long examId, Long userId) throws Exception {
+        ThirdPartToolJob thirdPartToolJob = thirdPartToolService.getThirdPartToolJobsByExamIdAndUserId(examId, userId);
+        if (null == thirdPartToolJob) {
+            return null;
+        }
+        UserDTOForMT user = userService.findByUserId(thirdPartToolJob.getOwnerId());
+        if (null == user) {
+            throw new Exception("this user is not exist:" + thirdPartToolJob.getOwnerId());
+        }
+        Gson gson = new Gson();
+        ThirdPartToolJobVO thirdPartToolJobVO = new ThirdPartToolJobVO();
+        thirdPartToolJobVO.setUserId(thirdPartToolJob.getOwnerId());
+        thirdPartToolJobVO.setExamId(thirdPartToolJob.getGroupId());
+        thirdPartToolJobVO.setOwnerName(user.getName());
+        thirdPartToolJobVO.setSchool(user.getSchool());
+        thirdPartToolJobVO.setName(thirdPartToolJob.getName());
+        thirdPartToolJobVO.setUrl(thirdPartToolJob.getUrl());
+        thirdPartToolJobVO.setStatus(thirdPartToolJob.getStatus());
+        thirdPartToolJobVO.setCreateTime(thirdPartToolJob.getCreateTime());
+        thirdPartToolJobVO.setInformation(thirdPartToolJob.getInformation());
+        thirdPartToolJobVO.setType(thirdPartToolJob.getType());
+        thirdPartToolJobVO.setReportUrl(thirdPartToolJob.getReportUrl());
+        thirdPartToolJobVO.setFile(thirdPartToolJob.getFile());
+        if (thirdPartToolJob.getType().equals(ThirdPartToolJobTypeConstants.WEB)) {
+            ThirdPartToolWebStartConfig thirdPartToolWebStartConfig;
+            try {
+                thirdPartToolWebStartConfig = gson.fromJson(thirdPartToolJob.getConfig(), ThirdPartToolWebStartConfig.class);
+            } catch (Exception e) {
+                throw new Exception("[jobs status:pending]fail to get config:" + thirdPartToolJob.getId());
+            }
+            thirdPartToolJobVO.setConfig(thirdPartToolWebStartConfig);
+            ThirdPartToolWebStatusResponseDataScoreModel score = gson.fromJson(thirdPartToolJob.getResult(), ThirdPartToolWebStatusResponseDataScoreModel.class);
+            if (null != score) {
+                JSONObject json = new JSONObject();
+                json.put("compatibility", score.getCompatibility());
+                json.put("complexity", score.getComplexity());
+                json.put("performance", score.getPerformance());
+                json.put("robustness", score.getRobustness());
+                json.put("security", score.getSecurity());
+                thirdPartToolJobVO.setScore(score.getTotalScore());
+                thirdPartToolJobVO.setResult(json);
+            }
+        } else if (thirdPartToolJob.getType().equals(ThirdPartToolJobTypeConstants.APP)) {
+            ThirdPartToolAppStatusResponseAppScore score = gson.fromJson(thirdPartToolJob.getResult(), ThirdPartToolAppStatusResponseAppScore.class);
+            try {
+                JSONObject config = JSONObject.fromObject(thirdPartToolJob.getConfig());
+                thirdPartToolJobVO.setConfig(config);
+            } catch (Exception e) {
+                thirdPartToolJobVO.setConfig(null);
+            }
+            if (null != score) {
+                JSONObject json = new JSONObject();
+                json.put("compatibility", score.getCompatibility());
+                json.put("complexity", score.getStability());
+                json.put("performance", score.getPerformance());
+                json.put("robustness", score.getRobustness());
+                json.put("security", 0);
+                thirdPartToolJobVO.setScore(thirdPartToolJob.getScore() == null?"0":thirdPartToolJob.getScore());
+                thirdPartToolJobVO.setResult(json);
+            }
+        }
+
+        return thirdPartToolJobVO;
+    }
+
+    @Override
+    public String getCaseTypeByCaseId(Long caseId) throws Exception {
+        CaseExtends caseExtends = caseService.getCaseExtendsById(caseId);
+        Map<String, String> properties = new Gson().fromJson(caseExtends.getProperties(), new
+                TypeToken<HashMap<String, String>>() {
+                }.getType());
+        return properties.get("testType");
+    }
+
+    @Override
+    public String getExamTypeByExamId(Long examId) throws Exception {
+        Long caseId = exam2CaseService.getCaseIdsByExamId(examId).get(0);
+        CaseExtends caseExtends = caseService.getCaseExtendsById(caseId);
+        Map<String, String> properties = new Gson().fromJson(caseExtends.getProperties(), new
+                TypeToken<HashMap<String, String>>() {
+                }.getType());
+        return properties.get("testType");
+    }
+
+    @Override
+    public Map<String, Object> test(String url) throws Exception {
+        thirdPartToolAppService.readObjectByUrl(url);
+        return null;
+    }
+
+    @Override
+    public ExamVO saveOrUpdateInspectExam(ExamVO exam, Long testType, Long userId) throws Exception {
+        exam.setStatus(0);
+        //创建target
+        Target savedTarget = thirdPartToolService.createDefaultInspectTarget(userId);
+        //创建case
+        CaseExtends savedCase = thirdPartToolService.createDefaultInspectCase(userId, savedTarget.getId(), testType);
+        //创建paper
+        Paper savedPaper = thirdPartToolService.createDefaultInspectPaper(userId, savedCase.getId());
+        //创建exam
+        ExamVO savedExam = examLogic.createExam(exam);
+        PaperVO paperVO = new PaperVO();
+        //创建exam_2_case;
+        Exam2Case exam2Case = thirdPartToolService.createExam2Case(savedExam.getId(), savedPaper.getId(), savedCase.getId());
+        //创建exam_2_paper;
+        Exam2Paper exam2Paper = thirdPartToolService.createExam2Paper(savedExam.getId(), savedPaper.getId());
+        //创建paper_2_case
+        Paper2Case paper2Case = thirdPartToolService.createPaper2Case(savedPaper.getId(), savedCase.getId());
+        savedExam.setPaperId(savedPaper.getId());
+        return savedExam;
+        //return thirdPartToolService.saveOrUpdateInspectExam(exam, testType, userId);
+    }
+
+    @Override
+    public Page<ThirdPartToolJobVO> getJobsPageByExamId(Long examId, String keyword, Boolean needFilter, Pageable pageable) {
+        Page<ThirdPartToolJob> thirdPartToolJobs = thirdPartToolService.getJobByExamIdPage(examId, keyword, pageable);
+        Page<ThirdPartToolJobVO> thirdPartToolJobVOS = thirdPartToolJobs.map(thirdPartToolJob -> {
+            ThirdPartToolJobVO thirdPartToolJobVO = new ThirdPartToolJobVO();
+            thirdPartToolJobVO.setScore(thirdPartToolJob.getScore());
+            thirdPartToolJobVO.setExamId(thirdPartToolJob.getGroupId());
+            thirdPartToolJobVO.setCreateTime(thirdPartToolJob.getCreateTime());
+            thirdPartToolJobVO.setInformation(thirdPartToolJob.getInformation());
+            thirdPartToolJobVO.setOwnerName(userService.findByUserId(thirdPartToolJob.getOwnerId()).getName());
+
+
+            Gson gson = new Gson();
+            thirdPartToolJobVO.setReportUrl(thirdPartToolJob.getReportUrl());
+            if (thirdPartToolJob.getType().equals(ThirdPartToolJobTypeConstants.WEB)) {
+                ThirdPartToolWebStatusResponseDataScoreModel score = gson.fromJson(thirdPartToolJob.getResult(), ThirdPartToolWebStatusResponseDataScoreModel.class);
+                if (null != score) {
+                    JSONObject json = new JSONObject();
+                    json.put("compatibility", score.getCompatibility());
+                    json.put("complexity", score.getComplexity());
+                    json.put("performance", score.getPerformance());
+                    json.put("robustness", score.getRobustness());
+                    json.put("security", score.getSecurity());
+                    thirdPartToolJobVO.setScore(score.getTotalScore());
+                    thirdPartToolJobVO.setResult(json);
+                }
+            } else if (thirdPartToolJob.getType().equals(ThirdPartToolJobTypeConstants.APP)) {
+                ThirdPartToolAppStatusResponseAppScore score = gson.fromJson(thirdPartToolJob.getResult(), ThirdPartToolAppStatusResponseAppScore.class);
+                if (null != score) {
+                    JSONObject json = new JSONObject();
+                    json.put("compatibility", score.getCompatibility());
+                    json.put("complexity", score.getStability());
+                    json.put("performance", score.getPerformance());
+                    json.put("robustness", score.getRobustness());
+                    json.put("security", 0);
+                    thirdPartToolJobVO.setScore(thirdPartToolJob.getScore() == null?"0":thirdPartToolJob.getScore());
+                    thirdPartToolJobVO.setResult(json);
+                }
+            }
+
+
+            thirdPartToolJobVO.setSchool(userService.findByUserId(thirdPartToolJob.getOwnerId()).getSchool());
+            thirdPartToolJobVO.setStatus(thirdPartToolJob.getStatus());
+            thirdPartToolJobVO.setType(thirdPartToolJob.getType());
+            thirdPartToolJobVO.setStartTime(thirdPartToolJob.getStartTime());
+            thirdPartToolJobVO.setName(thirdPartToolJob.getName());
+            thirdPartToolJobVO.setUserId(thirdPartToolJob.getOwnerId());
+            return thirdPartToolJobVO;
+        });
+        return thirdPartToolJobVOS;
+    }
+}

+ 146 - 0
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolAppServiceImplTest.java

@@ -0,0 +1,146 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.dao.Exam2CaseDao;
+import cn.iselab.mooctest.site.dao.ExamDao;
+import cn.iselab.mooctest.site.dao.ThirdPartToolJobDao;
+import cn.iselab.mooctest.site.data.inspect.web.statusResponse.ThirdPartToolWebStatusResponseDataScoreModel;
+import cn.iselab.mooctest.site.models.Exam;
+import cn.iselab.mooctest.site.models.Exam2Case;
+import cn.iselab.mooctest.site.models.SubmitRecord;
+import cn.iselab.mooctest.site.models.ThirdPartToolJob;
+import cn.iselab.mooctest.site.service.SubmitRecordService;
+import cn.iselab.mooctest.site.service.ThirdPartToolAppService;
+import cn.iselab.mooctest.site.service.UserService;
+import net.sf.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author sunjh
+ * @date 2020/3/17 15:12
+ */
+public class ThirdPartToolAppServiceImplTest {
+    @InjectMocks
+    private ThirdPartToolAppServiceImpl thirdPartToolAppService = new ThirdPartToolAppServiceImpl();
+
+    @Mock
+    private ThirdPartToolJobDao thirdPartToolJobDao;
+
+    @Mock
+    private SubmitRecordService submitRecordService;
+
+    @Mock
+    private UserService userService;
+
+    @Mock
+    private Exam2CaseDao exam2CaseDao;
+
+    @Mock
+    private ExamDao examDao;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void should_return_list_when_get_running_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolAppService.getRunningThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_pending_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolAppService.getPendingThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_run_finish_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolAppService.getRunFinishThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_write_finish_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolAppService.getWriteFinishThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_gen_report_pending_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolAppService.getGenReportPendingThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_gen_report_running_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolAppService.getGenReportRunningThirdPartToolJobIdList();
+    }
+
+//    @Test
+//    public void should_return_void_when_record_score() throws Exception{
+//        JSONObject extra = new JSONObject();
+//        JSONObject result = new JSONObject();
+//        extra.put("downloadUrl","http://www.mooctest.net");
+//        result.put("appScore","90.0");
+//        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+//        thirdPartToolJob.setExtra(extra.toString());
+//        thirdPartToolJob.setResult(JSONObject.fromObject(new ThirdPartToolWebStatusResponseDataScoreModel()).toString());
+//        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(thirdPartToolJob);
+//        when(thirdPartToolAppServices.readObjectByUrl(anyString())).thenReturn(result);
+//        thirdPartToolAppService.recordScore(111L);
+//    }
+
+    @Test
+    public void should_return_void_when_create_submit_record() throws Exception{
+        SubmitRecord expectSubmitRecord = new SubmitRecord();
+        Exam expectExam = new Exam();
+        JSONObject expectScore = new JSONObject();
+        expectScore.put("robustness","90.2");
+        List<Exam2Case> expectExam2CaseList = new LinkedList<>();
+        Exam2Case expectExam2Case = new Exam2Case();
+        expectExam2Case.setCaseId(111L);
+        expectExam2CaseList.add(expectExam2Case);
+        when(submitRecordService.getAssignedTask(anyLong(),anyLong())).thenReturn(expectSubmitRecord);
+        when(examDao.findOne(anyLong())).thenReturn(expectExam);
+        when(exam2CaseDao.findByExamId(anyLong())).thenReturn(expectExam2CaseList);
+        when(submitRecordService.saveOrUpdateAssignedTask(any(SubmitRecord.class))).thenReturn(null);
+        thirdPartToolAppService.createSubmitRecord(anyLong(),anyLong(),expectScore);
+    }
+}

+ 239 - 0
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolServiceImplTest.java

@@ -0,0 +1,239 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.common.constant.ThirdPartToolJobStatusConstants;
+import cn.iselab.mooctest.site.dao.Exam2CaseDao;
+import cn.iselab.mooctest.site.dao.Exam2PaperDao;
+import cn.iselab.mooctest.site.dao.Paper2CaseDao;
+import cn.iselab.mooctest.site.dao.ThirdPartToolJobDao;
+import cn.iselab.mooctest.site.data.inspect.ThirdPartToolJobDTO;
+import cn.iselab.mooctest.site.models.*;
+import cn.iselab.mooctest.site.service.*;
+import cn.iselab.mooctest.site.service.instancePermission.CasePermissionService;
+import cn.iselab.mooctest.site.service.instancePermission.PaperPermissionService;
+import cn.iselab.mooctest.site.service.instancePermission.TargetPermissionService;
+import cn.iselab.mooctest.site.web.data.forMongo.CaseGraphDTO;
+import cn.iselab.mooctest.site.web.data.forMongo.TargetGraphDTO;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author sunjh
+ * @date 2020/3/17 11:25
+ */
+public class ThirdPartToolServiceImplTest {
+    @InjectMocks
+    private ThirdPartToolServiceImpl thirdPartToolService = new ThirdPartToolServiceImpl();
+
+    @Mock
+    private ThirdPartToolJobDao thirdPartToolJobDao;
+
+    @Mock
+    private SubmitRecordService submitRecordService;
+
+    @Mock
+    private ThirdPartToolWebService thirdPartToolWebService;
+
+    @Mock
+    private ThirdPartToolAppService thirdPartToolAppService;
+
+    @Mock
+    private TargetService targetService;
+
+    @Mock
+    private TargetPermissionService targetPermissionService;
+
+    @Mock
+    private TargetGraphService targetGraphService;
+
+    @Mock
+    private CaseService caseService;
+
+    @Mock
+    private CaseGraphService caseGraphService;
+
+    @Mock
+    private CasePermissionService casePermissionService;
+
+    @Mock
+    private PaperService paperService;
+
+    @Mock
+    private PaperPermissionService paperPermissionService;
+
+    @Mock
+    private Exam2CaseDao exam2CaseDao;
+
+    @Mock
+    private Exam2PaperDao exam2PaperDao;
+
+    @Mock
+    private Paper2CaseDao paper2CaseDao;
+
+    @Mock
+    private AnswerWay2CaseBlockService answerWay2CaseBlockService;
+
+    ThirdPartToolJob expectJob;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        expectJob = new ThirdPartToolJob();
+        expectJob.setId(100L);
+    }
+
+    @Test
+    public void should_return_third_part_tool_job_when_create() throws Exception {
+        when(thirdPartToolJobDao.findThirdPartToolJobByOwnerIdAndGroupId(anyLong(), anyLong())).thenReturn(null);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        ThirdPartToolJobDTO thirdPartToolJobDTO = new ThirdPartToolJobDTO();
+        ThirdPartToolJob tmpJob = thirdPartToolService.create(thirdPartToolJobDTO, 111L, 111L);
+        Assert.assertEquals(tmpJob.getId(), expectJob.getId());
+    }
+
+    @Test
+    public void should_return_void_when_start_job() throws Exception {
+        when(thirdPartToolJobDao.findThirdPartToolJobByOwnerIdAndGroupId(anyLong(), anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.startThirdPartToolJob(111L, 111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_fail() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToFail(111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_fail_with_message() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToFail(111L, "nothing");
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_running() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToRunning(111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_run_finish() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToRunFinish(111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_write_result() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToWriteResult(111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_write_finish() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToWriteFinish(111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_report_pending() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToGenReportPending(111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_gen_report_running() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToGenReportRunning(111L);
+    }
+
+    @Test
+    public void should_return_void_when_change_job_status_to_gen_report_finish() throws Exception {
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(expectJob);
+        when(thirdPartToolJobDao.save(any(ThirdPartToolJob.class))).thenReturn(expectJob);
+        thirdPartToolService.changeJobStatusToGenReportFinish(111L);
+    }
+
+    @Test
+    public void should_return_void_when_batch_start_job() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        expectJob.setStatus(ThirdPartToolJobStatusConstants.CREATE);
+        expectList.add(expectJob);
+        when(thirdPartToolJobDao.findByGroupId(anyLong())).thenReturn(expectList);
+        when(thirdPartToolJobDao.saveAll(anyListOf(ThirdPartToolJob.class))).thenReturn(null);
+        thirdPartToolService.batchStartThirdPartToolJob(111L);
+    }
+
+    @Test
+    public void should_return_void_when_submit() throws Exception {
+        when(submitRecordService.saveOrUpdateAssignedTask(any(SubmitRecord.class))).thenReturn(null);
+        thirdPartToolService.submit(111L, 111L);
+    }
+
+    @Test
+    public void should_return_target_when_create_default_target() throws Exception {
+        Target expectTarget = new Target();
+        expectTarget.setId(111L);
+        when(targetService.createTarget(any(Target.class))).thenReturn(expectTarget);
+        when(targetPermissionService.uploadApp(anyLong(), anyLong())).thenReturn(null);
+        thirdPartToolService.createDefaultInspectTarget(111L);
+    }
+
+    @Test
+    public void should_return_case_when_create_default_case() throws Exception {
+        CaseExtends expectCaseExtends = new CaseExtends();
+        expectCaseExtends.setId(111L);
+        when(caseService.save(any(CaseExtends.class))).thenReturn(expectCaseExtends);
+        when(targetGraphService.getTargetGraph(anyLong())).thenReturn(new TargetGraphDTO());
+        when(caseGraphService.saveCaseGraph(any(CaseGraphDTO.class))).thenReturn(null);
+        when(casePermissionService.createCase(anyLong(), anyLong())).thenReturn(null);
+        when(casePermissionService.publishCase(anyLong())).thenReturn(null);
+        thirdPartToolService.createDefaultInspectCase(111L, 111L, 18L);
+    }
+
+    @Test
+    public void should_return_void_when_create_default_inspect_paper() throws Exception {
+        Paper expectPaper = new Paper();
+        expectPaper.setId(111L);
+        when(paperService.createOrUpdate(any(Paper.class))).thenReturn(expectPaper);
+        when(paperPermissionService.createPaper(anyLong(), anyLong())).thenReturn(null);
+        when(paperPermissionService.publishPaper(anyLong())).thenReturn(null);
+        thirdPartToolService.createDefaultInspectPaper(111L, 111L);
+    }
+
+    @Test
+    public void should_return_exam_2_case_when_create_exam_2_case() throws Exception {
+        when(answerWay2CaseBlockService.findByCaseId(anyLong())).thenReturn(new AnswerWay2CaseBlock());
+        when(exam2CaseDao.save(any(Exam2Case.class))).thenReturn(null);
+        thirdPartToolService.createExam2Case(111L, 111L, 11L);
+    }
+
+    @Test
+    public void should_return_exam_2_paper_when_create_exam_2_paper() throws Exception {
+        when(exam2PaperDao.save(any(Exam2Paper.class))).thenReturn(null);
+        thirdPartToolService.createExam2Paper(111L, 111L);
+    }
+
+    @Test
+    public void should_return_paper_2_case_when_create_paper_2_case() throws Exception{
+        when(paper2CaseDao.save(any(Paper2Case.class))).thenReturn(null);
+        thirdPartToolService.createPaper2Case(111L,111L);
+    }
+
+}

+ 137 - 0
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/ThirdPartToolWebServiceImplTest.java

@@ -0,0 +1,137 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.dao.Exam2CaseDao;
+import cn.iselab.mooctest.site.dao.ExamDao;
+import cn.iselab.mooctest.site.dao.ThirdPartToolJobDao;
+import cn.iselab.mooctest.site.data.inspect.web.statusResponse.ThirdPartToolWebStatusResponseDataScoreModel;
+import cn.iselab.mooctest.site.models.Exam;
+import cn.iselab.mooctest.site.models.Exam2Case;
+import cn.iselab.mooctest.site.models.SubmitRecord;
+import cn.iselab.mooctest.site.models.ThirdPartToolJob;
+import cn.iselab.mooctest.site.service.SubmitRecordService;
+import cn.iselab.mooctest.site.service.UserService;
+import net.sf.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author sunjh
+ * @date 2020/3/17 12:51
+ */
+public class ThirdPartToolWebServiceImplTest {
+    @InjectMocks
+    private ThirdPartToolWebServiceImpl thirdPartToolWebService = new ThirdPartToolWebServiceImpl();
+
+    @Mock
+    private ThirdPartToolJobDao thirdPartToolJobDao;
+
+    @Mock
+    private SubmitRecordService submitRecordService;
+
+    @Mock
+    private UserService userService;
+
+    @Mock
+    private Exam2CaseDao exam2CaseDao;
+
+    @Mock
+    private ExamDao examDao;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void should_return_list_when_get_running_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolWebService.getRunningThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_pending_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolWebService.getPendingThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_run_finish_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolWebService.getRunFinishThirdPartToolJobList();
+    }
+
+    @Test
+    public void should_return_list_when_get_write_finish_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolWebService.getWriteFinishThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_gen_report_pending_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolWebService.getGenReportPendingThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_list_when_get_gen_report_running_job_id_list() throws Exception {
+        List<ThirdPartToolJob> expectList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setId(111L);
+        expectList.add(thirdPartToolJob);
+        when(thirdPartToolJobDao.findByStatusAndTypeOrderByStartTime(anyInt(), anyString())).thenReturn(expectList);
+        thirdPartToolWebService.getGenReportRunningThirdPartToolJobIdList();
+    }
+
+    @Test
+    public void should_return_void_when_record_score() throws Exception{
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        thirdPartToolJob.setResult(JSONObject.fromObject(new ThirdPartToolWebStatusResponseDataScoreModel()).toString());
+        when(thirdPartToolJobDao.findOne(anyLong())).thenReturn(new ThirdPartToolJob());
+    }
+
+    @Test
+    public void should_return_void_when_create_submit_record() throws Exception{
+        SubmitRecord expectSubmitRecord = new SubmitRecord();
+        Exam expectExam = new Exam();
+        JSONObject expectScore = new JSONObject();
+        expectScore.put("totalScore","90.2");
+        List<Exam2Case> expectExam2CaseList = new LinkedList<>();
+        Exam2Case expectExam2Case = new Exam2Case();
+        expectExam2Case.setCaseId(111L);
+        expectExam2CaseList.add(expectExam2Case);
+        when(submitRecordService.getAssignedTask(anyLong(),anyLong())).thenReturn(expectSubmitRecord);
+        when(examDao.findOne(anyLong())).thenReturn(expectExam);
+        when(exam2CaseDao.findByExamId(anyLong())).thenReturn(expectExam2CaseList);
+        when(submitRecordService.saveOrUpdateAssignedTask(any(SubmitRecord.class))).thenReturn(null);
+        thirdPartToolWebService.createSubmitRecord(anyLong(),anyLong(),expectScore);
+    }
+}

+ 428 - 0
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/logic/impl/ThirdPartToolLogicImplTest.java

@@ -0,0 +1,428 @@
+package cn.iselab.mooctest.site.web.logic.impl;
+
+import cn.iselab.mooctest.site.data.UserDTOForMT;
+import cn.iselab.mooctest.site.models.*;
+import cn.iselab.mooctest.site.service.*;
+import cn.iselab.mooctest.site.web.data.ExamVO;
+import cn.iselab.mooctest.site.web.logic.ExamLogic;
+import net.sf.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Function;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author sunjh
+ * @date 2020/3/17 15:28
+ */
+public class ThirdPartToolLogicImplTest {
+    @InjectMocks
+    private ThirdPartToolLogicImpl thirdPartToolLogic = new ThirdPartToolLogicImpl();
+
+    @Mock
+    private ThirdPartToolAppService thirdPartToolAppService;
+
+    @Mock
+    private ThirdPartToolWebService thirdPartToolWebService;
+
+    @Mock
+    private ThirdPartToolService thirdPartToolService;
+
+    @Mock
+    private UserService userService;
+
+    @Mock
+    private CaseService caseService;
+
+    @Mock
+    private Exam2CaseService exam2CaseService;
+
+    @Mock
+    private ExamLogic examLogic;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_void_when_handle_running_job() throws Exception {
+        List<Long> expectList = new LinkedList<>();
+        expectList.add(11111L);
+        when(thirdPartToolAppService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolWebService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolAppService.getJobStatus(anyLong())).thenReturn(3L);
+        when(thirdPartToolWebService.getJobStatus(anyLong())).thenReturn(3L);
+        thirdPartToolLogic.handleRunningJob();
+    }
+
+    @Test
+    public void should_return_void_when_handle_running_job_fail() throws Exception {
+        List<Long> expectList = new LinkedList<>();
+        expectList.add(11111L);
+        when(thirdPartToolAppService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolWebService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolAppService.getJobStatus(anyLong())).thenReturn(4L);
+        when(thirdPartToolWebService.getJobStatus(anyLong())).thenReturn(4L);
+        thirdPartToolLogic.handleRunningJob();
+    }
+
+    @Test
+    public void should_return_void_when_handle_running_job_null() throws Exception {
+        List<Long> expectList = new LinkedList<>();
+        expectList.add(11111L);
+        when(thirdPartToolAppService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolWebService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolAppService.getJobStatus(anyLong())).thenReturn(null);
+        when(thirdPartToolWebService.getJobStatus(anyLong())).thenReturn(null);
+        thirdPartToolLogic.handleRunningJob();
+    }
+
+    @Test
+    public void should_return_void_when_handle_running_job_exception() throws Exception {
+        List<Long> expectList = new LinkedList<>();
+        expectList.add(11111L);
+        when(thirdPartToolAppService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolWebService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolAppService.getJobStatus(anyLong())).thenThrow(new Exception());
+        when(thirdPartToolWebService.getJobStatus(anyLong())).thenThrow(new Exception());
+        thirdPartToolLogic.handleRunningJob();
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_void_when_handle_pending_job() throws Exception {
+        List<Long> expectList = new LinkedList<>();
+        expectList.add(11111L);
+        List<Long> expectPendingList = new LinkedList<>();
+        expectPendingList.add(11111L);
+
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectAppPendingList = new LinkedList<>();
+        expectAppPendingList.add(11111L);
+
+        when(thirdPartToolAppService.getRunningThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getRunningThirdPartToolJobIdList()).thenReturn(expectList);
+        when(thirdPartToolAppService.getPendingThirdPartToolJobIdList()).thenReturn(expectAppPendingList);
+        when(thirdPartToolWebService.getPendingThirdPartToolJobIdList()).thenReturn(expectPendingList);
+
+        when(thirdPartToolAppService.isRunningQueueFull(anyListOf(Long.class))).thenReturn(false);
+        when(thirdPartToolWebService.isRunningQueueFull(anyListOf(Long.class))).thenReturn(false);
+
+        when(thirdPartToolAppService.getJobStatus(anyLong())).thenReturn(3L);
+        when(thirdPartToolWebService.getJobStatus(anyLong())).thenReturn(3L);
+
+        thirdPartToolLogic.handlePendingJob();
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_void_when_handle_run_finish_job() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getRunFinishThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getRunFinishThirdPartToolJobList()).thenReturn(expectWebList);
+        thirdPartToolLogic.handleRunFinishJob();
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_void_when_handle_write_result_finish_job() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getWriteFinishThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getWriteFinishThirdPartToolJobIdList()).thenReturn(expectWebList);
+        thirdPartToolLogic.handleWriteResultFinishJob();
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_void_when_handle_gen_report_pending_job() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getGenReportPendingThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getGenReportPendingThirdPartToolJobIdList()).thenReturn(expectWebList);
+        when(thirdPartToolAppService.runGenerateReport(anyLong())).thenReturn(0L);
+        when(thirdPartToolWebService.runGenerateReport(anyLong())).thenReturn(0L);
+        thirdPartToolLogic.handleGenReportPendingJob();
+    }
+
+    @Test
+    public void should_return_void_when_handle_gen_report_pending_job_fail() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getGenReportPendingThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getGenReportPendingThirdPartToolJobIdList()).thenReturn(expectWebList);
+        when(thirdPartToolAppService.runGenerateReport(anyLong())).thenReturn(-1L);
+        when(thirdPartToolWebService.runGenerateReport(anyLong())).thenReturn(-1L);
+        thirdPartToolLogic.handleGenReportPendingJob();
+    }
+
+    @Test
+    public void should_return_void_when_handle_gen_report_pending_job_error() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getGenReportPendingThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getGenReportPendingThirdPartToolJobIdList()).thenReturn(expectWebList);
+        when(thirdPartToolAppService.runGenerateReport(anyLong())).thenThrow(new Exception());
+        when(thirdPartToolWebService.runGenerateReport(anyLong())).thenThrow(new Exception());
+        thirdPartToolLogic.handleGenReportPendingJob();
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_void_when_handle_gen_report_running_job() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getGenReportRunningThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getGenReportRunningThirdPartToolJobIdList()).thenReturn(expectWebList);
+        when(thirdPartToolService.getGenerateStatus(anyLong())).thenReturn(0L);
+        thirdPartToolLogic.handleGenReportRunningJob();
+    }
+
+    @Test
+    public void should_return_void_when_handle_gen_report_running_job_fail() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getGenReportRunningThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getGenReportRunningThirdPartToolJobIdList()).thenReturn(expectWebList);
+        when(thirdPartToolService.getGenerateStatus(anyLong())).thenReturn(-1L);
+        thirdPartToolLogic.handleGenReportRunningJob();
+    }
+
+    @Test
+    public void should_return_void_when_handle_gen_report_running_job_error() throws Exception {
+        List<Long> expectAppList = new LinkedList<>();
+        expectAppList.add(11111L);
+        List<Long> expectWebList = new LinkedList<>();
+        expectWebList.add(11111L);
+        when(thirdPartToolAppService.getGenReportRunningThirdPartToolJobIdList()).thenReturn(expectAppList);
+        when(thirdPartToolWebService.getGenReportRunningThirdPartToolJobIdList()).thenReturn(expectWebList);
+        when(thirdPartToolService.getGenerateStatus(anyLong())).thenThrow(new Exception());
+        thirdPartToolLogic.handleGenReportRunningJob();
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_list_when_get_jobs_by_exam_id() throws Exception {
+        List<ThirdPartToolJob> thirdPartToolJobList = new LinkedList<>();
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        ThirdPartToolJob thirdPartToolAppJob = new ThirdPartToolJob();
+        thirdPartToolJob.setType("web");
+        thirdPartToolAppJob.setType("app");
+        JSONObject json = new JSONObject();
+        json.put("compatibility", "95");
+        json.put("complexity", "90");
+        json.put("performance", "87");
+        json.put("robustness", "80");
+        json.put("security", "83");
+        json.put("totalScore", "90");
+        thirdPartToolAppJob.setResult(json.toString());
+        thirdPartToolJob.setResult(json.toString());
+        thirdPartToolJobList.add(thirdPartToolJob);
+        thirdPartToolJobList.add(thirdPartToolAppJob);
+
+        UserDTOForMT expectUser = new UserDTOForMT();
+        when(thirdPartToolService.getAllThirdPartToolJobsByExamId(anyLong())).thenReturn(thirdPartToolJobList);
+        when(userService.findByUserId(anyLong())).thenReturn(expectUser);
+        thirdPartToolLogic.getAllThirdPartToolJobsByExamId(111L);
+    }
+
+    //********************************************************************************************
+    @Test
+    public void should_return_job_vo_when_get_job_by_exam_id_and_user_id() throws Exception {
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        JSONObject json = new JSONObject();
+        json.put("compatibility", "95");
+        json.put("complexity", "90");
+        json.put("performance", "87");
+        json.put("robustness", "80");
+        json.put("security", "83");
+        json.put("totalScore", "90");
+        thirdPartToolJob.setType("web");
+        thirdPartToolJob.setResult(json.toString());
+        UserDTOForMT expectUser = new UserDTOForMT();
+        when(thirdPartToolService.getThirdPartToolJobsByExamIdAndUserId(anyLong(), anyLong())).thenReturn(thirdPartToolJob);
+        when(userService.findByUserId(anyLong())).thenReturn(expectUser);
+        thirdPartToolLogic.getThirdPartToolJobByExamIdAndUserId(111L, 111L);
+
+    }
+
+    @Test
+    public void should_return_job_vo_when_get_job_by_exam_id_and_user_id_app() throws Exception {
+        ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+        JSONObject json = new JSONObject();
+        json.put("compatibility", "95");
+        json.put("complexity", "90");
+        json.put("performance", "87");
+        json.put("robustness", "80");
+        json.put("security", "83");
+        json.put("totalScore", "90");
+        thirdPartToolJob.setType("app");
+        thirdPartToolJob.setResult(json.toString());
+        UserDTOForMT expectUser = new UserDTOForMT();
+        when(thirdPartToolService.getThirdPartToolJobsByExamIdAndUserId(anyLong(), anyLong())).thenReturn(thirdPartToolJob);
+        when(userService.findByUserId(anyLong())).thenReturn(expectUser);
+        thirdPartToolLogic.getThirdPartToolJobByExamIdAndUserId(111L, 111L);
+
+    }
+    //*******************************************************************************************
+
+    @Test
+    public void should_return_exam_when_save_or_update_inspect_exam() throws Exception {
+        Target expectTarget = new Target();
+        expectTarget.setId(111L);
+        CaseExtends expectCase = new CaseExtends();
+        expectCase.setId(111L);
+        Paper expectPaper = new Paper();
+        expectPaper.setId(111L);
+        ExamVO expectExamVO = new ExamVO();
+        expectExamVO.setId(111L);
+        Exam2Case expectExam2Case = new Exam2Case();
+        Exam2Paper expectExam2Paper = new Exam2Paper();
+        Paper2Case expectPaper2Case = new Paper2Case();
+        when(thirdPartToolService.createDefaultInspectTarget(anyLong())).thenReturn(expectTarget);
+        when(thirdPartToolService.createDefaultInspectCase(anyLong(), anyLong(), anyLong())).thenReturn(expectCase);
+        when(thirdPartToolService.createDefaultInspectPaper(anyLong(), anyLong())).thenReturn(expectPaper);
+        when(examLogic.createExam(any(ExamVO.class))).thenReturn(expectExamVO);
+        when(thirdPartToolService.createExam2Case(anyLong(), anyLong(), anyLong())).thenReturn(expectExam2Case);
+        when(thirdPartToolService.createExam2Paper(anyLong(), anyLong())).thenReturn(expectExam2Paper);
+        when(thirdPartToolService.createPaper2Case(anyLong(), anyLong())).thenReturn(expectPaper2Case);
+        thirdPartToolLogic.saveOrUpdateInspectExam(new ExamVO(), 111L, 111L);
+
+    }
+    //*******************************************************************************************
+    @Test
+    public void should_return_page_when_get_page_by_exam_id() throws Exception{
+        Page<ThirdPartToolJob> page = new Page<ThirdPartToolJob>() {
+            @Override
+            public int getTotalPages() {
+                return 1;
+            }
+
+            @Override
+            public long getTotalElements() {
+                return 2;
+            }
+
+            @Override
+            public <U> Page<U> map(Function<? super ThirdPartToolJob, ? extends U> function) {
+                return null;
+            }
+
+            @Override
+            public int getNumber() {
+                return 2;
+            }
+
+            @Override
+            public int getSize() {
+                return 2;
+            }
+
+            @Override
+            public int getNumberOfElements() {
+                return 2;
+            }
+
+            @Override
+            public List<ThirdPartToolJob> getContent() {
+                List<ThirdPartToolJob> thirdPartToolJobList = new LinkedList<>();
+                ThirdPartToolJob thirdPartToolJob = new ThirdPartToolJob();
+                ThirdPartToolJob thirdPartToolAppJob = new ThirdPartToolJob();
+                thirdPartToolJob.setType("web");
+                thirdPartToolAppJob.setType("app");
+                JSONObject json = new JSONObject();
+                json.put("compatibility", "95");
+                json.put("complexity", "90");
+                json.put("performance", "87");
+                json.put("robustness", "80");
+                json.put("security", "83");
+                json.put("totalScore", "90");
+                thirdPartToolAppJob.setResult(json.toString());
+                thirdPartToolJob.setResult(json.toString());
+
+                thirdPartToolJobList.add(thirdPartToolAppJob);
+                thirdPartToolJobList.add(thirdPartToolJob);
+                return thirdPartToolJobList;
+            }
+
+            @Override
+            public boolean hasContent() {
+                return false;
+            }
+
+            @Override
+            public Sort getSort() {
+                return null;
+            }
+
+            @Override
+            public boolean isFirst() {
+                return false;
+            }
+
+            @Override
+            public boolean isLast() {
+                return false;
+            }
+
+            @Override
+            public boolean hasNext() {
+                return false;
+            }
+
+            @Override
+            public boolean hasPrevious() {
+                return false;
+            }
+
+            @Override
+            public Pageable nextPageable() {
+                return null;
+            }
+
+            @Override
+            public Pageable previousPageable() {
+                return null;
+            }
+
+            @Override
+            public Iterator<ThirdPartToolJob> iterator() {
+                return null;
+            }
+        };
+        UserDTOForMT expectUser = new UserDTOForMT();
+        when(userService.findByUserId(anyLong())).thenReturn(expectUser);
+        when(thirdPartToolService.getJobByExamIdPage(anyLong(),anyString(),any(Pageable.class))).thenReturn(page);
+        thirdPartToolLogic.getJobsPageByExamId(111L,"",false,null);
+    }
+}