Ver código fonte

补提交漏掉的文件

linyk 2 anos atrás
pai
commit
2eff7e8541
54 arquivos alterados com 2439 adições e 0 exclusões
  1. 27 0
      core/src/main/java/com/mooctest/crowd/domain/command/TaskAmountSaveCommand.java
  2. 12 0
      core/src/main/java/com/mooctest/crowd/domain/dao/BgrhPostDataResultDao.java
  3. 13 0
      core/src/main/java/com/mooctest/crowd/domain/dao/CpzlPostDataResultDao.java
  4. 12 0
      core/src/main/java/com/mooctest/crowd/domain/dao/FwzlPostDataResultDao.java
  5. 22 0
      core/src/main/java/com/mooctest/crowd/domain/dao/MixDefectDao.java
  6. 23 0
      core/src/main/java/com/mooctest/crowd/domain/dao/ProjectTypeCountDao.java
  7. 13 0
      core/src/main/java/com/mooctest/crowd/domain/dao/RypgPostDataResultDao.java
  8. 12 0
      core/src/main/java/com/mooctest/crowd/domain/dao/TaskAmountDao.java
  9. 12 0
      core/src/main/java/com/mooctest/crowd/domain/dao/TesterCertDao.java
  10. 2 0
      core/src/main/java/com/mooctest/crowd/domain/domainobject/Defect.java
  11. 9 0
      core/src/main/java/com/mooctest/crowd/domain/domainobject/ProjectTypeCount.java
  12. 13 0
      core/src/main/java/com/mooctest/crowd/domain/domainobject/TaskAmount.java
  13. 14 0
      core/src/main/java/com/mooctest/crowd/domain/domainobject/TesterCert.java
  14. 31 0
      core/src/main/java/com/mooctest/crowd/domain/model/BgrhPostDataResultPO.java
  15. 26 0
      core/src/main/java/com/mooctest/crowd/domain/model/CpzlPostDataResultPO.java
  16. 2 0
      core/src/main/java/com/mooctest/crowd/domain/model/DefectPO.java
  17. 26 0
      core/src/main/java/com/mooctest/crowd/domain/model/FwzlPostDataResultPO.java
  18. 25 0
      core/src/main/java/com/mooctest/crowd/domain/model/MixDefectPO.java
  19. 21 0
      core/src/main/java/com/mooctest/crowd/domain/model/ProjectTypeCountPO.java
  20. 26 0
      core/src/main/java/com/mooctest/crowd/domain/model/RypgPostDataResultPO.java
  21. 29 0
      core/src/main/java/com/mooctest/crowd/domain/model/TaskAmountPO.java
  22. 26 0
      core/src/main/java/com/mooctest/crowd/domain/model/TesterCertPO.java
  23. 15 0
      core/src/main/java/com/mooctest/crowd/domain/util/JsonUtils.java
  24. 11 0
      site/src/main/java/com/mooctest/crowd/site/command/MixDefectSaveCommand.java
  25. 37 0
      site/src/main/java/com/mooctest/crowd/site/controller/CpzlController.java
  26. 36 0
      site/src/main/java/com/mooctest/crowd/site/controller/FwzlController.java
  27. 302 0
      site/src/main/java/com/mooctest/crowd/site/controller/MixDefectController.java
  28. 58 0
      site/src/main/java/com/mooctest/crowd/site/controller/ProjectTypeCountController.java
  29. 37 0
      site/src/main/java/com/mooctest/crowd/site/controller/QrCodeController.java
  30. 37 0
      site/src/main/java/com/mooctest/crowd/site/controller/RypgController.java
  31. 23 0
      site/src/main/java/com/mooctest/crowd/site/controller/StatisticsController.java
  32. 38 0
      site/src/main/java/com/mooctest/crowd/site/controller/TaskAmountController.java
  33. 60 0
      site/src/main/java/com/mooctest/crowd/site/controller/TesterCertController.java
  34. 103 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/BaseProjectMoreInfoVO.java
  35. 13 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/MixDefectInfoVO.java
  36. 11 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/ProjectMoreInfoVO.java
  37. 48 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/StatisticsVO.java
  38. 62 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/TaskAmountVO.java
  39. 14 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/TesterCertVO.java
  40. 8 0
      site/src/main/java/com/mooctest/crowd/site/service/CpzlPostDataResultService.java
  41. 8 0
      site/src/main/java/com/mooctest/crowd/site/service/FwzlPostDataResultService.java
  42. 19 0
      site/src/main/java/com/mooctest/crowd/site/service/MixDefectService.java
  43. 8 0
      site/src/main/java/com/mooctest/crowd/site/service/RypgPostDataResultService.java
  44. 7 0
      site/src/main/java/com/mooctest/crowd/site/service/StatisticsService.java
  45. 14 0
      site/src/main/java/com/mooctest/crowd/site/service/TaskAmountService.java
  46. 9 0
      site/src/main/java/com/mooctest/crowd/site/service/TesterCertService.java
  47. 85 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/CpzlPostDataResultServiceImpl.java
  48. 2 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/DefectServiceImpl.java
  49. 82 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/FwzlPostDataResultServiceImpl.java
  50. 274 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/MixDefectServiceImpl.java
  51. 85 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/RypgPostDataResultServiceImpl.java
  52. 214 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/StatisticsServiceImpl.java
  53. 254 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/TaskAmountServiceImpl.java
  54. 69 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/TesterCertServiceImpl.java

+ 27 - 0
core/src/main/java/com/mooctest/crowd/domain/command/TaskAmountSaveCommand.java

@@ -0,0 +1,27 @@
+package com.mooctest.crowd.domain.command;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class TaskAmountSaveCommand {
+    @NotEmpty(message = "任务编号不能为空")
+    private String taskCode;
+    @NotNull(message = "有效工作量金额不能为空")
+    private BigDecimal effectiveWorkloadAmount; //有效工作量金额
+    @NotNull(message = "缺陷奖励金额不能为空")
+    private BigDecimal defectExciationAmount; //缺陷激励金额
+    @NotNull(message = "额外奖励金额不能为空")
+    private BigDecimal extraRewardAmount; //额外奖励金额
+    private List<TesterExtraRewardAmount> testerExtraRewardAmounts;
+
+    @Data
+    public static class TesterExtraRewardAmount {
+        private Long userId;
+        private BigDecimal amount;
+    }
+}

+ 12 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/BgrhPostDataResultDao.java

@@ -0,0 +1,12 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.BgrhPostDataResultPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+public interface BgrhPostDataResultDao extends PagingAndSortingRepository<BgrhPostDataResultPO, Long>, CrudRepository<BgrhPostDataResultPO, Long>,
+        JpaRepository<BgrhPostDataResultPO, Long>, JpaSpecificationExecutor<BgrhPostDataResultPO> {
+    BgrhPostDataResultPO findByTaskCode(String taskCode);
+}

+ 13 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/CpzlPostDataResultDao.java

@@ -0,0 +1,13 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.CpzlPostDataResultPO;
+import com.mooctest.crowd.domain.model.FwzlPostDataResultPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+public interface CpzlPostDataResultDao extends PagingAndSortingRepository<CpzlPostDataResultPO, Long>, CrudRepository<CpzlPostDataResultPO, Long>,
+        JpaRepository<CpzlPostDataResultPO, Long>, JpaSpecificationExecutor<CpzlPostDataResultPO> {
+    CpzlPostDataResultPO findByProjectCode(String projectCode);
+}

+ 12 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/FwzlPostDataResultDao.java

@@ -0,0 +1,12 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.FwzlPostDataResultPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+public interface FwzlPostDataResultDao extends PagingAndSortingRepository<FwzlPostDataResultPO, Long>, CrudRepository<FwzlPostDataResultPO, Long>,
+        JpaRepository<FwzlPostDataResultPO, Long>, JpaSpecificationExecutor<FwzlPostDataResultPO> {
+    FwzlPostDataResultPO findByProjectCode(String projectCode);
+}

+ 22 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/MixDefectDao.java

@@ -0,0 +1,22 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.MixDefectPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface MixDefectDao extends CrudRepository<MixDefectPO, Long>, JpaRepository<MixDefectPO, Long>, PagingAndSortingRepository<MixDefectPO, Long>, JpaSpecificationExecutor<MixDefectPO> {
+    List<MixDefectPO> findAllByTaskCode(String taskCode);
+    MixDefectPO findByDefectId(Long defectId);
+    @Modifying
+    @Query(value = "update MixDefectPO t set t.parentDefectId=:newParentDefectId where t.parentDefectId=:oldParentDefectId")
+    void changeParentDefectId(@Param("oldParentDefectId") Long oldParentDefectId, @Param("newParentDefectId") Long newParentDefectId);
+    List<MixDefectPO> findAllByParentDefectId(Long parentDefectId);
+    List<MixDefectPO> findAllByProjectCode(String projectCode);
+}

+ 23 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/ProjectTypeCountDao.java

@@ -0,0 +1,23 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.ProjectTypeCountPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ProjectTypeCountDao extends PagingAndSortingRepository<ProjectTypeCountPO, Long>, CrudRepository<ProjectTypeCountPO, Long>,
+        JpaRepository<ProjectTypeCountPO, Long>, JpaSpecificationExecutor<ProjectTypeCountPO> {
+    ProjectTypeCountPO findByType(String type);
+    @Modifying
+    @Query(value = "update project_type_count set count = count + :count where type = :type", nativeQuery = true)
+    void modifyCount(@Param("type") String type, @Param("count") int count);
+    @Query(value = "select t2.tt_name as name, t1.count as count from project_type_count t1 inner join test_type t2 on t1.type=t2.tt_code", nativeQuery = true)
+    List<Map> findAllMap();
+}

+ 13 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/RypgPostDataResultDao.java

@@ -0,0 +1,13 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.FwzlPostDataResultPO;
+import com.mooctest.crowd.domain.model.RypgPostDataResultPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+public interface RypgPostDataResultDao extends PagingAndSortingRepository<RypgPostDataResultPO, Long>, CrudRepository<RypgPostDataResultPO, Long>,
+        JpaRepository<RypgPostDataResultPO, Long>, JpaSpecificationExecutor<RypgPostDataResultPO> {
+    RypgPostDataResultPO findByProjectCode(String projectCode);
+}

+ 12 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/TaskAmountDao.java

@@ -0,0 +1,12 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.TaskAmountPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+public interface TaskAmountDao extends PagingAndSortingRepository<TaskAmountPO, Long>, CrudRepository<TaskAmountPO, Long>,
+        JpaRepository<TaskAmountPO, Long>, JpaSpecificationExecutor<TaskAmountPO> {
+    TaskAmountPO findByTaskCode(String taskCode);
+}

+ 12 - 0
core/src/main/java/com/mooctest/crowd/domain/dao/TesterCertDao.java

@@ -0,0 +1,12 @@
+package com.mooctest.crowd.domain.dao;
+
+import com.mooctest.crowd.domain.model.TesterCertPO;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+public interface TesterCertDao extends PagingAndSortingRepository<TesterCertPO, Long>, CrudRepository<TesterCertPO, Long>,
+        JpaRepository<TesterCertPO, Long>, JpaSpecificationExecutor<TesterCertPO> {
+    TesterCertPO findByUserId(Long userId);
+}

+ 2 - 0
core/src/main/java/com/mooctest/crowd/domain/domainobject/Defect.java

@@ -53,4 +53,6 @@ public class Defect {
     //提交日期
     private Timestamp commitTime;
     private String importCode;
+//    //历史严重等级
+//    private Seriousness hisSeriousness;
 }

+ 9 - 0
core/src/main/java/com/mooctest/crowd/domain/domainobject/ProjectTypeCount.java

@@ -0,0 +1,9 @@
+package com.mooctest.crowd.domain.domainobject;
+
+import lombok.Data;
+
+@Data
+public class ProjectTypeCount {
+    private String type;
+    private Integer count;
+}

+ 13 - 0
core/src/main/java/com/mooctest/crowd/domain/domainobject/TaskAmount.java

@@ -0,0 +1,13 @@
+package com.mooctest.crowd.domain.domainobject;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class TaskAmount {
+    private String taskCode;
+    private BigDecimal effectiveWorkloadAmount; //有效工作量金额
+    private BigDecimal defectExciationAmount; //缺陷激励金额
+    private BigDecimal extraRewardAmount; //额外奖励金额
+}

+ 14 - 0
core/src/main/java/com/mooctest/crowd/domain/domainobject/TesterCert.java

@@ -0,0 +1,14 @@
+package com.mooctest.crowd.domain.domainobject;
+
+import lombok.Data;
+
+import java.sql.Timestamp;
+
+@Data
+public class TesterCert {
+    private Long id;
+    private Long userId;
+    private Integer taskCount;
+    private Integer level;
+    private Timestamp updateDate;
+}

+ 31 - 0
core/src/main/java/com/mooctest/crowd/domain/model/BgrhPostDataResultPO.java

@@ -0,0 +1,31 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Data
+@Entity
+@Table(name = "bgrh_post_data_result", indexes = {
+        @Index(columnList = "task_code", name = "task_code_idx", unique = true)
+})
+public class BgrhPostDataResultPO {
+    public static final byte STATUS_NO_POST = -1;
+    public static final byte STATUS_POST_FAIL = 0;
+    public static final byte STATUS_POST_SUCCESS = 1;
+    public static final byte STATUS_RECEIVE_SUCCESS = 2;
+
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "task_code", length = 30, nullable = false)
+    private String taskCode;
+    @Column(name = "result", length = 100, nullable = true)
+    private String result;
+    @Column(name = "status", nullable = false)
+    private Byte status;
+    @Column(name = "update_time")
+    private Timestamp updateTime;
+}

+ 26 - 0
core/src/main/java/com/mooctest/crowd/domain/model/CpzlPostDataResultPO.java

@@ -0,0 +1,26 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Data
+@Entity
+@Table(name = "cpzl_post_data_result", indexes = {
+        @Index(columnList = "project_code", name = "project_code_idx", unique = true)
+})
+public class CpzlPostDataResultPO {
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "project_code", length = 30, nullable = false)
+    private String projectCode;
+    @Column(name = "result", length = 100, nullable = true)
+    private String result;
+    @Column(name = "success", nullable = false)
+    private Byte success;
+    @Column(name = "update_time")
+    private Timestamp updateTime;
+}

+ 2 - 0
core/src/main/java/com/mooctest/crowd/domain/model/DefectPO.java

@@ -64,4 +64,6 @@ public class DefectPO {
     private Timestamp commitTime;
     @Column(name = "import_code", length = 9, nullable = true)
     private String importCode;
+//    @Column(name = "his_seriousness", nullable = false)
+//    private Seriousness hisSeriousness;
 }

+ 26 - 0
core/src/main/java/com/mooctest/crowd/domain/model/FwzlPostDataResultPO.java

@@ -0,0 +1,26 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Data
+@Entity
+@Table(name = "fwzl_post_data_result", indexes = {
+        @Index(columnList = "project_code", name = "project_code_idx", unique = true)
+})
+public class FwzlPostDataResultPO {
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "project_code", length = 30, nullable = false)
+    private String projectCode;
+    @Column(name = "result", length = 100, nullable = true)
+    private String result;
+    @Column(name = "success", nullable = false)
+    private Byte success;
+    @Column(name = "update_time")
+    private Timestamp updateTime;
+}

+ 25 - 0
core/src/main/java/com/mooctest/crowd/domain/model/MixDefectPO.java

@@ -0,0 +1,25 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+
+@Data
+@Entity
+@Table(name = "mix_defect", indexes = {
+    @Index(columnList = "defect_id", name = "defect_id_idx", unique = true)
+})
+public class MixDefectPO {
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "defect_id", nullable = false)
+    private Long defectId;
+    @Column(name = "parent_defect_id", nullable = true)
+    private Long parentDefectId;
+    @Column(name = "task_code", nullable = false, length = 30)
+    private String taskCode;
+    @Column(name = "project_code", nullable = false, length = 30)
+    private String projectCode;
+}

+ 21 - 0
core/src/main/java/com/mooctest/crowd/domain/model/ProjectTypeCountPO.java

@@ -0,0 +1,21 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+
+@Data
+@Entity
+@Table(name = "project_type_count", indexes = {
+        @Index(name = "type_idx", columnList = "type", unique = true)
+})
+public class ProjectTypeCountPO {
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "type", length = 10, nullable = false)
+    private String type;
+    @Column(name = "count", nullable = false)
+    private Integer count;
+}

+ 26 - 0
core/src/main/java/com/mooctest/crowd/domain/model/RypgPostDataResultPO.java

@@ -0,0 +1,26 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Data
+@Entity
+@Table(name = "rypg_post_data_result", indexes = {
+        @Index(columnList = "project_code", name = "project_code_idx", unique = true)
+})
+public class RypgPostDataResultPO {
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "project_code", length = 30, nullable = false)
+    private String projectCode;
+    @Column(name = "result", length = 100, nullable = true)
+    private String result;
+    @Column(name = "success", nullable = false)
+    private Byte success;
+    @Column(name = "update_time")
+    private Timestamp updateTime;
+}

+ 29 - 0
core/src/main/java/com/mooctest/crowd/domain/model/TaskAmountPO.java

@@ -0,0 +1,29 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+
+@Data
+@Entity
+@Table(name = "task_amount", indexes = {
+        @Index(columnList = "task_code", name = "task_code_idx", unique = true),
+        @Index(columnList = "project_code", name = "project_code_idx")
+})
+public class TaskAmountPO {
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "project_code", length = 30, nullable = false)
+    private String projectCode;
+    @Column(name = "task_code", length = 30, nullable = false)
+    private String taskCode;
+    @Column(name = "effective_workload_amount", nullable = false)
+    private BigDecimal effectiveWorkloadAmount;
+    @Column(name = "defect_exciation_amount", nullable = false)
+    private BigDecimal defectExciationAmount;
+    @Column(name = "extra_reward_amount", nullable = false)
+    private BigDecimal extraRewardAmount;
+}

+ 26 - 0
core/src/main/java/com/mooctest/crowd/domain/model/TesterCertPO.java

@@ -0,0 +1,26 @@
+package com.mooctest.crowd.domain.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Data
+@Entity
+@Table(name = "tester_cert", indexes = {
+        @Index(columnList = "user_id", name = "user_id_idx", unique = true)
+})
+public class TesterCertPO {
+    @Id
+    @Column(name = "id")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "user_id", nullable = false)
+    private Long userId;
+    @Column(name = "task_count", nullable = false)
+    private Integer taskCount;
+    @Column(name = "level", nullable = false)
+    private Integer level;
+    @Column(name = "update_date", nullable = false)
+    private Timestamp updateDate;
+}

+ 15 - 0
core/src/main/java/com/mooctest/crowd/domain/util/JsonUtils.java

@@ -0,0 +1,15 @@
+package com.mooctest.crowd.domain.util;
+
+import com.alibaba.druid.util.StringUtils;
+import com.alibaba.fastjson.JSONArray;
+
+import java.util.List;
+
+public class JsonUtils {
+    public static List<String> toList(String str) {
+        if (!StringUtils.isEmpty(str) && !str.contains("[")) {
+            str = String.format("[\"%s\"]", str);
+        }
+        return JSONArray.parseArray(str, String.class);
+    }
+}

+ 11 - 0
site/src/main/java/com/mooctest/crowd/site/command/MixDefectSaveCommand.java

@@ -0,0 +1,11 @@
+package com.mooctest.crowd.site.command;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class MixDefectSaveCommand {
+    private String masterId;
+    private List<String> suppInfo;
+}

+ 37 - 0
site/src/main/java/com/mooctest/crowd/site/controller/CpzlController.java

@@ -0,0 +1,37 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.service.CpzlPostDataResultService;
+import com.mooctest.crowd.site.service.CrowdProjectService;
+import com.mooctest.crowd.site.service.ExceptionLogService;
+import com.mooctest.crowd.site.service.FwzlPostDataResultService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("/api/cpzl/")
+public class CpzlController {
+    @Autowired
+    private CpzlPostDataResultService cpzlPostDataResultService;
+    @Autowired
+    private CrowdProjectService crowdProjectService;
+    @Autowired
+    private ExceptionLogService exceptionLogService;
+
+    @GetMapping("status/{projectCode}")
+    public ResponseVO<Byte> status(@PathVariable("projectCode") String projectCode) {
+        return new ResponseVO(ServerCode.SUCCESS, cpzlPostDataResultService.getStatus(projectCode));
+    }
+
+    @PostMapping("generate/{projectCode}")
+    @LoginRequired
+    public ResponseVO generate(HttpServletRequest request, @PathVariable("projectCode") String projectCode) {
+        boolean res = cpzlPostDataResultService.post(crowdProjectService.getProjectMoreInfo(RequestUtils.getUserId(request), projectCode));
+        return new ResponseVO(res ? ServerCode.SUCCESS : ServerCode.ERROR);
+    }
+}

+ 36 - 0
site/src/main/java/com/mooctest/crowd/site/controller/FwzlController.java

@@ -0,0 +1,36 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.service.CrowdProjectService;
+import com.mooctest.crowd.site.service.ExceptionLogService;
+import com.mooctest.crowd.site.service.FwzlPostDataResultService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("/api/fwzl/")
+public class FwzlController {
+    @Autowired
+    private FwzlPostDataResultService fwzlPostDataResultService;
+    @Autowired
+    private CrowdProjectService crowdProjectService;
+    @Autowired
+    private ExceptionLogService exceptionLogService;
+
+    @GetMapping("status/{projectCode}")
+    public ResponseVO<Byte> status(@PathVariable("projectCode") String projectCode) {
+        return new ResponseVO(ServerCode.SUCCESS, fwzlPostDataResultService.getStatus(projectCode));
+    }
+
+    @PostMapping("generate/{projectCode}")
+    @LoginRequired
+    public ResponseVO generate(HttpServletRequest request, @PathVariable("projectCode") String projectCode) {
+        boolean res = fwzlPostDataResultService.post(crowdProjectService.getProjectMoreInfo(RequestUtils.getUserId(request), projectCode));
+        return new ResponseVO(res ? ServerCode.SUCCESS : ServerCode.ERROR);
+    }
+}

+ 302 - 0
site/src/main/java/com/mooctest/crowd/site/controller/MixDefectController.java

@@ -0,0 +1,302 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.domain.domainobject.CrowdTestProject;
+import com.mooctest.crowd.domain.domainobject.CrowdTestTask;
+import com.mooctest.crowd.domain.domainobject.Defect;
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.command.MixDefectSaveCommand;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.data.vo.MixDefectInfoVO;
+import com.mooctest.crowd.site.service.CrowdProjectService;
+import com.mooctest.crowd.site.service.CrowdTaskService;
+import com.mooctest.crowd.site.service.MixDefectService;
+import com.mooctest.crowd.site.service.UserService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import io.swagger.annotations.Api;
+import org.apache.http.client.utils.DateUtils;
+import org.apache.logging.log4j.util.Strings;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.util.*;
+
+@RestController
+@RequestMapping("/api/mixdefect")
+@Api(tags = "缺陷融合接口")
+public class MixDefectController {
+    @Autowired
+    private MixDefectService mixDefectService;
+    @Autowired
+    private UserService userService;
+    @Autowired
+    private CrowdTaskService crowdTaskService;
+    @Autowired
+    private CrowdProjectService crowdProjectService;
+
+    @PostMapping("/generate/{taskCode}")
+    public ResponseVO generate(@PathVariable("taskCode") String taskCode) {
+        boolean res = mixDefectService.post(taskCode);
+        return new ResponseVO(res ? ServerCode.SUCCESS : ServerCode.ERROR);
+    }
+
+    @GetMapping("/status/{taskCode}")
+    public ResponseVO<Byte> getStatus(@PathVariable("taskCode") String taskCode) {
+        return new ResponseVO(ServerCode.SUCCESS, mixDefectService.getStatus(taskCode));
+    }
+
+    @PostMapping("/batchsave")
+    public ResponseVO batchSave(@RequestBody List<MixDefectSaveCommand> mixDefectSaveCommands) {
+        mixDefectService.batchSave(mixDefectSaveCommands);
+        return new ResponseVO(ServerCode.SUCCESS);
+    }
+
+    @PostMapping("/exchange/{masterDefectId}/{suppDefectId}")
+    @LoginRequired
+    public ResponseVO exchange(HttpServletRequest request, @PathVariable("masterDefectId") Long masterDefectId, @PathVariable("suppDefectId") Long suppDefectId) {
+        mixDefectService.exchange(RequestUtils.getUserId(request), masterDefectId, suppDefectId);
+        return new ResponseVO(ServerCode.SUCCESS);
+    }
+
+    @PostMapping("/tomaster/{suppDefectId}")
+    @LoginRequired
+    public ResponseVO toMaster(HttpServletRequest request, @PathVariable("suppDefectId") Long suppDefectId) {
+        mixDefectService.toMaster(RequestUtils.getUserId(request), suppDefectId);
+        return new ResponseVO(ServerCode.SUCCESS);
+    }
+
+    @PostMapping("/toothermasterdefect/{masterDefectId}/{movedDefectId}")
+    @LoginRequired
+    public ResponseVO toMaster(HttpServletRequest request, @PathVariable("masterDefectId") Long masterDefectId, @PathVariable("movedDefectId") Long movedDefectId) {
+        mixDefectService.toOtherMasterDefect(RequestUtils.getUserId(request), masterDefectId, movedDefectId);
+        return new ResponseVO(ServerCode.SUCCESS);
+    }
+
+    @GetMapping("/{taskCode}")
+    @LoginRequired
+    public ResponseVO<List<MixDefectInfoVO>> list(HttpServletRequest request, @PathVariable("taskCode") String taskCode) {
+        List<MixDefectInfoVO> mixDefectInfoVOs = mixDefectService.findAllByTaskCode(RequestUtils.getUserId(request), taskCode);
+        return new ResponseVO(ServerCode.SUCCESS, mixDefectInfoVOs);
+    }
+
+    @GetMapping(value = "/export/{taskCode}")
+    @LoginRequired
+    public void export(HttpServletRequest request, HttpServletResponse response, @PathVariable("taskCode") String taskCode) throws IOException {
+        Workbook wb = new XSSFWorkbook();
+        Long userId = RequestUtils.getUserId(request);
+        createTaskDefectSheet(wb, userId, taskCode);
+        CrowdTestTask task = crowdTaskService.getByTaskCode(taskCode);
+        response.setHeader("Content-Disposition", String.format("attachment;filename=%s.xlsx",
+                URLEncoder.encode(task.getName(), "utf-8")));
+        response.setContentType("application/octet-stream");
+        OutputStream os = response.getOutputStream();
+        wb.write(os);
+    }
+
+    @GetMapping(value = "/exportproject/{projectCode}")
+    @LoginRequired
+    public void exportProject(HttpServletRequest request, HttpServletResponse response, @PathVariable("projectCode") String projectCode) throws IOException {
+        Workbook wb = new XSSFWorkbook();
+        Long userId = RequestUtils.getUserId(request);
+        createProjectDefectSheet(wb, userId, projectCode);
+        CrowdTestProject project = crowdProjectService.findByProjectCode(projectCode);
+        response.setHeader("Content-Disposition", String.format("attachment;filename=%s.xlsx",
+                URLEncoder.encode(project.getName(), "utf-8")));
+        response.setContentType("application/octet-stream");
+        OutputStream os = response.getOutputStream();
+        wb.write(os);
+    }
+
+    private void createTaskDefectSheet (Workbook wb, Long userId, String taskCode) {
+        List<MixDefectInfoVO> mixDefectInfoVOs = mixDefectService.findAllByTaskCode(userId, taskCode);
+        createSheet(wb, userId, mixDefectInfoVOs);
+    }
+
+    private void createProjectDefectSheet (Workbook wb, Long userId, String projectCode) {
+        List<MixDefectInfoVO> mixDefectInfoVOs = mixDefectService.findAllByProjectCode(userId, projectCode);
+        createSheet(wb, userId, mixDefectInfoVOs);
+    }
+
+    private void createSheet(Workbook wb, Long userId, List<MixDefectInfoVO> mixDefectInfoVOs) {
+        Sheet sheet = wb.createSheet("缺陷记录");
+        int i = 0;
+        Row row = sheet.createRow(i++);
+        row.setHeightInPoints(row.getHeightInPoints() * (short)2);
+        CellStyle baseStyle = wb.createCellStyle();
+        baseStyle.setAlignment(HorizontalAlignment.CENTER);
+        baseStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        baseStyle.setBorderBottom(BorderStyle.THIN);
+        baseStyle.setBorderLeft(BorderStyle.THIN);
+        baseStyle.setBorderRight(BorderStyle.THIN);
+        baseStyle.setBorderTop(BorderStyle.THIN);
+        baseStyle.setWrapText(true);
+
+        Font headerFont = wb.createFont();
+        headerFont.setFontHeightInPoints((short)10);
+        headerFont.setFontName("宋体");
+        headerFont.setBold(true);
+
+        Font contentFont = wb.createFont();
+        contentFont.setFontHeightInPoints((short)10);
+        contentFont.setFontName("宋体");
+
+        CellStyle headerStyle = wb.createCellStyle();
+        headerStyle.cloneStyleFrom(baseStyle);
+        headerStyle.setAlignment(HorizontalAlignment.CENTER);
+        headerStyle.setFont(headerFont);
+
+        Cell cell0 = row.createCell(0);
+        Cell cell1 = row.createCell(1);
+        Cell cell2 = row.createCell(2);
+        Cell cell3 = row.createCell(3);
+        Cell cell4 = row.createCell(4);
+        Cell cell5 = row.createCell(5);
+        Cell cell6 = row.createCell(6);
+        Cell cell7 = row.createCell(7);
+        Cell cell8 = row.createCell(8);
+        Cell cell9 = row.createCell(9);
+        Cell cell10 = row.createCell(10);
+        Cell cell11 = row.createCell(11);
+        Cell cell12 = row.createCell(12);
+        Cell cell13 = row.createCell(13);
+        Cell cell14 = row.createCell(14);
+        Cell cell15 = row.createCell(15);
+
+        cell0.setCellStyle(headerStyle);
+        cell0.setCellValue("缺陷编号");
+
+        cell1.setCellStyle(headerStyle);
+        cell1.setCellValue("用例编号");
+
+        cell2.setCellStyle(headerStyle);
+        cell2.setCellValue("缺陷描述");
+        sheet.setColumnWidth(2, sheet.getColumnWidth(2) * 3);
+
+        cell3.setCellStyle(headerStyle);
+        cell3.setCellValue("严重程度");
+
+        cell4.setCellStyle(headerStyle);
+        cell4.setCellValue("优先级");
+
+        cell5.setCellStyle(headerStyle);
+        cell5.setCellValue("问题类型");
+
+        cell6.setCellStyle(headerStyle);
+        cell6.setCellValue("前置条件");
+        sheet.setColumnWidth(6, sheet.getColumnWidth(6) * 3);
+
+        cell7.setCellStyle(headerStyle);
+        cell7.setCellValue("环境配置");
+        sheet.setColumnWidth(7, sheet.getColumnWidth(7) * 3);
+
+        cell8.setCellStyle(headerStyle);
+        cell8.setCellValue("操作步骤");
+        sheet.setColumnWidth(8, sheet.getColumnWidth(8) * 3);
+
+        cell9.setCellStyle(headerStyle);
+        cell9.setCellValue("输入数据");
+        sheet.setColumnWidth(9, sheet.getColumnWidth(9) * 3);
+
+        cell10.setCellStyle(headerStyle);
+        cell10.setCellValue("预期结果");
+        sheet.setColumnWidth(10, sheet.getColumnWidth(10) * 3);
+
+        cell11.setCellStyle(headerStyle);
+        cell11.setCellValue("实际结果");
+        sheet.setColumnWidth(11, sheet.getColumnWidth(11) * 3);
+
+        cell12.setCellStyle(headerStyle);
+        cell12.setCellValue("其他说明");
+        sheet.setColumnWidth(12, sheet.getColumnWidth(12) * 3);
+
+        cell13.setCellStyle(headerStyle);
+        cell13.setCellValue("提交人员");
+        sheet.setColumnWidth(13, sheet.getColumnWidth(13) * 2);
+
+        cell14.setCellStyle(headerStyle);
+        cell14.setCellValue("提交时间");
+        sheet.setColumnWidth(14, sheet.getColumnWidth(14) * 2);
+
+        cell15.setCellStyle(headerStyle);
+        cell15.setCellValue("附件");
+        sheet.setColumnWidth(15, sheet.getColumnWidth(15) * 4);
+
+        CellStyle contentStyle = wb.createCellStyle();
+        contentStyle.cloneStyleFrom(baseStyle);
+        contentStyle.setAlignment(HorizontalAlignment.CENTER);
+        contentStyle.setFont(contentFont);
+        Map<Long, String> userIdNameMap = new HashMap();
+
+        for(MixDefectInfoVO mixDefectInfoVO: mixDefectInfoVOs) {
+            Defect defect = mixDefectInfoVO.getMasterDefect();
+            if (!userIdNameMap.containsKey(defect.getCommiterId())) {
+                String realname = userService.getById(defect.getCommiterId()).getName();
+                userIdNameMap.put(defect.getCommiterId(), realname);
+            }
+
+            row = sheet.createRow(i++);
+            cell0 = row.createCell(0);
+            cell0.setCellStyle(contentStyle);
+            cell1 = row.createCell(1);
+            cell1.setCellStyle(contentStyle);
+            cell2 = row.createCell(2);
+            cell2.setCellStyle(contentStyle);
+            cell3 = row.createCell(3);
+            CellStyle cellStyle = wb.createCellStyle();
+            cellStyle.cloneStyleFrom(contentStyle);
+            cellStyle.setAlignment(HorizontalAlignment.LEFT);
+            cell3.setCellStyle(cellStyle);
+            cell4 = row.createCell(4);
+            cell4.setCellStyle(cellStyle);
+            cell5 = row.createCell(5);
+            cell5.setCellStyle(cellStyle);
+            cell6 = row.createCell(6);
+            cell6.setCellStyle(cellStyle);
+            cell7 = row.createCell(7);
+            cell7.setCellStyle(contentStyle);
+            cell8 = row.createCell(8);
+            cell8.setCellStyle(contentStyle);
+            cell9 = row.createCell(9);
+            cell9.setCellStyle(contentStyle);
+            cell10 = row.createCell(10);
+            cell10.setCellStyle(contentStyle);
+            cell11 = row.createCell(11);
+            cell11.setCellStyle(contentStyle);
+            cell12 = row.createCell(12);
+            cell12.setCellStyle(contentStyle);
+            cell13 = row.createCell(13);
+            cell13.setCellStyle(contentStyle);
+            cell14 = row.createCell(14);
+            cell14.setCellStyle(contentStyle);
+            cell15 = row.createCell(15);
+            cell15.setCellStyle(contentStyle);
+
+            cell0.setCellValue(defect.getCode());
+            cell1.setCellValue(defect.getTestCaseCode());
+            cell2.setCellValue(defect.getDescr());
+            cell3.setCellValue(defect.getSeriousness().getName());
+            cell4.setCellValue(defect.getPriority().getName());
+            cell5.setCellValue(defect.getDefectType().getName());
+            cell6.setCellValue(defect.getPreconditions() == null ? "" : defect.getPreconditions());
+            cell7.setCellValue(defect.getEnvConfig() == null ? "" : defect.getEnvConfig());
+            cell8.setCellValue(defect.getOpeSteps() == null ? "" : defect.getOpeSteps());
+            cell9.setCellValue(defect.getInputDatas() == null ? "" : defect.getInputDatas());
+            cell10.setCellValue(defect.getExpectedResult() == null ? "" : defect.getExpectedResult());
+            cell11.setCellValue(defect.getTestResult());
+            cell12.setCellValue(defect.getOthers() == null ? "" : defect.getOthers());
+            cell13.setCellValue(userIdNameMap.get(defect.getCommiterId()));
+            cell14.setCellValue(DateUtils.formatDate(new Date(defect.getCommitTime().getTime()), "yyyy-MM-dd"));
+            List<String> files = new ArrayList();
+            files.addAll(defect.getFiles());
+            files.addAll(defect.getScreenshots());
+            cell15.setCellValue(Strings.join(files, '、'));
+        }
+    }
+}

+ 58 - 0
site/src/main/java/com/mooctest/crowd/site/controller/ProjectTypeCountController.java

@@ -0,0 +1,58 @@
+package com.mooctest.crowd.site.controller;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.mooctest.crowd.domain.dao.CrowdTestProjectDao;
+import com.mooctest.crowd.domain.dao.ProjectTypeCountDao;
+import com.mooctest.crowd.domain.model.CrowdTestProjectPO;
+import com.mooctest.crowd.domain.model.ProjectTypeCountPO;
+import com.mooctest.crowd.domain.util.JsonUtils;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.data.vo.StatisticsVO;
+import com.mooctest.crowd.site.service.CrowdProjectService;
+import com.mooctest.crowd.site.service.StatisticsService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@RestController
+public class ProjectTypeCountController {
+    @Autowired
+    private CrowdTestProjectDao crowdTestProjectDao;
+    @Autowired
+    private ProjectTypeCountDao projectTypeCountDao;
+
+    @GetMapping("/api/projecttypecount/init")
+    public ResponseVO init() {
+        List<CrowdTestProjectPO> projects = crowdTestProjectDao.findAllByIsDeleted(0);
+        Map<String, Integer> typeCountMap = new HashMap();
+        projects.stream().forEach(project -> {
+            List<String> typeLst = JsonUtils.toList(project.getType());
+//            types = types.replace("\"", "").replace("[", "").replace("]", "");
+//            String[] typeArr = types.split(",");
+            for(String type: typeLst) {
+                Integer count = typeCountMap.get(type);
+                if (count == null) {
+                    count = 0;
+                }
+                typeCountMap.put(type, ++count);
+            }
+        });
+        projectTypeCountDao.deleteAll();
+        for(String type: typeCountMap.keySet()) {
+            Integer count = typeCountMap.get(type);
+            ProjectTypeCountPO projectTypeCountPO = new ProjectTypeCountPO();
+            projectTypeCountPO.setCount(count);
+            projectTypeCountPO.setType(type);
+            projectTypeCountDao.save(projectTypeCountPO);
+        }
+        return new ResponseVO(ServerCode.SUCCESS);
+    }
+}

+ 37 - 0
site/src/main/java/com/mooctest/crowd/site/controller/QrCodeController.java

@@ -0,0 +1,37 @@
+package com.mooctest.crowd.site.controller;
+
+import com.github.hui.quick.plugin.qrcode.wrapper.QrCodeGenWrapper;
+import com.google.zxing.WriterException;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletResponse;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+
+@Controller
+@RequestMapping("/api/qrcode")
+public class QrCodeController {
+    @GetMapping("/{content}")
+    public void generate(HttpServletResponse response, @PathVariable("content") String content) throws IOException, WriterException {
+        response.setContentType("image/png");
+        qrCodeColor(content, "png", response.getOutputStream());
+    }
+
+    /**
+     * QrCode生成彩色的二维码
+     * @param content
+     * @return
+     */
+    private static void qrCodeColor(String content, String format, OutputStream outputStream) throws IOException, WriterException {
+        BufferedImage image = QrCodeGenWrapper.of(content)
+                    .setDrawPreColor(new Color(0, 102, 102))  //画的着色点 也就是二维码的黑色设置为其他颜色
+                    .asBufferedImage();
+        ImageIO.write(image, format, outputStream);
+    }
+}

+ 37 - 0
site/src/main/java/com/mooctest/crowd/site/controller/RypgController.java

@@ -0,0 +1,37 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.service.CpzlPostDataResultService;
+import com.mooctest.crowd.site.service.CrowdProjectService;
+import com.mooctest.crowd.site.service.ExceptionLogService;
+import com.mooctest.crowd.site.service.RypgPostDataResultService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("/api/rypg/")
+public class RypgController {
+    @Autowired
+    private RypgPostDataResultService rypgPostDataResultService;
+    @Autowired
+    private CrowdProjectService crowdProjectService;
+    @Autowired
+    private ExceptionLogService exceptionLogService;
+
+    @GetMapping("status/{projectCode}")
+    public ResponseVO<Byte> status(@PathVariable("projectCode") String projectCode) {
+        return new ResponseVO(ServerCode.SUCCESS, rypgPostDataResultService.getStatus(projectCode));
+    }
+
+    @PostMapping("generate/{projectCode}")
+    @LoginRequired
+    public ResponseVO generate(HttpServletRequest request, @PathVariable("projectCode") String projectCode) {
+        boolean res = rypgPostDataResultService.post(crowdProjectService.getProjectMoreInfo(RequestUtils.getUserId(request), projectCode));
+        return new ResponseVO(res ? ServerCode.SUCCESS : ServerCode.ERROR);
+    }
+}

+ 23 - 0
site/src/main/java/com/mooctest/crowd/site/controller/StatisticsController.java

@@ -0,0 +1,23 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.data.vo.StatisticsVO;
+import com.mooctest.crowd.site.service.StatisticsService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@RestController
+public class StatisticsController {
+
+    @Autowired
+    private StatisticsService statisticsService;
+
+    @GetMapping("/api/statistics")
+    public ResponseVO<StatisticsVO> statistics() {
+        return new ResponseVO(ServerCode.SUCCESS, statisticsService.getStatistics());
+    }
+}

+ 38 - 0
site/src/main/java/com/mooctest/crowd/site/controller/TaskAmountController.java

@@ -0,0 +1,38 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.domain.command.TaskAmountSaveCommand;
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.data.vo.TaskAmountVO;
+import com.mooctest.crowd.site.service.TaskAmountService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+
+@RestController
+@RequestMapping("/api/taskamount/")
+public class TaskAmountController {
+    @Autowired
+    private TaskAmountService taskAmountService;
+
+    @GetMapping("calculation/{taskCode}/{effectiveWorkloadAmount}/{defectExciationAmount}/{extraRewardAmount}")
+    @LoginRequired
+    public ResponseVO<TaskAmountVO> calculation(HttpServletRequest request,
+                                                @PathVariable("taskCode") String taskCode,
+                                                @PathVariable("effectiveWorkloadAmount")BigDecimal effectiveWorkloadAmount,
+                                                @PathVariable("defectExciationAmount")BigDecimal defectExciationAmount,
+                                                @PathVariable("extraRewardAmount")BigDecimal extraRewardAmount) {
+        return new ResponseVO(ServerCode.SUCCESS, taskAmountService.calculation(RequestUtils.getUserId(request), taskCode, effectiveWorkloadAmount, defectExciationAmount, extraRewardAmount));
+    }
+
+    @PostMapping("calandend")
+    @LoginRequired
+    public ResponseVO calAndEnd(HttpServletRequest request, @RequestBody TaskAmountSaveCommand taskAmountSaveCommand) {
+        taskAmountService.calAndEnd(RequestUtils.getUserId(request), taskAmountSaveCommand);
+        return new ResponseVO(ServerCode.SUCCESS);
+    }
+}

+ 60 - 0
site/src/main/java/com/mooctest/crowd/site/controller/TesterCertController.java

@@ -0,0 +1,60 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.domain.domainobject.TesterCert;
+import com.mooctest.crowd.domain.domainobject.User;
+import com.mooctest.crowd.domain.util.Converter;
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.annotation.SysAdminRequired;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.data.vo.TesterCertVO;
+import com.mooctest.crowd.site.service.TesterCertService;
+import com.mooctest.crowd.site.service.UserService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import org.apache.http.client.utils.DateUtils;
+import org.apache.poi.ss.formula.functions.T;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+
+
+@RestController
+@RequestMapping("/api/testercert")
+public class TesterCertController {
+    @Autowired
+    private TesterCertService testerCertService;
+    @Autowired
+    private UserService userService;
+
+    @GetMapping(value = "/init")
+    @SysAdminRequired
+    public ResponseVO init() {
+        testerCertService.init();
+        return new ResponseVO(ServerCode.SUCCESS);
+    }
+
+    @GetMapping(value = "/")
+    @LoginRequired
+    public ResponseVO<TesterCertVO> getByUserId(HttpServletRequest request) {
+        Long userId = RequestUtils.getUserId(request);
+        TesterCert testerCert = testerCertService.findByUserId(userId);
+        User user = userService.getById(userId);
+        TesterCertVO testerCertVO = Converter.convert(TesterCertVO.class, testerCert);
+        StringBuilder codeEndSb = new StringBuilder("CFT");
+        String idStr = testerCertVO.getId().toString();
+        int zeroCount = 8 - idStr.length();
+        for (int i = 0; i < zeroCount; i++) {
+            codeEndSb.append("0");
+        }
+        codeEndSb.append(idStr);
+        testerCertVO.setCode(codeEndSb.toString());
+        testerCertVO.setUserRealname(user.getName());
+        testerCertVO.setUpdateDate(DateUtils.formatDate(new Date(testerCert.getUpdateDate().getTime()), "yyyy-MM-dd"));
+        return new ResponseVO(ServerCode.SUCCESS, testerCertVO);
+    }
+}

+ 103 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/BaseProjectMoreInfoVO.java

@@ -0,0 +1,103 @@
+package com.mooctest.crowd.site.data.vo;
+
+import com.mooctest.crowd.domain.domainobject.Defect;
+import com.mooctest.crowd.domain.domainobject.TestCase;
+import com.mooctest.crowd.domain.domainobject.TestEnv;
+import com.mooctest.crowd.domain.domainobject.TestTool;
+import lombok.Data;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+@Data
+public abstract class BaseProjectMoreInfoVO {
+    private ProjectInfoVO project;
+    private ReceivingOrgInfoVO receivingOrg;
+    private DemandUnitInfoVO demandUnit;
+    private Set<TesterInfoVO> testers = new LinkedHashSet();
+    private List<TestCase> testCases = new ArrayList();
+    private List<Defect> defects = new ArrayList();
+    private List<TestEnv> testEnvs = new ArrayList();
+    private List<TestTool> testTools = new ArrayList();
+
+    @Data
+    public static class TaskInfoVO {
+        private Long id;
+        private String name;
+        private String code;
+        private String type;
+        private String description;
+        private String requirementFile;
+        private Long distributionType;
+        private String distributionProvince;
+        private String distributionCity;
+        private Double quotedPrice;
+        private Double fixedPrice;
+        private int status;
+        private Timestamp deadTime;
+        private Timestamp endTime;
+        private Timestamp createTime;
+        private String testerIds;
+    }
+
+    @Data
+    public static class ProjectInfoVO {
+        private Long id;
+        private String name;
+        private String code;
+        private Long projectDistributionTypeId;
+        private String fieldType;
+        private String applicationType;
+        private String type;
+        private String description;
+        private String projectFile;
+        private String requirementFile;
+        private String distributionProvince;
+        private String distributionCity;
+        private String valuationStandard;
+        private Double quotedPrice;
+        private Double fixedPrice;
+        private Double restPrice;
+        private String entrustUnit;
+        private int status;
+        private Timestamp deadTime;
+        private Timestamp endTime;
+        private Timestamp createTime;
+    }
+
+    @Data
+    public static class TesterInfoVO {
+        private Long id;
+        private String name;
+        private String gender;
+        private String province;
+        private String city;
+        private String photoUrl;
+        private String unit;
+        private String county;
+        private String address;
+    }
+
+    @Data
+    public static class DemandUnitInfoVO {
+        private Long id;
+        private String name;
+        private String address;
+        private String province;
+        private String city;
+        private String county;
+    }
+
+    @Data
+    public static class ReceivingOrgInfoVO {
+        private Long id;
+        private String name;
+        private String address;
+        private String province;
+        private String city;
+        private String county;
+    }
+}

+ 13 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/MixDefectInfoVO.java

@@ -0,0 +1,13 @@
+package com.mooctest.crowd.site.data.vo;
+
+import com.mooctest.crowd.domain.domainobject.Defect;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class MixDefectInfoVO {
+    private Defect masterDefect;
+    private List<Defect> suppDefects = new ArrayList();
+}

+ 11 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/ProjectMoreInfoVO.java

@@ -0,0 +1,11 @@
+package com.mooctest.crowd.site.data.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class ProjectMoreInfoVO extends BaseProjectMoreInfoVO {
+    private List<TaskInfoVO> tasks = new ArrayList();
+}

+ 48 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/StatisticsVO.java

@@ -0,0 +1,48 @@
+package com.mooctest.crowd.site.data.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class StatisticsVO {
+    private int testerCount;
+    private int companyCount;
+    private int orgCount;
+    private int projectCount;
+    private int newProjectCount;
+    private int taskCount;
+    private int newTaskCount;
+    private int testerJoinCount;
+    private int newTesterJoinCount;
+    private int testCaseCount;
+    private int newTestCaseCount;
+    private int defectCount;
+    private int newDefectCount;
+    private List<ProvinceStatisticsVO> provinceStatisticses;
+    private Map<String, Integer> projectTypeCounts;
+    private Map<String, Integer> appTypeCounts;
+    private Map<String, Integer> projectFieldCounts;
+    private Map<String, Integer> monthSendTaskCounts;
+    private Map<String, Integer> monthRecTaskCounts;
+    private List<YearMonthCountVO> yearMonthCounts = new ArrayList();
+
+    @Data
+    public static class ProvinceStatisticsVO {
+        private String name;
+        private int testerCount = 0;
+        private int companyCount = 0;
+        private int orgCount = 0;
+        private int projectCount = 0;
+        private int taskCount = 0;
+    }
+
+    @Data
+    public static class YearMonthCountVO {
+        private String yearMonth;
+        private Integer sendTaskCount;
+        private Integer recvTaskCount;
+    }
+}

+ 62 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/TaskAmountVO.java

@@ -0,0 +1,62 @@
+package com.mooctest.crowd.site.data.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class TaskAmountVO {
+    private String projectCode;
+    private String taskCode;
+    private Integer status;
+    private BigDecimal totalAmount;
+    private BigDecimal effectiveWorkloadAmount; //有效工作量金额
+    private BigDecimal defectExciationAmount; //缺陷激励金额
+    private BigDecimal extraRewardAmount; //额外奖励金额
+    private Integer totalEffectiveTestCaseCount = 0;
+    private Integer totalDefectScore = 0;
+    private List<TesterAmountVO> testerAmounts = new ArrayList();
+
+    @Data
+    public static class TesterAmountVO {
+        private Long testerId;
+        private String testerRealName;
+        private BigDecimal effectiveWorkloadAmount = new BigDecimal(0.0); //有效工作量金额
+        private BigDecimal defectExciationAmount = new BigDecimal(0.0); //缺陷激励金额
+        private BigDecimal extraRewardAmount = new BigDecimal(0.0); //额外奖励金额
+        private Integer effectiveTestCaseCount = 0;
+        private Integer notTopVeryHighDefectCount = 0; //不是最优质的超高风险缺陷数量
+        private Integer notTopHighDefectCount = 0; //不是最优质的高风险缺陷数量
+        private Integer notTopMidDefectCount = 0; //不是最优质的中风险缺陷数量
+        private Integer notTopLowDefectCount = 0; //不是最优质的低风险缺陷数量
+        private Integer notTopVeryLowDefectCount = 0; //不是最优质的超低风险缺陷数量
+        private Integer topVeryHighDefectCount = 0; //最优质的超高风险缺陷数量,且存在其他相类似的缺陷
+        private Integer topHighDefectCount = 0; //最优质的高风险缺陷数量,且存在其他相类似的缺陷
+        private Integer topMidDefectCount = 0; //最优质的中风险缺陷数量,且存在其他相类似的缺陷
+        private Integer topLowDefectCount = 0; //最优质的低风险缺陷数量,且存在其他相类似的缺陷
+        private Integer topVeryLowDefectCount = 0; //最优质的超低风险缺陷数量,且存在其他相类似的缺陷
+        private Integer onlyOneVeryHighDefectCount = 0; //只有一个人发现的超高风险缺陷数量
+        private Integer onlyOneHighDefectCount = 0; //只有一个人发现的高风险缺陷数量
+        private Integer onlyOneMidDefectCount = 0; //只有一个人发现的中风险缺陷数量
+        private Integer onlyOneLowDefectCount = 0; //只有一个人发现的低风险缺陷数量
+        private Integer onlyOneVeryLowDefectCount = 0;
+        private Integer notTopVeryHighDefectScore = 0;
+        private Integer notTopHighDefectScore = 0;
+        private Integer notTopMidDefectScore = 0;
+        private Integer notTopLowDefectScore = 0;
+        private Integer notTopVeryLowDefectScore = 0;
+        private Integer topVeryHighDefectScore = 0;
+        private Integer topHighDefectScore = 0;
+        private Integer topMidDefectScore = 0;
+        private Integer topLowDefectScore = 0;
+        private Integer topVeryLowDefectScore = 0;
+        private Integer onlyOneVeryHighDefectScore = 0;
+        private Integer onlyOneHighDefectScore = 0;
+        private Integer onlyOneMidDefectScore = 0;
+        private Integer onlyOneLowDefectScore = 0;
+        private Integer onlyOneVeryLowDefectScore = 0;
+        private Integer defectScore = 0;
+    }
+}

+ 14 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/TesterCertVO.java

@@ -0,0 +1,14 @@
+package com.mooctest.crowd.site.data.vo;
+
+import lombok.Data;
+
+@Data
+public class TesterCertVO {
+    private Long id;
+    private Long userId;
+    private String code;
+    private String userRealname;
+    private Integer taskCount;
+    private Integer level;
+    private String updateDate;
+}

+ 8 - 0
site/src/main/java/com/mooctest/crowd/site/service/CpzlPostDataResultService.java

@@ -0,0 +1,8 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.site.data.vo.ProjectMoreInfoVO;
+
+public interface CpzlPostDataResultService {
+    boolean post(ProjectMoreInfoVO projectMoreInfoVO);
+    byte getStatus(String projectCode);
+}

+ 8 - 0
site/src/main/java/com/mooctest/crowd/site/service/FwzlPostDataResultService.java

@@ -0,0 +1,8 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.site.data.vo.ProjectMoreInfoVO;
+
+public interface FwzlPostDataResultService {
+    boolean post(ProjectMoreInfoVO projectMoreInfoVO);
+    byte getStatus(String projectCode);
+}

+ 19 - 0
site/src/main/java/com/mooctest/crowd/site/service/MixDefectService.java

@@ -0,0 +1,19 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.site.command.MixDefectSaveCommand;
+import com.mooctest.crowd.site.data.vo.MixDefectInfoVO;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+@Validated
+public interface MixDefectService {
+    void batchSave(List<MixDefectSaveCommand> mixDefectSaveCommands);
+    void exchange(Long userId, Long masterDefectId, Long suppDefectId);
+    void toMaster(Long userId, Long suppDefectId);
+    void toOtherMasterDefect(Long userId, Long masterDefectId, Long movedDefectId);
+    List<MixDefectInfoVO> findAllByTaskCode(Long userId, String taskCode);
+    boolean post(String taskCode);
+    byte getStatus(String taskCode);
+    List<MixDefectInfoVO> findAllByProjectCode(Long userId, String projectCode);
+}

+ 8 - 0
site/src/main/java/com/mooctest/crowd/site/service/RypgPostDataResultService.java

@@ -0,0 +1,8 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.site.data.vo.ProjectMoreInfoVO;
+
+public interface RypgPostDataResultService {
+    boolean post(ProjectMoreInfoVO projectMoreInfoVO);
+    byte getStatus(String projectCode);
+}

+ 7 - 0
site/src/main/java/com/mooctest/crowd/site/service/StatisticsService.java

@@ -0,0 +1,7 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.site.data.vo.StatisticsVO;
+
+public interface StatisticsService {
+    StatisticsVO getStatistics();
+}

+ 14 - 0
site/src/main/java/com/mooctest/crowd/site/service/TaskAmountService.java

@@ -0,0 +1,14 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.domain.command.TaskAmountSaveCommand;
+import com.mooctest.crowd.site.data.vo.TaskAmountVO;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+@Validated
+public interface TaskAmountService {
+    TaskAmountVO calculation(Long userId, String taskCode, BigDecimal effectiveWorkloadAmount, BigDecimal defectExciationAmount, BigDecimal extraRewardAmount);
+    void calAndEnd(@NotNull(message = "用户id不能为空") Long userId, @Validated TaskAmountSaveCommand taskAmountSaveCommand);
+}

+ 9 - 0
site/src/main/java/com/mooctest/crowd/site/service/TesterCertService.java

@@ -0,0 +1,9 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.domain.domainobject.TesterCert;
+
+public interface TesterCertService {
+    TesterCert findByUserId(Long userId);
+    void init();
+    void addTaskCount(Long userId);
+}

+ 85 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/CpzlPostDataResultServiceImpl.java

@@ -0,0 +1,85 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.mooctest.crowd.domain.dao.CpzlPostDataResultDao;
+import com.mooctest.crowd.domain.dao.FwzlPostDataResultDao;
+import com.mooctest.crowd.domain.domainobject.ExceptionLog;
+import com.mooctest.crowd.domain.model.CpzlPostDataResultPO;
+import com.mooctest.crowd.domain.model.FwzlPostDataResultPO;
+import com.mooctest.crowd.site.data.vo.ProjectMoreInfoVO;
+import com.mooctest.crowd.site.service.CpzlPostDataResultService;
+import com.mooctest.crowd.site.service.ExceptionLogService;
+import com.mooctest.crowd.site.service.FwzlPostDataResultService;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.sql.Timestamp;
+import java.util.Map;
+
+@Service
+public class CpzlPostDataResultServiceImpl implements CpzlPostDataResultService {
+    @Autowired
+    private CpzlPostDataResultDao cpzlPostDataResultDao;
+    @Autowired
+    private ExceptionLogService exceptionLogService;
+    @Value("${spring.profiles.active}")
+    private String springProfilesActive;
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Override
+    public boolean post(ProjectMoreInfoVO projectMoreInfoVO) {
+        boolean res = false;
+        String projectCode = projectMoreInfoVO.getProject().getCode();
+        CpzlPostDataResultPO cpzlPostDataResultPO = cpzlPostDataResultDao.findByProjectCode(projectCode);
+        if (cpzlPostDataResultPO == null) {
+            cpzlPostDataResultPO = new CpzlPostDataResultPO();
+            cpzlPostDataResultPO.setProjectCode(projectCode);
+            cpzlPostDataResultPO.setSuccess((byte)0);
+        }
+        if (cpzlPostDataResultPO.getSuccess() != 1) {
+            // 质量评估api
+            String url1 = "json/saveJson";
+            if ("online".equals(springProfilesActive)) {
+                url1 = "http://8.134.39.104:7492/" + url1;
+            } else {
+                url1 = "http://8.134.39.104:7492/" + url1;
+            }
+            try {
+                ResponseEntity<Map> result = restTemplate.postForEntity(url1, projectMoreInfoVO, Map.class);
+
+                cpzlPostDataResultPO.setResult(JSON.toJSONString(result.getBody()));
+                if (result.getBody().get("code") != null && result.getBody().get("code").toString().equals("200")) {
+                    cpzlPostDataResultPO.setSuccess((byte)1);
+                    res = true;
+                } else {
+                    cpzlPostDataResultPO.setSuccess((byte)0);
+                }
+                cpzlPostDataResultPO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
+                cpzlPostDataResultDao.save(cpzlPostDataResultPO);
+            } catch (Exception e) {
+                ExceptionLog exceptionLog = new ExceptionLog();
+                exceptionLog.setException(ExceptionUtils.getFullStackTrace(e));
+                exceptionLog.setParams(JSON.toJSONString(projectMoreInfoVO));
+                exceptionLog.setUri(url1);
+                exceptionLog.setAddTime(new Timestamp(System.currentTimeMillis()));
+                exceptionLogService.save(exceptionLog);
+            }
+        }
+        return res;
+    }
+
+    @Override
+    public byte getStatus(String projectCode) {
+        CpzlPostDataResultPO cpzlPostDataResultPO = cpzlPostDataResultDao.findByProjectCode(projectCode);
+        if (cpzlPostDataResultPO != null) {
+            return cpzlPostDataResultPO.getSuccess();
+        } else {
+            return 0;
+        }
+    }
+}

+ 2 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/DefectServiceImpl.java

@@ -74,6 +74,7 @@ public class DefectServiceImpl implements DefectService {
         defect.setCode(defectCode);
         defect.setProjectCode(projectCode);
         defect.setCommitTime(new Timestamp(System.currentTimeMillis()));
+//        defect.setHisSeriousness(defect.getSeriousness());
         defectRepo.save(defect);
         return defect;
     }
@@ -97,6 +98,7 @@ public class DefectServiceImpl implements DefectService {
             throw new BaseException("当前用户无权限操作该缺陷");
         }
         Converter.copy(defect, defectUpdatedCommand);
+//        defect.setHisSeriousness(defect.getSeriousness());
         defectRepo.save(defect);
         return defect;
     }

+ 82 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/FwzlPostDataResultServiceImpl.java

@@ -0,0 +1,82 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.mooctest.crowd.domain.dao.FwzlPostDataResultDao;
+import com.mooctest.crowd.domain.domainobject.ExceptionLog;
+import com.mooctest.crowd.domain.model.FwzlPostDataResultPO;
+import com.mooctest.crowd.site.data.vo.ProjectMoreInfoVO;
+import com.mooctest.crowd.site.service.ExceptionLogService;
+import com.mooctest.crowd.site.service.FwzlPostDataResultService;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.sql.Timestamp;
+import java.util.Map;
+
+@Service
+public class FwzlPostDataResultServiceImpl implements FwzlPostDataResultService {
+    @Autowired
+    private FwzlPostDataResultDao fwzlPostDataResultDao;
+    @Autowired
+    private ExceptionLogService exceptionLogService;
+    @Value("${spring.profiles.active}")
+    private String springProfilesActive;
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Override
+    public boolean post(ProjectMoreInfoVO projectMoreInfoVO) {
+        boolean res = false;
+        String projectCode = projectMoreInfoVO.getProject().getCode();
+        FwzlPostDataResultPO fwzlPostDataResultPO = fwzlPostDataResultDao.findByProjectCode(projectCode);
+        if (fwzlPostDataResultPO == null) {
+            fwzlPostDataResultPO = new FwzlPostDataResultPO();
+            fwzlPostDataResultPO.setProjectCode(projectCode);
+            fwzlPostDataResultPO.setSuccess((byte)0);
+        }
+        if (fwzlPostDataResultPO.getSuccess() != 1) {
+            // 质量评估api
+            String url1 = "json/saveJson";
+            if ("online".equals(springProfilesActive)) {
+                url1 = "http://8.134.39.104:7492/" + url1;
+            } else {
+                url1 = "http://8.134.39.104:7492/" + url1;
+            }
+            try {
+                ResponseEntity<Map> result = restTemplate.postForEntity(url1, projectMoreInfoVO, Map.class);
+
+                fwzlPostDataResultPO.setResult(JSON.toJSONString(result.getBody()));
+                if (result.getBody().get("code") != null && result.getBody().get("code").toString().equals("200")) {
+                    fwzlPostDataResultPO.setSuccess((byte)1);
+                    res = true;
+                } else {
+                    fwzlPostDataResultPO.setSuccess((byte)0);
+                }
+                fwzlPostDataResultPO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
+                fwzlPostDataResultDao.save(fwzlPostDataResultPO);
+            } catch (Exception e) {
+                ExceptionLog exceptionLog = new ExceptionLog();
+                exceptionLog.setException(ExceptionUtils.getFullStackTrace(e));
+                exceptionLog.setParams(JSON.toJSONString(projectMoreInfoVO));
+                exceptionLog.setUri(url1);
+                exceptionLog.setAddTime(new Timestamp(System.currentTimeMillis()));
+                exceptionLogService.save(exceptionLog);
+            }
+        }
+        return res;
+    }
+
+    @Override
+    public byte getStatus(String projectCode) {
+        FwzlPostDataResultPO fwzlPostDataResultPO = fwzlPostDataResultDao.findByProjectCode(projectCode);
+        if (fwzlPostDataResultPO != null) {
+            return fwzlPostDataResultPO.getSuccess();
+        } else {
+            return 0;
+        }
+    }
+}

+ 274 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/MixDefectServiceImpl.java

@@ -0,0 +1,274 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.mooctest.crowd.domain.dao.BgrhPostDataResultDao;
+import com.mooctest.crowd.domain.dao.DefectDao;
+import com.mooctest.crowd.domain.dao.MixDefectDao;
+import com.mooctest.crowd.domain.domainobject.*;
+import com.mooctest.crowd.domain.env.TestCaseExamStatus;
+import com.mooctest.crowd.domain.env.TestStatus;
+import com.mooctest.crowd.domain.exception.BaseException;
+import com.mooctest.crowd.domain.model.BgrhPostDataResultPO;
+import com.mooctest.crowd.domain.model.FwzlPostDataResultPO;
+import com.mooctest.crowd.domain.model.MixDefectPO;
+import com.mooctest.crowd.domain.repository.CrowdTestProjectRepo;
+import com.mooctest.crowd.domain.repository.CrowdTestTaskRepo;
+import com.mooctest.crowd.domain.repository.DefectRepo;
+import com.mooctest.crowd.domain.repository.TestCaseRepo;
+import com.mooctest.crowd.site.command.MixDefectSaveCommand;
+import com.mooctest.crowd.site.data.vo.MixDefectInfoVO;
+import com.mooctest.crowd.site.service.ExceptionLogService;
+import com.mooctest.crowd.site.service.MixDefectService;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestTemplate;
+
+import java.sql.Timestamp;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Service
+public class MixDefectServiceImpl implements MixDefectService {
+    @Autowired
+    private MixDefectDao mixDefectDao;
+    @Autowired
+    private DefectRepo defectRepo;
+    @Autowired
+    private DefectDao defectDao;
+    @Autowired
+    private CrowdTestTaskRepo crowdTestTaskRepo;
+    @Autowired
+    private CrowdTestProjectRepo crowdTestProjectRepo;
+    @Autowired
+    private TestCaseRepo testCaseRepo;
+    @Autowired
+    private BgrhPostDataResultDao bgrhPostDataResultDao;
+    @Autowired
+    private ExceptionLogService exceptionLogService;
+    @Autowired
+    private RestTemplate restTemplate;
+    @Value("${spring.profiles.active}")
+    private String springProfilesActive;
+
+    @Override
+    @Transactional
+    public void batchSave(List<MixDefectSaveCommand> mixDefectSaveCommands) {
+        if (mixDefectSaveCommands != null && mixDefectSaveCommands.size() > 0) {
+            MixDefectSaveCommand mixDefectSaveCommand = mixDefectSaveCommands.get(0);
+            Defect defect = defectRepo.findById(Long.parseLong(mixDefectSaveCommand.getMasterId()));
+            String taskCode = defect.getTaskCode();
+            String projectCode = defect.getProjectCode();
+            if (mixDefectDao.findAllByTaskCode(taskCode).size() > 0) {
+                throw new BaseException("该任务底下已存在融合数据,不能做二次批量保存");
+            }
+            List<Long> defectIds = defectDao.findAllIdByTaskCode(taskCode);
+            mixDefectSaveCommands.forEach(saveCommand -> {
+                Long masterId = Long.parseLong(saveCommand.getMasterId());
+                if (!defectIds.contains(masterId)) {
+                    throw new BaseException("缺陷不在同个任务底下");
+                }
+                List<String> suppIds = saveCommand.getSuppInfo();
+                MixDefectPO masterMixDefectPO = new MixDefectPO();
+                masterMixDefectPO.setDefectId(masterId);
+                masterMixDefectPO.setTaskCode(taskCode);
+                masterMixDefectPO.setProjectCode(projectCode);
+                mixDefectDao.save(masterMixDefectPO);
+
+                if (suppIds != null) {
+                    suppIds.forEach(suppId -> {
+                        Long suppIdLong = Long.parseLong(suppId);
+                        if (!defectIds.contains(suppIdLong)) {
+                            throw new BaseException("缺陷不在同个任务底下");
+                        }
+                        MixDefectPO suppMixDefectPO = new MixDefectPO();
+                        suppMixDefectPO.setDefectId(suppIdLong);
+                        suppMixDefectPO.setParentDefectId(masterId);
+                        suppMixDefectPO.setTaskCode(taskCode);
+                        suppMixDefectPO.setProjectCode(projectCode);
+                        mixDefectDao.save(suppMixDefectPO);
+                    });
+                }
+            });
+
+            BgrhPostDataResultPO bgrhPostDataResultPO = bgrhPostDataResultDao.findByTaskCode(taskCode);
+            bgrhPostDataResultPO.setStatus(BgrhPostDataResultPO.STATUS_RECEIVE_SUCCESS);
+            bgrhPostDataResultPO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
+            bgrhPostDataResultDao.save(bgrhPostDataResultPO);
+        }
+    }
+
+    @Override
+    @Transactional
+    public void exchange(Long userId, Long masterDefectId, Long suppDefectId) {
+        MixDefectPO suppMixDefectPO = mixDefectDao.findByDefectId(suppDefectId);
+        if (suppMixDefectPO == null) {
+            throw new BaseException("找不到对应的融合缺陷记录!");
+        }
+        if (!suppMixDefectPO.getParentDefectId().equals(masterDefectId)) {
+            throw new BaseException("当前要交换的两条记录没有从属关系!");
+        }
+
+        MixDefectPO masterMixDefectPO = mixDefectDao.findByDefectId(masterDefectId);
+
+        checkTaskAuth(userId, suppMixDefectPO.getTaskCode());
+
+        suppMixDefectPO.setParentDefectId(null);
+        mixDefectDao.save(suppMixDefectPO);
+
+        mixDefectDao.changeParentDefectId(masterDefectId, suppDefectId);
+
+        masterMixDefectPO.setParentDefectId(suppDefectId);
+        mixDefectDao.save(masterMixDefectPO);
+    }
+
+    @Override
+    @Transactional
+    public void toMaster(Long userId, Long suppDefectId) {
+        MixDefectPO suppMixDefectPO = mixDefectDao.findByDefectId(suppDefectId);
+        if (suppMixDefectPO == null) {
+            throw new BaseException("找不到对应的融合缺陷记录!");
+        }
+        checkTaskAuth(userId, suppMixDefectPO.getTaskCode());
+        suppMixDefectPO.setParentDefectId(null);
+        mixDefectDao.save(suppMixDefectPO);
+    }
+
+    @Override
+    @Transactional
+    public void toOtherMasterDefect(Long userId, Long masterDefectId, Long movedDefectId) {
+        MixDefectPO movedMixDefectPO = mixDefectDao.findByDefectId(movedDefectId);
+        if (movedMixDefectPO == null) {
+            throw new BaseException("找不到对应的融合缺陷记录!");
+        }
+        if (movedMixDefectPO.getParentDefectId() == null) {
+            if (mixDefectDao.findAllByParentDefectId(movedDefectId).size() > 0) {
+                throw new BaseException("主缺陷记录底下存在附属记录,不能变为其他主缺陷记录的附属记录");
+            }
+        }
+        MixDefectPO masterMixDefectPO = mixDefectDao.findByDefectId(masterDefectId);
+        if (masterMixDefectPO == null) {
+            throw new BaseException("找不到对应的融合缺陷记录!");
+        }
+        if (masterMixDefectPO.getParentDefectId() != null) {
+            throw new BaseException("附属记录不能作为其他附属记录的主记录");
+        }
+        if (!masterMixDefectPO.getTaskCode().equals(movedMixDefectPO.getTaskCode())) {
+            throw new BaseException("不能跨任务操作");
+        }
+        checkTaskAuth(userId, movedMixDefectPO.getTaskCode());
+        movedMixDefectPO.setParentDefectId(masterDefectId);
+        mixDefectDao.save(movedMixDefectPO);
+    }
+
+    @Override
+    public List<MixDefectInfoVO> findAllByTaskCode(Long userId, String taskCode) {
+        checkTaskAuth(userId, taskCode);
+        List<MixDefectPO> mixDefectPOs = mixDefectDao.findAllByTaskCode(taskCode);
+        return posToVos(mixDefectPOs);
+    }
+
+    @Override
+    @Transactional
+    public boolean post(String taskCode) {
+        boolean res = false;
+        BgrhPostDataResultPO bgrhPostDataResultPO = bgrhPostDataResultDao.findByTaskCode(taskCode);
+        if (bgrhPostDataResultPO == null) {
+            bgrhPostDataResultPO = new BgrhPostDataResultPO();
+            bgrhPostDataResultPO.setTaskCode(taskCode);
+            bgrhPostDataResultPO.setStatus(BgrhPostDataResultPO.STATUS_NO_POST);
+        }
+        if (bgrhPostDataResultPO.getStatus() < BgrhPostDataResultPO.STATUS_POST_SUCCESS) {
+            List<TestCase> testCases = testCaseRepo.findAllByTaskCodeAndExamStatusAndTestStatus(taskCode, TestCaseExamStatus.VALID, TestStatus.NO_PASS);
+            List<String> testCaseCodes = testCases.stream().map(testCase -> testCase.getCode()).collect(Collectors.toList());
+            List<Defect> defects = defectRepo.findAllByTestCaseCodeIn(testCaseCodes);
+
+            // 质量评估api
+            String url = "test_reportcombination/postdata";
+            if ("online".equals(springProfilesActive)) {
+                url = "http://8.134.39.104:7480/" + url;
+            } else {
+                url = "http://8.134.39.104:7480/" + url;
+            }
+            try {
+                ResponseEntity<Map> result = restTemplate.postForEntity(url, defects, Map.class);
+
+                bgrhPostDataResultPO.setResult(JSON.toJSONString(result.getBody()));
+                if (result.getBody().get("msg") != null && result.getBody().get("msg").equals("已接收数据")) {
+                    bgrhPostDataResultPO.setStatus(BgrhPostDataResultPO.STATUS_POST_SUCCESS);
+                    res = true;
+                } else {
+                    bgrhPostDataResultPO.setStatus(BgrhPostDataResultPO.STATUS_POST_FAIL);
+                }
+                bgrhPostDataResultPO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
+                bgrhPostDataResultDao.save(bgrhPostDataResultPO);
+            } catch (Exception e) {
+                ExceptionLog exceptionLog = new ExceptionLog();
+                exceptionLog.setException(ExceptionUtils.getFullStackTrace(e));
+                exceptionLog.setParams(JSON.toJSONString(defects));
+                exceptionLog.setUri(url);
+                exceptionLog.setAddTime(new Timestamp(System.currentTimeMillis()));
+                exceptionLogService.save(exceptionLog);
+            }
+        }
+        return res;
+    }
+
+    @Override
+    public byte getStatus(String taskCode) {
+        BgrhPostDataResultPO bgrhPostDataResultPO = bgrhPostDataResultDao.findByTaskCode(taskCode);
+        if (bgrhPostDataResultPO != null) {
+            return bgrhPostDataResultPO.getStatus();
+        }
+        return BgrhPostDataResultPO.STATUS_NO_POST;
+    }
+
+    @Override
+    public List<MixDefectInfoVO> findAllByProjectCode(Long userId, String projectCode) {
+        checkProjectAuth(userId, projectCode);
+        List<MixDefectPO> mixDefectPOs = mixDefectDao.findAllByProjectCode(projectCode);
+        return posToVos(mixDefectPOs);
+    }
+
+    private List<MixDefectInfoVO> posToVos(List<MixDefectPO> mixDefectPOs) {
+        Map<Long, MixDefectInfoVO> idMixDefectInfoMap = new LinkedHashMap();
+        mixDefectPOs.forEach(mixDefectPO -> {
+            Long key = null;
+            if (mixDefectPO.getParentDefectId() == null) {
+                key = mixDefectPO.getDefectId();
+            } else {
+                key = mixDefectPO.getParentDefectId();
+            }
+            MixDefectInfoVO mixDefectInfoVO = idMixDefectInfoMap.get(key);
+            if (mixDefectInfoVO == null) {
+                mixDefectInfoVO = new MixDefectInfoVO();
+                idMixDefectInfoMap.put(key, mixDefectInfoVO);
+            }
+
+            Defect defect = defectRepo.findById(mixDefectPO.getDefectId());
+            if (mixDefectPO.getParentDefectId() == null) {
+                mixDefectInfoVO.setMasterDefect(defect);
+            } else {
+                mixDefectInfoVO.getSuppDefects().add(defect);
+            }
+        });
+        return idMixDefectInfoMap.values().stream().collect(Collectors.toList());
+    }
+
+    private void checkTaskAuth(Long userId, String taskCode) {
+        CrowdTestTask task = crowdTestTaskRepo.findByCode(taskCode);
+        checkProjectAuth(userId, task.getCrowdTestProjectCode());
+    }
+
+    private void checkProjectAuth(Long userId, String projectCode) {
+        CrowdTestProject project = crowdTestProjectRepo.getByProjectCode(projectCode);
+        if (!project.getRegionalManagerId().equals(userId)) {
+            throw new BaseException("您无权限操作该测试用例!");
+        }
+    }
+}

+ 85 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/RypgPostDataResultServiceImpl.java

@@ -0,0 +1,85 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.mooctest.crowd.domain.dao.CpzlPostDataResultDao;
+import com.mooctest.crowd.domain.dao.RypgPostDataResultDao;
+import com.mooctest.crowd.domain.domainobject.ExceptionLog;
+import com.mooctest.crowd.domain.model.CpzlPostDataResultPO;
+import com.mooctest.crowd.domain.model.RypgPostDataResultPO;
+import com.mooctest.crowd.site.data.vo.ProjectMoreInfoVO;
+import com.mooctest.crowd.site.service.ExceptionLogService;
+import com.mooctest.crowd.site.service.FwzlPostDataResultService;
+import com.mooctest.crowd.site.service.RypgPostDataResultService;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.sql.Timestamp;
+import java.util.Map;
+
+@Service
+public class RypgPostDataResultServiceImpl implements RypgPostDataResultService {
+    @Autowired
+    private RypgPostDataResultDao rypgPostDataResultDao;
+    @Autowired
+    private ExceptionLogService exceptionLogService;
+    @Value("${spring.profiles.active}")
+    private String springProfilesActive;
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Override
+    public boolean post(ProjectMoreInfoVO projectMoreInfoVO) {
+        boolean res = false;
+        String projectCode = projectMoreInfoVO.getProject().getCode();
+        RypgPostDataResultPO rypgPostDataResultPO = rypgPostDataResultDao.findByProjectCode(projectCode);
+        if (rypgPostDataResultPO == null) {
+            rypgPostDataResultPO = new RypgPostDataResultPO();
+            rypgPostDataResultPO.setProjectCode(projectCode);
+            rypgPostDataResultPO.setSuccess((byte)0);
+        }
+        if (rypgPostDataResultPO.getSuccess() != 1) {
+            // 质量评估api
+            String url1 = "evaluate/uploadproject";
+            if ("online".equals(springProfilesActive)) {
+                url1 = "http://8.134.39.104:7477/" + url1;
+            } else {
+                url1 = "http://8.134.39.104:7477/" + url1;
+            }
+            try {
+                ResponseEntity<Map> result = restTemplate.postForEntity(url1, projectMoreInfoVO, Map.class);
+
+                rypgPostDataResultPO.setResult(JSON.toJSONString(result.getBody()));
+                if (result.getBody().get("code") != null && result.getBody().get("code").toString().equals("200")) {
+                    rypgPostDataResultPO.setSuccess((byte)1);
+                    res = true;
+                } else {
+                    rypgPostDataResultPO.setSuccess((byte)0);
+                }
+                rypgPostDataResultPO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
+                rypgPostDataResultDao.save(rypgPostDataResultPO);
+            } catch (Exception e) {
+                ExceptionLog exceptionLog = new ExceptionLog();
+                exceptionLog.setException(ExceptionUtils.getFullStackTrace(e));
+                exceptionLog.setParams(JSON.toJSONString(projectMoreInfoVO));
+                exceptionLog.setUri(url1);
+                exceptionLog.setAddTime(new Timestamp(System.currentTimeMillis()));
+                exceptionLogService.save(exceptionLog);
+            }
+        }
+        return res;
+    }
+
+    @Override
+    public byte getStatus(String projectCode) {
+        RypgPostDataResultPO rypgPostDataResultPO = rypgPostDataResultDao.findByProjectCode(projectCode);
+        if (rypgPostDataResultPO != null) {
+            return rypgPostDataResultPO.getSuccess();
+        } else {
+            return 0;
+        }
+    }
+}

+ 214 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/StatisticsServiceImpl.java

@@ -0,0 +1,214 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.mooctest.crowd.domain.dao.*;
+import com.mooctest.crowd.site.data.vo.StatisticsVO;
+import com.mooctest.crowd.site.service.StatisticsService;
+import org.apache.http.client.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigInteger;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class StatisticsServiceImpl implements StatisticsService {
+    @Autowired
+    private CrowdTestProjectDao crowdTestProjectDao;
+    @Autowired
+    private CrowdTestTaskDao crowdTestTaskDao;
+    @Autowired
+    private TestCaseDao testCaseDao;
+    @Autowired
+    private DefectDao defectDao;
+    @Autowired
+    private TaskToUserDao taskToUserDao;
+    @Autowired
+    private EvaluationAgencyDao evaluationAgencyDao;
+    @Autowired
+    private UserToRoleDao userToRoleDao;
+    @Autowired
+    private ProjectTypeCountDao projectTypeCountDao;
+    @Autowired
+    private UserDao userDao;
+
+    @Override
+    public StatisticsVO getStatistics() {
+        StatisticsVO statisticsVO = new StatisticsVO();
+        Calendar calendar = Calendar.getInstance();
+        String yearMonth = DateUtils.formatDate(calendar.getTime(), "yyyy-MM");
+        calendar.add(Calendar.MONTH, -1);
+        String yearMonth1 = DateUtils.formatDate(calendar.getTime(), "yyyy-MM");
+        calendar.add(Calendar.MONTH, -1);
+        String yearMonth2 = DateUtils.formatDate(calendar.getTime(), "yyyy-MM");
+        calendar.add(Calendar.MONTH, -1);
+        String yearMonth3 = DateUtils.formatDate(calendar.getTime(), "yyyy-MM");
+        calendar.add(Calendar.MONTH, -1);
+        String yearMonth4 = DateUtils.formatDate(calendar.getTime(), "yyyy-MM");
+        calendar.add(Calendar.MONTH, -1);
+        String yearMonth5 = DateUtils.formatDate(calendar.getTime(), "yyyy-MM");
+        String time = yearMonth5 + "-01 00:00:01";
+
+        List<String> yearMonths = new ArrayList();
+        yearMonths.add(yearMonth5);
+        yearMonths.add(yearMonth4);
+        yearMonths.add(yearMonth3);
+        yearMonths.add(yearMonth2);
+        yearMonths.add(yearMonth1);
+        yearMonths.add(yearMonth);
+
+        List<Map> yearMonthSendTaskCounts = crowdTestTaskDao.countAllByTimeAndIsDeletedGroupByYearMonth(time, 0);
+        List<Map> yearMonthRecvTaskCounts = taskToUserDao.countAllByTimeGroupByYearMonth(time);
+
+        int projectCount = crowdTestProjectDao.countAllByIsDeleted(0);
+        int newProjectCount = crowdTestProjectDao.countAllByYearMonthAndIsDeleted(yearMonth, 0);
+        int taskCount = crowdTestTaskDao.countAllByIsDeleted(0);
+        int newTaskCount = crowdTestTaskDao.countAllByYearMonthAndIsDeleted(yearMonth, 0);
+        int testCaseCount = (int)testCaseDao.count();
+        int newTestCaseCount = testCaseDao.countAllByYearMonth(yearMonth);
+        int defectCount = (int)defectDao.count();
+        int newDefectCount = defectDao.countAllByYearMonth(yearMonth);
+        int testerJoinCount = (int)taskToUserDao.count();
+        int newTesterJoinCount = taskToUserDao.countAllByYearMonth(yearMonth);
+        int testerCount = userToRoleDao.countAllByRoleIdAndIsDeleted(1, 0);
+        int companyCount = evaluationAgencyDao.countAllByType("[1]");
+        int orgCount = evaluationAgencyDao.countAllByTypeNot("[1]");
+        List<Map> projectTypeCountMaps = projectTypeCountDao.findAllMap();
+        Map<String, Integer> projectTypeCounts = new HashMap();
+        projectTypeCountMaps.stream().forEach(map -> {
+            String name = (String)map.get("name");
+            Integer count = (Integer)map.get("count");
+            projectTypeCounts.put(name, count);
+        });
+        List<Map> appTypeCountMap = crowdTestProjectDao.countAllGroupByApplicationType();
+        Map<String, Integer> appTypeCounts = new HashMap();
+        appTypeCountMap.stream().forEach(map -> {
+            String name = (String)map.get("name");
+            Integer count = ((BigInteger)map.get("count")).intValue();
+            appTypeCounts.put(name, count);
+        });
+        List<Map> fieldTypeCountMap = crowdTestProjectDao.countAllGroupByFieldType();
+        Map<String, Integer> projectFieldCounts = new HashMap();
+        fieldTypeCountMap.stream().forEach(map -> {
+            String name = (String)map.get("name");
+            Integer count = ((BigInteger)map.get("count")).intValue();
+            projectFieldCounts.put(name, count);
+        });
+
+        List<Map> provinceTesterCountMaps = userDao.countAllByRoleIdGroupByProvince(1);
+        List<Map> provinceEnterpriseCountMaps = userDao.countAllByRoleIdGroupByProvince(5);
+        List<Map> provinceEvaluationAgencyCountMaps = userDao.countAllByRoleIdGroupByProvince(2);
+        List<Map> provinceDistProjectCountMaps = crowdTestProjectDao.countAllByIsDeletedGroupByProvince(0);
+        List<Map> provinceDistTaskCountMaps = crowdTestTaskDao.countAllByIsDeletedGroupByProvince(0);
+
+        Map<String, StatisticsVO.ProvinceStatisticsVO> provinceStatisticsMap = new LinkedHashMap();
+        provinceTesterCountMaps.stream().forEach(countMap -> {
+            String province = (String)countMap.get("province");
+            StatisticsVO.ProvinceStatisticsVO provinceStatisticsVO = provinceStatisticsMap.get(province);
+            if (provinceStatisticsVO == null) {
+                provinceStatisticsVO = new StatisticsVO.ProvinceStatisticsVO();
+                provinceStatisticsVO.setName(province);
+                provinceStatisticsMap.put(province, provinceStatisticsVO);
+            }
+            provinceStatisticsVO.setTesterCount(provinceStatisticsVO.getTesterCount() + ((BigInteger)countMap.get("count")).intValue());
+        });
+        provinceEnterpriseCountMaps.stream().forEach(countMap -> {
+            String province = (String)countMap.get("province");
+            StatisticsVO.ProvinceStatisticsVO provinceStatisticsVO = provinceStatisticsMap.get(province);
+            if (provinceStatisticsVO == null) {
+                provinceStatisticsVO = new StatisticsVO.ProvinceStatisticsVO();
+                provinceStatisticsVO.setName(province);
+                provinceStatisticsMap.put(province, provinceStatisticsVO);
+            }
+            provinceStatisticsVO.setCompanyCount(provinceStatisticsVO.getCompanyCount() + ((BigInteger)countMap.get("count")).intValue());
+        });
+        provinceEvaluationAgencyCountMaps.stream().forEach(countMap -> {
+            String province = (String)countMap.get("province");
+            StatisticsVO.ProvinceStatisticsVO provinceStatisticsVO = provinceStatisticsMap.get(province);
+            if (provinceStatisticsVO == null) {
+                provinceStatisticsVO = new StatisticsVO.ProvinceStatisticsVO();
+                provinceStatisticsVO.setName(province);
+                provinceStatisticsMap.put(province, provinceStatisticsVO);
+            }
+            provinceStatisticsVO.setOrgCount(provinceStatisticsVO.getOrgCount() + ((BigInteger)countMap.get("count")).intValue());
+        });
+        provinceDistProjectCountMaps.stream().forEach(countMap -> {
+            String province = (String)countMap.get("province");
+            StatisticsVO.ProvinceStatisticsVO provinceStatisticsVO = provinceStatisticsMap.get(province);
+            if (provinceStatisticsVO == null) {
+                provinceStatisticsVO = new StatisticsVO.ProvinceStatisticsVO();
+                provinceStatisticsVO.setName(province);
+                provinceStatisticsMap.put(province, provinceStatisticsVO);
+            }
+            provinceStatisticsVO.setProjectCount(provinceStatisticsVO.getProjectCount() + ((BigInteger)countMap.get("count")).intValue());
+        });
+        provinceDistTaskCountMaps.stream().forEach(countMap -> {
+            String province = (String)countMap.get("province");
+            StatisticsVO.ProvinceStatisticsVO provinceStatisticsVO = provinceStatisticsMap.get(province);
+            if (provinceStatisticsVO == null) {
+                provinceStatisticsVO = new StatisticsVO.ProvinceStatisticsVO();
+                provinceStatisticsVO.setName(province);
+                provinceStatisticsMap.put(province, provinceStatisticsVO);
+            }
+            provinceStatisticsVO.setTaskCount(provinceStatisticsVO.getTaskCount() + ((BigInteger)countMap.get("count")).intValue());
+        });
+
+        int projectCompanyCount = crowdTestProjectDao.countUnitGroupByUnit();
+
+        statisticsVO.setDefectCount(40000 + defectCount);
+        statisticsVO.setNewDefectCount(newDefectCount);
+        statisticsVO.setTestCaseCount(300000 + testCaseCount);
+        statisticsVO.setNewTestCaseCount(newTestCaseCount);
+        statisticsVO.setCompanyCount(projectCompanyCount + companyCount);
+        statisticsVO.setOrgCount(orgCount);
+        statisticsVO.setTesterCount(testerCount);
+        statisticsVO.setProjectCount(projectCount);
+        statisticsVO.setNewProjectCount(newProjectCount);
+        statisticsVO.setTaskCount(taskCount);
+        statisticsVO.setNewTaskCount(newTaskCount);
+        statisticsVO.setTesterJoinCount(testerJoinCount);
+        statisticsVO.setNewTesterJoinCount(newTesterJoinCount);
+        statisticsVO.setProjectTypeCounts(projectTypeCounts);
+        statisticsVO.setAppTypeCounts(appTypeCounts);
+        statisticsVO.setProjectFieldCounts(projectFieldCounts);
+        List<StatisticsVO.ProvinceStatisticsVO> provinceStatisticsVOs = provinceStatisticsMap.values().stream().collect(Collectors.toList());
+        int totalProjectCount = provinceStatisticsVOs.stream().map(provinceStatisticsVO -> provinceStatisticsVO.getProjectCount()).reduce((count1, count2) -> count1 + count2).get();
+        provinceStatisticsVOs.stream().forEach(provinceStatisticsVO -> {
+            double r = (double)(provinceStatisticsVO.getProjectCount()) / totalProjectCount;
+            int pcc = (int)Math.floor(r * statisticsVO.getCompanyCount());
+            provinceStatisticsVO.setCompanyCount(provinceStatisticsVO.getCompanyCount() + pcc);
+        });
+        int ppc = provinceStatisticsVOs.stream().map(provinceStatisticsVO -> provinceStatisticsVO.getCompanyCount()).reduce((count1, count2) -> count1 + count2).get();
+        StatisticsVO.ProvinceStatisticsVO gdsStatisticsVO = provinceStatisticsMap.get("广东省");
+        gdsStatisticsVO.setCompanyCount(gdsStatisticsVO.getCompanyCount() + (statisticsVO.getCompanyCount() - ppc));
+
+        statisticsVO.setProvinceStatisticses(provinceStatisticsVOs);
+
+        Map<String, Integer> sendTaskYearMonthCountMap = new HashMap();
+        Map<String, Integer> recvTaskYearMonthCountMap = new HashMap();
+        for (Map taskCountMap: yearMonthSendTaskCounts) {
+            sendTaskYearMonthCountMap.put((String)taskCountMap.get("yearMonth"), ((BigInteger)taskCountMap.get("count")).intValue());
+        }
+        for (Map taskCountMap: yearMonthRecvTaskCounts) {
+            recvTaskYearMonthCountMap.put((String)taskCountMap.get("yearMonth"), ((BigInteger)taskCountMap.get("count")).intValue());
+        }
+
+        for (String ym: yearMonths) {
+            Integer sendTaskCount = sendTaskYearMonthCountMap.get(ym);
+            Integer recvTaskCount = recvTaskYearMonthCountMap.get(ym);
+            if (sendTaskCount == null) {
+                sendTaskCount = 0;
+            }
+            if (recvTaskCount == null) {
+                recvTaskCount = 0;
+            }
+            StatisticsVO.YearMonthCountVO yearMonthCountVO = new StatisticsVO.YearMonthCountVO();
+            yearMonthCountVO.setYearMonth(ym);
+            yearMonthCountVO.setSendTaskCount(sendTaskCount);
+            yearMonthCountVO.setRecvTaskCount(recvTaskCount);
+            statisticsVO.getYearMonthCounts().add(yearMonthCountVO);
+        }
+
+        return statisticsVO;
+    }
+}

+ 254 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/TaskAmountServiceImpl.java

@@ -0,0 +1,254 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.mooctest.crowd.domain.command.TaskAmountSaveCommand;
+import com.mooctest.crowd.domain.dao.TaskAmountDao;
+import com.mooctest.crowd.domain.dao.TestCaseDao;
+import com.mooctest.crowd.domain.domainobject.*;
+import com.mooctest.crowd.domain.env.TestCaseExamStatus;
+import com.mooctest.crowd.domain.exception.BaseException;
+import com.mooctest.crowd.domain.model.TaskAmountPO;
+import com.mooctest.crowd.domain.repository.CrowdTestProjectRepo;
+import com.mooctest.crowd.domain.repository.CrowdTestTaskRepo;
+import com.mooctest.crowd.domain.repository.TestCaseRepo;
+import com.mooctest.crowd.site.data.vo.MixDefectInfoVO;
+import com.mooctest.crowd.site.data.vo.TaskAmountVO;
+import com.mooctest.crowd.site.service.CrowdTaskService;
+import com.mooctest.crowd.site.service.MixDefectService;
+import com.mooctest.crowd.site.service.TaskAmountService;
+import com.mooctest.crowd.site.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class TaskAmountServiceImpl implements TaskAmountService {
+    public static final int VERY_HIGH_SCORE = 5;
+    public static final int HIGH_SCORE = 4;
+    public static final int MID_SCORE = 3;
+    public static final int LOW_SCORE = 2;
+    public static final int VERY_LOW_SCORE = 1;
+
+    public static final int TOP_ADD_SCORE = 1;
+    public static final int ONLY_ONE_ADD_SCORE = 2;
+
+    @Autowired
+    private CrowdTestTaskRepo crowdTestTaskRepo;
+    @Autowired
+    private MixDefectService mixDefectService;
+    @Autowired
+    private UserService userService;
+    @Autowired
+    private TestCaseDao testCaseDao;
+    @Autowired
+    private CrowdTestProjectRepo crowdTestProjectRepo;
+    @Autowired
+    private TaskAmountDao taskAmountDao;
+    @Autowired
+    private CrowdTaskService crowdTaskService;
+
+    @Override
+    public TaskAmountVO calculation(Long userId, String taskCode, BigDecimal effectiveWorkloadAmount, BigDecimal defectExciationAmount, BigDecimal extraRewardAmount) {
+        checkTaskAuth(userId, taskCode);
+        CrowdTestTask crowdTestTask = crowdTestTaskRepo.findByCode(taskCode);
+        BigDecimal totalAmount = BigDecimal.valueOf(crowdTestTask.getQuotedPrice());
+//        if (effectiveWorkloadAmount.add(defectExciationAmount).add(extraRewardAmount).compareTo(totalAmount) != 0) {
+//            throw new BaseException("不能超出总金额!");
+//        }
+        List<Map> validTestCaseCountMaps = testCaseDao.countAllByTaskCodeAndExamStatusGroupByDesignerId(taskCode, TestCaseExamStatus.VALID.getValue());
+        Map<Long, TaskAmountVO.TesterAmountVO> userIdTaskAmountInfoMap = new HashMap();
+        Integer totalEffectiveTestCaseCount = 0;
+        Integer totalDefectScore = 0;
+
+        for(Map validTestCaseCountMap : validTestCaseCountMaps) {
+            int count = ((BigInteger)validTestCaseCountMap.get("count")).intValue();
+            Long designerId = ((BigInteger)validTestCaseCountMap.get("userId")).longValue();
+            TaskAmountVO.TesterAmountVO testerAmountVO = new TaskAmountVO.TesterAmountVO();
+            User user = userService.getById(designerId);
+            testerAmountVO.setTesterId(designerId);
+            testerAmountVO.setTesterRealName(user.getName());
+            testerAmountVO.setEffectiveTestCaseCount(count);
+            userIdTaskAmountInfoMap.put(designerId, testerAmountVO);
+            totalEffectiveTestCaseCount += count;
+        };
+
+        List<MixDefectInfoVO> mixDefectInfoVOs = mixDefectService.findAllByTaskCode(userId, taskCode);
+        for(MixDefectInfoVO mixDefectInfoVO : mixDefectInfoVOs) {
+            Defect masterDefect = mixDefectInfoVO.getMasterDefect();
+            List<Defect> suppDefects = mixDefectInfoVO.getSuppDefects();
+            Long commiterId = masterDefect.getCommiterId();
+            TaskAmountVO.TesterAmountVO testerAmountVO = userIdTaskAmountInfoMap.get(commiterId);
+            Integer score = 0;
+            switch(masterDefect.getSeriousness()) {
+                case VERY_HIGH:
+                    if (suppDefects.size() == 0) {
+                        testerAmountVO.setOnlyOneVeryHighDefectCount(testerAmountVO.getOnlyOneVeryHighDefectCount() + 1);
+                        score = VERY_HIGH_SCORE + ONLY_ONE_ADD_SCORE;
+                        testerAmountVO.setOnlyOneVeryHighDefectScore(testerAmountVO.getOnlyOneVeryHighDefectScore() + score);
+                    } else {
+                        testerAmountVO.setTopVeryHighDefectCount(testerAmountVO.getTopVeryHighDefectCount() + 1);
+                        score = VERY_HIGH_SCORE + TOP_ADD_SCORE;
+                        testerAmountVO.setTopVeryHighDefectScore(testerAmountVO.getTopVeryHighDefectScore() + score);
+                    }
+                    totalDefectScore += score;
+                    testerAmountVO.setDefectScore(testerAmountVO.getDefectScore() + score);
+                    break;
+                case HIGH:
+                    if (suppDefects.size() == 0) {
+                        testerAmountVO.setOnlyOneHighDefectCount(testerAmountVO.getOnlyOneHighDefectCount() + 1);
+                        score = HIGH_SCORE + ONLY_ONE_ADD_SCORE;
+                        testerAmountVO.setOnlyOneHighDefectScore(testerAmountVO.getOnlyOneHighDefectScore() + score);
+                    } else {
+                        testerAmountVO.setTopHighDefectCount(testerAmountVO.getTopHighDefectCount() + 1);
+                        score = HIGH_SCORE + TOP_ADD_SCORE;
+                        testerAmountVO.setTopHighDefectScore(testerAmountVO.getTopHighDefectScore() + score);
+                    }
+                    totalDefectScore += score;
+                    testerAmountVO.setDefectScore(testerAmountVO.getDefectScore() + score);
+                    break;
+                case MID:
+                    if (suppDefects.size() == 0) {
+                        testerAmountVO.setOnlyOneMidDefectCount(testerAmountVO.getOnlyOneMidDefectCount() + 1);
+                        score = MID_SCORE + ONLY_ONE_ADD_SCORE;
+                        testerAmountVO.setOnlyOneMidDefectScore(testerAmountVO.getOnlyOneMidDefectScore() + score);
+                    } else {
+                        testerAmountVO.setTopMidDefectCount(testerAmountVO.getTopMidDefectCount() + 1);
+                        score = MID_SCORE + TOP_ADD_SCORE;
+                        testerAmountVO.setTopMidDefectScore(testerAmountVO.getTopMidDefectScore() + score);
+                    }
+                    totalDefectScore += score;
+                    testerAmountVO.setDefectScore(testerAmountVO.getDefectScore() + score);
+                    break;
+                case LOW:
+                    if (suppDefects.size() == 0) {
+                        testerAmountVO.setOnlyOneLowDefectCount(testerAmountVO.getOnlyOneLowDefectCount() + 1);
+                        score = LOW_SCORE + ONLY_ONE_ADD_SCORE;
+                        testerAmountVO.setOnlyOneLowDefectScore(testerAmountVO.getOnlyOneLowDefectScore() + score);
+                    } else {
+                        testerAmountVO.setTopLowDefectCount(testerAmountVO.getTopLowDefectCount() + 1);
+                        score = LOW_SCORE + TOP_ADD_SCORE;
+                        testerAmountVO.setTopLowDefectScore(testerAmountVO.getTopLowDefectScore() + score);
+                    }
+                    totalDefectScore += score;
+                    testerAmountVO.setDefectScore(testerAmountVO.getDefectScore() + score);
+                    break;
+                case VERY_LOW:
+                    if (suppDefects.size() == 0) {
+                        testerAmountVO.setOnlyOneVeryLowDefectCount(testerAmountVO.getOnlyOneVeryLowDefectCount() + 1);
+                        score = VERY_LOW_SCORE + ONLY_ONE_ADD_SCORE;
+                        testerAmountVO.setOnlyOneVeryLowDefectScore(testerAmountVO.getOnlyOneVeryLowDefectScore() + score);
+                    } else {
+                        testerAmountVO.setTopVeryLowDefectCount(testerAmountVO.getTopVeryLowDefectCount() + 1);
+                        score = VERY_LOW_SCORE + TOP_ADD_SCORE;
+                        testerAmountVO.setTopVeryLowDefectScore(testerAmountVO.getTopVeryLowDefectScore() + score);
+                    }
+                    totalDefectScore += score;
+                    testerAmountVO.setDefectScore(testerAmountVO.getDefectScore() + score);
+                    break;
+            }
+            for (Defect defect : suppDefects) {
+                TaskAmountVO.TesterAmountVO suppTesterAmountVO = userIdTaskAmountInfoMap.get(defect.getCommiterId());
+                switch(defect.getSeriousness()) {
+                    case VERY_HIGH:
+                        suppTesterAmountVO.setNotTopVeryHighDefectCount(suppTesterAmountVO.getNotTopVeryHighDefectCount() + 1);
+                        totalDefectScore += VERY_HIGH_SCORE;
+                        suppTesterAmountVO.setDefectScore(suppTesterAmountVO.getDefectScore() + VERY_HIGH_SCORE);
+                        suppTesterAmountVO.setNotTopVeryHighDefectScore(suppTesterAmountVO.getNotTopVeryHighDefectScore() + VERY_HIGH_SCORE);
+                        break;
+                    case HIGH:
+                        suppTesterAmountVO.setNotTopHighDefectCount(suppTesterAmountVO.getNotTopHighDefectCount() + 1);
+                        totalDefectScore += HIGH_SCORE;
+                        suppTesterAmountVO.setDefectScore(suppTesterAmountVO.getDefectScore() + HIGH_SCORE);
+                        suppTesterAmountVO.setNotTopHighDefectScore(suppTesterAmountVO.getNotTopHighDefectScore() + HIGH_SCORE);
+                        break;
+                    case MID:
+                        suppTesterAmountVO.setNotTopMidDefectCount(suppTesterAmountVO.getNotTopMidDefectCount() + 1);
+                        totalDefectScore += MID_SCORE;
+                        suppTesterAmountVO.setDefectScore(suppTesterAmountVO.getDefectScore() + MID_SCORE);
+                        suppTesterAmountVO.setNotTopMidDefectScore(suppTesterAmountVO.getNotTopMidDefectScore() + MID_SCORE);
+                        break;
+                    case LOW:
+                        suppTesterAmountVO.setNotTopLowDefectCount(suppTesterAmountVO.getNotTopLowDefectCount() + 1);
+                        totalDefectScore += LOW_SCORE;
+                        suppTesterAmountVO.setDefectScore(suppTesterAmountVO.getDefectScore() + LOW_SCORE);
+                        suppTesterAmountVO.setNotTopLowDefectScore(suppTesterAmountVO.getNotTopLowDefectScore() + LOW_SCORE);
+                        break;
+                    case VERY_LOW:
+                        suppTesterAmountVO.setNotTopVeryLowDefectCount(suppTesterAmountVO.getNotTopVeryLowDefectCount() + 1);
+                        totalDefectScore += VERY_LOW_SCORE;
+                        suppTesterAmountVO.setDefectScore(suppTesterAmountVO.getDefectScore() + VERY_LOW_SCORE);
+                        suppTesterAmountVO.setNotTopVeryLowDefectScore(suppTesterAmountVO.getNotTopVeryLowDefectScore() + VERY_LOW_SCORE);
+                        break;
+                }
+            };
+        };
+        TaskAmountVO taskAmountVO = new TaskAmountVO();
+        taskAmountVO.setProjectCode(crowdTestTask.getCrowdTestProjectCode());
+        taskAmountVO.setTaskCode(taskCode);
+        taskAmountVO.setStatus(crowdTestTask.getStatus());
+        taskAmountVO.setTotalAmount(totalAmount.setScale(2, RoundingMode.FLOOR));
+        taskAmountVO.setDefectExciationAmount(defectExciationAmount.setScale(2, RoundingMode.FLOOR));
+        taskAmountVO.setExtraRewardAmount(extraRewardAmount.setScale(2, RoundingMode.FLOOR));
+        taskAmountVO.setEffectiveWorkloadAmount(effectiveWorkloadAmount.setScale(2, RoundingMode.FLOOR));
+        taskAmountVO.setTotalEffectiveTestCaseCount(totalEffectiveTestCaseCount);
+        taskAmountVO.setTotalDefectScore(totalDefectScore);
+        BigDecimal totalDefectScoreDecimal = BigDecimal.valueOf(taskAmountVO.getTotalDefectScore());
+        BigDecimal totalEffectiveTestCaseCountDecimal = BigDecimal.valueOf(taskAmountVO.getTotalEffectiveTestCaseCount());
+        userIdTaskAmountInfoMap.values().stream().forEach(testerAmountVO -> {
+            BigDecimal defectScoreDecimal = BigDecimal.valueOf(testerAmountVO.getDefectScore());
+            BigDecimal effectiveTestCaseCountDecimal = BigDecimal.valueOf(testerAmountVO.getEffectiveTestCaseCount());
+            testerAmountVO.setDefectExciationAmount(taskAmountVO.getDefectExciationAmount().multiply(defectScoreDecimal.divide(totalDefectScoreDecimal, 10, RoundingMode.HALF_UP)).setScale(2, BigDecimal.ROUND_FLOOR));
+            testerAmountVO.setEffectiveWorkloadAmount(taskAmountVO.getEffectiveWorkloadAmount().multiply(effectiveTestCaseCountDecimal.divide(totalEffectiveTestCaseCountDecimal, 10, RoundingMode.HALF_UP)).setScale(2, BigDecimal.ROUND_FLOOR));
+            taskAmountVO.getTesterAmounts().add(testerAmountVO);
+        });
+        return taskAmountVO;
+    }
+
+    @Override
+    @Transactional
+    public void calAndEnd(Long userId, TaskAmountSaveCommand taskAmountSaveCommand) {
+        TaskAmountVO taskAmountVO = calculation(userId, taskAmountSaveCommand.getTaskCode(),
+                taskAmountSaveCommand.getEffectiveWorkloadAmount(), taskAmountSaveCommand.getDefectExciationAmount(),
+                taskAmountSaveCommand.getExtraRewardAmount());
+        if (taskAmountVO.getStatus() != CrowdTestTask.HAS_COMMITED) {
+            throw new BaseException("该任务还未提交!");
+        }
+        if (taskAmountSaveCommand.getEffectiveWorkloadAmount().add(taskAmountSaveCommand.getDefectExciationAmount())
+                .add(taskAmountSaveCommand.getExtraRewardAmount()).compareTo(taskAmountVO.getTotalAmount()) != 0) {
+            throw new BaseException("有效测试用例奖励金额+缺陷奖励金额+额外奖励金额需等于任务总金额!");
+        }
+        BigDecimal totalExtraRewardAmount = BigDecimal.valueOf(0.0);
+        for(TaskAmountSaveCommand.TesterExtraRewardAmount testerExtraRewardAmount : taskAmountSaveCommand.getTesterExtraRewardAmounts()) {
+            totalExtraRewardAmount.add(testerExtraRewardAmount.getAmount());
+        }
+        if (!totalExtraRewardAmount.equals(taskAmountSaveCommand.getExtraRewardAmount())) {
+            throw new BaseException("所有测试人员的额外奖励金额加起来需等于总的额外奖励金额!");
+        }
+        TaskAmountPO taskAmountPO = new TaskAmountPO();
+        taskAmountPO.setTaskCode(taskAmountSaveCommand.getTaskCode());
+        taskAmountPO.setEffectiveWorkloadAmount(taskAmountSaveCommand.getEffectiveWorkloadAmount());
+        taskAmountPO.setDefectExciationAmount(taskAmountSaveCommand.getDefectExciationAmount());
+        taskAmountPO.setExtraRewardAmount(taskAmountSaveCommand.getExtraRewardAmount());
+        taskAmountDao.save(taskAmountPO);
+
+        crowdTaskService.confirmFinish(taskAmountVO.getProjectCode(), taskAmountPO.getTaskCode(), userId);
+    }
+
+    private void checkTaskAuth(Long userId, String taskCode) {
+        CrowdTestTask task = crowdTestTaskRepo.findByCode(taskCode);
+        checkProjectAuth(userId, task.getCrowdTestProjectCode());
+    }
+
+    private void checkProjectAuth(Long userId, String projectCode) {
+        CrowdTestProject project = crowdTestProjectRepo.getByProjectCode(projectCode);
+        if (!project.getRegionalManagerId().equals(userId)) {
+            throw new BaseException("您无权限操作该测试用例!");
+        }
+    }
+}

+ 69 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/TesterCertServiceImpl.java

@@ -0,0 +1,69 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.mooctest.crowd.domain.dao.TaskToUserDao;
+import com.mooctest.crowd.domain.dao.TesterCertDao;
+import com.mooctest.crowd.domain.domainobject.TesterCert;
+import com.mooctest.crowd.domain.model.TesterCertPO;
+import com.mooctest.crowd.domain.util.Converter;
+import com.mooctest.crowd.site.service.TesterCertService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class TesterCertServiceImpl implements TesterCertService {
+    @Autowired
+    private TesterCertDao testerCertDao;
+    @Autowired
+    private TaskToUserDao taskToUserDao;
+
+    @Override
+    public TesterCert findByUserId(Long userId) {
+        TesterCertPO testerCertPO = testerCertDao.findByUserId(userId);
+        TesterCert testerCert = Converter.convert(TesterCert.class, testerCertPO);
+        return testerCert;
+    }
+
+    @Override
+    @Transactional
+    public void init() {
+        List<Map> userCommittedTaskCounts = taskToUserDao.countAllCommittedGroupByUserId();
+        userCommittedTaskCounts.stream().forEach(userCommittedTaskCount -> {
+            Long userId = ((BigInteger)userCommittedTaskCount.get("userId")).longValue();
+            Integer count = ((BigInteger)(userCommittedTaskCount.get("count"))).intValue();
+            TesterCertPO testerCertPO = testerCertDao.findByUserId(userId);
+            if (testerCertPO == null) {
+                testerCertPO = new TesterCertPO();
+                testerCertPO.setUserId(userId);
+            }
+            testerCertPO.setTaskCount(count);
+            testerCertPO.setLevel(generateLevel(count));
+            testerCertPO.setUpdateDate(new Timestamp(System.currentTimeMillis()));
+            testerCertDao.save(testerCertPO);
+        });
+    }
+
+    @Override
+    @Transactional
+    public void addTaskCount(Long userId) {
+        TesterCertPO testerCertPO = testerCertDao.findByUserId(userId);
+        if (testerCertPO == null) {
+            testerCertPO = new TesterCertPO();
+            testerCertPO.setTaskCount(0);
+            testerCertPO.setUserId(userId);
+        }
+        testerCertPO.setTaskCount(testerCertPO.getTaskCount() + 1);
+        testerCertPO.setUpdateDate(new Timestamp(System.currentTimeMillis()));
+        testerCertPO.setLevel(generateLevel(testerCertPO.getTaskCount()));
+        testerCertDao.save(testerCertPO);
+    }
+
+    private int generateLevel(int taskCount) {
+        return (taskCount - 1) / 10 + 1;
+    }
+}