deason 5 yıl önce
işleme
55399d914e
100 değiştirilmiş dosya ile 7528 ekleme ve 0 silme
  1. 11 0
      .gitignore
  2. 21 0
      examcloud-core-reports-api-provider/pom.xml
  3. 96 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamCourseDataReportController.java
  4. 33 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamDataController.java
  5. 118 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamOrgReportController.java
  6. 30 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamStudentCountController.java
  7. 132 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ProjectController.java
  8. 25 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentCountController.java
  9. 30 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentCumulativeCountController.java
  10. 37 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentTotalCountController.java
  11. 28 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/UserCountController.java
  12. 125 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/KafkaConsumerListener.java
  13. 13 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/RocketMqConsumerListener.java
  14. 61 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamCourseDataReportCloudServiceProvider.java
  15. 37 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamDataCloudServiceProvider.java
  16. 60 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamOrgReportCloudServiceProvider.java
  17. 33 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamStudentCountCloudServiceProvider.java
  18. 76 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ProjectCloudServiceProvider.java
  19. 35 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentCountCloudServiceProvider.java
  20. 35 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentCumulativeCountCloudServiceProvider.java
  21. 37 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentTotalCountCloudServiceProvider.java
  22. 36 0
      examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/UserCountCloudServiceProvider.java
  23. 43 0
      examcloud-core-reports-base/pom.xml
  24. 104 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/AddProjectReq.java
  25. 177 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamCourseMainTopTen.java
  26. 224 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamCourseReportMainBean.java
  27. 125 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamDataBean.java
  28. 129 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamOrgMainTopTen.java
  29. 152 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamOrgReportMainBean.java
  30. 141 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamReportMainBean.java
  31. 55 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/PartitionDataBean.java
  32. 57 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/PartitionTopTen.java
  33. 180 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ProjectBean.java
  34. 79 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/QueryProjectPageReq.java
  35. 53 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/StudentCountBean.java
  36. 29 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/StudentCumulativeCountBean.java
  37. 60 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/UpdateProjectReq.java
  38. 56 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/UpdateProjectScoreReq.java
  39. 24 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/enums/AnalyseType.java
  40. 24 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/enums/DataOrigin.java
  41. 30 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/enums/ReportStatus.java
  42. 52 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/BatchGetDataUtil.java
  43. 153 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/excel/ExportUtils.java
  44. 28 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/excel/SheetData.java
  45. 167 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/ActiveDataUtil.java
  46. 35 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/ExamStudentActive.java
  47. 26 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/OnlineCount.java
  48. 28 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/StudentActive.java
  49. 28 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/StudentLogin.java
  50. 28 0
      examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/UserActive.java
  51. 22 0
      examcloud-core-reports-dao/pom.xml
  52. 16 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamCourseDataReportRepo.java
  53. 12 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamDataRepo.java
  54. 16 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamOrgReportRepo.java
  55. 25 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamStudentCountRepo.java
  56. 24 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ProjectRepo.java
  57. 26 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/StudentCountRepo.java
  58. 12 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/StudentCumulativeCountRepo.java
  59. 17 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/StudentTotalCountRepo.java
  60. 17 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/UserCountRepo.java
  61. 208 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamCourseDataReportEntity.java
  62. 108 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamDataEntity.java
  63. 135 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamOrgReportEntity.java
  64. 50 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamStudentCountEntity.java
  65. 202 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ProjectEntity.java
  66. 39 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/StudentCountEntity.java
  67. 51 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/StudentCumulativeCountEntity.java
  68. 38 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/StudentTotalCountEntity.java
  69. 39 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/UserCountEntity.java
  70. 24 0
      examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/share/IdEntity.java
  71. 42 0
      examcloud-core-reports-service/pom.xml
  72. 19 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamCourseDataReportService.java
  73. 12 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamDataService.java
  74. 24 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamOrgReportService.java
  75. 8 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamStudentCountService.java
  76. 35 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ProjectService.java
  77. 11 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/StudentCountService.java
  78. 10 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/StudentCumulativeCountService.java
  79. 14 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/StudentTotalCountService.java
  80. 6 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/UserCountService.java
  81. 586 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamCourseDataReportServiceImpl.java
  82. 189 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamDataServiceImpl.java
  83. 688 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamOrgReportServiceImpl.java
  84. 69 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamStudentCountServiceImpl.java
  85. 445 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ProjectServiceImpl.java
  86. 74 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/StudentCountServiceImpl.java
  87. 139 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/StudentCumulativeCountServiceImpl.java
  88. 211 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/StudentTotalCountServiceImpl.java
  89. 62 0
      examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/UserCountServiceImpl.java
  90. 30 0
      examcloud-core-reports-starter/assembly.xml
  91. 69 0
      examcloud-core-reports-starter/pom.xml
  92. 1 0
      examcloud-core-reports-starter/shell/start.args
  93. 27 0
      examcloud-core-reports-starter/shell/start.sh
  94. 1 0
      examcloud-core-reports-starter/shell/start.vmoptions
  95. 18 0
      examcloud-core-reports-starter/shell/stop.sh
  96. 1 0
      examcloud-core-reports-starter/shell/version
  97. 34 0
      examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/CoreReportsApp.java
  98. 132 0
      examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/ExamCloudResourceManager.java
  99. 47 0
      examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/ExamCloudWebMvcConfigurer.java
  100. 47 0
      examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/MqConsumerListenerStartup.java

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+.project
+.classpath
+.settings
+target/
+.idea/
+*.iml
+*test/
+# Package Files #
+*.jar
+logs/
+

+ 21 - 0
examcloud-core-reports-api-provider/pom.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+		<artifactId>examcloud-core-reports</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-reports-api-provider</artifactId>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+			<artifactId>examcloud-core-reports-service</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+	</dependencies>
+</project>

+ 96 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamCourseDataReportController.java

@@ -0,0 +1,96 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamCourseMainTopTen;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamCourseReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.enums.ReportStatus;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+import cn.com.qmth.examcloud.core.reports.service.ExamCourseDataReportService;
+import cn.com.qmth.examcloud.core.reports.service.ProjectService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "考试课程报表")
+@RequestMapping("${$rmp.ctr.reports}/" + "examCourseDataReport")
+public class ExamCourseDataReportController extends ControllerSupport {
+	@Autowired
+	private ExamCourseDataReportService examCourseDataReportService;
+	@Autowired
+	private ProjectService projectService;
+
+	@ApiOperation(value = "查询考试课程数据")
+	@GetMapping(value = "/getExamCourseMainList")
+	public List<ExamCourseReportMainBean> getExamCourseMainList(@RequestParam Long projectId,
+			@RequestParam(required = false) Long examId) {
+		if (projectId == null) {
+			throw new StatusException("100011", "projectId不能为空");
+		}
+		User user = getAccessUser();
+		ProjectEntity pe = projectService.findProjectById(projectId);
+		if (pe == null) {
+			throw new StatusException("100012", "未找到项目");
+		}
+		if (!user.getRootOrgId().equals(pe.getRootOrgId())) {
+			throw new StatusException("100013", "非法操作");
+		}
+		if (!ReportStatus.SUCCESS.equals(pe.getReportStatus())) {
+			throw new StatusException("100014", "只能查看报表计算成功的项目");
+		}
+		return examCourseDataReportService.getExamCourseMainList(projectId, examId);
+	}
+
+	@ApiOperation(value = "查询考试课程数据Top10")
+	@GetMapping(value = "/getExamCourseMainTop10")
+	public ExamCourseMainTopTen getExamCourseMainTop10(@RequestParam Long projectId,
+			@RequestParam(required = false) Long examId) {
+		if (projectId == null) {
+			throw new StatusException("100021", "projectId不能为空");
+		}
+		User user = getAccessUser();
+		ProjectEntity pe = projectService.findProjectById(projectId);
+		if (pe == null) {
+			throw new StatusException("100022", "未找到项目");
+		}
+		if (!user.getRootOrgId().equals(pe.getRootOrgId())) {
+			throw new StatusException("100023", "非法操作");
+		}
+		if (!ReportStatus.SUCCESS.equals(pe.getReportStatus())) {
+			throw new StatusException("100024", "只能查看报表计算成功的项目");
+		}
+		return examCourseDataReportService.getExamCourseMainTop10(projectId, examId);
+	}
+
+	@ApiOperation(value = "导出数据")
+	@GetMapping(value = "/export")
+	public void exportAll(@RequestParam Long projectId, @RequestParam(required = false) Long examId,
+			HttpServletResponse response) {
+		if (projectId == null) {
+			throw new StatusException("100031", "projectId不能为空");
+		}
+		User user = getAccessUser();
+		ProjectEntity pe = projectService.findProjectById(projectId);
+		if (pe == null) {
+			throw new StatusException("100032", "未找到项目");
+		}
+		if (!user.getRootOrgId().equals(pe.getRootOrgId())) {
+			throw new StatusException("100033", "非法操作");
+		}
+		if (!ReportStatus.SUCCESS.equals(pe.getReportStatus())) {
+			throw new StatusException("100034", "只能导出报表计算成功的项目");
+		}
+		examCourseDataReportService.exportAll(pe, examId, response);
+	}
+}

+ 33 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamDataController.java

@@ -0,0 +1,33 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamDataBean;
+import cn.com.qmth.examcloud.core.reports.service.ExamDataService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@RestController
+@Api(tags = "考试数据")
+@RequestMapping("${$rmp.ctr.reports}/" + "examData")
+public class ExamDataController extends ControllerSupport {
+
+    @Autowired
+    private ExamDataService examDataService;
+
+    @GetMapping("page/{pageNo}/{pageSize}")
+    @ApiOperation(value = "分页查询考试在线人数及完成情况")
+    public PageInfo<ExamDataBean> queryPage(@RequestParam Long rootOrgId,
+            @RequestParam(required = false) String examName,
+            @PathVariable @ApiParam(value = "pageNo = 1,2,3...") Integer pageNo, @PathVariable Integer pageSize) {
+        return examDataService.queryPage(rootOrgId, examName, pageNo, pageSize);
+    }
+}

+ 118 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamOrgReportController.java

@@ -0,0 +1,118 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamOrgMainTopTen;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamOrgReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.enums.ReportStatus;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+import cn.com.qmth.examcloud.core.reports.service.ExamOrgReportService;
+import cn.com.qmth.examcloud.core.reports.service.ProjectService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "考试-学习中心报表")
+@RequestMapping("${$rmp.ctr.reports}/" + "examOrgReport")
+public class ExamOrgReportController extends ControllerSupport {
+	@Autowired
+	private ExamOrgReportService examOrgReportService;
+	@Autowired
+	private ProjectService projectService;
+
+
+	@ApiOperation(value = "查询考试数据")
+	@GetMapping(value = "/getExamMainList")
+	public List<ExamReportMainBean> getExamMainList(@RequestParam Long projectId,
+			@RequestParam(required = false) Long examId) {
+		if (projectId == null) {
+			throw new StatusException("100001", "projectId不能为空");
+		}
+		User user = getAccessUser();
+		ProjectEntity pe = projectService.findProjectById(projectId);
+		if (pe == null) {
+			throw new StatusException("100002", "未找到项目");
+		}
+		if (!user.getRootOrgId().equals(pe.getRootOrgId())) {
+			throw new StatusException("100003", "非法操作");
+		}
+		if (!ReportStatus.SUCCESS.equals(pe.getReportStatus())) {
+			throw new StatusException("100004", "只能查看报表计算成功的项目");
+		}
+		return examOrgReportService.getExamMainList(projectId, examId);
+	}
+
+	@ApiOperation(value = "查询考试-中心数据")
+	@GetMapping(value = "/getExamOrgMainList")
+	public List<ExamOrgReportMainBean> getExamOrgMainList(@RequestParam Long projectId,
+			@RequestParam(required = false) Long examId) {
+		if (projectId == null) {
+			throw new StatusException("100011", "projectId不能为空");
+		}
+		User user = getAccessUser();
+		ProjectEntity pe = projectService.findProjectById(projectId);
+		if (pe == null) {
+			throw new StatusException("100012", "未找到项目");
+		}
+		if (!user.getRootOrgId().equals(pe.getRootOrgId())) {
+			throw new StatusException("100013", "非法操作");
+		}
+		if (!ReportStatus.SUCCESS.equals(pe.getReportStatus())) {
+			throw new StatusException("100014", "只能查看报表计算成功的项目");
+		}
+		return examOrgReportService.getExamOrgMainList(projectId, examId);
+	}
+
+	@ApiOperation(value = "查询考试-中心数据Top10")
+	@GetMapping(value = "/getExamOrgMainTop10")
+	public ExamOrgMainTopTen getExamOrgMainTop10(@RequestParam Long projectId,
+			@RequestParam(required = false) Long examId) {
+		if (projectId == null) {
+			throw new StatusException("100021", "projectId不能为空");
+		}
+		User user = getAccessUser();
+		ProjectEntity pe = projectService.findProjectById(projectId);
+		if (pe == null) {
+			throw new StatusException("100022", "未找到项目");
+		}
+		if (!user.getRootOrgId().equals(pe.getRootOrgId())) {
+			throw new StatusException("100023", "非法操作");
+		}
+		if (!ReportStatus.SUCCESS.equals(pe.getReportStatus())) {
+			throw new StatusException("100024", "只能查看报表计算成功的项目");
+		}
+		return examOrgReportService.getExamOrgMainTop10(projectId, examId);
+	}
+	@ApiOperation(value = "导出数据")
+	@GetMapping(value = "/export")
+	public void exportAll(@RequestParam Long projectId, @RequestParam(required = false) Long examId,
+			@RequestParam(required = false) String items, HttpServletResponse response) {
+		if (projectId == null) {
+			throw new StatusException("100031", "projectId不能为空");
+		}
+		User user = getAccessUser();
+		ProjectEntity pe = projectService.findProjectById(projectId);
+		if (pe == null) {
+			throw new StatusException("100032", "未找到项目");
+		}
+		if (!user.getRootOrgId().equals(pe.getRootOrgId())) {
+			throw new StatusException("100033", "非法操作");
+		}
+		if (!ReportStatus.SUCCESS.equals(pe.getReportStatus())) {
+			throw new StatusException("100034", "只能导出报表计算成功的项目");
+		}
+		examOrgReportService.exportAll(pe,examId,items, response);
+	}
+}

+ 30 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ExamStudentCountController.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.reports.service.ExamStudentCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "考生在线人数")
+@RequestMapping("${$rmp.ctr.reports}/" + "examStudentCount")
+public class ExamStudentCountController extends ControllerSupport {
+
+    @Autowired
+    private ExamStudentCountService examStudentCountService;
+
+    @GetMapping("/getOnlineCount")
+    @ApiOperation(value = "获取在线人数")
+    public Long getOnlineCount(@RequestParam(required = false) Long orgId,
+            @RequestParam(required = false) Long examId) {
+        User user = getAccessUser();
+        return examStudentCountService.getOnlineCount(user.getRootOrgId(), orgId, examId);
+    }
+}

+ 132 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/ProjectController.java

@@ -0,0 +1,132 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.base.bean.AddProjectReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.ProjectBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.QueryProjectPageReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.UpdateProjectReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.UpdateProjectScoreReq;
+import cn.com.qmth.examcloud.core.reports.base.enums.DataOrigin;
+import cn.com.qmth.examcloud.core.reports.service.ProjectService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@RestController
+@Api(tags = "项目列表")
+@RequestMapping("${$rmp.ctr.reports}/" + "project")
+public class ProjectController extends ControllerSupport {
+	@Autowired
+	private ProjectService projectService;
+
+	@ApiOperation(value = "查询已结束的考试")
+	@GetMapping(value = "/allExam")
+	public List<ExamBean> queryAllExam() {
+		User user = getAccessUser();
+		List<ExamBean> list = projectService.queryAllExam(user.getRootOrgId());
+		return list;
+	}
+
+	@DeleteMapping("/{id}")
+	@ApiOperation(value = "删除项目")
+	public void delProject(@PathVariable Long id) {
+		User user = getAccessUser();
+		projectService.delProject(id, user.getRootOrgId());
+	}
+
+	@PostMapping("/add")
+	@ApiOperation(value = "新增项目")
+	public Long addProject(@RequestBody @Valid AddProjectReq req) {
+		if (req.getDataOrigin().equals(DataOrigin.SYNC) && (req.getExamIds() == null || req.getExamIds().size() == 0)) {
+			throw new StatusException("1000001", "请选择考试");
+		}
+		if (req.getDataOrigin().equals(DataOrigin.IMPORT)) {
+			throw new StatusException("1000002", "暂不支持导入");
+		}
+		User user = getAccessUser();
+		return projectService.addProject(req, user.getRootOrgId());
+	}
+
+	@PostMapping("/update")
+	@ApiOperation(value = "修改项目")
+	public void updateProject(@RequestBody @Valid UpdateProjectReq req) {
+		User user = getAccessUser();
+		projectService.updateProject(req, user.getRootOrgId());
+	}
+
+	@PutMapping("/enable")
+	@ApiOperation(value = "启用")
+	public void enable(
+			@RequestParam(required = true) @NotBlank(message = "请选择项目") @ApiParam(value = "项目Id集合") String ids) {
+		User user = getAccessUser();
+		projectService.updateEnable(ids, true, user.getRootOrgId());
+	}
+
+	@PutMapping("/disable")
+	@ApiOperation(value = "禁用")
+	public void disable(
+			@RequestParam(required = true) @NotBlank(message = "请选择项目") @ApiParam(value = "项目Id集合") String ids) {
+		User user = getAccessUser();
+		projectService.updateEnable(ids, false, user.getRootOrgId());
+	}
+
+	@GetMapping("page/{pageNo}/{pageSize}")
+	@ApiOperation(value = "项目列表分页查询")
+	public PageInfo<ProjectBean> queryPage(@ModelAttribute QueryProjectPageReq req,
+			@PathVariable @ApiParam(value = "pageNo = 1,2,3...") Integer pageNo, @PathVariable Integer pageSize) {
+		User user = getAccessUser();
+		return projectService.queryPage(req, pageNo, pageSize, user.getRootOrgId());
+	}
+
+	@PostMapping("/compute/{id}")
+	@ApiOperation(value = "开始计算")
+	public void addReportsCompute(@PathVariable Long id) {
+		User user = getAccessUser();
+		projectService.addReportsCompute(id, user.getRootOrgId());
+	}
+
+	@PostMapping("/updateScore")
+	@ApiOperation(value = "修改项目分数")
+	public void updateScore(@RequestBody @Valid UpdateProjectScoreReq req) {
+		User user = getAccessUser();
+		projectService.updateScore(req, user.getRootOrgId());
+	}
+
+	@GetMapping("{id}")
+	@ApiOperation(value = "查询项目")
+	public ProjectBean getProject(@PathVariable @ApiParam(value = "id") Long id) {
+		User user = getAccessUser();
+		return projectService.getProject(id, user.getRootOrgId());
+	}
+    @ApiOperation(value="批量获取评项目")
+	@GetMapping("/getProjectList")
+	public List<ProjectBean> getProjectList(@RequestParam(required = true) String ids) {
+    	List<Long> pids = Stream.of(ids.split(",")).map(s -> Long.parseLong(s.trim()))
+				.collect(Collectors.toList());
+    	User user = getAccessUser();
+		return projectService.getProjectList(pids, user.getRootOrgId());
+	}
+}

+ 25 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentCountController.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.service.StudentCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "学生在线人数")
+@RequestMapping("${$rmp.ctr.reports}/" + "studentCount")
+public class StudentCountController extends ControllerSupport {
+	@Autowired
+	private StudentCountService studentCountService;
+
+	@GetMapping("/getSumOnlineCount")
+	@ApiOperation(value = "获取总在线人数")
+	public Long getSumOnlineCount() {
+		return studentCountService.getSumOnlineCount();
+	}
+}

+ 30 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentCumulativeCountController.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCumulativeCountBean;
+import cn.com.qmth.examcloud.core.reports.service.StudentCumulativeCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@RestController
+@Api(tags = "学生累计在线人数")
+@RequestMapping("${$rmp.ctr.reports}/" + "studentCumulativeCount")
+public class StudentCumulativeCountController extends ControllerSupport {
+	@Autowired
+	private StudentCumulativeCountService studentCumulativeCountService;
+
+	@GetMapping("/getLastNdayOnlineCount")
+	@ApiOperation(value = "获取近N日在线人数")
+	public List<StudentCumulativeCountBean> getLastNdayOnlineCount(@RequestParam @ApiParam(value = "nday 1~100") Integer nday) {
+		return studentCumulativeCountService.getLastNdayOnlineCount(nday);
+	}
+}

+ 37 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/StudentTotalCountController.java

@@ -0,0 +1,37 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCountBean;
+import cn.com.qmth.examcloud.core.reports.service.StudentTotalCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@RestController
+@Api(tags = "学生总人数")
+@RequestMapping("${$rmp.ctr.reports}/" + "studentTotalCount")
+public class StudentTotalCountController extends ControllerSupport {
+	@Autowired
+	private StudentTotalCountService studentTotalCountService;
+
+	@GetMapping("/getSumTotalCount")
+	@ApiOperation(value = "获取总的学生人数")
+	public Long getSumTotalCount() {
+		return studentTotalCountService.getSumTotalCount();
+	}
+	
+	@GetMapping("page/{pageNo}/{pageSize}")
+	@ApiOperation(value = "分页查询学生在线人数和总人数")
+	public PageInfo<StudentCountBean> queryPage(@RequestParam(required = false) String rootOrgId,
+			@PathVariable @ApiParam(value = "pageNo = 1,2,3...") Integer pageNo, @PathVariable Integer pageSize) {
+		return studentTotalCountService.queryPage(rootOrgId, pageNo, pageSize);
+	}
+}

+ 28 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/controller/UserCountController.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.reports.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.reports.service.UserCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "后台用户在线人数")
+@RequestMapping("${$rmp.ctr.reports}/" + "userCount")
+public class UserCountController extends ControllerSupport {
+	@Autowired
+	private UserCountService userCountService;
+
+	@GetMapping("/getOnlineCount")
+	@ApiOperation(value = "获取在线人数")
+	public Long getOnlineCount(@RequestParam(required = false) Long orgId) {
+		User user = getAccessUser();
+		return userCountService.getOnlineCount(user.getRootOrgId(),orgId);
+	}
+}

+ 125 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/KafkaConsumerListener.java

@@ -0,0 +1,125 @@
+package cn.com.qmth.examcloud.core.reports.api.listener;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.assertj.core.util.Lists;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+
+import cn.com.qmth.examcloud.core.reports.base.util.online.ActiveDataUtil;
+import cn.com.qmth.examcloud.core.reports.base.util.online.ExamStudentActive;
+import cn.com.qmth.examcloud.core.reports.base.util.online.StudentActive;
+import cn.com.qmth.examcloud.core.reports.base.util.online.StudentLogin;
+import cn.com.qmth.examcloud.core.reports.base.util.online.UserActive;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineExamStudentReport;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineStudentReport;
+import cn.com.qmth.examcloud.reports.commons.bean.OnlineUserReport;
+import cn.com.qmth.examcloud.reports.commons.enums.Topic;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+
+public class KafkaConsumerListener {
+
+    private final static Logger logger = LoggerFactory.getLogger(KafkaConsumerListener.class);
+
+    private static Properties props;
+
+    static {
+        props = new Properties();
+        props.put("group.id", "online-count-group");
+        props.put("bootstrap.servers", PropertyHolder.getString("$kafka-bootstrap-servers"));
+        props.put("key.deserializer", PropertyHolder.getString("$kafka-key-deserializer",
+                "org.apache.kafka.common.serialization.StringDeserializer"));
+        props.put("value.deserializer", PropertyHolder.getString("$kafka-value-deserializer",
+                "org.apache.kafka.common.serialization.StringDeserializer"));
+    }
+
+    public static void start() {
+        KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
+        List<String> subs = Lists.newArrayList();
+        subs.add(Topic.STUDENT.getCode());
+        subs.add(Topic.EXAM_STUDENT.getCode());
+        subs.add(Topic.USER.getCode());
+        consumer.subscribe(subs);
+        try {
+            for (;;) {
+                // 100 是超时时间(ms),在该时间内 poll 会等待服务器返回数据
+                ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
+
+                // poll 返回一个记录列表。
+                // 每条记录都包含了记录所属主题的信息、记录所在分区的信息、记录在分区里的偏移量,以及记录的键值对。
+                for (ConsumerRecord<String, String> record : records) {
+                    if (Topic.STUDENT.getCode().equals(record.topic())) {
+                        onMessageStudent(record.value());
+                    } else if (Topic.EXAM_STUDENT.getCode().equals(record.topic())) {
+                        onMessageExamStudent(record.value());
+                    } else if (Topic.USER.getCode().equals(record.topic())) {
+                        onMessageUser(record.value());
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error("消息消费线程出错", e);
+        } finally {
+            // 关闭消费者,网络连接和 socket 也会随之关闭,并立即触发一次再均衡
+            consumer.close();
+        }
+    }
+
+    private static void onMessageStudent(String message) {
+        logger.debug("STUDENT message:" + message);
+        OnlineStudentReport r = JSON.parseObject(message, OnlineStudentReport.class);
+        StudentActive ac = new StudentActive();
+        ac.setActiveTime(r.getReportTime().getTime());
+        ac.setRootOrgId(r.getRootOrgId());
+        ac.setStudentId(r.getStudentId());
+        ActiveDataUtil.updateStudentActive(ac);
+        onMessageLoginStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
+    }
+
+    private static void onMessageExamStudent(String message) {
+        logger.debug("EXAM_STUDENT message:" + message);
+        OnlineExamStudentReport r = JSON.parseObject(message, OnlineExamStudentReport.class);
+        ExamStudentActive ac = new ExamStudentActive();
+        ac.setActiveTime(r.getReportTime().getTime());
+        ac.setRootOrgId(r.getRootOrgId());
+        ac.setStudentId(r.getStudentId());
+        ac.setExamId(r.getExamId());
+        ActiveDataUtil.updateExamStudentActive(ac);
+        onMessageStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
+        onMessageLoginStudent(r.getReportTime().getTime(), r.getRootOrgId(), r.getStudentId());
+    }
+
+    private static void onMessageUser(String message) {
+        logger.debug("USER message:" + message);
+        OnlineUserReport r = JSON.parseObject(message, OnlineUserReport.class);
+        UserActive ac = new UserActive();
+        ac.setActiveTime(r.getReportTime().getTime());
+        ac.setRootOrgId(r.getRootOrgId());
+        ac.setUserId(r.getUserId());
+        ActiveDataUtil.updateUserActive(ac);
+    }
+
+    private static void onMessageLoginStudent(Long reportTime, Long rootOrgId, Long studentId) {
+        StudentLogin sl = new StudentLogin();
+        sl.setActiveTime(reportTime);
+        sl.setRootOrgId(rootOrgId);
+        sl.setStudentId(studentId);
+        ActiveDataUtil.updateStudentlogin(sl);
+    }
+
+    private static void onMessageStudent(Long reportTime, Long rootOrgId, Long studentId) {
+        StudentActive ac = new StudentActive();
+        ac.setActiveTime(reportTime);
+        ac.setRootOrgId(rootOrgId);
+        ac.setStudentId(studentId);
+        ActiveDataUtil.updateStudentActive(ac);
+    }
+
+}

+ 13 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/listener/RocketMqConsumerListener.java

@@ -0,0 +1,13 @@
+package cn.com.qmth.examcloud.core.reports.api.listener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RocketMqConsumerListener {
+	private final static Logger logger = LoggerFactory.getLogger(RocketMqConsumerListener.class);
+	
+	public static void start() {
+		
+	}
+
+}

+ 61 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamCourseDataReportCloudServiceProvider.java

@@ -0,0 +1,61 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.api.ExamCourseDataReportCloudService;
+import cn.com.qmth.examcloud.core.reports.api.request.DeleteExamCourseDataReportByProjectReq;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveExamCourseDataReportListReq;
+import cn.com.qmth.examcloud.core.reports.api.response.DeleteExamCourseDataReportByProjectResp;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveExamCourseDataReportListResp;
+import cn.com.qmth.examcloud.core.reports.service.ExamCourseDataReportService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "考试课程数值接口")
+@RequestMapping("${$rmp.cloud.reports}"+"examCourseDataReport")
+public class ExamCourseDataReportCloudServiceProvider extends ControllerSupport implements ExamCourseDataReportCloudService {
+
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -6660188438072178202L;
+	@Autowired
+	private ExamCourseDataReportService examCourseDataReportService;
+
+	@ApiOperation(value = "批量保存")
+    @PostMapping("/saveList")
+	@Override
+	public SaveExamCourseDataReportListResp saveExamCourseDataReportList(@RequestBody SaveExamCourseDataReportListReq req) {
+		if(req.getBeans()==null||req.getBeans().size()==0) {
+			throw new StatusException("1000001", "数据为空");
+		}
+		examCourseDataReportService.saveExamCourseDataReportList(req.getBeans());
+		return new SaveExamCourseDataReportListResp();
+	}
+
+	@ApiOperation(value = "根据projec删除")
+    @PostMapping("/deleteByProject")
+	@Override
+	public DeleteExamCourseDataReportByProjectResp deleteExamCourseDataReportByProject(@RequestBody
+			DeleteExamCourseDataReportByProjectReq req) {
+		Long rootOrgId=req.getRootOrgId();
+		if(rootOrgId==null) {
+			throw new StatusException("1000011", "rootOrgId不能为空");
+		}
+		Long projectId=req.getProjectId();
+		if(projectId==null) {
+			throw new StatusException("1000012", "projectId不能为空");
+		}
+		examCourseDataReportService.deleteByProject(projectId, rootOrgId);
+		return new DeleteExamCourseDataReportByProjectResp();
+	}
+
+}

+ 37 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamDataCloudServiceProvider.java

@@ -0,0 +1,37 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.api.ExamDataCloudService;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveExamDataReq;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveExamDataResp;
+import cn.com.qmth.examcloud.core.reports.service.ExamDataService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "考试数据接口")
+@RequestMapping("${$rmp.cloud.reports}" + "examData")
+public class ExamDataCloudServiceProvider extends ControllerSupport implements ExamDataCloudService {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 4065377147777658448L;
+
+    @Autowired
+    private ExamDataService examDataService;
+
+    @ApiOperation(value = "保存考试数据")
+    @PostMapping("/save")
+    @Override
+    public SaveExamDataResp save(@RequestBody SaveExamDataReq req) {
+        examDataService.save(req);
+        return new SaveExamDataResp();
+    }
+}

+ 60 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamOrgReportCloudServiceProvider.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.api.ExamOrgReportCloudService;
+import cn.com.qmth.examcloud.core.reports.api.request.DeleteExamOrgReportByProjectReq;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveExamOrgReportListReq;
+import cn.com.qmth.examcloud.core.reports.api.response.DeleteExamOrgReportByProjectResp;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveExamOrgReportListResp;
+import cn.com.qmth.examcloud.core.reports.service.ExamOrgReportService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "考试-学习中心数值接口")
+@RequestMapping("${$rmp.cloud.reports}"+"examOrgReport")
+public class ExamOrgReportCloudServiceProvider extends ControllerSupport implements ExamOrgReportCloudService {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -2138703572207468208L;
+	
+	@Autowired
+	private ExamOrgReportService examOrgReportService;
+
+	@ApiOperation(value = "批量保存")
+    @PostMapping("/saveList")
+	@Override
+	public SaveExamOrgReportListResp saveExamOrgReportList(@RequestBody SaveExamOrgReportListReq req) {
+		if(req.getBeans()==null||req.getBeans().size()==0) {
+			throw new StatusException("1000001", "数据为空");
+		}
+		examOrgReportService.saveExamOrgReportList(req.getBeans());
+		return new SaveExamOrgReportListResp();
+	}
+
+	@ApiOperation(value = "根据projec删除")
+    @PostMapping("/deleteByProject")
+	@Override
+	public DeleteExamOrgReportByProjectResp deleteExamOrgReportByProject(@RequestBody DeleteExamOrgReportByProjectReq req) {
+		Long rootOrgId=req.getRootOrgId();
+		if(rootOrgId==null) {
+			throw new StatusException("1000011", "rootOrgId不能为空");
+		}
+		Long projectId=req.getProjectId();
+		if(projectId==null) {
+			throw new StatusException("1000012", "projectId不能为空");
+		}
+		examOrgReportService.deleteByProject(projectId, rootOrgId);
+		return new DeleteExamOrgReportByProjectResp();
+	}
+
+}

+ 33 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ExamStudentCountCloudServiceProvider.java

@@ -0,0 +1,33 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.api.ExamStudentCountCloudService;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveExamStudentCountResp;
+import cn.com.qmth.examcloud.core.reports.service.ExamStudentCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "考生在线人数接口")
+@RequestMapping("${$rmp.cloud.reports}" + "examStudentCount")
+public class ExamStudentCountCloudServiceProvider extends ControllerSupport implements ExamStudentCountCloudService {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3223439983825840881L;
+	@Autowired
+	private ExamStudentCountService examStudentCountService;
+
+	@ApiOperation(value = "计算在线人数")
+    @PostMapping("/saveCount")
+	@Override
+	public SaveExamStudentCountResp saveCount() {
+		examStudentCountService.saveCount();
+		return new SaveExamStudentCountResp();
+	}
+}

+ 76 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/ProjectCloudServiceProvider.java

@@ -0,0 +1,76 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.api.ProjectCloudService;
+import cn.com.qmth.examcloud.core.reports.api.request.GetProjectInfoBeanReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectCourseOrgCountReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectStatusByIdsReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectStatusReq;
+import cn.com.qmth.examcloud.core.reports.api.response.GetProjectInfoBeanResp;
+import cn.com.qmth.examcloud.core.reports.api.response.UpdateProjectCourseOrgCountResp;
+import cn.com.qmth.examcloud.core.reports.api.response.UpdateProjectStatusByIdsResp;
+import cn.com.qmth.examcloud.core.reports.api.response.UpdateProjectStatusResp;
+import cn.com.qmth.examcloud.core.reports.service.ProjectService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "项目接口")
+@RequestMapping("${$rmp.cloud.reports}"+"project")
+public class ProjectCloudServiceProvider extends ControllerSupport implements ProjectCloudService {
+
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 7840494213770095239L;
+	@Autowired
+	private ProjectService projectService;
+
+	@ApiOperation(value = "修改状态")
+    @PostMapping("/updateStatus")
+	@Override
+	public UpdateProjectStatusResp updateProjectStatus(@RequestBody UpdateProjectStatusReq req) {
+		if(req.getProjectId()==null) {
+			throw new StatusException("1000001", "projectId不能为空");
+		}
+		projectService.updateProjectStatus(req);
+		return new UpdateProjectStatusResp();
+	}
+
+	@ApiOperation(value = "获取项目信息")
+    @PostMapping("/getProjectBean")
+	@Override
+	public GetProjectInfoBeanResp getProjectBean(@RequestBody GetProjectInfoBeanReq req) {
+		GetProjectInfoBeanResp res=new GetProjectInfoBeanResp();
+		res.setBean(projectService.getProjectInfoBean(req));
+		return res;
+	}
+
+	@ApiOperation(value = "更新项目中心、课程数量")
+    @PostMapping("/updateCourseOrgCount")
+	@Override
+	public UpdateProjectCourseOrgCountResp updateProjectCourseOrgCount(@RequestBody UpdateProjectCourseOrgCountReq req) {
+		if(req.getProjectId()==null) {
+			throw new StatusException("1000011", "projectId不能为空");
+		}
+		projectService.updateProjectCourseOrgCount(req);
+		return new UpdateProjectCourseOrgCountResp();
+	}
+
+	@ApiOperation(value = "批量修改状态")
+    @PostMapping("/updateStatusByIds")
+	@Override
+	public UpdateProjectStatusByIdsResp updateProjectStatusByIds(@RequestBody UpdateProjectStatusByIdsReq req) {
+		projectService.updateProjectStatusByIds(req);
+		return new UpdateProjectStatusByIdsResp();
+	}
+
+}

+ 35 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentCountCloudServiceProvider.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.api.StudentCountCloudService;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveStudentCountResp;
+import cn.com.qmth.examcloud.core.reports.service.StudentCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "学生在线人数接口")
+@RequestMapping("${$rmp.cloud.reports}"+"studentCount")
+public class StudentCountCloudServiceProvider extends ControllerSupport implements StudentCountCloudService {
+
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5584819592660072512L;
+	@Autowired
+	private StudentCountService studentCountService;
+
+	@ApiOperation(value = "计算在线人数")
+    @PostMapping("/saveCount")
+	@Override
+	public SaveStudentCountResp saveCount() {
+		studentCountService.saveCount();
+		return new SaveStudentCountResp();
+	}
+}

+ 35 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentCumulativeCountCloudServiceProvider.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.api.StudentCumulativeCountCloudService;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveStudentCumulativeCountResp;
+import cn.com.qmth.examcloud.core.reports.service.StudentCumulativeCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "学生累计在线人数接口")
+@RequestMapping("${$rmp.cloud.reports}"+"studentCumulativeCount")
+public class StudentCumulativeCountCloudServiceProvider extends ControllerSupport implements StudentCumulativeCountCloudService {
+
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3673284538600937213L;
+	@Autowired
+	private StudentCumulativeCountService studentCumulativeCountService;
+
+	@ApiOperation(value = "计算累计在线人数")
+    @PostMapping("/saveCount")
+	@Override
+	public SaveStudentCumulativeCountResp saveCount() {
+		studentCumulativeCountService.saveCount();
+		return new SaveStudentCumulativeCountResp();
+	}
+}

+ 37 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/StudentTotalCountCloudServiceProvider.java

@@ -0,0 +1,37 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.api.StudentTotalCountCloudService;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveStudentTotalCountReq;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveStudentTotalCountResp;
+import cn.com.qmth.examcloud.core.reports.service.StudentTotalCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "学生总人数接口")
+@RequestMapping("${$rmp.cloud.reports}" + "studentTotalCount")
+public class StudentTotalCountCloudServiceProvider extends ControllerSupport implements StudentTotalCountCloudService {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 3112398369884979088L;
+
+    @Autowired
+    private StudentTotalCountService studentTotalCountService;
+
+    @ApiOperation(value = "保存总人数")
+    @PostMapping("/save")
+    @Override
+    public SaveStudentTotalCountResp save(@RequestBody SaveStudentTotalCountReq req) {
+        studentTotalCountService.save(req);
+        return new SaveStudentTotalCountResp();
+    }
+}

+ 36 - 0
examcloud-core-reports-api-provider/src/main/java/cn/com/qmth/examcloud/core/reports/api/provider/UserCountCloudServiceProvider.java

@@ -0,0 +1,36 @@
+package cn.com.qmth.examcloud.core.reports.api.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.examcloud.core.reports.api.UserCountCloudService;
+import cn.com.qmth.examcloud.core.reports.api.response.SaveUserCountResp;
+import cn.com.qmth.examcloud.core.reports.service.UserCountService;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "后台用户在线人数接口")
+@RequestMapping("${$rmp.cloud.reports}"+"userCount")
+public class UserCountCloudServiceProvider extends ControllerSupport implements UserCountCloudService {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3472625622982671318L;
+	@Autowired
+	private UserCountService userCountService;
+	
+	@ApiOperation(value = "计算在线人数")
+    @PostMapping("/saveCount")
+	@Override
+	public SaveUserCountResp saveCount() {
+		userCountService.saveCount();
+		return new SaveUserCountResp();
+	}
+
+
+}

+ 43 - 0
examcloud-core-reports-base/pom.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+		<artifactId>examcloud-core-reports</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-reports-base</artifactId>
+	<name>examcloud-core-reports-base</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-web</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-support</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.reports</groupId>
+			<artifactId>examcloud-reports-commons</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-basic-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-oe-admin-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+	</dependencies>
+
+</project>

+ 104 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/AddProjectReq.java

@@ -0,0 +1,104 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import org.hibernate.validator.constraints.Length;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.reports.base.enums.AnalyseType;
+import cn.com.qmth.examcloud.core.reports.base.enums.DataOrigin;
+import io.swagger.annotations.ApiModelProperty;
+
+public class AddProjectReq implements JsonSerializable {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5807098559991360206L;
+
+	// 项目名
+	@NotBlank(message = "项目名称不能为空")
+	@Length(min = 1, max = 100, message = "项目名称长度需要在1和100之间")
+    @ApiModelProperty(value = "项目名称")
+	private String name;
+
+	// 分析类型
+	@NotNull(message = "分析类型不能为空")
+    @ApiModelProperty(value = "分析类型")
+	@Enumerated(EnumType.STRING)
+	private AnalyseType analyseType;
+
+	// 数据来源
+	@NotNull(message = "数据来源不能为空")
+    @ApiModelProperty(value = "数据来源")
+	@Enumerated(EnumType.STRING)
+	private DataOrigin dataOrigin;
+
+	// 样本数量
+    @ApiModelProperty(value = "样本数量")
+	private Integer sampleCount;
+
+	// 所选考试id
+	private List<Long> examIds;
+
+	// 备注
+	@Length(min = 0, max = 100, message = "备注长度需要在0和100之间")
+	private String remarks;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public AnalyseType getAnalyseType() {
+		return analyseType;
+	}
+
+	public void setAnalyseType(AnalyseType analyseType) {
+		this.analyseType = analyseType;
+	}
+
+	public DataOrigin getDataOrigin() {
+		return dataOrigin;
+	}
+
+	public void setDataOrigin(DataOrigin dataOrigin) {
+		this.dataOrigin = dataOrigin;
+	}
+
+	public Integer getSampleCount() {
+		return sampleCount;
+	}
+
+	public void setSampleCount(Integer sampleCount) {
+		this.sampleCount = sampleCount;
+	}
+
+
+	public List<Long> getExamIds() {
+		return examIds;
+	}
+
+	public void setExamIds(List<Long> examIds) {
+		this.examIds = examIds;
+	}
+
+	public String getRemarks() {
+		return remarks;
+	}
+
+	public void setRemarks(String remarks) {
+		this.remarks = remarks;
+	}
+	
+	
+}

+ 177 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamCourseMainTopTen.java

@@ -0,0 +1,177 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class ExamCourseMainTopTen {
+	@ApiModelProperty(value = "调卷平均难度前十")
+	private List<ExamCourseReportMainBean> difficultyDegreeDesc;
+	@ApiModelProperty(value = "调卷平均难度后十")
+	private List<ExamCourseReportMainBean> difficultyDegree;
+	@ApiModelProperty(value = "标准差前十")
+	private List<ExamCourseReportMainBean> stdDesc;
+	@ApiModelProperty(value = "标准差后十")
+	private List<ExamCourseReportMainBean> std;
+	@ApiModelProperty(value = "差异系数前十")
+	private List<ExamCourseReportMainBean> cdiDesc;
+	@ApiModelProperty(value = "差异系数后十")
+	private List<ExamCourseReportMainBean> cdi;
+	@ApiModelProperty(value = "实考人数后十")
+	private List<ExamCourseReportMainBean> participant;
+	@ApiModelProperty(value = "实考人数前十")
+	private List<ExamCourseReportMainBean> participantDesc;
+	@ApiModelProperty(value = "实考比例后十")
+	private List<ExamCourseReportMainBean> participantRatio;
+	@ApiModelProperty(value = "实考比例前十")
+	private List<ExamCourseReportMainBean> participantRatioDesc;
+	@ApiModelProperty(value = "缺考人数后十")
+	private List<ExamCourseReportMainBean> miss;
+	@ApiModelProperty(value = "缺考人数前十")
+	private List<ExamCourseReportMainBean> missDesc;
+	@ApiModelProperty(value = "缺考比例数后十")
+	private List<ExamCourseReportMainBean> missRatio;
+	@ApiModelProperty(value = "缺考比例前十")
+	private List<ExamCourseReportMainBean> missRatioDesc;
+	@ApiModelProperty(value = "及格人数后十")
+	private List<ExamCourseReportMainBean> pass;
+	@ApiModelProperty(value = "及格人数前十")
+	private List<ExamCourseReportMainBean> passDesc;
+	@ApiModelProperty(value = "及格/报名人数比例后十")
+	private List<ExamCourseReportMainBean> passSignRatio;
+	@ApiModelProperty(value = "及格/报名人数比例前十")
+	private List<ExamCourseReportMainBean> passSignRatioDesc;
+	@ApiModelProperty(value = "及格/实考人数比例后十")
+	private List<ExamCourseReportMainBean> passParticipantRatio;
+	@ApiModelProperty(value = "及格/实考人数比例前十")
+	private List<ExamCourseReportMainBean> passParticipantRatioDesc;
+	@ApiModelProperty(value = "分段人数")
+	private List<PartitionTopTen<PartitionDataBean>> partition;
+	public List<ExamCourseReportMainBean> getParticipant() {
+		return participant;
+	}
+	public void setParticipant(List<ExamCourseReportMainBean> participant) {
+		this.participant = participant;
+	}
+	public List<ExamCourseReportMainBean> getParticipantDesc() {
+		return participantDesc;
+	}
+	public void setParticipantDesc(List<ExamCourseReportMainBean> participantDesc) {
+		this.participantDesc = participantDesc;
+	}
+	public List<ExamCourseReportMainBean> getMiss() {
+		return miss;
+	}
+	public void setMiss(List<ExamCourseReportMainBean> miss) {
+		this.miss = miss;
+	}
+	public List<ExamCourseReportMainBean> getMissDesc() {
+		return missDesc;
+	}
+	public void setMissDesc(List<ExamCourseReportMainBean> missDesc) {
+		this.missDesc = missDesc;
+	}
+	public List<ExamCourseReportMainBean> getPass() {
+		return pass;
+	}
+	public void setPass(List<ExamCourseReportMainBean> pass) {
+		this.pass = pass;
+	}
+	public List<ExamCourseReportMainBean> getPassDesc() {
+		return passDesc;
+	}
+	public void setPassDesc(List<ExamCourseReportMainBean> passDesc) {
+		this.passDesc = passDesc;
+	}
+	public List<PartitionTopTen<PartitionDataBean>> getPartition() {
+		return partition;
+	}
+	public void setPartition(List<PartitionTopTen<PartitionDataBean>> partition) {
+		this.partition = partition;
+	}
+	public List<ExamCourseReportMainBean> getParticipantRatio() {
+		return participantRatio;
+	}
+	public void setParticipantRatio(List<ExamCourseReportMainBean> participantRatio) {
+		this.participantRatio = participantRatio;
+	}
+	public List<ExamCourseReportMainBean> getParticipantRatioDesc() {
+		return participantRatioDesc;
+	}
+	public void setParticipantRatioDesc(List<ExamCourseReportMainBean> participantRatioDesc) {
+		this.participantRatioDesc = participantRatioDesc;
+	}
+	public List<ExamCourseReportMainBean> getMissRatio() {
+		return missRatio;
+	}
+	public void setMissRatio(List<ExamCourseReportMainBean> missRatio) {
+		this.missRatio = missRatio;
+	}
+	public List<ExamCourseReportMainBean> getMissRatioDesc() {
+		return missRatioDesc;
+	}
+	public void setMissRatioDesc(List<ExamCourseReportMainBean> missRatioDesc) {
+		this.missRatioDesc = missRatioDesc;
+	}
+	public List<ExamCourseReportMainBean> getPassSignRatio() {
+		return passSignRatio;
+	}
+	public void setPassSignRatio(List<ExamCourseReportMainBean> passSignRatio) {
+		this.passSignRatio = passSignRatio;
+	}
+	public List<ExamCourseReportMainBean> getPassSignRatioDesc() {
+		return passSignRatioDesc;
+	}
+	public void setPassSignRatioDesc(List<ExamCourseReportMainBean> passSignRatioDesc) {
+		this.passSignRatioDesc = passSignRatioDesc;
+	}
+	public List<ExamCourseReportMainBean> getPassParticipantRatio() {
+		return passParticipantRatio;
+	}
+	public void setPassParticipantRatio(List<ExamCourseReportMainBean> passParticipantRatio) {
+		this.passParticipantRatio = passParticipantRatio;
+	}
+	public List<ExamCourseReportMainBean> getPassParticipantRatioDesc() {
+		return passParticipantRatioDesc;
+	}
+	public void setPassParticipantRatioDesc(List<ExamCourseReportMainBean> passParticipantRatioDesc) {
+		this.passParticipantRatioDesc = passParticipantRatioDesc;
+	}
+	public List<ExamCourseReportMainBean> getDifficultyDegreeDesc() {
+		return difficultyDegreeDesc;
+	}
+	public void setDifficultyDegreeDesc(List<ExamCourseReportMainBean> difficultyDegreeDesc) {
+		this.difficultyDegreeDesc = difficultyDegreeDesc;
+	}
+	public List<ExamCourseReportMainBean> getDifficultyDegree() {
+		return difficultyDegree;
+	}
+	public void setDifficultyDegree(List<ExamCourseReportMainBean> difficultyDegree) {
+		this.difficultyDegree = difficultyDegree;
+	}
+	public List<ExamCourseReportMainBean> getStdDesc() {
+		return stdDesc;
+	}
+	public void setStdDesc(List<ExamCourseReportMainBean> stdDesc) {
+		this.stdDesc = stdDesc;
+	}
+	public List<ExamCourseReportMainBean> getStd() {
+		return std;
+	}
+	public void setStd(List<ExamCourseReportMainBean> std) {
+		this.std = std;
+	}
+	public List<ExamCourseReportMainBean> getCdiDesc() {
+		return cdiDesc;
+	}
+	public void setCdiDesc(List<ExamCourseReportMainBean> cdiDesc) {
+		this.cdiDesc = cdiDesc;
+	}
+	public List<ExamCourseReportMainBean> getCdi() {
+		return cdi;
+	}
+	public void setCdi(List<ExamCourseReportMainBean> cdi) {
+		this.cdi = cdi;
+	}
+	
+}

+ 224 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamCourseReportMainBean.java

@@ -0,0 +1,224 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class ExamCourseReportMainBean implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3443224190555618785L;
+	@ApiModelProperty(value = "项目id")
+	private Long projectId;
+	@ApiModelProperty(value = "rootOrgId")
+	private Long rootOrgId;
+	@ApiModelProperty(value = "examId")
+	private Long examId;
+	@ApiModelProperty(value = "考试名称")
+	private String examName;
+	@ApiModelProperty(value = "考试code")
+	private String examCode;
+	@ApiModelProperty(value = "课程id")
+	private Long courseId;
+	@ApiModelProperty(value = "课程名称")
+	private String courseName;
+	@ApiModelProperty(value = "课程code")
+	private String courseCode;
+	@ApiModelProperty(value = "满分")
+	private Double totalScore;
+	@ApiModelProperty(value = "最高分")
+	private Double maxScore;
+	@ApiModelProperty(value = "最低分")
+	private Double minScore;
+	@ApiModelProperty(value = "平均分")
+	private Double avgScore;
+	@ApiModelProperty(value = "标准差")
+	private Double std;
+	@ApiModelProperty(value = "平均调卷难度")
+    private Double avgDifficultyDegree;
+	@ApiModelProperty(value = "差异系数")
+    private Double cdi;
+	@ApiModelProperty(value = "报名人数")
+	private Long signCount;
+	@ApiModelProperty(value = "考试人数")
+    private Long participantCount;
+	@ApiModelProperty(value = "考试人数/报名人数比率(%)")
+	private Double participantRatio;
+	@ApiModelProperty(value = "缺考人数")
+	private Long missCount;
+	@ApiModelProperty(value = "缺考率(%)")
+	private Double missRatio;
+	@ApiModelProperty(value = "满分人数")
+    private Long fullCount;
+	@ApiModelProperty(value = "零分人数")
+    private Long zeroCount;
+	@ApiModelProperty(value = "及格人数")
+    private Long passCount;
+	@ApiModelProperty(value = "及格报名人数占比(%)")
+	private Double passSignRatio;
+	@ApiModelProperty(value = "及格实考人数占比(%)")
+	private Double passParticipantRatio;
+	@ApiModelProperty(value = "分段人数数据")
+	private List<PartitionDataBean> partitionData;
+	public Long getProjectId() {
+		return projectId;
+	}
+	public void setProjectId(Long projectId) {
+		this.projectId = projectId;
+	}
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getExamId() {
+		return examId;
+	}
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getExamCode() {
+		return examCode;
+	}
+	public void setExamCode(String examCode) {
+		this.examCode = examCode;
+	}
+	public Long getSignCount() {
+		return signCount;
+	}
+	public void setSignCount(Long signCount) {
+		this.signCount = signCount;
+	}
+	public Long getParticipantCount() {
+		return participantCount;
+	}
+	public void setParticipantCount(Long participantCount) {
+		this.participantCount = participantCount;
+	}
+	public Long getMissCount() {
+		return missCount;
+	}
+	public void setMissCount(Long missCount) {
+		this.missCount = missCount;
+	}
+	public Double getMissRatio() {
+		return missRatio;
+	}
+	public void setMissRatio(Double missRatio) {
+		this.missRatio = missRatio;
+	}
+	public Long getPassCount() {
+		return passCount;
+	}
+	public void setPassCount(Long passCount) {
+		this.passCount = passCount;
+	}
+	public Double getPassSignRatio() {
+		return passSignRatio;
+	}
+	public void setPassSignRatio(Double passSignRatio) {
+		this.passSignRatio = passSignRatio;
+	}
+	public List<PartitionDataBean> getPartitionData() {
+		return partitionData;
+	}
+	public void setPartitionData(List<PartitionDataBean> partitionData) {
+		this.partitionData = partitionData;
+	}
+	public Double getPassParticipantRatio() {
+		return passParticipantRatio;
+	}
+	public void setPassParticipantRatio(Double passParticipantRatio) {
+		this.passParticipantRatio = passParticipantRatio;
+	}
+	public Double getParticipantRatio() {
+		return participantRatio;
+	}
+	public void setParticipantRatio(Double participantRatio) {
+		this.participantRatio = participantRatio;
+	}
+	public Long getCourseId() {
+		return courseId;
+	}
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+	public String getCourseName() {
+		return courseName;
+	}
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+	public String getCourseCode() {
+		return courseCode;
+	}
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+	public Double getTotalScore() {
+		return totalScore;
+	}
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+	public Double getMaxScore() {
+		return maxScore;
+	}
+	public void setMaxScore(Double maxScore) {
+		this.maxScore = maxScore;
+	}
+	public Double getMinScore() {
+		return minScore;
+	}
+	public void setMinScore(Double minScore) {
+		this.minScore = minScore;
+	}
+	public Double getAvgScore() {
+		return avgScore;
+	}
+	public void setAvgScore(Double avgScore) {
+		this.avgScore = avgScore;
+	}
+	public Double getStd() {
+		return std;
+	}
+	public void setStd(Double std) {
+		this.std = std;
+	}
+	public Double getAvgDifficultyDegree() {
+		return avgDifficultyDegree;
+	}
+	public void setAvgDifficultyDegree(Double avgDifficultyDegree) {
+		this.avgDifficultyDegree = avgDifficultyDegree;
+	}
+	public Double getCdi() {
+		return cdi;
+	}
+	public void setCdi(Double cdi) {
+		this.cdi = cdi;
+	}
+	public Long getFullCount() {
+		return fullCount;
+	}
+	public void setFullCount(Long fullCount) {
+		this.fullCount = fullCount;
+	}
+	public Long getZeroCount() {
+		return zeroCount;
+	}
+	public void setZeroCount(Long zeroCount) {
+		this.zeroCount = zeroCount;
+	}
+
+	
+}

+ 125 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamDataBean.java

@@ -0,0 +1,125 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class ExamDataBean implements JsonSerializable {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 3392394929374130898L;
+
+    @ApiModelProperty(value = "考试名称")
+    private Long rootOrgId;
+
+    @ApiModelProperty(value = "考试名称")
+    private String examName;
+
+    @ApiModelProperty(value = "考试类型")
+    private String examType;
+
+    @ApiModelProperty(value = "考试开始时间")
+    private Date examStartDate;
+
+    @ApiModelProperty(value = "考试结束时间")
+    private Date examEndDate;
+
+    @ApiModelProperty(value = "在线人数")
+    private Integer onlineCount;
+
+    @ApiModelProperty(value = "计划人数")
+    private Integer planCount;
+
+    @ApiModelProperty(value = "完成人数")
+    private Integer completeCount;
+
+    @ApiModelProperty(value = "完成率")
+    private Double completeRatio;
+
+    @ApiModelProperty(value = "考试ID")
+    private Long examId;
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public String getExamName() {
+        return examName;
+    }
+
+    public void setExamName(String examName) {
+        this.examName = examName;
+    }
+
+    public String getExamType() {
+        return examType;
+    }
+
+    public void setExamType(String examType) {
+        this.examType = examType;
+    }
+
+    public Date getExamStartDate() {
+        return examStartDate;
+    }
+
+    public void setExamStartDate(Date examStartDate) {
+        this.examStartDate = examStartDate;
+    }
+
+    public Date getExamEndDate() {
+        return examEndDate;
+    }
+
+    public void setExamEndDate(Date examEndDate) {
+        this.examEndDate = examEndDate;
+    }
+
+    public Integer getOnlineCount() {
+        return onlineCount;
+    }
+
+    public void setOnlineCount(Integer onlineCount) {
+        this.onlineCount = onlineCount;
+    }
+
+    public Integer getPlanCount() {
+        return planCount;
+    }
+
+    public void setPlanCount(Integer planCount) {
+        this.planCount = planCount;
+    }
+
+    public Integer getCompleteCount() {
+        return completeCount;
+    }
+
+    public void setCompleteCount(Integer completeCount) {
+        this.completeCount = completeCount;
+    }
+
+    public Double getCompleteRatio() {
+        return completeRatio;
+    }
+
+    public void setCompleteRatio(Double completeRatio) {
+        this.completeRatio = completeRatio;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+}

+ 129 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamOrgMainTopTen.java

@@ -0,0 +1,129 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class ExamOrgMainTopTen {
+	@ApiModelProperty(value = "实考人数后十")
+	private List<ExamOrgReportMainBean> participant;
+	@ApiModelProperty(value = "实考人数前十")
+	private List<ExamOrgReportMainBean> participantDesc;
+	@ApiModelProperty(value = "实考比例后十")
+	private List<ExamOrgReportMainBean> participantRatio;
+	@ApiModelProperty(value = "实考比例前十")
+	private List<ExamOrgReportMainBean> participantRatioDesc;
+	@ApiModelProperty(value = "缺考人数后十")
+	private List<ExamOrgReportMainBean> miss;
+	@ApiModelProperty(value = "缺考人数前十")
+	private List<ExamOrgReportMainBean> missDesc;
+	@ApiModelProperty(value = "缺考比例数后十")
+	private List<ExamOrgReportMainBean> missRatio;
+	@ApiModelProperty(value = "缺考比例前十")
+	private List<ExamOrgReportMainBean> missRatioDesc;
+	@ApiModelProperty(value = "及格人数后十")
+	private List<ExamOrgReportMainBean> pass;
+	@ApiModelProperty(value = "及格人数前十")
+	private List<ExamOrgReportMainBean> passDesc;
+	@ApiModelProperty(value = "及格/报名人数比例后十")
+	private List<ExamOrgReportMainBean> passSignRatio;
+	@ApiModelProperty(value = "及格/报名人数比例前十")
+	private List<ExamOrgReportMainBean> passSignRatioDesc;
+	@ApiModelProperty(value = "及格/实考人数比例后十")
+	private List<ExamOrgReportMainBean> passParticipantRatio;
+	@ApiModelProperty(value = "及格/实考人数比例前十")
+	private List<ExamOrgReportMainBean> passParticipantRatioDesc;
+	@ApiModelProperty(value = "分段人数")
+	private List<PartitionTopTen<PartitionDataBean>> partition;
+	public List<ExamOrgReportMainBean> getParticipant() {
+		return participant;
+	}
+	public void setParticipant(List<ExamOrgReportMainBean> participant) {
+		this.participant = participant;
+	}
+	public List<ExamOrgReportMainBean> getParticipantDesc() {
+		return participantDesc;
+	}
+	public void setParticipantDesc(List<ExamOrgReportMainBean> participantDesc) {
+		this.participantDesc = participantDesc;
+	}
+	public List<ExamOrgReportMainBean> getMiss() {
+		return miss;
+	}
+	public void setMiss(List<ExamOrgReportMainBean> miss) {
+		this.miss = miss;
+	}
+	public List<ExamOrgReportMainBean> getMissDesc() {
+		return missDesc;
+	}
+	public void setMissDesc(List<ExamOrgReportMainBean> missDesc) {
+		this.missDesc = missDesc;
+	}
+	public List<ExamOrgReportMainBean> getPass() {
+		return pass;
+	}
+	public void setPass(List<ExamOrgReportMainBean> pass) {
+		this.pass = pass;
+	}
+	public List<ExamOrgReportMainBean> getPassDesc() {
+		return passDesc;
+	}
+	public void setPassDesc(List<ExamOrgReportMainBean> passDesc) {
+		this.passDesc = passDesc;
+	}
+	public List<PartitionTopTen<PartitionDataBean>> getPartition() {
+		return partition;
+	}
+	public void setPartition(List<PartitionTopTen<PartitionDataBean>> partition) {
+		this.partition = partition;
+	}
+	public List<ExamOrgReportMainBean> getParticipantRatio() {
+		return participantRatio;
+	}
+	public void setParticipantRatio(List<ExamOrgReportMainBean> participantRatio) {
+		this.participantRatio = participantRatio;
+	}
+	public List<ExamOrgReportMainBean> getParticipantRatioDesc() {
+		return participantRatioDesc;
+	}
+	public void setParticipantRatioDesc(List<ExamOrgReportMainBean> participantRatioDesc) {
+		this.participantRatioDesc = participantRatioDesc;
+	}
+	public List<ExamOrgReportMainBean> getMissRatio() {
+		return missRatio;
+	}
+	public void setMissRatio(List<ExamOrgReportMainBean> missRatio) {
+		this.missRatio = missRatio;
+	}
+	public List<ExamOrgReportMainBean> getMissRatioDesc() {
+		return missRatioDesc;
+	}
+	public void setMissRatioDesc(List<ExamOrgReportMainBean> missRatioDesc) {
+		this.missRatioDesc = missRatioDesc;
+	}
+	public List<ExamOrgReportMainBean> getPassSignRatio() {
+		return passSignRatio;
+	}
+	public void setPassSignRatio(List<ExamOrgReportMainBean> passSignRatio) {
+		this.passSignRatio = passSignRatio;
+	}
+	public List<ExamOrgReportMainBean> getPassSignRatioDesc() {
+		return passSignRatioDesc;
+	}
+	public void setPassSignRatioDesc(List<ExamOrgReportMainBean> passSignRatioDesc) {
+		this.passSignRatioDesc = passSignRatioDesc;
+	}
+	public List<ExamOrgReportMainBean> getPassParticipantRatio() {
+		return passParticipantRatio;
+	}
+	public void setPassParticipantRatio(List<ExamOrgReportMainBean> passParticipantRatio) {
+		this.passParticipantRatio = passParticipantRatio;
+	}
+	public List<ExamOrgReportMainBean> getPassParticipantRatioDesc() {
+		return passParticipantRatioDesc;
+	}
+	public void setPassParticipantRatioDesc(List<ExamOrgReportMainBean> passParticipantRatioDesc) {
+		this.passParticipantRatioDesc = passParticipantRatioDesc;
+	}
+	
+}

+ 152 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamOrgReportMainBean.java

@@ -0,0 +1,152 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class ExamOrgReportMainBean implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1825839376905271279L;
+	@ApiModelProperty(value = "项目id")
+	private Long projectId;
+	@ApiModelProperty(value = "rootOrgId")
+	private Long rootOrgId;
+	@ApiModelProperty(value = "examId")
+	private Long examId;
+	@ApiModelProperty(value = "考试名称")
+	private String examName;
+	@ApiModelProperty(value = "考试code")
+	private String examCode;
+	@ApiModelProperty(value = "中心id")
+	private Long orgId;
+	@ApiModelProperty(value = "中心名称")
+	private String orgName;
+	@ApiModelProperty(value = "中心code")
+	private String orgCode;
+	@ApiModelProperty(value = "报名人数")
+	private Long signCount;
+	@ApiModelProperty(value = "实考人数")
+	private Long participantCount;
+	@ApiModelProperty(value = "实考人数/报名人数比率(%)")
+	private Double participantRatio;
+	@ApiModelProperty(value = "缺考人数")
+	private Long missCount;
+	@ApiModelProperty(value = "缺考率(%)")
+	private Double missRatio;
+	@ApiModelProperty(value = "及格人数")
+	private Long passCount;
+	@ApiModelProperty(value = "及格报名人数占比(%)")
+	private Double passSignRatio;
+	@ApiModelProperty(value = "及格实考人数占比(%)")
+	private Double passParticipantRatio;
+	@ApiModelProperty(value = "分段人数数据")
+	private List<PartitionDataBean> partitionData;
+	public Long getProjectId() {
+		return projectId;
+	}
+	public void setProjectId(Long projectId) {
+		this.projectId = projectId;
+	}
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getExamId() {
+		return examId;
+	}
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getExamCode() {
+		return examCode;
+	}
+	public void setExamCode(String examCode) {
+		this.examCode = examCode;
+	}
+	public Long getOrgId() {
+		return orgId;
+	}
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+	public String getOrgName() {
+		return orgName;
+	}
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+	public String getOrgCode() {
+		return orgCode;
+	}
+	public void setOrgCode(String orgCode) {
+		this.orgCode = orgCode;
+	}
+	public Long getSignCount() {
+		return signCount;
+	}
+	public void setSignCount(Long signCount) {
+		this.signCount = signCount;
+	}
+	public Long getParticipantCount() {
+		return participantCount;
+	}
+	public void setParticipantCount(Long participantCount) {
+		this.participantCount = participantCount;
+	}
+	public Long getMissCount() {
+		return missCount;
+	}
+	public void setMissCount(Long missCount) {
+		this.missCount = missCount;
+	}
+	public Double getMissRatio() {
+		return missRatio;
+	}
+	public void setMissRatio(Double missRatio) {
+		this.missRatio = missRatio;
+	}
+	public Long getPassCount() {
+		return passCount;
+	}
+	public void setPassCount(Long passCount) {
+		this.passCount = passCount;
+	}
+	public Double getPassSignRatio() {
+		return passSignRatio;
+	}
+	public void setPassSignRatio(Double passSignRatio) {
+		this.passSignRatio = passSignRatio;
+	}
+	public List<PartitionDataBean> getPartitionData() {
+		return partitionData;
+	}
+	public void setPartitionData(List<PartitionDataBean> partitionData) {
+		this.partitionData = partitionData;
+	}
+	public Double getPassParticipantRatio() {
+		return passParticipantRatio;
+	}
+	public void setPassParticipantRatio(Double passParticipantRatio) {
+		this.passParticipantRatio = passParticipantRatio;
+	}
+	public Double getParticipantRatio() {
+		return participantRatio;
+	}
+	public void setParticipantRatio(Double participantRatio) {
+		this.participantRatio = participantRatio;
+	}
+
+	
+}

+ 141 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ExamReportMainBean.java

@@ -0,0 +1,141 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class ExamReportMainBean implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5215145697414220201L;
+	@ApiModelProperty(value = "项目id")
+	private Long projectId;
+	@ApiModelProperty(value = "rootOrgId")
+	private Long rootOrgId;
+	@ApiModelProperty(value = "examId")
+	private Long examId;
+	@ApiModelProperty(value = "考试名称")
+	private String examName;
+	@ApiModelProperty(value = "考试code")
+	private String examCode;
+	@ApiModelProperty(value = "报名人数")
+	private Long signCount;
+	@ApiModelProperty(value = "实考人数")
+	private Long participantCount;
+	@ApiModelProperty(value = "实考人数/报名人数比率(%)")
+	private Double participantRatio;
+	@ApiModelProperty(value = "缺考人数")
+	private Long missCount;
+	@ApiModelProperty(value = "缺考率(%)")
+	private Double missRatio;
+	@ApiModelProperty(value = "及格人数")
+	private Long passCount;
+	@ApiModelProperty(value = "及格报名人数占比(%)")
+	private Double passSignRatio;
+	@ApiModelProperty(value = "及格实考人数占比(%)")
+	private Double passParticipantRatio;
+	@ApiModelProperty(value = "分段人数数据")
+	private List<PartitionDataBean> partitionData;
+	public Long getProjectId() {
+		return projectId;
+	}
+	public void setProjectId(Long projectId) {
+		this.projectId = projectId;
+	}
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getExamId() {
+		return examId;
+	}
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getExamCode() {
+		return examCode;
+	}
+	public void setExamCode(String examCode) {
+		this.examCode = examCode;
+	}
+	public Long getSignCount() {
+		return signCount;
+	}
+	public void setSignCount(Long signCount) {
+		this.signCount = signCount;
+	}
+	public Long getParticipantCount() {
+		return participantCount;
+	}
+	public void setParticipantCount(Long participantCount) {
+		this.participantCount = participantCount;
+	}
+	public Long getMissCount() {
+		return missCount;
+	}
+	public void setMissCount(Long missCount) {
+		this.missCount = missCount;
+	}
+	public Double getMissRatio() {
+		return missRatio;
+	}
+	public void setMissRatio(Double missRatio) {
+		this.missRatio = missRatio;
+	}
+	public Long getPassCount() {
+		return passCount;
+	}
+	public void setPassCount(Long passCount) {
+		this.passCount = passCount;
+	}
+	public Double getPassSignRatio() {
+		return passSignRatio;
+	}
+	public void setPassSignRatio(Double passSignRatio) {
+		this.passSignRatio = passSignRatio;
+	}
+	public List<PartitionDataBean> getPartitionData() {
+		return partitionData;
+	}
+	public void setPartitionData(List<PartitionDataBean> partitionData) {
+		this.partitionData = partitionData;
+	}
+	public Double getPassParticipantRatio() {
+		return passParticipantRatio;
+	}
+	public void setPassParticipantRatio(Double passParticipantRatio) {
+		this.passParticipantRatio = passParticipantRatio;
+	}
+	public Double getParticipantRatio() {
+		return participantRatio;
+	}
+	public void setParticipantRatio(Double participantRatio) {
+		this.participantRatio = participantRatio;
+	}
+	public void init(int partitionCount) {
+		this.signCount=0l;
+		this.participantCount=0l;
+		this.missCount=0l;
+		this.passCount=0l;
+		List<PartitionDataBean> partitionData=new ArrayList<PartitionDataBean>();
+		for (int i = 0; i < partitionCount; i++) {
+			PartitionDataBean pb = new PartitionDataBean();
+			pb.setCount(0l);
+			partitionData.add(pb);
+		}
+		this.partitionData=partitionData;
+	}
+	
+}

+ 55 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/PartitionDataBean.java

@@ -0,0 +1,55 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class PartitionDataBean {
+	@ApiModelProperty(value = "考试名称")
+	private String examName;
+	@ApiModelProperty(value = "中心名称")
+	private String orgName;
+	@ApiModelProperty(value = "课程名称")
+	private String courseName;
+	@ApiModelProperty(value = "分段人数")
+	private Long count;
+	@ApiModelProperty(value = "分段人数报名占比(%)")
+	private Double signRatio;
+	@ApiModelProperty(value = "分段人数实考占比(%)")
+	private Double participantRatio;
+	public Long getCount() {
+		return count;
+	}
+	public void setCount(Long count) {
+		this.count = count;
+	}
+	public Double getSignRatio() {
+		return signRatio;
+	}
+	public void setSignRatio(Double signRatio) {
+		this.signRatio = signRatio;
+	}
+	public Double getParticipantRatio() {
+		return participantRatio;
+	}
+	public void setParticipantRatio(Double participantRatio) {
+		this.participantRatio = participantRatio;
+	}
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getOrgName() {
+		return orgName;
+	}
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+	public String getCourseName() {
+		return courseName;
+	}
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+	
+}

+ 57 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/PartitionTopTen.java

@@ -0,0 +1,57 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class PartitionTopTen<T> {
+	@ApiModelProperty(value = "分段人数后十")
+	private List<T> countAsc;
+	@ApiModelProperty(value = "分段人数前十")
+	private List<T> countDesc;
+	@ApiModelProperty(value = "分段人数/报名人数比例后十")
+	private List<T> signRatioAsc;
+	@ApiModelProperty(value = "分段人数/报名人数比例前十")
+	private List<T> signRatioDesc;
+	@ApiModelProperty(value = "分段人数/实考人数比例后十")
+	private List<T> participantRatioAsc;
+	@ApiModelProperty(value = "分段人数/实考人数比例前十")
+	private List<T> participantRatioDesc;
+	public List<T> getCountAsc() {
+		return countAsc;
+	}
+	public void setCountAsc(List<T> countAsc) {
+		this.countAsc = countAsc;
+	}
+	public List<T> getCountDesc() {
+		return countDesc;
+	}
+	public void setCountDesc(List<T> countDesc) {
+		this.countDesc = countDesc;
+	}
+	public List<T> getSignRatioAsc() {
+		return signRatioAsc;
+	}
+	public void setSignRatioAsc(List<T> signRatioAsc) {
+		this.signRatioAsc = signRatioAsc;
+	}
+	public List<T> getSignRatioDesc() {
+		return signRatioDesc;
+	}
+	public void setSignRatioDesc(List<T> signRatioDesc) {
+		this.signRatioDesc = signRatioDesc;
+	}
+	public List<T> getParticipantRatioAsc() {
+		return participantRatioAsc;
+	}
+	public void setParticipantRatioAsc(List<T> participantRatioAsc) {
+		this.participantRatioAsc = participantRatioAsc;
+	}
+	public List<T> getParticipantRatioDesc() {
+		return participantRatioDesc;
+	}
+	public void setParticipantRatioDesc(List<T> participantRatioDesc) {
+		this.participantRatioDesc = participantRatioDesc;
+	}
+	
+}

+ 180 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/ProjectBean.java

@@ -0,0 +1,180 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.reports.base.enums.AnalyseType;
+import cn.com.qmth.examcloud.core.reports.base.enums.DataOrigin;
+import cn.com.qmth.examcloud.core.reports.base.enums.ReportStatus;
+import io.swagger.annotations.ApiModelProperty;
+
+public class ProjectBean implements JsonSerializable {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -5807098559991360206L;
+	
+	@ApiModelProperty(value = "项目id")
+	private Long id;
+	@ApiModelProperty(value = "rootOrgId")
+	private Long rootOrgId;
+	@ApiModelProperty(value = "及格线")
+	private Double passScore;
+	@ApiModelProperty(value = "满分分数")
+	private Double totalScore;
+	@ApiModelProperty(value = "分数段分段详情")
+	private List<Double> partitionDetails;
+	@ApiModelProperty(value = "分段数量")
+	private Integer partitionCount;
+    @ApiModelProperty(value = "项目名称")
+	private String name;
+    @ApiModelProperty(value = "分析类型")
+	@Enumerated(EnumType.STRING)
+	private AnalyseType analyseType;
+    @ApiModelProperty(value = "分析类型名称")
+	private String analyseTypeName;
+    @ApiModelProperty(value = "报表状态")
+	@Enumerated(EnumType.STRING)
+	private ReportStatus reportStatus;
+    @ApiModelProperty(value = "报表状态名称")
+	private String reportStatusName;
+    @ApiModelProperty(value = "数据来源")
+	@Enumerated(EnumType.STRING)
+	private DataOrigin dataOrigin;
+    @ApiModelProperty(value = "数据来源名称")
+	private String dataOriginName;
+    @ApiModelProperty(value = "样本数量")
+	private Integer sampleCount;
+    @ApiModelProperty(value = "课程数量")
+	private Integer courseCount;
+    @ApiModelProperty(value = "中心数量")
+	private Integer orgCount;
+    @ApiModelProperty(value = "所选考试id")
+	private List<Long> examIds;
+    @ApiModelProperty(value = "备注")
+	private String remarks;
+    @ApiModelProperty(value = "启用禁用")
+	private Boolean enable;
+	public Long getId() {
+		return id;
+	}
+	public void setId(Long id) {
+		this.id = id;
+	}
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Double getPassScore() {
+		return passScore;
+	}
+	public void setPassScore(Double passScore) {
+		this.passScore = passScore;
+	}
+	public Double getTotalScore() {
+		return totalScore;
+	}
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+	public List<Double> getPartitionDetails() {
+		return partitionDetails;
+	}
+	public void setPartitionDetails(List<Double> partitionDetails) {
+		this.partitionDetails = partitionDetails;
+	}
+	public Integer getPartitionCount() {
+		return partitionCount;
+	}
+	public void setPartitionCount(Integer partitionCount) {
+		this.partitionCount = partitionCount;
+	}
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	public AnalyseType getAnalyseType() {
+		return analyseType;
+	}
+	public void setAnalyseType(AnalyseType analyseType) {
+		this.analyseType = analyseType;
+	}
+	public ReportStatus getReportStatus() {
+		return reportStatus;
+	}
+	public void setReportStatus(ReportStatus reportStatus) {
+		this.reportStatus = reportStatus;
+	}
+	public DataOrigin getDataOrigin() {
+		return dataOrigin;
+	}
+	public void setDataOrigin(DataOrigin dataOrigin) {
+		this.dataOrigin = dataOrigin;
+	}
+	public Integer getSampleCount() {
+		return sampleCount;
+	}
+	public void setSampleCount(Integer sampleCount) {
+		this.sampleCount = sampleCount;
+	}
+	public Integer getCourseCount() {
+		return courseCount;
+	}
+	public void setCourseCount(Integer courseCount) {
+		this.courseCount = courseCount;
+	}
+	public Integer getOrgCount() {
+		return orgCount;
+	}
+	public void setOrgCount(Integer orgCount) {
+		this.orgCount = orgCount;
+	}
+	public List<Long> getExamIds() {
+		return examIds;
+	}
+	public void setExamIds(List<Long> examIds) {
+		this.examIds = examIds;
+	}
+	public String getRemarks() {
+		return remarks;
+	}
+	public void setRemarks(String remarks) {
+		this.remarks = remarks;
+	}
+	public Boolean getEnable() {
+		return enable;
+	}
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+	public String getAnalyseTypeName() {
+		return analyseTypeName;
+	}
+	public void setAnalyseTypeName(String analyseTypeName) {
+		this.analyseTypeName = analyseTypeName;
+	}
+	public String getReportStatusName() {
+		return reportStatusName;
+	}
+	public void setReportStatusName(String reportStatusName) {
+		this.reportStatusName = reportStatusName;
+	}
+	public String getDataOriginName() {
+		return dataOriginName;
+	}
+	public void setDataOriginName(String dataOriginName) {
+		this.dataOriginName = dataOriginName;
+	}
+
+	
+	
+}

+ 79 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/QueryProjectPageReq.java

@@ -0,0 +1,79 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.reports.base.enums.AnalyseType;
+import cn.com.qmth.examcloud.core.reports.base.enums.DataOrigin;
+import io.swagger.annotations.ApiModelProperty;
+
+public class QueryProjectPageReq implements JsonSerializable {
+
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -845826847340443744L;
+	// 项目id
+    @ApiModelProperty(value = "项目id")
+	private Long id;
+	// 项目名
+    @ApiModelProperty(value = "项目名称")
+	private String name;
+
+	// 分析类型
+    @ApiModelProperty(value = "分析类型")
+	@Enumerated(EnumType.STRING)
+	private AnalyseType analyseType;
+
+	// 数据来源
+    @ApiModelProperty(value = "数据来源")
+	@Enumerated(EnumType.STRING)
+	private DataOrigin dataOrigin;
+    // 启用禁用
+    @ApiModelProperty(value = "启用禁用")
+	private Boolean enable;
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public AnalyseType getAnalyseType() {
+		return analyseType;
+	}
+
+	public void setAnalyseType(AnalyseType analyseType) {
+		this.analyseType = analyseType;
+	}
+
+	public DataOrigin getDataOrigin() {
+		return dataOrigin;
+	}
+
+	public void setDataOrigin(DataOrigin dataOrigin) {
+		this.dataOrigin = dataOrigin;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	
+}

+ 53 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/StudentCountBean.java

@@ -0,0 +1,53 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class StudentCountBean implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 7681687345714701612L;
+	@ApiModelProperty(value = "学校名称")
+	private String rootOrgName;
+	@ApiModelProperty(value = "rootOrgId")
+	private Long rootOrgId;
+	@ApiModelProperty(value = "总人数")
+	private Integer totalCount;
+	@ApiModelProperty(value = "在线人数")
+	private Integer onlineCount;
+	@ApiModelProperty(value = "在考人数")
+	private Integer onExamCount;
+	public String getRootOrgName() {
+		return rootOrgName;
+	}
+	public void setRootOrgName(String rootOrgName) {
+		this.rootOrgName = rootOrgName;
+	}
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Integer getTotalCount() {
+		return totalCount;
+	}
+	public void setTotalCount(Integer totalCount) {
+		this.totalCount = totalCount;
+	}
+	public Integer getOnlineCount() {
+		return onlineCount;
+	}
+	public void setOnlineCount(Integer onlineCount) {
+		this.onlineCount = onlineCount;
+	}
+	public Integer getOnExamCount() {
+		return onExamCount;
+	}
+	public void setOnExamCount(Integer onExamCount) {
+		this.onExamCount = onExamCount;
+	}
+
+}

+ 29 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/StudentCumulativeCountBean.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class StudentCumulativeCountBean implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3717372589234196602L;
+	@ApiModelProperty(value = "人数")
+	private Integer totalCount;
+	@ApiModelProperty(value = "日期")
+	private String reportDay;
+	public Integer getTotalCount() {
+		return totalCount;
+	}
+	public void setTotalCount(Integer totalCount) {
+		this.totalCount = totalCount;
+	}
+	public String getReportDay() {
+		return reportDay;
+	}
+	public void setReportDay(String reportDay) {
+		this.reportDay = reportDay;
+	}
+
+}

+ 60 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/UpdateProjectReq.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import org.hibernate.validator.constraints.Length;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class UpdateProjectReq implements JsonSerializable {
+
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -6517778374791797274L;
+
+	// 项目id
+	@NotNull(message = "项目id不能为空")
+    @ApiModelProperty(value = "项目id")
+	private Long id;
+	// 项目名
+	@NotBlank(message = "项目名称不能为空")
+	@Length(min = 1, max = 100, message = "项目名称长度需要在1和100之间")
+    @ApiModelProperty(value = "项目名称")
+	private String name;
+
+
+	// 备注
+	@Length(min = 0, max = 100, message = "备注长度需要在0和100之间")
+	private String remarks;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getRemarks() {
+		return remarks;
+	}
+
+	public void setRemarks(String remarks) {
+		this.remarks = remarks;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	
+}

+ 56 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/bean/UpdateProjectScoreReq.java

@@ -0,0 +1,56 @@
+package cn.com.qmth.examcloud.core.reports.base.bean;
+
+import java.util.List;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class UpdateProjectScoreReq implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5900413305905814904L;
+	@NotNull(message = "项目id不能为空")
+	@ApiModelProperty(value = "项目id")
+	private Long id;
+	@NotNull(message = "项目满分分数不能为空")
+	@ApiModelProperty(value = "项目满分分数")
+	private Double totalScore;
+	@NotNull(message = "项目及格分数不能为空")
+	@ApiModelProperty(value = "项目及格分数")
+	private Double passScore;
+	@NotEmpty(message = "项目分段分数不能为空")
+	@ApiModelProperty(value = "项目分段分数")
+	private List<Double> partitionDetails;
+	public Long getId() {
+		return id;
+	}
+	public void setId(Long id) {
+		this.id = id;
+	}
+	public Double getTotalScore() {
+		return totalScore;
+	}
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+	public Double getPassScore() {
+		return passScore;
+	}
+	public void setPassScore(Double passScore) {
+		this.passScore = passScore;
+	}
+	public List<Double> getPartitionDetails() {
+		return partitionDetails;
+	}
+	public void setPartitionDetails(List<Double> partitionDetails) {
+		this.partitionDetails = partitionDetails;
+	}
+	
+	
+
+}

+ 24 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/enums/AnalyseType.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.examcloud.core.reports.base.enums;
+
+public enum AnalyseType {
+	SINGLE("单项分析"), TREND("趋势分析");
+	// ===========================================================================
+
+	/**
+	 * 描述
+	 */
+	private String desc;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param desc
+	 */
+	private AnalyseType(String desc) {
+		this.desc = desc;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+}

+ 24 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/enums/DataOrigin.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.examcloud.core.reports.base.enums;
+
+public enum DataOrigin {
+	SYNC("同步"), IMPORT("导入");
+	// ===========================================================================
+
+	/**
+	 * 描述
+	 */
+	private String desc;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param desc
+	 */
+	private DataOrigin(String desc) {
+		this.desc = desc;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+}

+ 30 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/enums/ReportStatus.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.examcloud.core.reports.base.enums;
+
+public enum ReportStatus {
+	NONE("未计算"),
+	WAITCOMPUTE("待计算"), 
+	COMPUTING("计算中"), 
+	SUCCESS("计算成功"),
+	FAIL("计算失败"),
+	STOP("计算终止")
+	;
+	// ===========================================================================
+
+	/**
+	 * 描述
+	 */
+	private String desc;
+
+	/**
+	 * 构造函数
+	 *
+	 * @param desc
+	 */
+	private ReportStatus(String desc) {
+		this.desc = desc;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+}

+ 52 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/BatchGetDataUtil.java

@@ -0,0 +1,52 @@
+package cn.com.qmth.examcloud.core.reports.base.util;
+
+import java.util.List;
+
+/**
+ *	多次批量获取数据
+ *	需重写getData方法
+ * @author xiatian
+ * @param <R> 结果类
+ * @param <P> 参数类
+ */
+public  class BatchGetDataUtil<R,P> {
+	/**
+	 * @param resultList 全部结果集合
+	 * @param paramList 全部参数集合
+	 * @param batchSize 每批参数数量
+	 */
+	public final void getDataForBatch(List<R> resultList,List<P> paramList,int batchSize) {
+		if(resultList==null||paramList==null||paramList.size()==0) {
+			return;
+		}
+		if(paramList.size()<=batchSize) {
+			List<R> temlist = getData(paramList);
+			if(temlist!=null&&temlist.size()>0) {
+				resultList.addAll(temlist);
+			}
+		}else {
+			int size = paramList.size();
+			int len=batchSize;
+			int count = (size + len - 1) / len;
+
+			for (int i = 0; i < count; i++) {
+				List<P> subList = paramList.subList(i * len, ((i + 1) * len > size ? size : len * (i + 1)));
+				List<R> temlist = getData(subList);
+				if(temlist!=null&&temlist.size()>0) {
+					resultList.addAll(temlist);
+				}
+			}
+		}
+	}
+	/**
+	 * 	Need Override
+	 * 	每批获取数据方法
+	 * @param <R>
+	 * @param <P>
+	 * @param paramList 获取每批数据时参数
+	 * @return
+	 */
+	public  List<R> getData(List<P> paramList) {
+		return null;
+	}
+}

+ 153 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/excel/ExportUtils.java

@@ -0,0 +1,153 @@
+package cn.com.qmth.examcloud.core.reports.base.util.excel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * excel导出工具
+ */
+public class ExportUtils {
+	private static final Logger logger = LoggerFactory.getLogger(ExportUtils.class);
+	private static final String DEFALUT_CONTENT_TYPE = "application/vnd.ms-excel";
+
+	private static final String DEFALUT_EXT = ".xlsx";
+
+	public static void exportExcel(String fileName, List<SheetData> data, HttpServletResponse response) {
+		try {
+
+			response.setHeader("Content-Disposition",
+					"inline; filename=" + URLEncoder.encode(fileName, "UTF-8") + DEFALUT_EXT);
+			response.setContentType(DEFALUT_CONTENT_TYPE);
+			ServletOutputStream outputStream = response.getOutputStream();
+			write(data, outputStream);
+			outputStream.flush();
+			outputStream.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	private static void write(List<SheetData> data, OutputStream out) {
+		// 声明一个工作薄
+		XSSFWorkbook workbook = new XSSFWorkbook();
+		try {
+			for (SheetData sheetData : data) {
+				// 生成一个表格
+				XSSFSheet sheet = workbook.createSheet(sheetData.getName());
+
+				// 生成一个样式
+				XSSFCellStyle style = workbook.createCellStyle();
+
+				// 产生表格标题行
+				List<String> header = sheetData.getHeader();
+				int[] colwidth = new int[header.size()];
+				XSSFRow row = sheet.createRow(0);
+				for (short i = 0; i < header.size(); i++) {
+					colwidth[i] = 15;
+					XSSFCell cell = row.createCell(i);
+					cell.setCellStyle(style);
+					XSSFRichTextString text = new XSSFRichTextString(header.get(i));
+					cell.setCellValue(text);
+					int textlen = length(header.get(i));
+					if (textlen > colwidth[i]) {
+						colwidth[i] = textlen;
+					}
+				}
+
+				List<Object[]> rows = sheetData.getData();
+				// 遍历集合数据,产生数据行
+				for (short r = 0; r < rows.size(); r++) {
+					row = sheet.createRow(r + 1);
+					Object[] rowdata = rows.get(r);
+					for (short i = 0; i < header.size(); i++) {
+						XSSFCell cell = row.createCell(i);
+						Object value = rowdata[i];
+						// 判断值的类型后进行强制类型转换
+						String textValue = null;
+						if (value == null) {
+							textValue = "";
+						} else if (value instanceof Boolean) {
+							boolean bValue = (Boolean) value;
+							textValue = "是";
+							if (!bValue) {
+								textValue = "否";
+							}
+						} else if (value instanceof Date) {
+							Date date = (Date) value;
+							SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+							textValue = sdf.format(date);
+						} else {
+							// 其它数据类型都当作字符串简单处理
+							textValue = String.valueOf(value);
+						}
+						// 利用正则表达式判断textValue是否全部由数字组成
+						if (textValue != null) {
+							Pattern p = Pattern.compile("^\\d+(\\.\\d+)?$");
+							Matcher matcher = p.matcher(textValue);
+							if (matcher.matches()) {
+								// 是数字当作double处理
+								cell.setCellValue(Double.parseDouble(textValue));
+							} else {
+								XSSFRichTextString richString = new XSSFRichTextString(textValue);
+
+								cell.setCellValue(richString);
+							}
+						} else {
+							textValue = "";
+						}
+						int textlen = length(textValue);
+						if (textlen > colwidth[i]) {
+							colwidth[i] = textlen;
+						}
+					}
+
+				}
+				for (int i = 0; i < colwidth.length; i++) {
+					sheet.setColumnWidth(i, 286 * colwidth[i]);
+				}
+			}
+			workbook.write(out);
+		} catch (Exception e) {
+			logger.error("导出出错", e);
+		} finally {
+			try {
+				workbook.close();
+			} catch (IOException e) {
+				logger.error("workbook close 出错", e);
+			}
+		}
+
+	}
+
+	private static int length(String value) {
+		int valueLength = 0;
+		String chinese = "[\u4e00-\u9fa5]";
+		for (int i = 0; i < value.length(); i++) {
+			String temp = value.substring(i, i + 1);
+			if (temp.matches(chinese)) {
+				valueLength += 2;
+			} else {
+				valueLength += 1;
+			}
+		}
+		return valueLength;
+	}
+}

+ 28 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/excel/SheetData.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.reports.base.util.excel;
+
+import java.util.List;
+
+public class SheetData {
+	private String name;
+	private List<String> header;
+	private List<Object[]> data;
+	public String getName() {
+		return name;
+	}
+	public void setName(String name) {
+		this.name = name;
+	}
+	public List<Object[]> getData() {
+		return data;
+	}
+	public void setData(List<Object[]> data) {
+		this.data = data;
+	}
+	public List<String> getHeader() {
+		return header;
+	}
+	public void setHeader(List<String> header) {
+		this.header = header;
+	}
+	
+}

+ 167 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/ActiveDataUtil.java

@@ -0,0 +1,167 @@
+package cn.com.qmth.examcloud.core.reports.base.util.online;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+
+public class ActiveDataUtil {
+	private final static String spe = "_";
+	private final static Long defTimeOut = 5 * 60 * 1000L;
+	private static String studentLoginDataReportDay=DateUtil.format(new Date(), DateUtil.DatePatterns.YYYY_MM_DD);
+	private final static Map<String, Map<Long, Long>> studentLoginData = new ConcurrentHashMap<String, Map<Long, Long>>();
+	private final static Map<String, Map<Long, Long>> userActiveData = new ConcurrentHashMap<String, Map<Long, Long>>();
+	private final static Map<String, Map<Long, Long>> studentActiveData = new ConcurrentHashMap<String, Map<Long, Long>>();
+	private final static Map<String, Map<Long, Long>> examStudentActiveData = new ConcurrentHashMap<String, Map<Long, Long>>();
+
+	public static void updateUserActive(UserActive ac) {
+		String key = ac.getKey(spe);
+		Map<Long, Long> map = userActiveData.get(key);
+		if (map == null) {
+			map = new ConcurrentHashMap<Long, Long>();
+			userActiveData.put(key, map);
+		}
+		map.put(ac.getUserId(), ac.getActiveTime());
+	}
+
+	public static void updateStudentActive(StudentActive ac) {
+		String key = ac.getKey(spe);
+		Map<Long, Long> map = studentActiveData.get(key);
+		if (map == null) {
+			map = new ConcurrentHashMap<Long, Long>();
+			studentActiveData.put(key, map);
+		}
+		map.put(ac.getStudentId(), ac.getActiveTime());
+	}
+
+	public static void updateExamStudentActive(ExamStudentActive ac) {
+		String key = ac.getKey(spe);
+		Map<Long, Long> map = examStudentActiveData.get(key);
+		if (map == null) {
+			map = new ConcurrentHashMap<Long, Long>();
+			examStudentActiveData.put(key, map);
+		}
+		map.put(ac.getStudentId(), ac.getActiveTime());
+	}
+
+	public static void updateStudentlogin(StudentLogin sl) {
+		String key = sl.getKey(spe);
+		Map<Long, Long> map = studentLoginData.get(key);
+		if (map == null) {
+			map = new ConcurrentHashMap<Long, Long>();
+			studentLoginData.put(key, map);
+		}
+		map.put(sl.getStudentId(), sl.getActiveTime());
+	}
+
+	private static void clearTimeOutUserActive(Long timeOut) {
+		if (timeOut == null) {
+			timeOut = defTimeOut;
+		}
+		Date d = new Date();
+		Long now = d.getTime();
+		for (Map<Long, Long> map : userActiveData.values()) {
+			for (Long k : map.keySet()) {
+				if (now - map.get(k) > timeOut) {
+					map.remove(k);
+				}
+			}
+		}
+	}
+
+	private static void clearTimeOutStudentActive(Long timeOut) {
+		if (timeOut == null) {
+			timeOut = defTimeOut;
+		}
+		Date d = new Date();
+		Long now = d.getTime();
+		for (Map<Long, Long> map : studentActiveData.values()) {
+			for (Long k : map.keySet()) {
+				if (now - map.get(k) > timeOut) {
+					map.remove(k);
+				}
+			}
+		}
+	}
+
+	private static void clearTimeOutExamStudentActive(Long timeOut) {
+		if (timeOut == null) {
+			timeOut = defTimeOut;
+		}
+		Date d = new Date();
+		Long now = d.getTime();
+		for (Map<Long, Long> map : examStudentActiveData.values()) {
+			for (Long k : map.keySet()) {
+				if (now - map.get(k) > timeOut) {
+					map.remove(k);
+				}
+			}
+		}
+	}
+
+	public static List<OnlineCount> getUserCount(Long timeOut) {
+		clearTimeOutUserActive(timeOut);
+		List<OnlineCount> ret = new ArrayList<OnlineCount>();
+		for (String key : userActiveData.keySet()) {
+			OnlineCount oc = new OnlineCount();
+			String[] ks = key.split(spe);
+			oc.setRootOrgId(Long.valueOf(ks[0]));
+			oc.setOnlineCount(userActiveData.get(key).size());
+			ret.add(oc);
+		}
+		return ret;
+	}
+
+	public static List<OnlineCount> getStudentCount(Long timeOut) {
+		clearTimeOutStudentActive(timeOut);
+		List<OnlineCount> ret = new ArrayList<OnlineCount>();
+		for (String key : studentActiveData.keySet()) {
+			OnlineCount oc = new OnlineCount();
+			String[] ks = key.split(spe);
+			oc.setRootOrgId(Long.valueOf(ks[0]));
+			oc.setOnlineCount(studentActiveData.get(key).size());
+			ret.add(oc);
+		}
+		return ret;
+	}
+
+	public static List<OnlineCount> getExamStudentCount(Long timeOut) {
+		clearTimeOutExamStudentActive(timeOut);
+		List<OnlineCount> ret = new ArrayList<OnlineCount>();
+		for (String key : examStudentActiveData.keySet()) {
+			OnlineCount oc = new OnlineCount();
+			String[] ks = key.split(spe);
+			oc.setRootOrgId(Long.valueOf(ks[0]));
+			oc.setExamId(Long.valueOf(ks[1]));
+			oc.setOnlineCount(examStudentActiveData.get(key).size());
+			ret.add(oc);
+		}
+		return ret;
+	}
+
+	public static List<OnlineCount> getStudentLoginCount() {
+		List<OnlineCount> ret = new ArrayList<OnlineCount>();
+		for (String key : studentLoginData.keySet()) {
+			OnlineCount oc = new OnlineCount();
+			String[] ks = key.split(spe);
+			oc.setRootOrgId(Long.valueOf(ks[0]));
+			oc.setOnlineCount(studentLoginData.get(key).size());
+			ret.add(oc);
+		}
+		return ret;
+	}
+
+	public static void clearStudentLoginCount() {
+		studentLoginDataReportDay=DateUtil.format(new Date(), DateUtil.DatePatterns.YYYY_MM_DD);
+		studentLoginData.clear();
+	}
+
+	public static String getStudentLoginDataReportDay() {
+		return studentLoginDataReportDay;
+	}
+	
+	
+}

+ 35 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/ExamStudentActive.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.reports.base.util.online;
+
+public class ExamStudentActive {
+	private Long rootOrgId;
+	private Long examId;
+	private Long studentId;
+	private Long activeTime;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getExamId() {
+		return examId;
+	}
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+	public Long getStudentId() {
+		return studentId;
+	}
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+	public Long getActiveTime() {
+		return activeTime;
+	}
+	public void setActiveTime(Long activeTime) {
+		this.activeTime = activeTime;
+	}
+	public String getKey(String spe) {
+		return rootOrgId+spe+examId;
+	}
+}

+ 26 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/OnlineCount.java

@@ -0,0 +1,26 @@
+package cn.com.qmth.examcloud.core.reports.base.util.online;
+
+public class OnlineCount {
+	private Long examId;
+	private Long rootOrgId;
+	private Integer onlineCount;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Integer getOnlineCount() {
+		return onlineCount;
+	}
+	public void setOnlineCount(Integer onlineCount) {
+		this.onlineCount = onlineCount;
+	}
+	public Long getExamId() {
+		return examId;
+	}
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+	
+}

+ 28 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/StudentActive.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.reports.base.util.online;
+
+public class StudentActive {
+	private Long rootOrgId;
+	private Long studentId;
+	private Long activeTime;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getStudentId() {
+		return studentId;
+	}
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+	public Long getActiveTime() {
+		return activeTime;
+	}
+	public void setActiveTime(Long activeTime) {
+		this.activeTime = activeTime;
+	}
+	public String getKey(String spe) {
+		return rootOrgId+spe;
+	}
+}

+ 28 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/StudentLogin.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.reports.base.util.online;
+
+public class StudentLogin {
+	private Long rootOrgId;
+	private Long studentId;
+	private Long activeTime;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getStudentId() {
+		return studentId;
+	}
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+	public Long getActiveTime() {
+		return activeTime;
+	}
+	public void setActiveTime(Long activeTime) {
+		this.activeTime = activeTime;
+	}
+	public String getKey(String spe) {
+		return rootOrgId+spe;
+	}
+}

+ 28 - 0
examcloud-core-reports-base/src/main/java/cn/com/qmth/examcloud/core/reports/base/util/online/UserActive.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.reports.base.util.online;
+
+public class UserActive {
+	private Long rootOrgId;
+	private Long userId;
+	private Long activeTime;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getUserId() {
+		return userId;
+	}
+	public void setUserId(Long userId) {
+		this.userId = userId;
+	}
+	public Long getActiveTime() {
+		return activeTime;
+	}
+	public void setActiveTime(Long activeTime) {
+		this.activeTime = activeTime;
+	}
+	public String getKey(String spe) {
+		return rootOrgId+spe;
+	}
+}

+ 22 - 0
examcloud-core-reports-dao/pom.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+		<artifactId>examcloud-core-reports</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-reports-dao</artifactId>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+			<artifactId>examcloud-core-reports-base</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+	</dependencies>
+
+</project>

+ 16 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamCourseDataReportRepo.java

@@ -0,0 +1,16 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+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 cn.com.qmth.examcloud.core.reports.dao.entity.ExamCourseDataReportEntity;
+
+public interface ExamCourseDataReportRepo extends JpaRepository<ExamCourseDataReportEntity, Long>,
+		JpaSpecificationExecutor<ExamCourseDataReportEntity> {
+	
+	@Modifying
+	@Query(value = "delete from ec_r_exam_course_data_report where project_id =?1 and root_org_id=?2 ", nativeQuery = true)
+	public void deleteByProject(Long projectId, Long rootOrgId);
+}

+ 12 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamDataRepo.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.ExamDataEntity;
+
+public interface ExamDataRepo extends JpaRepository<ExamDataEntity, Long>, JpaSpecificationExecutor<ExamDataEntity> {
+
+    public ExamDataEntity findByRootOrgIdAndExamId(Long rootOrgId, Long examId);
+
+}

+ 16 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamOrgReportRepo.java

@@ -0,0 +1,16 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+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 cn.com.qmth.examcloud.core.reports.dao.entity.ExamOrgReportEntity;
+
+public interface ExamOrgReportRepo extends JpaRepository<ExamOrgReportEntity, Long>,
+		JpaSpecificationExecutor<ExamOrgReportEntity> {
+	
+	@Modifying
+	@Query(value = "delete from ec_r_exam_org_report where project_id =?1 and root_org_id=?2 ", nativeQuery = true)
+	public void deleteByProject(Long projectId, Long rootOrgId);
+}

+ 25 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ExamStudentCountRepo.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+import java.util.List;
+
+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 cn.com.qmth.examcloud.core.reports.dao.entity.ExamStudentCountEntity;
+
+public interface ExamStudentCountRepo extends JpaRepository<ExamStudentCountEntity, Long>,
+		JpaSpecificationExecutor<ExamStudentCountEntity> {
+	public ExamStudentCountEntity findByRootOrgIdAndExamId(Long rootOrgId,Long examId);
+	
+	@Modifying
+	@Query(value = "update ec_r_exam_student_count set online_count=0", nativeQuery = true)
+	public void resetAllCount();
+	
+	@Query(value = "select * from ec_r_exam_student_count where root_org_id in(?1)", nativeQuery = true)
+	public List<ExamStudentCountEntity> getByRootOrgIds(List<Long> rootOrgIds);
+	
+	@Query(value = "select * from ec_r_exam_student_count where exam_id in(?1) and root_org_id=(?2)", nativeQuery = true)
+	public List<ExamStudentCountEntity> getByExamIdsRootOrgId(List<Long> examIds,Long rootOrgId);
+}

+ 24 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/ProjectRepo.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+import java.util.List;
+
+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 cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+
+public interface ProjectRepo extends JpaRepository<ProjectEntity, Long>,
+		JpaSpecificationExecutor<ProjectEntity> {
+	@Modifying
+	@Query(value = "update ec_r_project set enable = ?3 where id in(?1) and root_org_id=?2 ", nativeQuery = true)
+	int updateEnable(List<Long> ids, Long rootOrgId, Boolean enable);
+	@Modifying
+	@Query(value = "update ec_r_project set course_count = ?2,org_count=?3 where id =?1 ", nativeQuery = true)
+	int updateProjectCourseOrgCount(Long id, Integer courseCount, Integer orgCount);
+	@Modifying
+	int deleteByIdAndRootOrgId(Long id, Long rootOrgId);
+	@Query(value = "select * from ec_r_project where id in(?1) and root_org_id=?2 ", nativeQuery = true)
+	List<ProjectEntity> getByIds(List<Long> ids, Long rootOrgId);
+}

+ 26 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/StudentCountRepo.java

@@ -0,0 +1,26 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+import java.util.List;
+
+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 cn.com.qmth.examcloud.core.reports.dao.entity.StudentCountEntity;
+
+public interface StudentCountRepo
+        extends JpaRepository<StudentCountEntity, Long>, JpaSpecificationExecutor<StudentCountEntity> {
+
+    public StudentCountEntity findByRootOrgId(Long rootOrgId);
+
+    @Modifying
+    @Query(value = "update ec_r_student_count set online_count=0", nativeQuery = true)
+    public void resetAllCount();
+
+    @Query(value = "select * from ec_r_student_count where root_org_id in(?1)", nativeQuery = true)
+    public List<StudentCountEntity> getCountByRootOrgIds(List<Long> rootOrgIds);
+
+    @Query(value = "select sum(online_count) from ec_r_student_count", nativeQuery = true)
+    public Long getSumOnlineCount();
+}

+ 12 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/StudentCumulativeCountRepo.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.StudentCumulativeCountEntity;
+
+public interface StudentCumulativeCountRepo extends JpaRepository<StudentCumulativeCountEntity, Long>,
+		JpaSpecificationExecutor<StudentCumulativeCountEntity> {
+	public StudentCumulativeCountEntity findByRootOrgIdAndReportDay(Long rootOrgId,String reportDay);
+	
+}

+ 17 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/StudentTotalCountRepo.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.StudentTotalCountEntity;
+
+public interface StudentTotalCountRepo
+        extends JpaRepository<StudentTotalCountEntity, Long>, JpaSpecificationExecutor<StudentTotalCountEntity> {
+
+    public StudentTotalCountEntity findByRootOrgId(Long rootOrgId);
+
+    @Query(value = "select sum(total_count) from ec_r_student_total_count", nativeQuery = true)
+    public Long getSumTotalCount();
+
+}

+ 17 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/UserCountRepo.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.examcloud.core.reports.dao;
+
+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 cn.com.qmth.examcloud.core.reports.dao.entity.UserCountEntity;
+
+public interface UserCountRepo extends JpaRepository<UserCountEntity, Long>,
+		JpaSpecificationExecutor<UserCountEntity> {
+	public UserCountEntity findByRootOrgId(Long rootOrgId);
+	
+	@Modifying
+	@Query(value = "update ec_r_user_count set online_count=0", nativeQuery = true)
+	public void resetAllCount();
+}

+ 208 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamCourseDataReportEntity.java

@@ -0,0 +1,208 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+
+@Entity
+@Table(name = "ec_r_exam_course_data_report",indexes = {
+		@Index(name = "IDX_R_EXAM_COURSE_DATA_REPORT_01", columnList = "projectId,rootOrgId,examId,courseId", unique = true),
+		@Index(name = "IDX_R_EXAM_COURSE_DATA_REPORT_02", columnList = "projectId", unique = false),
+		@Index(name = "IDX_R_EXAM_COURSE_DATA_REPORT_03", columnList = "rootOrgId", unique = false),
+		@Index(name = "IDX_R_EXAM_COURSE_DATA_REPORT_04", columnList = "examId", unique = false),
+		@Index(name = "IDX_R_EXAM_COURSE_DATA_REPORT_05", columnList = "courseId", unique = false)})
+public class ExamCourseDataReportEntity extends IdEntity {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -8566179598613256946L;
+	//项目id
+	@NotNull
+    private Long projectId;
+	//rootOrgId
+    @NotNull
+    private Long rootOrgId;
+    //考试id
+    @NotNull
+    private Long examId;
+    //考试名称
+    @NotNull
+    private String examName;
+    //考试code
+    @NotNull
+    private String examCode;
+    //课程id
+    @NotNull
+    private Long courseId;
+    //课程名称
+    @NotNull
+    private String courseName;
+    //课程code
+    @NotNull
+    private String courseCode;
+    //满分
+    private Double totalScore;
+    //最高分
+    private Double maxScore;
+    //最低分
+    private Double minScore;
+    //平均分
+    private Double avgScore;
+    //标准差
+    private Double std;
+    //平均调卷难度
+    private Double avgDifficultyDegree;
+    //差异系数
+    private Double cdi;
+    //报名人数
+    @NotNull
+    private Long signCount;
+    //考试人数
+    @NotNull
+    private Long participantCount;
+    //满分人数
+    @NotNull
+    private Long fullCount;
+    //零分人数
+    @NotNull
+    private Long zeroCount;
+    //及格人数
+    @NotNull
+    private Long passCount;
+    //分段人数数据
+    @NotNull
+    private String partitionData;
+	public Long getProjectId() {
+		return projectId;
+	}
+	public void setProjectId(Long projectId) {
+		this.projectId = projectId;
+	}
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getCourseId() {
+		return courseId;
+	}
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+	public String getCourseName() {
+		return courseName;
+	}
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+	public String getCourseCode() {
+		return courseCode;
+	}
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+	public Double getTotalScore() {
+		return totalScore;
+	}
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+	public Double getMaxScore() {
+		return maxScore;
+	}
+	public void setMaxScore(Double maxScore) {
+		this.maxScore = maxScore;
+	}
+	public Double getMinScore() {
+		return minScore;
+	}
+	public void setMinScore(Double minScore) {
+		this.minScore = minScore;
+	}
+	public Double getAvgScore() {
+		return avgScore;
+	}
+	public void setAvgScore(Double avgScore) {
+		this.avgScore = avgScore;
+	}
+	public Double getStd() {
+		return std;
+	}
+	public void setStd(Double std) {
+		this.std = std;
+	}
+	public Double getAvgDifficultyDegree() {
+		return avgDifficultyDegree;
+	}
+	public void setAvgDifficultyDegree(Double avgDifficultyDegree) {
+		this.avgDifficultyDegree = avgDifficultyDegree;
+	}
+	public Double getCdi() {
+		return cdi;
+	}
+	public void setCdi(Double cdi) {
+		this.cdi = cdi;
+	}
+	public Long getParticipantCount() {
+		return participantCount;
+	}
+	public void setParticipantCount(Long participantCount) {
+		this.participantCount = participantCount;
+	}
+	public Long getFullCount() {
+		return fullCount;
+	}
+	public void setFullCount(Long fullCount) {
+		this.fullCount = fullCount;
+	}
+	public Long getZeroCount() {
+		return zeroCount;
+	}
+	public void setZeroCount(Long zeroCount) {
+		this.zeroCount = zeroCount;
+	}
+	public Long getPassCount() {
+		return passCount;
+	}
+	public void setPassCount(Long passCount) {
+		this.passCount = passCount;
+	}
+	public String getPartitionData() {
+		return partitionData;
+	}
+	public void setPartitionData(String partitionData) {
+		this.partitionData = partitionData;
+	}
+	public Long getExamId() {
+		return examId;
+	}
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getExamCode() {
+		return examCode;
+	}
+	public void setExamCode(String examCode) {
+		this.examCode = examCode;
+	}
+	public Long getSignCount() {
+		return signCount;
+	}
+	public void setSignCount(Long signCount) {
+		this.signCount = signCount;
+	}
+    
+}

+ 108 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamDataEntity.java

@@ -0,0 +1,108 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import java.util.Date;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+@Entity
+@Table(name = "ec_r_exam_data", indexes = {
+        @Index(name = "IDX_R_EXAM_DATA_01", columnList = "rootOrgId,examId", unique = true) })
+public class ExamDataEntity extends IdEntity {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 34505670159492793L;
+
+    @NotNull
+    private Long rootOrgId;
+
+    @NotNull
+    private Long examId;
+
+    @NotNull
+    private String examName;
+
+    @NotNull
+    private String examType;
+
+    @NotNull
+    private Date startTime;
+
+    @NotNull
+    private Date endTime;
+
+    private Integer planCount;
+
+    private Integer completeCount;
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamName() {
+        return examName;
+    }
+
+    public void setExamName(String examName) {
+        this.examName = examName;
+    }
+
+    public String getExamType() {
+        return examType;
+    }
+
+    public void setExamType(String examType) {
+        this.examType = examType;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Integer getPlanCount() {
+        return planCount;
+    }
+
+    public void setPlanCount(Integer planCount) {
+        this.planCount = planCount;
+    }
+
+    public Integer getCompleteCount() {
+        return completeCount;
+    }
+
+    public void setCompleteCount(Integer completeCount) {
+        this.completeCount = completeCount;
+    }
+
+}

+ 135 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamOrgReportEntity.java

@@ -0,0 +1,135 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+
+@Entity
+@Table(name = "ec_r_exam_org_report",indexes = {
+		@Index(name = "IDX_R_EXAM_ORG_REPORT_01", columnList = "projectId,rootOrgId,examId,orgId", unique = true),
+		@Index(name = "IDX_R_EXAM_ORG_REPORT_02", columnList = "projectId", unique = false),
+		@Index(name = "IDX_R_EXAM_ORG_REPORT_03", columnList = "rootOrgId", unique = false),
+		@Index(name = "IDX_R_EXAM_ORG_REPORT_04", columnList = "examId", unique = false),
+		@Index(name = "IDX_R_EXAM_ORG_REPORT_05", columnList = "orgId", unique = false)})
+public class ExamOrgReportEntity extends IdEntity {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8626023407766283369L;
+
+	//项目id
+	@NotNull
+    private Long projectId;
+	//rootOrgId
+    @NotNull
+    private Long rootOrgId;
+    //考试id
+    @NotNull
+    private Long examId;
+    //考试名称
+    @NotNull
+    private String examName;
+    //考试code
+    @NotNull
+    private String examCode;
+    //中心id
+    @NotNull
+    private Long orgId;
+    //中心名称
+    @NotNull
+    private String orgName;
+    //中心code
+    @NotNull
+    private String orgCode;
+    //报名人数
+    @NotNull
+    private Long signCount;
+    //实考人数
+    @NotNull
+    private Long participantCount;
+    //及格人数
+    @NotNull
+    private Long passCount;
+    //分段人数数据
+    @NotNull
+    private String partitionData;
+	public Long getProjectId() {
+		return projectId;
+	}
+	public void setProjectId(Long projectId) {
+		this.projectId = projectId;
+	}
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Long getExamId() {
+		return examId;
+	}
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+	public String getExamName() {
+		return examName;
+	}
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+	public String getExamCode() {
+		return examCode;
+	}
+	public void setExamCode(String examCode) {
+		this.examCode = examCode;
+	}
+	public Long getOrgId() {
+		return orgId;
+	}
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+	public String getOrgName() {
+		return orgName;
+	}
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+	public String getOrgCode() {
+		return orgCode;
+	}
+	public void setOrgCode(String orgCode) {
+		this.orgCode = orgCode;
+	}
+	public Long getSignCount() {
+		return signCount;
+	}
+	public void setSignCount(Long signCount) {
+		this.signCount = signCount;
+	}
+	public Long getParticipantCount() {
+		return participantCount;
+	}
+	public void setParticipantCount(Long participantCount) {
+		this.participantCount = participantCount;
+	}
+	public Long getPassCount() {
+		return passCount;
+	}
+	public void setPassCount(Long passCount) {
+		this.passCount = passCount;
+	}
+	public String getPartitionData() {
+		return partitionData;
+	}
+	public void setPartitionData(String partitionData) {
+		this.partitionData = partitionData;
+	}
+    
+}

+ 50 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ExamStudentCountEntity.java

@@ -0,0 +1,50 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+@Entity
+@Table(name = "ec_r_exam_student_count", indexes = {
+		@Index(name = "IDX_R_EXAM_STUDENT_COUNT_01", columnList = "rootOrgId,examId", unique = true),
+		@Index(name = "IDX_R_EXAM_STUDENT_COUNT_02", columnList = "examId", unique = false) })
+public class ExamStudentCountEntity extends IdEntity {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8818247713349613850L;
+	@NotNull
+	private Long rootOrgId;
+	@NotNull
+	private Long examId;
+	@NotNull
+	private Integer onlineCount;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Integer getOnlineCount() {
+		return onlineCount;
+	}
+
+	public void setOnlineCount(Integer onlineCount) {
+		this.onlineCount = onlineCount;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+}

+ 202 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/ProjectEntity.java

@@ -0,0 +1,202 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.base.enums.AnalyseType;
+import cn.com.qmth.examcloud.core.reports.base.enums.DataOrigin;
+import cn.com.qmth.examcloud.core.reports.base.enums.ReportStatus;
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+
+@Entity
+@Table(name = "ec_r_project",indexes = {
+		@Index(name = "IDX_R_PROJECT_01", columnList = "rootOrgId", unique = false)})
+public class ProjectEntity extends IdEntity {
+
+
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 2211635731419184249L;
+
+	//项目名
+	@NotNull
+    private String name;
+
+    @NotNull
+    private Long rootOrgId;
+    
+    //分析类型
+    @Enumerated(EnumType.STRING)
+    @NotNull
+    private AnalyseType analyseType;
+    
+    //数据来源
+    @Enumerated(EnumType.STRING)
+    @NotNull
+    private DataOrigin dataOrigin;
+    
+    //样本数量
+    @NotNull
+    private Integer sampleCount;
+    
+    //所选考试id
+    private String examIds;
+    
+    //备注
+    private String remarks;
+    
+    //启用禁用
+    @NotNull
+    private Boolean enable;
+    
+    //报表状态
+    @Enumerated(EnumType.STRING)
+    @NotNull
+    private ReportStatus reportStatus;
+    
+    //中心数量
+    private Integer orgCount;
+    
+    //课程数量
+    private Integer courseCount;
+    
+    //及格分数
+    private Double passScore;
+    
+    //满分分数
+    private Double totalScore;
+    
+    //分数段数量
+    private Integer partitionCount;
+    
+    //分数段详情json
+    private String partitionDetails;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public AnalyseType getAnalyseType() {
+		return analyseType;
+	}
+
+	public void setAnalyseType(AnalyseType analyseType) {
+		this.analyseType = analyseType;
+	}
+
+	public DataOrigin getDataOrigin() {
+		return dataOrigin;
+	}
+
+	public void setDataOrigin(DataOrigin dataOrigin) {
+		this.dataOrigin = dataOrigin;
+	}
+
+	public Integer getSampleCount() {
+		return sampleCount;
+	}
+
+	public void setSampleCount(Integer sampleCount) {
+		this.sampleCount = sampleCount;
+	}
+
+	public String getExamIds() {
+		return examIds;
+	}
+
+	public void setExamIds(String examIds) {
+		this.examIds = examIds;
+	}
+
+	public String getRemarks() {
+		return remarks;
+	}
+
+	public void setRemarks(String remarks) {
+		this.remarks = remarks;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+	public ReportStatus getReportStatus() {
+		return reportStatus;
+	}
+
+	public void setReportStatus(ReportStatus reportStatus) {
+		this.reportStatus = reportStatus;
+	}
+
+	public Integer getOrgCount() {
+		return orgCount;
+	}
+
+	public void setOrgCount(Integer orgCount) {
+		this.orgCount = orgCount;
+	}
+
+	public Integer getCourseCount() {
+		return courseCount;
+	}
+
+	public void setCourseCount(Integer courseCount) {
+		this.courseCount = courseCount;
+	}
+
+	public Double getPassScore() {
+		return passScore;
+	}
+
+	public void setPassScore(Double passScore) {
+		this.passScore = passScore;
+	}
+
+	public Integer getPartitionCount() {
+		return partitionCount;
+	}
+
+	public void setPartitionCount(Integer partitionCount) {
+		this.partitionCount = partitionCount;
+	}
+
+	public String getPartitionDetails() {
+		return partitionDetails;
+	}
+
+	public void setPartitionDetails(String partitionDetails) {
+		this.partitionDetails = partitionDetails;
+	}
+
+	public Double getTotalScore() {
+		return totalScore;
+	}
+
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+    
+    
+}

+ 39 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/StudentCountEntity.java

@@ -0,0 +1,39 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+@Entity
+@Table(name = "ec_r_student_count", indexes = {
+		@Index(name = "IDX_R_STUDENT_COUNT_01", columnList = "rootOrgId", unique = true) })
+public class StudentCountEntity extends IdEntity {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 6732497600896224190L;
+	@NotNull
+	private Long rootOrgId;
+	@NotNull
+	private Integer onlineCount;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Integer getOnlineCount() {
+		return onlineCount;
+	}
+
+	public void setOnlineCount(Integer onlineCount) {
+		this.onlineCount = onlineCount;
+	}
+}

+ 51 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/StudentCumulativeCountEntity.java

@@ -0,0 +1,51 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+@Entity
+@Table(name = "ec_r_student_cumulative_count", indexes = {
+		@Index(name = "IDX_R_STUDENT_CUMULATIVE_COUNT_01", columnList = "rootOrgId,reportDay", unique = true),
+		@Index(name = "IDX_R_STUDENT_CUMULATIVE_COUNT_02", columnList = "reportDay", unique = false)})
+public class StudentCumulativeCountEntity extends IdEntity {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -7619316177918217328L;
+	@NotNull
+	private Long rootOrgId;
+	@NotNull
+	private Integer totalCount;
+	@NotNull
+	private String reportDay;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Integer getTotalCount() {
+		return totalCount;
+	}
+
+	public void setTotalCount(Integer totalCount) {
+		this.totalCount = totalCount;
+	}
+
+	public String getReportDay() {
+		return reportDay;
+	}
+
+	public void setReportDay(String reportDay) {
+		this.reportDay = reportDay;
+	}
+	
+	
+}

+ 38 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/StudentTotalCountEntity.java

@@ -0,0 +1,38 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+@Entity
+@Table(name = "ec_r_student_total_count", indexes = {
+		@Index(name = "IDX_R_STUDENT_TOTAL_COUNT_01", columnList = "rootOrgId", unique = true) })
+public class StudentTotalCountEntity extends IdEntity {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 4690035104380375292L;
+	@NotNull
+	private Long rootOrgId;
+	@NotNull
+	private Integer totalCount;
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public Integer getTotalCount() {
+		return totalCount;
+	}
+
+	public void setTotalCount(Integer totalCount) {
+		this.totalCount = totalCount;
+	}
+}

+ 39 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/UserCountEntity.java

@@ -0,0 +1,39 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.examcloud.core.reports.dao.entity.share.IdEntity;
+
+
+@Entity
+@Table(name = "ec_r_user_count",indexes = {
+		@Index(name = "IDX_R_USER_COUNT_01", columnList = "rootOrgId", unique = true)})
+public class UserCountEntity extends IdEntity {
+
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -6420107706111319346L;
+    @NotNull
+    private Long rootOrgId;
+    
+    @NotNull
+    private Integer onlineCount;
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+	public Integer getOnlineCount() {
+		return onlineCount;
+	}
+	public void setOnlineCount(Integer onlineCount) {
+		this.onlineCount = onlineCount;
+	}
+}

+ 24 - 0
examcloud-core-reports-dao/src/main/java/cn/com/qmth/examcloud/core/reports/dao/entity/share/IdEntity.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.examcloud.core.reports.dao.entity.share;
+
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+import cn.com.qmth.examcloud.web.jpa.JpaEntity;
+
+@MappedSuperclass
+public abstract class IdEntity extends JpaEntity{
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+}

+ 42 - 0
examcloud-core-reports-service/pom.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+		<artifactId>examcloud-core-reports</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-reports-service</artifactId>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+			<artifactId>examcloud-core-reports-dao</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-reports-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-task-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-global-api</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud.rpc</groupId>
+            <artifactId>examcloud-core-examwork-api-client</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+	</dependencies>
+
+</project>

+ 19 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamCourseDataReportService.java

@@ -0,0 +1,19 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.examcloud.core.reports.api.bean.ExamCourseDataReportBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamCourseMainTopTen;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamCourseReportMainBean;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+
+public interface ExamCourseDataReportService {
+
+	public void saveExamCourseDataReportList(List<ExamCourseDataReportBean> beans);
+	public void deleteByProject(Long projectId,Long rootOrgId);
+	public List<ExamCourseReportMainBean> getExamCourseMainList(Long projectId, Long examId);
+	public ExamCourseMainTopTen getExamCourseMainTop10(Long projectId, Long examId);
+	public void exportAll(ProjectEntity pe, Long examId, HttpServletResponse response);
+}

+ 12 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamDataService.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveExamDataReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamDataBean;
+
+public interface ExamDataService {
+
+    public PageInfo<ExamDataBean> queryPage(Long rootOrgId, String examName, Integer pageNo, Integer pageSize);
+
+    public void save(SaveExamDataReq req);
+}

+ 24 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamOrgReportService.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.examcloud.core.reports.api.bean.ExamOrgReportBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamOrgMainTopTen;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamOrgReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.util.excel.SheetData;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+
+public interface ExamOrgReportService {
+
+	public void saveExamOrgReportList(List<ExamOrgReportBean> beans);
+	public void deleteByProject(Long projectId,Long rootOrgId);
+	public List<ExamOrgReportMainBean> getExamOrgMainList(Long projectId,Long examId);
+	public List<ExamReportMainBean> getExamMainList(Long projectId,Long examId);
+	public ExamOrgMainTopTen getExamOrgMainTop10(Long projectId, Long examId);
+	public void exportAll(ProjectEntity pe,Long examId,String items,HttpServletResponse response);
+	public void fillExamMainSheetdata(List<SheetData> sheets, List<ExamReportMainBean> list, List<String> header,
+			int partitionCount);
+}

+ 8 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ExamStudentCountService.java

@@ -0,0 +1,8 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+public interface ExamStudentCountService {
+
+    public void saveCount();
+
+    public Long getOnlineCount(Long rootOrgId, Long orgId, Long examId);
+}

+ 35 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/ProjectService.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.core.reports.api.bean.ProjectInfoBean;
+import cn.com.qmth.examcloud.core.reports.api.request.GetProjectInfoBeanReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectCourseOrgCountReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectStatusByIdsReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectStatusReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.AddProjectReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.ProjectBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.QueryProjectPageReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.UpdateProjectReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.UpdateProjectScoreReq;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+
+public interface ProjectService {
+	public void delProject(Long id,Long rootOrgId);
+	public Long addProject(AddProjectReq req,Long rootOrgId);
+	public void updateProject(UpdateProjectReq req,Long rootOrgId);
+	public void updateEnable(String ids,Boolean enable,Long rootOrgId);
+	public PageInfo<ProjectBean> queryPage(QueryProjectPageReq req,Integer pageNo,Integer pageSize,Long rootOrgId);
+	public void updateProjectStatus(UpdateProjectStatusReq req) ;
+	public void addReportsCompute(Long id,Long rootOrgId) ;
+	public ProjectInfoBean getProjectInfoBean(GetProjectInfoBeanReq req);
+	public void updateScore(UpdateProjectScoreReq req,Long rootOrgId);
+	public ProjectEntity findProjectById(Long projectId);
+	public void updateProjectCourseOrgCount(UpdateProjectCourseOrgCountReq req);
+	public List<ExamBean> queryAllExam(Long rootOrgId);
+	public void updateProjectStatusByIds(UpdateProjectStatusByIdsReq req) ;
+	public ProjectBean getProject(Long id,Long rootOrgId);
+	public List<ProjectBean> getProjectList(List<Long> ids,Long rootOrgId);
+}

+ 11 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/StudentCountService.java

@@ -0,0 +1,11 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCountBean;
+
+public interface StudentCountService {
+	public void saveCount();
+	public List<StudentCountBean> getCountByRootOrgIds(List<Long> rootOrgIds);
+	public Long getSumOnlineCount();
+}

+ 10 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/StudentCumulativeCountService.java

@@ -0,0 +1,10 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCumulativeCountBean;
+
+public interface StudentCumulativeCountService {
+	public void saveCount();
+	public List<StudentCumulativeCountBean> getLastNdayOnlineCount(Integer nday);
+}

+ 14 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/StudentTotalCountService.java

@@ -0,0 +1,14 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveStudentTotalCountReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCountBean;
+
+public interface StudentTotalCountService {
+
+    public Long getSumTotalCount();
+
+    public PageInfo<StudentCountBean> queryPage(String rootOrgId, Integer pageNo, Integer pageSize);
+
+    public void save(SaveStudentTotalCountReq req);
+}

+ 6 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/UserCountService.java

@@ -0,0 +1,6 @@
+package cn.com.qmth.examcloud.core.reports.service;
+
+public interface UserCountService {
+	public void saveCount();
+	public Long getOnlineCount(Long rootOrgId,Long orgId);
+}

+ 586 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamCourseDataReportServiceImpl.java

@@ -0,0 +1,586 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.persistence.criteria.Predicate;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.examcloud.core.reports.api.bean.ExamCourseDataReportBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamCourseMainTopTen;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamCourseReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.PartitionDataBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.PartitionTopTen;
+import cn.com.qmth.examcloud.core.reports.base.util.excel.ExportUtils;
+import cn.com.qmth.examcloud.core.reports.base.util.excel.SheetData;
+import cn.com.qmth.examcloud.core.reports.dao.ExamCourseDataReportRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ExamCourseDataReportEntity;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+import cn.com.qmth.examcloud.core.reports.service.ExamCourseDataReportService;
+import cn.com.qmth.examcloud.core.reports.service.ExamOrgReportService;
+
+@Service
+public class ExamCourseDataReportServiceImpl implements ExamCourseDataReportService {
+	private final static int asc = 0;
+	private final static int desc = 2;
+	@Autowired
+	private ExamCourseDataReportRepo examCourseDataReportRepo;
+	@Autowired
+	private ExamOrgReportService examOrgReportService;
+	
+	@Transactional
+	@Override
+	public void saveExamCourseDataReportList(List<ExamCourseDataReportBean> beans) {
+		Date now=new Date();
+		List<ExamCourseDataReportEntity> list = new ArrayList<ExamCourseDataReportEntity>();
+		for (ExamCourseDataReportBean bean : beans) {
+			ExamCourseDataReportEntity e = new ExamCourseDataReportEntity();
+			BeanUtils.copyProperties(bean, e);
+			e.setCreationTime(now);
+			e.setUpdateTime(now);
+			e.setPartitionData(StringUtils.join(bean.getPartitionData().toArray(), ","));
+			list.add(e);
+		}
+		examCourseDataReportRepo.saveAll(list);
+	}
+	@Transactional
+	@Override
+	public void deleteByProject(Long projectId, Long rootOrgId) {
+		examCourseDataReportRepo.deleteByProject(projectId, rootOrgId);
+	}
+
+	@Override
+	public List<ExamCourseReportMainBean> getExamCourseMainList(Long projectId, Long examId) {
+		Specification<ExamCourseDataReportEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("projectId"), projectId));
+			if (examId != null) {
+				predicates.add(cb.equal(root.get("examId"), examId));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Sort sort = new Sort(Direction.ASC, "id");
+		List<ExamCourseDataReportEntity> entityList = examCourseDataReportRepo.findAll(specification, sort);
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>();
+		for (ExamCourseDataReportEntity e : entityList) {
+			ret.add(of(e));
+		}
+		return ret;
+	}
+
+	private ExamCourseReportMainBean of(ExamCourseDataReportEntity e) {
+		ExamCourseReportMainBean b = new ExamCourseReportMainBean();
+		BeanUtils.copyProperties(e, b);
+		b.setParticipantRatio(getPercentage(b.getParticipantCount(), b.getSignCount()));
+		b.setMissCount(e.getSignCount() - e.getParticipantCount());
+		b.setMissRatio(getPercentage(b.getMissCount(), b.getSignCount()));
+		b.setPassSignRatio(getPercentage(b.getPassCount(), b.getSignCount()));
+		b.setPassParticipantRatio(getPercentage(b.getPassCount(), b.getParticipantCount()));
+		List<Long> li = Arrays.asList(e.getPartitionData().split(",")).stream().map(str -> Long.parseLong(str))
+				.collect(Collectors.toList());
+		List<PartitionDataBean> partitionData = new ArrayList<PartitionDataBean>();
+		for (Long l : li) {
+			PartitionDataBean pb = new PartitionDataBean();
+			pb.setCount(l);
+			pb.setParticipantRatio(getPercentage(l, b.getParticipantCount()));
+			pb.setSignRatio(getPercentage(l, b.getSignCount()));
+			partitionData.add(pb);
+		}
+		b.setPartitionData(partitionData);
+		return b;
+	}
+
+	private Double getPercentage(Long a, Long b) {
+		if (b == 0) {
+			return null;
+		}
+		Double da = Double.valueOf(a);
+		Double db = Double.valueOf(b);
+		BigDecimal bd = new BigDecimal(da * 100 / db);
+		Double tem = bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+		return tem;
+	}
+
+	@Override
+	public ExamCourseMainTopTen getExamCourseMainTop10(Long projectId, Long examId) {
+		ExamCourseMainTopTen ret = new ExamCourseMainTopTen();
+		List<ExamCourseReportMainBean> list = getExamCourseMainList(projectId, examId);
+		if (list != null && list.size() > 0) {
+			ret.setDifficultyDegree(getDifficultyDegreeSort(list, asc));
+			ret.setDifficultyDegreeDesc(getDifficultyDegreeSort(list, desc));
+			ret.setStd(getStdSort(list, asc));
+			ret.setStdDesc(getStdSort(list, desc));
+			ret.setCdi(getCdiSort(list, asc));
+			ret.setCdiDesc(getCdiSort(list, desc));
+			ret.setParticipant(getParticipantSort(list, asc));
+			ret.setParticipantDesc(getParticipantSort(list, desc));
+			ret.setParticipantRatio(getParticipantRatioSort(list, asc));
+			ret.setParticipantRatioDesc(getParticipantRatioSort(list, desc));
+			ret.setMiss(getMissSort(list, asc));
+			ret.setMissDesc(getMissSort(list, desc));
+			ret.setMissRatio(getMissRatioSort(list, asc));
+			ret.setMissRatioDesc(getMissRatioSort(list, desc));
+			ret.setPass(getPassSort(list, asc));
+			ret.setPassDesc(getPassSort(list, desc));
+			ret.setPassSignRatio(getPassSignRatioSort(list, asc));
+			ret.setPassSignRatioDesc(getPassSignRatioSort(list, desc));
+			ret.setPassParticipantRatio(getPassParticipantRatioSort(list, asc));
+			ret.setPassParticipantRatioDesc(getPassParticipantRatioSort(list, desc));
+			ret.setPartition(getPartitionSort(list));
+		}
+		return ret;
+	}
+
+	private List<ExamCourseReportMainBean> getParticipantSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				if (o1.getParticipantCount() > o2.getParticipantCount()) {
+					return 1 - ascOrDesc;
+				} else if (o1.getParticipantCount() < o2.getParticipantCount()) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamCourseReportMainBean> getParticipantRatioSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				if (o1.getParticipantRatio() > o2.getParticipantRatio()) {
+					return 1 - ascOrDesc;
+				} else if (o1.getParticipantRatio() < o2.getParticipantRatio()) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamCourseReportMainBean> getMissSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				if (o1.getMissCount() > o2.getMissCount()) {
+					return 1 - ascOrDesc;
+				} else if (o1.getMissCount() < o2.getMissCount()) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamCourseReportMainBean> getMissRatioSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				if (o1.getMissRatio() > o2.getMissRatio()) {
+					return 1 - ascOrDesc;
+				} else if (o1.getMissRatio() < o2.getMissRatio()) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamCourseReportMainBean> getPassSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				if (o1.getPassCount() > o2.getPassCount()) {
+					return 1 - ascOrDesc;
+				} else if (o1.getPassCount() < o2.getPassCount()) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamCourseReportMainBean> getPassSignRatioSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				if (o1.getPassSignRatio() > o2.getPassSignRatio()) {
+					return 1 - ascOrDesc;
+				} else if (o1.getPassSignRatio() < o2.getPassSignRatio()) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamCourseReportMainBean> getPassParticipantRatioSort(List<ExamCourseReportMainBean> list,
+			int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				Double d1=o1.getPassParticipantRatio();
+				Double d2=o2.getPassParticipantRatio();
+				if(d1==null) {
+					d1=0d;
+				}
+				if(d2==null) {
+					d2=0d;
+				}
+				if (d1 > d2) {
+					return 1 - ascOrDesc;
+				} else if (d1 < d2) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<PartitionTopTen<PartitionDataBean>> getPartitionSort(List<ExamCourseReportMainBean> list) {
+		List<PartitionTopTen<PartitionDataBean>> ret = new ArrayList<PartitionTopTen<PartitionDataBean>>();
+		int size = list.get(0).getPartitionData().size();
+		for (int i = 0; i < size; i++) {
+			PartitionTopTen<PartitionDataBean> pt = new PartitionTopTen<PartitionDataBean>();
+			pt.setCountAsc(getPartitionCountSort(list, i, asc));
+			pt.setCountDesc(getPartitionCountSort(list, i, desc));
+			pt.setSignRatioAsc(getPartitionSignRatioSort(list, i, asc));
+			pt.setSignRatioDesc(getPartitionSignRatioSort(list, i, desc));
+			pt.setParticipantRatioAsc(getPartitionParticipantRatioSort(list, i, asc));
+			pt.setParticipantRatioDesc(getPartitionParticipantRatioSort(list, i, desc));
+			ret.add(pt);
+		}
+		return ret;
+	}
+
+	private List<PartitionDataBean> getPartitionCountSort(List<ExamCourseReportMainBean> list, int index,
+			int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				Long c1 = o1.getPartitionData().get(index).getCount();
+				Long c2 = o2.getPartitionData().get(index).getCount();
+				if (c1 > c2) {
+					return 1 - ascOrDesc;
+				} else if (c1 < c2) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return of(ret.subList(0, 10),index);
+		} else {
+			return of(ret,index);
+		}
+	}
+
+	private List<PartitionDataBean> getPartitionSignRatioSort(List<ExamCourseReportMainBean> list, int index,
+			int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				Double c1 = o1.getPartitionData().get(index).getSignRatio();
+				Double c2 = o2.getPartitionData().get(index).getSignRatio();
+				if (c1 > c2) {
+					return 1 - ascOrDesc;
+				} else if (c1 < c2) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return of(ret.subList(0, 10),index);
+		} else {
+			return of(ret,index);
+		}
+	}
+
+	private List<PartitionDataBean> getPartitionParticipantRatioSort(List<ExamCourseReportMainBean> list,
+			int index, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				Double c1 = o1.getPartitionData().get(index).getParticipantRatio();
+				Double c2 = o2.getPartitionData().get(index).getParticipantRatio();
+				if(c1==null) {
+					c1=0d;
+				}
+				if(c2==null) {
+					c2=0d;
+				}
+				if (c1 > c2) {
+					return 1 - ascOrDesc;
+				} else if (c1 < c2) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return of(ret.subList(0, 10),index);
+		} else {
+			return of(ret,index);
+		}
+	}
+	private List<PartitionDataBean> of(List<ExamCourseReportMainBean> list, int index) {
+		List<PartitionDataBean> ret = new ArrayList<PartitionDataBean>();
+		for(ExamCourseReportMainBean b:list) {
+			PartitionDataBean pb=new PartitionDataBean();
+			pb.setCount(b.getPartitionData().get(index).getCount());
+			pb.setParticipantRatio(b.getPartitionData().get(index).getParticipantRatio());
+			pb.setSignRatio(b.getPartitionData().get(index).getSignRatio());
+			pb.setCourseName(b.getCourseName());
+			pb.setExamName(b.getExamName());
+			ret.add(pb);
+		}
+		return ret;
+	}
+	private List<ExamCourseReportMainBean> getDifficultyDegreeSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				Double d1=o1.getAvgDifficultyDegree();
+				Double d2=o2.getAvgDifficultyDegree();
+				if(d1==null) {
+					d1=0d;
+				}
+				if(d2==null) {
+					d2=0d;
+				}
+				if (d1 > d2) {
+					return 1 - ascOrDesc;
+				} else if (d1 < d2) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+	private List<ExamCourseReportMainBean> getStdSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				Double d1=o1.getStd();
+				Double d2=o2.getStd();
+				if(d1==null) {
+					d1=0d;
+				}
+				if(d2==null) {
+					d2=0d;
+				}
+				if (d1 > d2) {
+					return 1 - ascOrDesc;
+				} else if (d1 < d2) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+	private List<ExamCourseReportMainBean> getCdiSort(List<ExamCourseReportMainBean> list, int ascOrDesc) {
+		List<ExamCourseReportMainBean> ret = new ArrayList<ExamCourseReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamCourseReportMainBean>() {
+
+			@Override
+			public int compare(ExamCourseReportMainBean o1, ExamCourseReportMainBean o2) {
+				Double d1=o1.getCdi();
+				Double d2=o2.getCdi();
+				if(d1==null) {
+					d1=0d;
+				}
+				if(d2==null) {
+					d2=0d;
+				}
+				if (d1 > d2) {
+					return 1 - ascOrDesc;
+				} else if (d1 < d2) {
+					return -1 + ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	@Override
+	public void exportAll(ProjectEntity pe, Long examId, HttpServletResponse response) {
+		Long projectId = pe.getId();
+		List<SheetData> sheets = new ArrayList<SheetData>();
+		List<String> examheader = new ArrayList<String>();
+		examheader.addAll(
+				Arrays.asList(new String[] { "考试名称", "报名人数", "实考人数","实考比例(%)", "缺考人数", "缺考率(%)", "及格人数", "及格报名人数占比(%)", "及格实考人数占比(%)" }));
+		int partitionCount = pe.getPartitionCount();
+		for (int i = 1; i <= partitionCount; i++) {
+			examheader.add("分段" + i + "人数");
+			examheader.add("分段" + i + "报名人数占比(%)");
+			examheader.add("分段" + i + "实考人数占比(%)");
+		}
+		List<String> header = new ArrayList<String>();
+		header.addAll(
+				Arrays.asList(new String[] { "考试名称","课程名称", "满分", "最高分","最低分", "平均分", "标准差", "调用试卷平均难度", "差异系数", "报名人数","考试人数" ,"满分人数","零分人数","及格人数","及格占比"}));
+		for (int i = 1; i <= partitionCount; i++) {
+			header.add("分段" + i + "人数");
+			header.add("分段" + i + "报名人数占比(%)");
+			header.add("分段" + i + "实考人数占比(%)");
+		}
+		List<ExamReportMainBean> list1 = examOrgReportService.getExamMainList(projectId, examId);
+		examOrgReportService.fillExamMainSheetdata(sheets,list1, examheader, partitionCount);
+		List<ExamCourseReportMainBean> list2 = getExamCourseMainList(projectId, examId);
+		fillExamCourseMainSheetdata(sheets, list2, header, partitionCount);
+		ExportUtils.exportExcel("考试课程分析结果", sheets, response);
+	}
+	private void fillExamCourseMainSheetdata(List<SheetData> sheets,List<ExamCourseReportMainBean> list, List<String> header, int partitionCount) {
+		SheetData sheet = new SheetData();
+		sheet.setHeader(header);
+		sheet.setName("考试课程数值分析");
+		fillSheetData(sheets,sheet, list, header.size(), partitionCount);
+	}
+	private void fillSheetData(List<SheetData> sheets,SheetData sheet, List<ExamCourseReportMainBean> list, int headerSize, int partitionCount) {
+		List<Object[]> data = new ArrayList<Object[]>();
+		for (ExamCourseReportMainBean b : list) {
+			Object[] ob = new Object[headerSize];
+			ob[0] = b.getExamName();
+			ob[1] = b.getCourseName();
+			ob[2] = b.getTotalScore();
+			ob[3] = b.getMaxScore();
+			ob[4] = b.getMinScore();
+			ob[5] = b.getAvgScore();
+			ob[6] = b.getStd();
+			ob[7] = b.getAvgDifficultyDegree();
+			ob[8] = b.getCdi();
+			ob[9] = b.getSignCount();
+			ob[10] = b.getParticipantCount();
+			ob[11] = b.getFullCount();
+			ob[12] = b.getZeroCount();
+			ob[13] = b.getPassCount();
+			ob[14] = b.getPassParticipantRatio();
+			for (int i = 0; i < partitionCount; i++) {
+				ob[15 + i*3] = b.getPartitionData().get(i).getCount();
+				ob[15 + i*3 + 1] = b.getPartitionData().get(i).getSignRatio();
+				ob[15 + i*3 + 2] = b.getPartitionData().get(i).getParticipantRatio();
+			}
+			data.add(ob);
+		}
+		sheet.setData(data);
+		sheets.add(sheet);
+	}
+}

+ 189 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamDataServiceImpl.java

@@ -0,0 +1,189 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.persistence.criteria.Predicate;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveExamDataReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamDataBean;
+import cn.com.qmth.examcloud.core.reports.dao.ExamDataRepo;
+import cn.com.qmth.examcloud.core.reports.dao.ExamStudentCountRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ExamDataEntity;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ExamStudentCountEntity;
+import cn.com.qmth.examcloud.core.reports.service.ExamDataService;
+
+@Service
+public class ExamDataServiceImpl implements ExamDataService {
+
+    @Autowired
+    private ExamDataRepo examDataRepo;
+
+    @Autowired
+    private ExamStudentCountRepo examStudentCountRepo;
+
+    @Override
+    public PageInfo<ExamDataBean> queryPage(Long rootOrgId, String examName, Integer pageNo, Integer pageSize) {
+        Specification<ExamDataEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            if (null != rootOrgId) {
+                predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+            }
+            if (StringUtils.isNotBlank(examName)) {
+                predicates.add(cb.like(root.get("examName"), toSqlSearchPattern(examName)));
+            }
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        List<ExamDataEntity> es = examDataRepo.findAll(specification);
+
+        List<ExamDataBean> ret = Lists.newArrayList();
+
+        for (ExamDataEntity cur : es) {
+            ExamDataBean bean = new ExamDataBean();
+            bean.setPlanCount(cur.getPlanCount());
+            bean.setCompleteCount(cur.getCompleteCount());
+            bean.setCompleteRatio(getPercentage(bean.getCompleteCount(), bean.getPlanCount()));
+            bean.setExamEndDate(cur.getEndTime());
+            bean.setExamStartDate(cur.getStartTime());
+            bean.setExamId(cur.getExamId());
+            bean.setExamName(cur.getExamName());
+            bean.setExamType(cur.getExamType());
+            bean.setRootOrgId(cur.getRootOrgId());
+            bean.setOnlineCount(0);
+            ret.add(bean);
+        }
+        fillOnlineCount(ret, rootOrgId);
+        Collections.sort(ret, new Comparator<ExamDataBean>() {
+
+            @Override
+            public int compare(ExamDataBean o1, ExamDataBean o2) {
+                if (o1.getOnlineCount() > o2.getOnlineCount()) {
+                    return -1;
+                } else if (o1.getOnlineCount() < o2.getOnlineCount()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+
+        });
+        int total = ret.size();
+        ret = ret.stream().skip((pageNo - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
+        PageInfo<ExamDataBean> pi = new PageInfo<ExamDataBean>();
+        pi.setLimit(pageSize);
+        pi.setList(ret);
+        pi.setPages(pageNo);
+        pi.setTotal(total);
+        return pi;
+    }
+
+    private Double getPercentage(Integer a, Integer b) {
+        if (a == null) {
+            a = 0;
+        }
+        if (b == null) {
+            return null;
+        }
+        if (b == 0) {
+            return null;
+        }
+        Double da = Double.valueOf(a);
+        Double db = Double.valueOf(b);
+        BigDecimal bd = new BigDecimal(da * 100 / db);
+        Double tem = bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+        return tem;
+    }
+
+    private void fillOnlineCount(List<ExamDataBean> ret, Long rootOrgId) {
+        if (CollectionUtils.isEmpty(ret)) {
+            return;
+        }
+        List<Long> temList = ret.stream().map(str -> str.getExamId()).collect(Collectors.toList());
+        List<ExamStudentCountEntity> sl = examStudentCountRepo.getByExamIdsRootOrgId(temList, rootOrgId);
+        if (CollectionUtils.isEmpty(sl)) {
+            return;
+        }
+        Map<Long, Integer> map = sl.stream().collect(Collectors.toMap(ExamStudentCountEntity::getExamId,
+                account -> account.getOnlineCount(), (key1, key2) -> key2));
+        for (ExamDataBean b : ret) {
+            if (map.get(b.getExamId()) != null) {
+                b.setOnlineCount(map.get(b.getExamId()));
+            }
+        }
+    }
+
+    @Transactional
+    @Override
+    public void save(SaveExamDataReq req) {
+        Long rootOrgId = req.getRootOrgId();
+        Integer planCount = req.getPlanCount();
+        Integer completeCount = req.getCompleteCount();
+        Date endTime = req.getEndTime();
+        Date startTime = req.getStartTime();
+        Long examId = req.getExamId();
+        String examName = req.getExamName();
+        String examType = req.getExamType();
+        if (planCount == null) {
+            planCount = 0;
+        }
+        if (completeCount == null) {
+            completeCount = 0;
+        }
+        if (rootOrgId == null) {
+            throw new StatusException("1000001", "rootOrgId不能为空");
+        }
+        if (examId == null) {
+            throw new StatusException("1000002", "examId不能为空");
+        }
+        if (examName == null) {
+            throw new StatusException("1000003", "examName不能为空");
+        }
+        if (examType == null) {
+            throw new StatusException("1000004", "examType不能为空");
+        }
+        ExamDataEntity ed = examDataRepo.findByRootOrgIdAndExamId(rootOrgId, examId);
+        if (ed == null) {
+            Date now = new Date();
+            ed = new ExamDataEntity();
+            ed.setEndTime(endTime);
+            ed.setStartTime(startTime);
+            ed.setExamId(examId);
+            ed.setExamName(examName);
+            ed.setExamType(examType);
+            ed.setRootOrgId(rootOrgId);
+            ed.setCreationTime(now);
+            ed.setUpdateTime(now);
+        }
+        ed.setPlanCount(planCount.intValue());
+        ed.setCompleteCount(completeCount.intValue());
+        examDataRepo.save(ed);
+    }
+
+    private String toSqlSearchPattern(String column) {
+        if (StringUtils.isBlank(column)) {
+            return "%";
+        } else {
+            column = column.trim().replaceAll("\\s", "%");
+            column = "%" + column + "%";
+            return column;
+        }
+    }
+}

+ 688 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamOrgReportServiceImpl.java

@@ -0,0 +1,688 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.persistence.criteria.Predicate;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.examcloud.core.reports.api.bean.ExamOrgReportBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamOrgMainTopTen;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamOrgReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.ExamReportMainBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.PartitionDataBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.PartitionTopTen;
+import cn.com.qmth.examcloud.core.reports.base.util.excel.ExportUtils;
+import cn.com.qmth.examcloud.core.reports.base.util.excel.SheetData;
+import cn.com.qmth.examcloud.core.reports.dao.ExamOrgReportRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ExamOrgReportEntity;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+import cn.com.qmth.examcloud.core.reports.service.ExamOrgReportService;
+
+@Service
+public class ExamOrgReportServiceImpl implements ExamOrgReportService {
+	private final static int asc=0;
+	private final static int desc=2;
+	@Autowired
+	private ExamOrgReportRepo examOrgReportRepo;
+
+	@Transactional
+	@Override
+	public void saveExamOrgReportList(List<ExamOrgReportBean> beans) {
+		Date now=new Date();
+		List<ExamOrgReportEntity> list = new ArrayList<ExamOrgReportEntity>();
+		for (ExamOrgReportBean bean : beans) {
+			ExamOrgReportEntity e = new ExamOrgReportEntity();
+			BeanUtils.copyProperties(bean, e);
+			e.setCreationTime(now);
+			e.setUpdateTime(now);
+			e.setPartitionData(StringUtils.join(bean.getPartitionData().toArray(), ","));
+			list.add(e);
+		}
+		examOrgReportRepo.saveAll(list);
+	}
+	@Transactional
+	@Override
+	public void deleteByProject(Long projectId, Long rootOrgId) {
+		examOrgReportRepo.deleteByProject(projectId, rootOrgId);
+	}
+
+	@Override
+	public List<ExamOrgReportMainBean> getExamOrgMainList(Long projectId, Long examId) {
+		Specification<ExamOrgReportEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("projectId"), projectId));
+			if (examId != null) {
+				predicates.add(cb.equal(root.get("examId"), examId));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Sort sort = new Sort(Direction.ASC, "id");
+		List<ExamOrgReportEntity> orgEntityList = examOrgReportRepo.findAll(specification, sort);
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>();
+		for (ExamOrgReportEntity e : orgEntityList) {
+			ret.add(of(e));
+		}
+		return ret;
+	}
+
+	private ExamOrgReportMainBean of(ExamOrgReportEntity e) {
+		ExamOrgReportMainBean b = new ExamOrgReportMainBean();
+		BeanUtils.copyProperties(e, b);
+		b.setParticipantRatio(getPercentage(b.getParticipantCount(), b.getSignCount()));
+		b.setMissCount(e.getSignCount() - e.getParticipantCount());
+		b.setMissRatio(getPercentage(b.getMissCount(), b.getSignCount()));
+		b.setPassSignRatio(getPercentage(b.getPassCount(), b.getSignCount()));
+		b.setPassParticipantRatio(getPercentage(b.getPassCount(), b.getParticipantCount()));
+		List<Long> li = Arrays.asList(e.getPartitionData().split(",")).stream().map(str -> Long.parseLong(str))
+				.collect(Collectors.toList());
+		List<PartitionDataBean> partitionData = new ArrayList<PartitionDataBean>();
+		for (Long l : li) {
+			PartitionDataBean pb = new PartitionDataBean();
+			pb.setCount(l);
+			pb.setParticipantRatio(getPercentage(l, b.getParticipantCount()));
+			pb.setSignRatio(getPercentage(l, b.getSignCount()));
+			partitionData.add(pb);
+		}
+		b.setPartitionData(partitionData);
+		return b;
+	}
+
+	private Double getPercentage(Long a, Long b) {
+		if (b == 0) {
+			return null;
+		}
+		Double da = Double.valueOf(a);
+		Double db = Double.valueOf(b);
+		BigDecimal bd = new BigDecimal(da * 100 / db);
+		Double tem = bd.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+		return tem;
+	}
+
+	@Override
+	public List<ExamReportMainBean> getExamMainList(Long projectId, Long examId) {
+		Specification<ExamOrgReportEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("projectId"), projectId));
+			if (examId != null) {
+				predicates.add(cb.equal(root.get("examId"), examId));
+			}
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		Sort sort = new Sort(Direction.ASC, "id");
+		List<ExamOrgReportEntity> orgEntityList = examOrgReportRepo.findAll(specification, sort);
+		Map<Long, ExamReportMainBean> map = new LinkedHashMap<Long, ExamReportMainBean>();
+		for (ExamOrgReportEntity e : orgEntityList) {
+			ExamReportMainBean b = map.get(e.getExamId());
+			List<Long> li = Arrays.asList(e.getPartitionData().split(",")).stream().map(str -> Long.parseLong(str))
+					.collect(Collectors.toList());
+			if (b == null) {
+				b = new ExamReportMainBean();
+				b.setExamCode(e.getExamCode());
+				b.setExamId(e.getExamId());
+				b.setExamName(e.getExamName());
+				b.setRootOrgId(e.getRootOrgId());
+				b.setProjectId(e.getProjectId());
+				b.init(li.size());
+				map.put(e.getExamId(), b);
+			}
+			b.setSignCount(b.getSignCount() + e.getSignCount());
+			b.setParticipantCount(b.getParticipantCount() + e.getParticipantCount());
+			b.setPassCount(b.getPassCount() + e.getPassCount());
+			List<PartitionDataBean> partitionData = b.getPartitionData();
+			for (int i = 0; i < partitionData.size(); i++) {
+				PartitionDataBean pb = partitionData.get(i);
+				pb.setCount(pb.getCount() + li.get(i));
+			}
+			
+		}
+		List<ExamReportMainBean> ret = new ArrayList<ExamReportMainBean>(map.values());
+		computExamReportRatio(ret);
+		return ret;
+	}
+
+	private void computExamReportRatio(List<ExamReportMainBean> ret) {
+		for (ExamReportMainBean b : ret) {
+			b.setParticipantRatio(getPercentage(b.getParticipantCount(), b.getSignCount()));
+			b.setMissCount(b.getSignCount() - b.getParticipantCount());
+			b.setMissRatio(getPercentage(b.getMissCount(), b.getSignCount()));
+			b.setPassSignRatio(getPercentage(b.getPassCount(), b.getSignCount()));
+			b.setPassParticipantRatio(getPercentage(b.getPassCount(), b.getParticipantCount()));
+			List<PartitionDataBean> partitionData = b.getPartitionData();
+			for (PartitionDataBean pb : partitionData) {
+				pb.setParticipantRatio(getPercentage(pb.getCount(), b.getParticipantCount()));
+				pb.setSignRatio(getPercentage(pb.getCount(), b.getSignCount()));
+			}
+		}
+	}
+
+	@Override
+	public ExamOrgMainTopTen getExamOrgMainTop10(Long projectId, Long examId) {
+		ExamOrgMainTopTen ret = new ExamOrgMainTopTen();
+		List<ExamOrgReportMainBean> list = getExamOrgMainList(projectId, examId);
+		if (list != null && list.size() > 0) {
+			ret.setParticipant(getParticipantSort(list,asc));
+			ret.setParticipantDesc(getParticipantSort(list,desc));
+			ret.setParticipantRatio(getParticipantRatioSort(list, asc));
+			ret.setParticipantRatioDesc(getParticipantRatioSort(list, desc));
+			ret.setMiss(getMissSort(list,asc));
+			ret.setMissDesc(getMissSort(list,desc));
+			ret.setMissRatio(getMissRatioSort(list, asc));
+			ret.setMissRatioDesc(getMissRatioSort(list, desc));
+			ret.setPass(getPassSort(list,asc));
+			ret.setPassDesc(getPassSort(list,desc));
+			ret.setPassSignRatio(getPassSignRatioSort(list, asc));
+			ret.setPassSignRatioDesc(getPassSignRatioSort(list, desc));
+			ret.setPassParticipantRatio(getPassParticipantRatioSort(list, asc));
+			ret.setPassParticipantRatioDesc(getPassParticipantRatioSort(list, desc));
+			ret.setPartition(getPartitionSort(list));
+		}
+		return ret;
+	}
+
+	private List<ExamOrgReportMainBean> getParticipantSort(List<ExamOrgReportMainBean> list,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				if (o1.getParticipantCount() > o2.getParticipantCount()) {
+					return 1-ascOrDesc;
+				} else if (o1.getParticipantCount() < o2.getParticipantCount()) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamOrgReportMainBean> getParticipantRatioSort(List<ExamOrgReportMainBean> list,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				if (o1.getParticipantRatio() > o2.getParticipantRatio()) {
+					return 1-ascOrDesc;
+				} else if (o1.getParticipantRatio() < o2.getParticipantRatio()) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+
+	private List<ExamOrgReportMainBean> getMissSort(List<ExamOrgReportMainBean> list,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				if (o1.getMissCount() > o2.getMissCount()) {
+					return 1-ascOrDesc;
+				} else if (o1.getMissCount() < o2.getMissCount()) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+	private List<ExamOrgReportMainBean> getMissRatioSort(List<ExamOrgReportMainBean> list,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				if (o1.getMissRatio() > o2.getMissRatio()) {
+					return 1-ascOrDesc;
+				} else if (o1.getMissRatio() < o2.getMissRatio()) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<ExamOrgReportMainBean> getPassSort(List<ExamOrgReportMainBean> list,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				if (o1.getPassCount() > o2.getPassCount()) {
+					return 1-ascOrDesc;
+				} else if (o1.getPassCount() < o2.getPassCount()) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+	private List<ExamOrgReportMainBean> getPassSignRatioSort(List<ExamOrgReportMainBean> list,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				if (o1.getPassSignRatio() > o2.getPassSignRatio()) {
+					return 1-ascOrDesc;
+				} else if (o1.getPassSignRatio() < o2.getPassSignRatio()) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+	private List<ExamOrgReportMainBean> getPassParticipantRatioSort(List<ExamOrgReportMainBean> list,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				Double d1=o1.getPassParticipantRatio();
+				Double d2=o2.getPassParticipantRatio();
+				if(d1==null) {
+					d1=0d;
+				}
+				if(d2==null) {
+					d2=0d;
+				}
+				if (d1 > d2) {
+					return 1-ascOrDesc;
+				} else if (d1 < d2) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return ret.subList(0, 10);
+		} else {
+			return ret;
+		}
+	}
+
+	private List<PartitionTopTen<PartitionDataBean>> getPartitionSort(List<ExamOrgReportMainBean> list) {
+		List<PartitionTopTen<PartitionDataBean>> ret = new ArrayList<PartitionTopTen<PartitionDataBean>>();
+		int size = list.get(0).getPartitionData().size();
+		for (int i = 0; i < size; i++) {
+			PartitionTopTen<PartitionDataBean> pt = new PartitionTopTen<PartitionDataBean>();
+			pt.setCountAsc(getPartitionCountSort(list, i,asc));
+			pt.setCountDesc(getPartitionCountSort(list, i,desc));
+			pt.setSignRatioAsc(getPartitionSignRatioSort(list, i, asc));
+			pt.setSignRatioDesc(getPartitionSignRatioSort(list, i, desc));
+			pt.setParticipantRatioAsc(getPartitionParticipantRatioSort(list, i, asc));
+			pt.setParticipantRatioDesc(getPartitionParticipantRatioSort(list, i, desc));
+			ret.add(pt);
+		}
+		return ret;
+	}
+
+	private List<PartitionDataBean> getPartitionCountSort(List<ExamOrgReportMainBean> list, int index,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				Long c1 = o1.getPartitionData().get(index).getCount();
+				Long c2 = o2.getPartitionData().get(index).getCount();
+				if (c1 > c2) {
+					return 1-ascOrDesc;
+				} else if (c1 < c2) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return of(ret.subList(0, 10),index);
+		} else {
+			return of(ret,index);
+		}
+	}
+
+	private List<PartitionDataBean> getPartitionSignRatioSort(List<ExamOrgReportMainBean> list, int index,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				Double c1 = o1.getPartitionData().get(index).getSignRatio();
+				Double c2 = o2.getPartitionData().get(index).getSignRatio();
+				if (c1 > c2) {
+					return 1-ascOrDesc;
+				} else if (c1 < c2) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return of(ret.subList(0, 10),index);
+		} else {
+			return of(ret,index);
+		}
+	}
+	
+	private List<PartitionDataBean> getPartitionParticipantRatioSort(List<ExamOrgReportMainBean> list, int index,int ascOrDesc) {
+		List<ExamOrgReportMainBean> ret = new ArrayList<ExamOrgReportMainBean>(list);
+		Collections.sort(ret, new Comparator<ExamOrgReportMainBean>() {
+
+			@Override
+			public int compare(ExamOrgReportMainBean o1, ExamOrgReportMainBean o2) {
+				Double c1 = o1.getPartitionData().get(index).getParticipantRatio();
+				Double c2 = o2.getPartitionData().get(index).getParticipantRatio();
+				if(c1==null) {
+					c1=0d;
+				}
+				if(c2==null) {
+					c2=0d;
+				}
+				if (c1 > c2) {
+					return 1-ascOrDesc;
+				} else if (c1 < c2) {
+					return -1+ascOrDesc;
+				} else {
+					return 0;
+				}
+			}
+
+		});
+		if (ret.size() > 10) {
+			return of(ret.subList(0, 10),index);
+		} else {
+			return of(ret,index);
+		}
+	}
+	private List<PartitionDataBean> of(List<ExamOrgReportMainBean> list, int index) {
+		List<PartitionDataBean> ret = new ArrayList<PartitionDataBean>();
+		for(ExamOrgReportMainBean b:list) {
+			PartitionDataBean pb=new PartitionDataBean();
+			pb.setCount(b.getPartitionData().get(index).getCount());
+			pb.setParticipantRatio(b.getPartitionData().get(index).getParticipantRatio());
+			pb.setSignRatio(b.getPartitionData().get(index).getSignRatio());
+			pb.setOrgName(b.getOrgName());
+			pb.setExamName(b.getExamName());
+			ret.add(pb);
+		}
+		return ret;
+	}
+	@Override
+	public void exportAll(ProjectEntity pe, Long examId, String items, HttpServletResponse response) {
+		Long projectId = pe.getId();
+		List<SheetData> sheets = new ArrayList<SheetData>();
+		List<String> examheader = new ArrayList<String>();
+		examheader.addAll(
+				Arrays.asList(new String[] { "考试名称", "报名人数", "实考人数","实考比例(%)", "缺考人数", "缺考率(%)", "及格人数", "及格报名人数占比(%)", "及格实考人数占比(%)" }));
+		int partitionCount = pe.getPartitionCount();
+		for (int i = 1; i <= partitionCount; i++) {
+			examheader.add("分段" + i + "人数");
+			examheader.add("分段" + i + "报名人数占比(%)");
+			examheader.add("分段" + i + "实考人数占比(%)");
+		}
+		List<String> header = new ArrayList<String>();
+		header.addAll(
+				Arrays.asList(new String[] { "考试名称","中心名称", "报名人数", "实考人数","实考比例(%)", "缺考人数", "缺考率(%)", "及格人数", "及格报名人数占比(%)", "及格实考人数占比(%)" }));
+		for (int i = 1; i <= partitionCount; i++) {
+			header.add("分段" + i + "人数");
+			header.add("分段" + i + "报名人数占比(%)");
+			header.add("分段" + i + "实考人数占比(%)");
+		}
+		List<ExamReportMainBean> list1 = getExamMainList(projectId, examId);
+		fillExamMainSheetdata(sheets,list1, examheader, partitionCount);
+		List<ExamOrgReportMainBean> list2 = getExamOrgMainList(projectId, examId);
+		fillExamOrgMainSheetdata(sheets, list2, header, partitionCount);
+//		if (StringUtils.isNotBlank(items)) {
+//			String[] itemstr = items.split(",");
+//			for (String it : itemstr) {
+//				if ("1".equals(it)) {
+//					fillParticipantSheetdata(sheets, list2, header,partitionCount);
+//				} else if ("2".equals(it)) {
+//					fillMissSheetdata(sheets, list2, header, partitionCount);
+//				} else if ("3".equals(it)) {
+//					fillPassSheetdata(sheets, list2, header, partitionCount);
+//				} else if ("4".equals(it)) {
+//					fillPassRatioSheetdata(sheets, list2, header, partitionCount);
+//				} else if ("5".equals(it)) {
+//					fillPartitionSheetdata(sheets, list2, header, partitionCount);
+//				} else if ("6".equals(it)) {
+//					fillPartitionRatioSheetdata(sheets, list2, header, partitionCount);
+//				}
+//			}
+//		}
+		ExportUtils.exportExcel("考试学习中心分析结果", sheets, response);
+	}
+
+	@Override
+	public void fillExamMainSheetdata(List<SheetData> sheets,List<ExamReportMainBean> list,List<String> header, int partitionCount) {
+		SheetData sheet = new SheetData();
+		sheet.setHeader(header);
+		sheet.setName("考试总体数值分析");
+		List<Object[]> data = new ArrayList<Object[]>();
+		for (ExamReportMainBean b : list) {
+			Object[] ob = new Object[header.size()];
+			ob[0] = b.getExamName();
+			ob[1] = b.getSignCount();
+			ob[2] = b.getParticipantCount();
+			ob[3] = b.getParticipantRatio();
+			ob[4] = b.getMissCount();
+			ob[5] = b.getMissRatio();
+			ob[6] = b.getPassCount();
+			ob[7] = b.getPassSignRatio();
+			ob[8] = b.getPassParticipantRatio();
+			for (int i = 0; i < partitionCount; i++) {
+				ob[9 + i*3] = b.getPartitionData().get(i).getCount();
+				ob[9 + i*3 + 1] = b.getPartitionData().get(i).getSignRatio();
+				ob[9 + i*3 + 2] = b.getPartitionData().get(i).getParticipantRatio();
+			}
+			data.add(ob);
+		}
+		sheet.setData(data);
+		sheets.add(sheet);
+	}
+
+	private void fillExamOrgMainSheetdata(List<SheetData> sheets,List<ExamOrgReportMainBean> list, List<String> header, int partitionCount) {
+		SheetData sheet = new SheetData();
+		sheet.setHeader(header);
+		sheet.setName("考试学习中心数值分析");
+		fillSheetData(sheets,sheet, list, header.size(), partitionCount);
+	}
+
+	private void fillSheetData(List<SheetData> sheets,SheetData sheet, List<ExamOrgReportMainBean> list, int headerSize, int partitionCount) {
+		List<Object[]> data = new ArrayList<Object[]>();
+		for (ExamOrgReportMainBean b : list) {
+			Object[] ob = new Object[headerSize];
+			ob[0] = b.getExamName();
+			ob[1] = b.getOrgName();
+			ob[2] = b.getSignCount();
+			ob[3] = b.getParticipantCount();
+			ob[4] = b.getParticipantRatio();
+			ob[5] = b.getMissCount();
+			ob[6] = b.getMissRatio();
+			ob[7] = b.getPassCount();
+			ob[8] = b.getPassSignRatio();
+			ob[9] = b.getPassParticipantRatio();
+			for (int i = 0; i < partitionCount; i++) {
+				ob[10 + i*3] = b.getPartitionData().get(i).getCount();
+				ob[10 + i*3 + 1] = b.getPartitionData().get(i).getSignRatio();
+				ob[10 + i*3 + 2] = b.getPartitionData().get(i).getParticipantRatio();
+			}
+			data.add(ob);
+		}
+		sheet.setData(data);
+		sheets.add(sheet);
+	}
+
+//	private void fillParticipantSheetdata(List<SheetData> sheets, List<ExamOrgReportMainBean> list, List<String> header, int partitionCount) {
+//		SheetData sheet1 = new SheetData();
+//		sheet1.setHeader(header);
+//		sheet1.setName("实考人数前十");
+//		fillSheetData(sheets,sheet1, getParticipantSort(list,desc), header.size(), partitionCount);
+//		SheetData sheet2 = new SheetData();
+//		sheet2.setHeader(header);
+//		sheet2.setName("实考人数后十");
+//		fillSheetData(sheets,sheet2, getParticipantSort(list,asc), header.size(), partitionCount);
+//		SheetData sheet3 = new SheetData();
+//		sheet3.setHeader(header);
+//		sheet3.setName("实考比例前十");
+//		fillSheetData(sheets,sheet3, getParticipantRatioSort(list,desc), header.size(), partitionCount);
+//		SheetData sheet4 = new SheetData();
+//		sheet4.setHeader(header);
+//		sheet4.setName("实考比例后十");
+//		fillSheetData(sheets,sheet4, getParticipantRatioSort(list,asc), header.size(), partitionCount);
+//	}
+//	private void fillMissSheetdata(List<SheetData> sheets, List<ExamOrgReportMainBean> list, List<String> header,int partitionCount) {
+//		SheetData sheet1 = new SheetData();
+//		sheet1.setHeader(header);
+//		sheet1.setName("缺考人数前十");
+//		fillSheetData(sheets,sheet1, getMissSort(list,desc), header.size(), partitionCount);
+//		SheetData sheet2 = new SheetData();
+//		sheet2.setHeader(header);
+//		sheet2.setName("缺考人数后十");
+//		fillSheetData(sheets,sheet2, getMissSort(list,asc), header.size(), partitionCount);
+//		SheetData sheet3 = new SheetData();
+//		sheet3.setHeader(header);
+//		sheet3.setName("缺考比例前十");
+//		fillSheetData(sheets,sheet3, getMissRatioSort(list,desc), header.size(), partitionCount);
+//		SheetData sheet4 = new SheetData();
+//		sheet4.setHeader(header);
+//		sheet4.setName("缺考比例后十");
+//		fillSheetData(sheets,sheet4, getMissRatioSort(list,asc), header.size(), partitionCount);
+//	}
+//	private void fillPassSheetdata(List<SheetData> sheets, List<ExamOrgReportMainBean> list, List<String> header,int partitionCount) {
+//		SheetData sheet1 = new SheetData();
+//		sheet1.setHeader(header);
+//		sheet1.setName("及格人数前十");
+//		fillSheetData(sheets,sheet1, getPassSort(list,desc), header.size(), partitionCount);
+//		SheetData sheet2 = new SheetData();
+//		sheet2.setHeader(header);
+//		sheet2.setName("及格人数后十");
+//		fillSheetData(sheets,sheet2, getPassSort(list,asc), header.size(), partitionCount);
+//	}
+//	private void fillPassRatioSheetdata(List<SheetData> sheets, List<ExamOrgReportMainBean> list, List<String> header, int partitionCount) {
+//		SheetData sheet1 = new SheetData();
+//		sheet1.setHeader(header);
+//		sheet1.setName("及格报名人数比例前十");
+//		fillSheetData(sheets,sheet1, getPassSignRatioSort(list,desc), header.size(), partitionCount);
+//		SheetData sheet2 = new SheetData();
+//		sheet2.setHeader(header);
+//		sheet2.setName("及格报名人数比例后十");
+//		fillSheetData(sheets,sheet2, getPassSignRatioSort(list,asc), header.size(), partitionCount);
+//		SheetData sheet3 = new SheetData();
+//		sheet3.setHeader(header);
+//		sheet3.setName("及格实考人数比例前十");
+//		fillSheetData(sheets,sheet3, getPassParticipantRatioSort(list,desc), header.size(), partitionCount);
+//		SheetData sheet4 = new SheetData();
+//		sheet4.setHeader(header);
+//		sheet4.setName("及格实考人数比例后十");
+//		fillSheetData(sheets,sheet4, getPassParticipantRatioSort(list,asc), header.size(), partitionCount);
+//	}
+//	private void fillPartitionSheetdata(List<SheetData> sheets, List<ExamOrgReportMainBean> list, List<String> header, int partitionCount) {
+//		for (int i = 1; i <= partitionCount; i++) {
+//			SheetData sheet1 = new SheetData();
+//			sheet1.setHeader(header);
+//			sheet1.setName("分段" + i + "人数前十");
+//			fillSheetData(sheets, sheet1, getPartitionCountSort(list, i-1, desc), header.size(), partitionCount);
+//		}
+//		for (int i = 1; i <= partitionCount; i++) {
+//			SheetData sheet1 = new SheetData();
+//			sheet1.setHeader(header);
+//			sheet1.setName("分段" + i + "人数后十");
+//			fillSheetData(sheets, sheet1, getPartitionCountSort(list, i-1, asc), header.size(), partitionCount);
+//		}
+//	}
+//	private void fillPartitionRatioSheetdata(List<SheetData> sheets, List<ExamOrgReportMainBean> list, List<String> header, int partitionCount) {
+//		for (int i = 1; i <= partitionCount; i++) {
+//			SheetData sheet1 = new SheetData();
+//			sheet1.setHeader(header);
+//			sheet1.setName("分段" + i + "报名人数比例前十");
+//			fillSheetData(sheets, sheet1, getPartitionSignRatioSort(list, i-1, desc), header.size(), partitionCount);
+//		}
+//		for (int i = 1; i <= partitionCount; i++) {
+//			SheetData sheet1 = new SheetData();
+//			sheet1.setHeader(header);
+//			sheet1.setName("分段" + i + "报名人数比例后十");
+//			fillSheetData(sheets, sheet1, getPartitionSignRatioSort(list, i-1, asc), header.size(), partitionCount);
+//		}
+//		for (int i = 1; i <= partitionCount; i++) {
+//			SheetData sheet1 = new SheetData();
+//			sheet1.setHeader(header);
+//			sheet1.setName("分段" + i + "实考人数比例前十");
+//			fillSheetData(sheets, sheet1, getPartitionParticipantRatioSort(list, i-1, desc), header.size(), partitionCount);
+//		}
+//		for (int i = 1; i <= partitionCount; i++) {
+//			SheetData sheet1 = new SheetData();
+//			sheet1.setHeader(header);
+//			sheet1.setName("分段" + i + "实考人数比例后十");
+//			fillSheetData(sheets, sheet1, getPartitionParticipantRatioSort(list, i-1, asc), header.size(), partitionCount);
+//		}
+//	}
+}

+ 69 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ExamStudentCountServiceImpl.java

@@ -0,0 +1,69 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.util.Date;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.examcloud.core.reports.base.util.online.ActiveDataUtil;
+import cn.com.qmth.examcloud.core.reports.base.util.online.OnlineCount;
+import cn.com.qmth.examcloud.core.reports.dao.ExamStudentCountRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ExamStudentCountEntity;
+import cn.com.qmth.examcloud.core.reports.service.ExamStudentCountService;
+
+@Service
+public class ExamStudentCountServiceImpl implements ExamStudentCountService {
+
+    @Autowired
+    private ExamStudentCountRepo examStudentCountRepo;
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Override
+    public Long getOnlineCount(Long rootOrgId, Long orgId, Long examId) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" select sum(t.online_count) from ec_r_exam_student_count t where t.root_org_id=").append(rootOrgId);
+        if (orgId != null) {
+            sb.append(" and t.org_id=").append(orgId);
+        }
+        if (examId != null) {
+            sb.append(" and t.exam_id=").append(examId);
+        }
+        Long count = jdbcTemplate.queryForObject(sb.toString(), Long.class);
+        if (count == null) {
+            count = 0l;
+        }
+        return count;
+    }
+
+    @Transactional
+    @Override
+    public synchronized void saveCount() {
+        examStudentCountRepo.resetAllCount();
+        List<OnlineCount> ret = ActiveDataUtil.getExamStudentCount(null);
+        if (ret != null) {
+            for (OnlineCount oc : ret) {
+                addOrUpdateCount(oc);
+            }
+        }
+    }
+
+    private void addOrUpdateCount(OnlineCount oc) {
+        ExamStudentCountEntity e = examStudentCountRepo.findByRootOrgIdAndExamId(oc.getRootOrgId(), oc.getExamId());
+        if (e == null) {
+            e = new ExamStudentCountEntity();
+            e.setRootOrgId(oc.getRootOrgId());
+            e.setExamId(oc.getExamId());
+            Date now = new Date();
+            e.setCreationTime(now);
+            e.setUpdateTime(now);
+        }
+        e.setOnlineCount(oc.getOnlineCount());
+        examStudentCountRepo.save(e);
+    }
+
+}

+ 445 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/ProjectServiceImpl.java

@@ -0,0 +1,445 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.persistence.criteria.Predicate;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.enums.ExamType;
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.reports.api.bean.ProjectInfoBean;
+import cn.com.qmth.examcloud.core.reports.api.request.GetProjectInfoBeanReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectCourseOrgCountReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectStatusByIdsReq;
+import cn.com.qmth.examcloud.core.reports.api.request.UpdateProjectStatusReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.AddProjectReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.ProjectBean;
+import cn.com.qmth.examcloud.core.reports.base.bean.QueryProjectPageReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.UpdateProjectReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.UpdateProjectScoreReq;
+import cn.com.qmth.examcloud.core.reports.base.enums.AnalyseType;
+import cn.com.qmth.examcloud.core.reports.base.enums.DataOrigin;
+import cn.com.qmth.examcloud.core.reports.base.enums.ReportStatus;
+import cn.com.qmth.examcloud.core.reports.dao.ProjectRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ProjectEntity;
+import cn.com.qmth.examcloud.core.reports.service.ExamCourseDataReportService;
+import cn.com.qmth.examcloud.core.reports.service.ExamOrgReportService;
+import cn.com.qmth.examcloud.core.reports.service.ProjectService;
+import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.examwork.api.request.GetExamListReq;
+import cn.com.qmth.examcloud.examwork.api.response.GetExamListResp;
+import cn.com.qmth.examcloud.task.api.ReportsComputeCloudService;
+import cn.com.qmth.examcloud.task.api.request.AddReportsComputeReq;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+
+@Service
+public class ProjectServiceImpl implements ProjectService {
+	@Autowired
+	private ExamOrgReportService examOrgReportService;
+	@Autowired
+	private ExamCourseDataReportService examCourseDataReportService;
+	@Autowired
+	private ExamCloudService examCloudService;
+	@Autowired
+	private ProjectRepo projectRepo;
+	@Autowired
+	private ReportsComputeCloudService reportsComputeCloudService;
+
+	@Transactional
+	@Override
+	public Long addProject(AddProjectReq req, Long rootOrgId) {
+		Date now = new Date();
+		ProjectEntity pe = new ProjectEntity();
+		BeanUtils.copyProperties(req, pe);
+		pe.setCreationTime(now);
+		pe.setUpdateTime(now);
+		pe.setRootOrgId(rootOrgId);
+		pe.setEnable(true);
+		pe.setReportStatus(ReportStatus.NONE);
+		pe.setExamIds(StringUtils.join(req.getExamIds().toArray(), ","));
+		if (pe.getDataOrigin().equals(DataOrigin.SYNC)) {
+			pe.setSampleCount(req.getExamIds().size());
+		}
+		projectRepo.save(pe);
+		return pe.getId();
+	}
+
+	@Transactional
+	@Override
+	public void updateProject(UpdateProjectReq req, Long rootOrgId) {
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, req.getId(), ProjectEntity.class);
+		if (pe == null) {
+			throw new StatusException("1000001", "项目不存在");
+		}
+		if (!rootOrgId.equals(pe.getRootOrgId())) {
+			throw new StatusException("1000002", "RootOrgId不一致");
+		}
+		pe.setName(req.getName());
+		pe.setRemarks(req.getRemarks());
+		projectRepo.save(pe);
+	}
+
+	@Transactional
+	@Override
+	public void updateEnable(String ids, Boolean enable, Long rootOrgId) {
+		List<Long> idList = Arrays.asList(ids.split(",")).stream().map(str -> Long.parseLong(str))
+				.collect(Collectors.toList());
+		projectRepo.updateEnable(idList, rootOrgId, enable);
+	}
+
+	private String toSqlSearchPattern(String column) {
+		if (StringUtils.isBlank(column)) {
+			return "%";
+		} else {
+			column = column.trim().replaceAll("\\s", "%");
+			column = "%" + column + "%";
+			return column;
+		}
+	}
+
+	@Override
+	public PageInfo<ProjectBean> queryPage(QueryProjectPageReq req, Integer pageNo, Integer pageSize, Long rootOrgId) {
+		Long id = req.getId();
+		String name = req.getName();
+		AnalyseType analyseType = req.getAnalyseType();
+		DataOrigin dataOrigin = req.getDataOrigin();
+		Specification<ProjectEntity> specification = (root, query, cb) -> {
+			List<Predicate> predicates = new ArrayList<>();
+			predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+
+			if (null != req.getEnable()) {
+				predicates.add(cb.equal(root.get("enable"), req.getEnable()));
+			}
+			if (null != id) {
+				predicates.add(cb.equal(root.get("id"), id));
+			}
+			if (null != analyseType) {
+				predicates.add(cb.equal(root.get("analyseType"), analyseType));
+			}
+			if (null != dataOrigin) {
+				predicates.add(cb.equal(root.get("dataOrigin"), dataOrigin));
+			}
+			if (StringUtils.isNotEmpty(name)) {
+				predicates.add(cb.like(root.get("name"), toSqlSearchPattern(name)));
+			}
+
+			return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+		};
+
+		PageRequest pageRequest = PageRequest.of(pageNo - 1, pageSize, new Sort(Direction.DESC, "id"));
+
+		Page<ProjectEntity> projects = projectRepo.findAll(specification, pageRequest);
+
+		List<ProjectBean> ret = Lists.newArrayList();
+
+		for (ProjectEntity cur : projects) {
+			ProjectBean bean = new ProjectBean();
+			BeanUtils.copyProperties(cur, bean);
+			bean.setAnalyseTypeName(bean.getAnalyseType().getDesc());
+			bean.setDataOriginName(bean.getDataOrigin().getDesc());
+			bean.setReportStatusName(bean.getReportStatus().getDesc());
+			if (StringUtils.isNotBlank(cur.getExamIds())) {
+				List<Long> temList = Arrays.asList(cur.getExamIds().split(",")).stream().map(str -> Long.parseLong(str))
+						.collect(Collectors.toList());
+				bean.setExamIds(temList);
+			}
+			if (StringUtils.isNotBlank(cur.getPartitionDetails())) {
+				List<Double> temList = Arrays.asList(cur.getPartitionDetails().split(",")).stream()
+						.map(str -> Double.parseDouble(str)).collect(Collectors.toList());
+				bean.setPartitionDetails(temList);
+			}
+			ret.add(bean);
+		}
+		return new PageInfo<ProjectBean>(projects, ret);
+	}
+
+	@Transactional
+	@Override
+	public void updateProjectStatus(UpdateProjectStatusReq req) {
+		Long id = req.getProjectId();
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, id, ProjectEntity.class);
+		if (pe == null) {
+			throw new StatusException("1000003", "项目不存在");
+		}
+		if (req.getStatus() == 2) {
+			pe.setReportStatus(ReportStatus.COMPUTING);
+		} else if (req.getStatus() == 3) {
+			pe.setReportStatus(ReportStatus.SUCCESS);
+		} else if (req.getStatus() == 4) {
+			pe.setReportStatus(ReportStatus.FAIL);
+		} else if (req.getStatus() == 5) {
+			pe.setReportStatus(ReportStatus.STOP);
+		} else {
+			throw new StatusException("1000004", "无效的Status");
+		}
+		projectRepo.save(pe);
+	}
+
+	@Override
+	public ProjectInfoBean getProjectInfoBean(GetProjectInfoBeanReq req) {
+		if (req.getProjectId() == null) {
+			throw new StatusException("1000005", "项目id不能为空");
+		}
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, req.getProjectId(), ProjectEntity.class);
+		if (pe == null) {
+			throw new StatusException("1000006", "项目不存在");
+		}
+		ProjectInfoBean bean = new ProjectInfoBean();
+		BeanUtils.copyProperties(pe, bean);
+		bean.setAnalyseType(pe.getAnalyseType().name());
+		bean.setDataOrigin(pe.getDataOrigin().name());
+		bean.setReportStatus(pe.getReportStatus().name());
+		List<Double> partitionDetails = Arrays.asList(pe.getPartitionDetails().split(",")).stream()
+				.map(str -> Double.parseDouble(str)).collect(Collectors.toList());
+		bean.setPartitionDetails(partitionDetails);
+		return bean;
+	}
+
+	@Transactional
+	@Override
+	public void addReportsCompute(Long id, Long rootOrgId) {
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, id, ProjectEntity.class);
+		if (pe == null) {
+			throw new StatusException("1000021", "项目不存在");
+		}
+		if (!rootOrgId.equals(pe.getRootOrgId())) {
+			throw new StatusException("1000022", "非法操作");
+		}
+		if (!pe.getEnable()) {
+			throw new StatusException("1000023", "项目已禁用");
+		}
+		if (pe.getReportStatus().equals(ReportStatus.WAITCOMPUTE)) {
+			throw new StatusException("1000024", "项目已处于待计算状态");
+		}
+		if (pe.getReportStatus().equals(ReportStatus.COMPUTING)) {
+			throw new StatusException("1000025", "项目已处于计算状态");
+		}
+		if (pe.getPassScore() == null || pe.getTotalScore() == null || pe.getPartitionDetails() == null) {
+			throw new StatusException("1000026", "项目未设定及格分及分数段");
+		}
+		pe.setReportStatus(ReportStatus.WAITCOMPUTE);
+		projectRepo.saveAndFlush(pe);
+		AddReportsComputeReq req = new AddReportsComputeReq();
+		req.setProjectId(id);
+		req.setRootOrgId(rootOrgId);
+		reportsComputeCloudService.addReportsCompute(req);
+	}
+
+	@Transactional
+	@Override
+	public void updateScore(UpdateProjectScoreReq req, Long rootOrgId) {
+		Long id = req.getId();
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, id, ProjectEntity.class);
+		if (pe == null) {
+			throw new StatusException("1000031", "项目不存在");
+		}
+		if (!rootOrgId.equals(pe.getRootOrgId())) {
+			throw new StatusException("1000032", "非法操作");
+		}
+		if (!pe.getEnable()) {
+			throw new StatusException("1000033", "项目已禁用");
+		}
+		if (pe.getReportStatus().equals(ReportStatus.WAITCOMPUTE)) {
+			throw new StatusException("1000034", "项目已处于待计算状态");
+		}
+		if (pe.getReportStatus().equals(ReportStatus.COMPUTING)) {
+			throw new StatusException("1000035", "项目已处于计算状态");
+		}
+		Double passScore = req.getPassScore();
+		Double totalScore = req.getTotalScore();
+		if (passScore <= 0) {
+			throw new StatusException("1000036", "及格分数必须大于0");
+		}
+		if (totalScore <= 0) {
+			throw new StatusException("1000037", "满分分数必须大于0");
+		}
+		if (passScore > totalScore) {
+			throw new StatusException("1000038", "及格分数不能大于满分分数");
+		}
+		List<Double> scores = req.getPartitionDetails();
+		Set<Double> set = new HashSet<Double>();
+		for (Double d : scores) {
+			set.add(d);
+		}
+		if (set.size() != scores.size()) {
+			throw new StatusException("1000039", "分段分数不能相同");
+		}
+		Collections.sort(scores);
+		if (scores.get(0) <= 0) {
+			throw new StatusException("1000040", "第一个分段分数必须大于0");
+		}
+		if (scores.get(scores.size() - 1) >= totalScore) {
+			throw new StatusException("1000041", "最后一个分段分数必须小于满分分数");
+		}
+		pe.setUpdateTime(new Date());
+		pe.setPassScore(passScore);
+		pe.setTotalScore(totalScore);
+		pe.setPartitionCount(scores.size()+1);
+		pe.setPartitionDetails(StringUtils.join(scores.toArray(), ","));
+		if (!ReportStatus.NONE.equals(pe.getReportStatus())) {
+			pe.setReportStatus(ReportStatus.WAITCOMPUTE);
+		}
+		projectRepo.saveAndFlush(pe);
+		if (!ReportStatus.NONE.equals(pe.getReportStatus())) {
+			AddReportsComputeReq areq = new AddReportsComputeReq();
+			areq.setProjectId(id);
+			areq.setRootOrgId(rootOrgId);
+			reportsComputeCloudService.addReportsCompute(areq);
+		}
+	}
+
+	@Override
+	public ProjectEntity findProjectById(Long projectId) {
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, projectId, ProjectEntity.class);
+		return pe;
+	}
+
+	@Transactional
+	@Override
+	public void updateProjectCourseOrgCount(UpdateProjectCourseOrgCountReq req) {
+		projectRepo.updateProjectCourseOrgCount(req.getProjectId(), req.getCourseCount(), req.getOrgCount());
+	}
+
+	@Override
+	public List<ExamBean> queryAllExam(Long rootOrgId) {
+		Date now = new Date();
+		List<ExamBean> exams = new ArrayList<ExamBean>();
+		// 从考务获取启用的考试列表集合信息
+		GetExamListReq getExamListReq = new GetExamListReq();
+		getExamListReq.setEnable(true);// 启用的考试列表
+		getExamListReq.setRootOrgId(rootOrgId);
+		for (;;) {
+			GetExamListResp getExamListResp = examCloudService.getExamList(getExamListReq);
+			if (getExamListResp.getExamList() == null || getExamListResp.getExamList().size() == 0) {
+				break;
+			} else {
+				exams.addAll(getExamListResp.getExamList());
+				getExamListReq.setStart(getExamListResp.getNext());
+			}
+		}
+		// 按一定条件过滤考试集合
+		exams = exams.stream().filter(exam -> {
+			if (!exam.getExamType().equals(ExamType.ONLINE.toString())) {
+				return false;
+			}
+			if (now.before(exam.getEndTime())) {
+				return false;
+			}
+			return true;
+		}).collect(Collectors.toList());
+		return exams;
+	}
+
+	@Transactional
+	@Override
+	public void delProject(Long id, Long rootOrgId) {
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, id, ProjectEntity.class);
+		if (pe == null) {
+			throw new StatusException("1000041", "项目不存在");
+		}
+		if (!rootOrgId.equals(pe.getRootOrgId())) {
+			throw new StatusException("1000042", "非法操作");
+		}
+		if (pe.getReportStatus().equals(ReportStatus.WAITCOMPUTE)) {
+			throw new StatusException("1000044", "不能删除待计算的项目");
+		}
+		if (pe.getReportStatus().equals(ReportStatus.COMPUTING)) {
+			throw new StatusException("1000045", "不能删除计算中的项目");
+		}
+		examOrgReportService.deleteByProject(id, rootOrgId);
+		examCourseDataReportService.deleteByProject(id, rootOrgId);
+		projectRepo.deleteByIdAndRootOrgId(id, rootOrgId);
+	}
+
+	@Transactional
+	@Override
+	public void updateProjectStatusByIds(UpdateProjectStatusByIdsReq req) {
+		List<Long> cids = req.getComputingProjectIds();
+		List<Long> sids = req.getStopingProjectIds();
+		if(cids!=null&&cids.size()!=0) {
+			for (Long id : cids) {
+				ProjectEntity pe = GlobalHelper.getEntity(projectRepo, id, ProjectEntity.class);
+				if (pe == null) {
+					throw new StatusException("1000051", "项目不存在");
+				}
+				pe.setReportStatus(ReportStatus.WAITCOMPUTE);
+				projectRepo.save(pe);
+			}
+		}
+		if(sids!=null&&sids.size()!=0) {
+			for (Long id : sids) {
+				ProjectEntity pe = GlobalHelper.getEntity(projectRepo, id, ProjectEntity.class);
+				if (pe == null) {
+					throw new StatusException("1000052", "项目不存在");
+				}
+				pe.setReportStatus(ReportStatus.STOP);
+				projectRepo.save(pe);
+			}
+		}
+	}
+
+	@Override
+	public ProjectBean getProject(Long id, Long rootOrgId) {
+		ProjectEntity pe = GlobalHelper.getEntity(projectRepo, id, ProjectEntity.class);
+		if (pe == null) {
+			throw new StatusException("1000061", "项目不存在");
+		}
+		if (!pe.getRootOrgId().equals(rootOrgId)) {
+			throw new StatusException("1000062", "非法操作");
+		}
+		ProjectBean bean = new ProjectBean();
+		BeanUtils.copyProperties(pe, bean);
+		bean.setAnalyseTypeName(bean.getAnalyseType().getDesc());
+		bean.setDataOriginName(bean.getDataOrigin().getDesc());
+		bean.setReportStatusName(bean.getReportStatus().getDesc());
+		if (StringUtils.isNotBlank(pe.getExamIds())) {
+			List<Long> temList = Arrays.asList(pe.getExamIds().split(",")).stream().map(str -> Long.parseLong(str))
+					.collect(Collectors.toList());
+			bean.setExamIds(temList);
+		}
+		if (StringUtils.isNotBlank(pe.getPartitionDetails())) {
+			List<Double> temList = Arrays.asList(pe.getPartitionDetails().split(",")).stream()
+					.map(str -> Double.parseDouble(str)).collect(Collectors.toList());
+			bean.setPartitionDetails(temList);
+		}
+		return bean;
+	}
+
+	@Override
+	public List<ProjectBean> getProjectList(List<Long> ids, Long rootOrgId) {
+		List<ProjectBean> ret=new ArrayList<ProjectBean>();
+		List<ProjectEntity> list=projectRepo.getByIds(ids, rootOrgId);
+		if(list!=null) {
+			for(ProjectEntity pe:list) {
+				ProjectBean bean = new ProjectBean();
+				BeanUtils.copyProperties(pe, bean);
+				bean.setAnalyseTypeName(bean.getAnalyseType().getDesc());
+				bean.setDataOriginName(bean.getDataOrigin().getDesc());
+				bean.setReportStatusName(bean.getReportStatus().getDesc());
+				ret.add(bean);
+			}
+		}
+		return ret;
+	}
+}

+ 74 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/StudentCountServiceImpl.java

@@ -0,0 +1,74 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCountBean;
+import cn.com.qmth.examcloud.core.reports.base.util.online.ActiveDataUtil;
+import cn.com.qmth.examcloud.core.reports.base.util.online.OnlineCount;
+import cn.com.qmth.examcloud.core.reports.dao.StudentCountRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.StudentCountEntity;
+import cn.com.qmth.examcloud.core.reports.service.StudentCountService;
+
+@Service
+public class StudentCountServiceImpl implements StudentCountService {
+	@Autowired
+	private StudentCountRepo studentCountRepo;
+
+
+	@Transactional
+	@Override
+	public synchronized void saveCount() {
+		studentCountRepo.resetAllCount();
+		List<OnlineCount> ret = ActiveDataUtil.getStudentCount(null);
+		if (ret != null) {
+			for (OnlineCount oc : ret) {
+				addOrUpdateCount(oc);
+			}
+		}
+	}
+
+	private void addOrUpdateCount(OnlineCount oc) {
+		StudentCountEntity e = studentCountRepo.findByRootOrgId(oc.getRootOrgId());
+		if (e == null) {
+			e = new StudentCountEntity();
+			e.setRootOrgId(oc.getRootOrgId());
+			Date now = new Date();
+			e.setCreationTime(now);
+			e.setUpdateTime(now);
+		}
+		e.setOnlineCount(oc.getOnlineCount());
+		studentCountRepo.save(e);
+	}
+
+	@Override
+	public List<StudentCountBean> getCountByRootOrgIds(List<Long> rootOrgIds) {
+		if (CollectionUtils.isEmpty(rootOrgIds)) {
+			return null;
+		}
+		List<StudentCountEntity> list = studentCountRepo.getCountByRootOrgIds(rootOrgIds);
+		if (CollectionUtils.isEmpty(list)) {
+			return null;
+		}
+		List<StudentCountBean> ret = Lists.newArrayList();
+		for (StudentCountEntity e : list) {
+			StudentCountBean bean = new StudentCountBean();
+			bean.setRootOrgId(e.getRootOrgId());
+			bean.setOnlineCount(e.getOnlineCount());
+			ret.add(bean);
+		}
+		return ret;
+	}
+
+	@Override
+	public Long getSumOnlineCount() {
+		return studentCountRepo.getSumOnlineCount();
+	}
+}

+ 139 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/StudentCumulativeCountServiceImpl.java

@@ -0,0 +1,139 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCumulativeCountBean;
+import cn.com.qmth.examcloud.core.reports.base.util.online.ActiveDataUtil;
+import cn.com.qmth.examcloud.core.reports.base.util.online.OnlineCount;
+import cn.com.qmth.examcloud.core.reports.dao.StudentCumulativeCountRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.StudentCumulativeCountEntity;
+import cn.com.qmth.examcloud.core.reports.service.StudentCumulativeCountService;
+
+@Service
+public class StudentCumulativeCountServiceImpl implements StudentCumulativeCountService {
+	@Autowired
+	private StudentCumulativeCountRepo studentCumulativeCountRepo;
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+
+	@Transactional
+	@Override
+	public synchronized void saveCount() {
+		// 取当前人数累计数据
+		List<OnlineCount> ret = ActiveDataUtil.getStudentLoginCount();
+		// 判断StudentLoginDataReportDay跟当前系统日期是否一致,不一致说明到了第二天,累计数据需要清空
+		Date now = new Date();
+		Date last = DateUtils.addDays(now, -1);
+		String lastday = DateUtil.format(last, DateUtil.DatePatterns.YYYY_MM_DD);
+		String today = DateUtil.format(now, DateUtil.DatePatterns.YYYY_MM_DD);
+		boolean isTomorrow = !today.equals(ActiveDataUtil.getStudentLoginDataReportDay());
+		if (isTomorrow) {
+			ActiveDataUtil.clearStudentLoginCount();
+		}
+
+		if (ret != null) {
+			for (OnlineCount oc : ret) {
+				addOrUpdateCount(oc, lastday, today, isTomorrow);
+			}
+		}
+	}
+
+	private void addOrUpdateCount(OnlineCount oc, String lastday, String today, boolean isTomorrow) {
+		if (isTomorrow) {
+			// 第二天
+			StudentCumulativeCountEntity oe = studentCumulativeCountRepo.findByRootOrgIdAndReportDay(oc.getRootOrgId(),
+					lastday);
+			if (oe != null) {
+				// 更新昨天的数据
+				oe.setTotalCount(oc.getOnlineCount());
+				studentCumulativeCountRepo.save(oe);
+			}
+			// 插入一条人数为零的今天数据
+			StudentCumulativeCountEntity e = new StudentCumulativeCountEntity();
+			e.setRootOrgId(oc.getRootOrgId());
+			e.setTotalCount(0);
+			e.setReportDay(today);
+			Date now = new Date();
+			e.setCreationTime(now);
+			e.setUpdateTime(now);
+			studentCumulativeCountRepo.save(e);
+		} else {
+			// 今天 插入或更新一条当前人数的今天数据
+			StudentCumulativeCountEntity e = studentCumulativeCountRepo.findByRootOrgIdAndReportDay(oc.getRootOrgId(),
+					today);
+			if (e == null) {
+				e = new StudentCumulativeCountEntity();
+				e.setRootOrgId(oc.getRootOrgId());
+				e.setReportDay(today);
+				Date now = new Date();
+				e.setCreationTime(now);
+				e.setUpdateTime(now);
+			}
+			e.setTotalCount(oc.getOnlineCount());
+			studentCumulativeCountRepo.save(e);
+		}
+	}
+
+	@Override
+	public List<StudentCumulativeCountBean> getLastNdayOnlineCount(Integer nday) {
+		if (nday < 1 || nday > 100) {
+			throw new StatusException("100001", "查询天数1到100之间");
+		}
+		List<String> ds=getDays(nday);
+		StringBuffer sb = new StringBuffer();
+		sb.append(" select sum(t.total_count) totalCount,t.report_day reportDay from ec_r_student_cumulative_count t where t.report_day in ('-1'");
+		for (String d:ds) {
+			sb.append(",'").append(d).append("'");
+		}
+		sb.append(") group by t.report_day");
+		RowMapper<StudentCumulativeCountBean> rowMapper=new BeanPropertyRowMapper<StudentCumulativeCountBean>(StudentCumulativeCountBean.class);
+		List<StudentCumulativeCountBean> ret = jdbcTemplate.query(sb.toString(), rowMapper);
+		
+		List<StudentCumulativeCountBean> initList=getInitList(ds);
+		if (CollectionUtils.isNotEmpty(ret)) {
+			Map<String, Integer> map = ret.stream().collect(Collectors.toMap(StudentCumulativeCountBean::getReportDay,
+					account -> account.getTotalCount(), (key1, key2) -> key2));
+			for(StudentCumulativeCountBean b:initList) {
+				Integer c=map.get(b.getReportDay());
+				b.setTotalCount(c==null?0:c);
+			}
+		}
+		return initList;
+	}
+	private List<StudentCumulativeCountBean> getInitList(List<String> ds) {
+		List<StudentCumulativeCountBean> ret = Lists.newArrayList();
+		for(String d:ds) {
+			StudentCumulativeCountBean sb=new StudentCumulativeCountBean();
+			sb.setReportDay(d);
+			sb.setTotalCount(0);
+			ret.add(sb);
+		}
+		return ret;
+	}
+	private List<String> getDays(Integer nday) {
+		List<String> ds = Lists.newArrayList();
+		Date now = new Date();
+		for (int i = nday; i > 0; i--) {
+			Date day = DateUtils.addDays(now, 1-i);
+			String daystr = DateUtil.format(day, DateUtil.DatePatterns.YYYY_MM_DD);
+			ds.add(daystr);
+		}
+		return ds;
+	}
+}

+ 211 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/StudentTotalCountServiceImpl.java

@@ -0,0 +1,211 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.persistence.criteria.Predicate;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.google.common.collect.Lists;
+
+import cn.com.qmth.examcloud.api.commons.exchange.PageInfo;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.OrgCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.OrgBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetOrgsByIdListReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetOrgsByIdListResp;
+import cn.com.qmth.examcloud.core.reports.api.request.SaveStudentTotalCountReq;
+import cn.com.qmth.examcloud.core.reports.base.bean.StudentCountBean;
+import cn.com.qmth.examcloud.core.reports.base.util.BatchGetDataUtil;
+import cn.com.qmth.examcloud.core.reports.dao.ExamStudentCountRepo;
+import cn.com.qmth.examcloud.core.reports.dao.StudentTotalCountRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.ExamStudentCountEntity;
+import cn.com.qmth.examcloud.core.reports.dao.entity.StudentTotalCountEntity;
+import cn.com.qmth.examcloud.core.reports.service.StudentCountService;
+import cn.com.qmth.examcloud.core.reports.service.StudentTotalCountService;
+
+@Service
+public class StudentTotalCountServiceImpl implements StudentTotalCountService {
+
+    @Autowired
+    private StudentTotalCountRepo studentTotalCountRepo;
+
+    @Autowired
+    private ExamStudentCountRepo examStudentCountRepo;
+
+    @Autowired
+    private StudentCountService studentCountService;
+
+    @Autowired
+    private OrgCloudService orgCloudService;
+
+    @Override
+    public Long getSumTotalCount() {
+        Long ret = studentTotalCountRepo.getSumTotalCount();
+        if (ret == null) {
+            ret = 0l;
+        }
+        return ret;
+    }
+
+    @Override
+    public PageInfo<StudentCountBean> queryPage(String rootOrgId, Integer pageNo, Integer pageSize) {
+        Specification<StudentTotalCountEntity> specification = (root, query, cb) -> {
+            List<Predicate> predicates = new ArrayList<>();
+            if (null != rootOrgId) {
+                predicates.add(cb.equal(root.get("rootOrgId"), rootOrgId));
+            }
+
+            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
+        };
+
+        List<StudentTotalCountEntity> es = studentTotalCountRepo.findAll(specification);
+
+        List<StudentCountBean> ret = Lists.newArrayList();
+
+        for (StudentTotalCountEntity cur : es) {
+            StudentCountBean bean = new StudentCountBean();
+            bean.setTotalCount(cur.getTotalCount());
+            bean.setRootOrgId(cur.getRootOrgId());
+            bean.setOnlineCount(0);
+            bean.setOnExamCount(0);
+            ret.add(bean);
+        }
+        fillOnlineCount(ret);
+        fillOnExamCount(ret);
+        fillRootOrgName(ret);
+        Collections.sort(ret, new Comparator<StudentCountBean>() {
+
+            @Override
+            public int compare(StudentCountBean o1, StudentCountBean o2) {
+                if (o1.getOnlineCount() > o2.getOnlineCount()) {
+                    return -1;
+                } else if (o1.getOnlineCount() < o2.getOnlineCount()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+
+        });
+        int total = ret.size();
+        ret = ret.stream().skip((pageNo - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
+        PageInfo<StudentCountBean> pi = new PageInfo<StudentCountBean>();
+        pi.setLimit(pageSize);
+        pi.setList(ret);
+        pi.setPages(pageNo);
+        pi.setTotal(total);
+        return pi;
+    }
+
+    private void fillOnlineCount(List<StudentCountBean> ret) {
+        if (CollectionUtils.isEmpty(ret)) {
+            return;
+        }
+        List<Long> temList = ret.stream().map(str -> str.getRootOrgId()).collect(Collectors.toList());
+        List<StudentCountBean> sl = studentCountService.getCountByRootOrgIds(temList);
+        if (CollectionUtils.isEmpty(sl)) {
+            return;
+        }
+        Map<Long, Integer> map = sl.stream().collect(Collectors.toMap(StudentCountBean::getRootOrgId,
+                account -> account.getOnlineCount(), (key1, key2) -> key2));
+        for (StudentCountBean b : ret) {
+            if (map.get(b.getRootOrgId()) != null) {
+                b.setOnlineCount(map.get(b.getRootOrgId()));
+            }
+        }
+    }
+
+    private void fillOnExamCount(List<StudentCountBean> ret) {
+        if (CollectionUtils.isEmpty(ret)) {
+            return;
+        }
+        List<Long> temList = ret.stream().map(str -> str.getRootOrgId()).collect(Collectors.toList());
+        List<ExamStudentCountEntity> sl = examStudentCountRepo.getByRootOrgIds(temList);
+        if (CollectionUtils.isEmpty(sl)) {
+            return;
+        }
+        Map<Long, Integer> map = groupByRootOrg(sl);
+        for (StudentCountBean b : ret) {
+            if (map.get(b.getRootOrgId()) != null) {
+                b.setOnExamCount(map.get(b.getRootOrgId()));
+            }
+        }
+    }
+
+    private Map<Long, Integer> groupByRootOrg(List<ExamStudentCountEntity> sl) {
+        Map<Long, Integer> map = new HashMap<Long, Integer>();
+        for (ExamStudentCountEntity s : sl) {
+            Integer c = map.get(s.getRootOrgId());
+            if (c == null) {
+                map.put(s.getRootOrgId(), s.getOnlineCount());
+            } else {
+                map.put(s.getRootOrgId(), s.getOnlineCount() + c);
+            }
+        }
+        return map;
+    }
+
+    private void fillRootOrgName(List<StudentCountBean> ret) {
+        if (CollectionUtils.isEmpty(ret)) {
+            return;
+        }
+        List<Long> param = ret.stream().map(str -> str.getRootOrgId()).collect(Collectors.toList());
+        List<OrgBean> retList = new ArrayList<OrgBean>();
+        GetOrgsByIdListReq req = new GetOrgsByIdListReq();
+        BatchGetDataUtil<OrgBean, Long> tool = new BatchGetDataUtil<OrgBean, Long>() {
+
+            @Override
+            public List<OrgBean> getData(List<Long> paramList) {
+
+                req.setOrgIdList(paramList);
+                GetOrgsByIdListResp resp = orgCloudService.getOrgsByIdList(req);
+                return resp.getOrgList();
+            }
+
+        };
+        tool.getDataForBatch(retList, param, 100);
+        if (CollectionUtils.isEmpty(retList)) {
+            return;
+        }
+        Map<Long, String> map = retList.stream()
+                .collect(Collectors.toMap(OrgBean::getId, account -> account.getName(), (key1, key2) -> key2));
+        for (StudentCountBean b : ret) {
+            b.setRootOrgName(map.get(b.getRootOrgId()));
+        }
+    }
+
+    @Transactional
+    @Override
+    public void save(SaveStudentTotalCountReq req) {
+        Long rootOrgId = req.getRootOrgId();
+        Integer totalCount = req.getCount();
+        if (totalCount == null) {
+            totalCount = 0;
+        }
+        if (rootOrgId == null) {
+            throw new StatusException("1000001", "rootOrgId不能为空");
+        }
+        StudentTotalCountEntity se = studentTotalCountRepo.findByRootOrgId(rootOrgId);
+        if (se == null) {
+            Date now = new Date();
+            se = new StudentTotalCountEntity();
+            se.setRootOrgId(rootOrgId);
+            se.setCreationTime(now);
+            se.setUpdateTime(now);
+        }
+        se.setTotalCount(totalCount.intValue());
+        studentTotalCountRepo.save(se);
+    }
+}

+ 62 - 0
examcloud-core-reports-service/src/main/java/cn/com/qmth/examcloud/core/reports/service/impl/UserCountServiceImpl.java

@@ -0,0 +1,62 @@
+package cn.com.qmth.examcloud.core.reports.service.impl;
+
+import java.util.Date;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.examcloud.core.reports.base.util.online.ActiveDataUtil;
+import cn.com.qmth.examcloud.core.reports.base.util.online.OnlineCount;
+import cn.com.qmth.examcloud.core.reports.dao.UserCountRepo;
+import cn.com.qmth.examcloud.core.reports.dao.entity.UserCountEntity;
+import cn.com.qmth.examcloud.core.reports.service.UserCountService;
+
+@Service
+public class UserCountServiceImpl implements UserCountService {
+	@Autowired
+	private UserCountRepo userCountRepo;
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+
+	@Override
+	public Long getOnlineCount(Long rootOrgId, Long orgId) {
+		StringBuffer sb = new StringBuffer();
+		sb.append(" select sum(t.online_count) from ec_r_user_count t where t.root_org_id=").append(rootOrgId);
+		if (orgId != null) {
+			sb.append(" and t.org_id=").append(orgId);
+		}
+		Long count = jdbcTemplate.queryForObject(sb.toString(), Long.class);
+		if (count == null) {
+			count = 0l;
+		}
+		return count;
+	}
+
+	@Transactional
+	@Override
+	public synchronized void saveCount() {
+		userCountRepo.resetAllCount();
+		List<OnlineCount> ret = ActiveDataUtil.getUserCount(null);
+		if (ret != null) {
+			for (OnlineCount oc : ret) {
+				addOrUpdateCount(oc);
+			}
+		}
+	}
+
+	private void addOrUpdateCount(OnlineCount oc) {
+		UserCountEntity e = userCountRepo.findByRootOrgId(oc.getRootOrgId());
+		if (e == null) {
+			e = new UserCountEntity();
+			e.setRootOrgId(oc.getRootOrgId());
+			Date now=new Date();
+			e.setCreationTime(now);
+			e.setUpdateTime(now);
+		}
+		e.setOnlineCount(oc.getOnlineCount());
+		userCountRepo.save(e);
+	}
+}

+ 30 - 0
examcloud-core-reports-starter/assembly.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+	<id>distribution</id>
+	<formats>
+		<format>zip</format>
+	</formats>
+	<fileSets>
+		<fileSet>
+			<directory>${project.basedir}/src/main/resources</directory>
+			<outputDirectory>/config</outputDirectory>
+		</fileSet>
+		<fileSet>
+			<directory>${project.basedir}/shell</directory>
+			<excludes>
+				<exclude>start.args</exclude>
+				<exclude>start.vmoptions</exclude>
+			</excludes>
+			<outputDirectory>/</outputDirectory>
+			<fileMode>0777</fileMode>
+		</fileSet>
+	</fileSets>
+	<dependencySets>
+		<dependencySet>
+			<useProjectArtifact>true</useProjectArtifact>
+			<outputDirectory>lib</outputDirectory>
+			<scope>runtime</scope>
+		</dependencySet>
+	</dependencySets>
+</assembly>

+ 69 - 0
examcloud-core-reports-starter/pom.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<project
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+		<artifactId>examcloud-core-reports</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-core-reports-starter</artifactId>
+
+	<dependencies>
+		<dependency>
+			<groupId>com.github.xiaoymin</groupId>
+			<artifactId>swagger-bootstrap-ui</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.core.reports</groupId>
+			<artifactId>examcloud-core-reports-api-provider</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>cn.com.qmth.examcloud.core.reports.starter.CoreReportsApp</mainClass>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>./</classpathPrefix>
+						</manifest>
+						<manifestEntries>
+							<Class-Path>../config/</Class-Path>
+						</manifestEntries>
+					</archive>
+					<excludes>
+						<exclude>*.properties</exclude>
+						<exclude>*.xml </exclude>
+						<exclude>*.conf </exclude>
+					</excludes>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<finalName>examcloud-core-reports</finalName>
+					<descriptors>
+						<descriptor>assembly.xml</descriptor>
+					</descriptors>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<phase>install</phase>
+						<goals>
+							<goal>assembly</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>

+ 1 - 0
examcloud-core-reports-starter/shell/start.args

@@ -0,0 +1 @@
+--spring.profiles.active=test

+ 27 - 0
examcloud-core-reports-starter/shell/start.sh

@@ -0,0 +1,27 @@
+#!/bin/bash
+
+FILE_PATH=$(cd `dirname $0`; pwd)
+APP_VERSION=`cat $FILE_PATH/version`
+APP_MAIN_JAR="examcloud-core-reports-starter-"$APP_VERSION"-SNAPSHOT.jar"
+
+JAVA_OPTS=`cat $FILE_PATH/start.vmoptions`
+APP_ARGS=`cat $FILE_PATH/start.args`
+
+PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
+
+if [ ! -z "$PID_LIST" ]; then
+    echo "[ERROR] : APP is already running!"
+    exit -1
+fi
+APP_ARGS=$APP_ARGS" --examcloud.startup.startupCode="$1
+echo "java options:"
+echo "$JAVA_OPTS"
+echo "args:"
+echo "$APP_ARGS"
+    
+nohup java $JAVA_OPTS -jar $FILE_PATH/lib/$APP_MAIN_JAR $APP_ARGS >/dev/null 2>&1 &
+
+echo "starting......"
+
+exit 0
+

+ 1 - 0
examcloud-core-reports-starter/shell/start.vmoptions

@@ -0,0 +1 @@
+-server -Xms512m -Xmx512m

+ 18 - 0
examcloud-core-reports-starter/shell/stop.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+
+FILE_PATH=$(cd `dirname $0`; pwd)
+APP_VERSION=`cat $FILE_PATH/version`
+APP_MAIN_JAR="examcloud-core-reports-starter-"$APP_VERSION"-SNAPSHOT.jar"
+
+PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
+
+if [ ! -z "$PID_LIST" ]; then
+    echo "Runnable jar is $APP_MAIN_JAR."
+    for PID in $PID_LIST 
+    do
+        kill -9 $PID
+    done
+    echo "stopped !"
+fi
+
+exit 0

+ 1 - 0
examcloud-core-reports-starter/shell/version

@@ -0,0 +1 @@
+2019

+ 34 - 0
examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/CoreReportsApp.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.examcloud.core.reports.starter;
+
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
+
+
+@ComponentScan(basePackages = { "cn.com.qmth" })
+@EntityScan(basePackages = { "com.qmth.commons", "cn.com.qmth" })
+@SpringBootApplication
+@EnableEurekaClient
+@EnableFeignClients
+@EnableJpaRepositories(basePackages = {"cn.com.qmth"})
+@EnableAutoConfiguration
+public class CoreReportsApp {
+	static {
+		String runtimeLevel = System.getProperty("log.commonLevel");
+		if (null == runtimeLevel) {
+			System.setProperty("log.commonLevel", "DEBUG");
+		}
+		System.setProperty("hibernate.dialect.storage_engine", "innodb");
+	}
+
+	public static void main(String[] args) throws Exception {
+
+		AppBootstrap.run(CoreReportsApp.class,args);
+	}
+}

+ 132 - 0
examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/ExamCloudResourceManager.java

@@ -0,0 +1,132 @@
+package cn.com.qmth.examcloud.core.reports.starter.config;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Sets;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.AccessApp;
+import cn.com.qmth.examcloud.api.commons.security.bean.Role;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
+import cn.com.qmth.examcloud.commons.util.RegExpUtil;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.AppCacheBean;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.security.ResourceManager;
+import cn.com.qmth.examcloud.web.support.ApiInfo;
+
+/**
+ * Demo资源管理器
+ *
+ * @author WANGWEI
+ * @date 2019年2月18日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Component
+public class ExamCloudResourceManager implements ResourceManager {
+
+	@Autowired
+	RedisClient redisClient;
+
+	static {
+		PropertiesUtil.loadFromPath(PathUtil.getResoucePath("security.properties"));
+	}
+
+	@Override
+	public AccessApp getAccessApp(Long appId) {
+		AppCacheBean appCacheBean = CacheHelper.getApp(appId);
+		AccessApp app = new AccessApp();
+		app.setAppId(appCacheBean.getId());
+		app.setAppCode(appCacheBean.getCode());
+		app.setAppName(appCacheBean.getName());
+		app.setSecretKey(appCacheBean.getSecretKey());
+		app.setTimeRange(appCacheBean.getTimeRange());
+		return app;
+	}
+
+	@Override
+	public boolean isNaked(ApiInfo apiInfo, String mapping) {
+		if (null == apiInfo) {
+			return true;
+		}
+
+		if (mapping.matches(".*swagger.*")) {
+			return true;
+		}
+
+		if (null != apiInfo) {
+			if (apiInfo.isNaked()) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	@Override
+	public boolean hasPermission(User user, ApiInfo apiInfo, String mapping) {
+
+		// 学生鉴权
+		if (user.getUserType().equals(UserType.STUDENT)) {
+			String key = "[s]" + mapping;
+			return PropertiesUtil.getBoolean(key, false);
+		}
+
+		List<Role> roleList = user.getRoleList();
+
+		if (CollectionUtils.isEmpty(roleList)) {
+			return false;
+		}
+
+		for (Role role : roleList) {
+			if (role.getRoleCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+				return true;
+			}
+		}
+
+		// 权限组集合
+		String privilegeGroups = PropertiesUtil.getString(mapping);
+		if (StringUtils.isBlank(privilegeGroups)) {
+			return true;
+		}
+
+		// 用户权限集合
+		Set<String> rolePrivilegeList = Sets.newHashSet();
+		Long rootOrgId = user.getRootOrgId();
+		for (Role role : roleList) {
+			String key = "$_P_" + rootOrgId + "_" + role.getRoleId();
+			String rolePrivileges = redisClient.get(key, String.class);
+
+			List<String> rpList = RegExpUtil.findAll(rolePrivileges, "\\w+");
+			rolePrivilegeList.addAll(rpList);
+		}
+
+		List<String> privilegeGroupList = RegExpUtil.findAll(privilegeGroups, "[^\\;]+");
+
+		for (String pg : privilegeGroupList) {
+			pg = pg.trim();
+			if (StringUtils.isBlank(pg)) {
+				continue;
+			}
+
+			List<String> pList = RegExpUtil.findAll(pg, "[^\\,]+");
+			if (rolePrivilegeList.containsAll(pList)) {
+				return true;
+			} else {
+				continue;
+			}
+		}
+
+		return false;
+	}
+
+}

+ 47 - 0
examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/ExamCloudWebMvcConfigurer.java

@@ -0,0 +1,47 @@
+package cn.com.qmth.examcloud.core.reports.starter.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import cn.com.qmth.examcloud.web.interceptor.FirstInterceptor;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.security.RequestPermissionInterceptor;
+import cn.com.qmth.examcloud.web.security.ResourceManager;
+import cn.com.qmth.examcloud.web.security.RpcInterceptor;
+
+/**
+ * WebMvcConfigurer
+ *
+ * @author WANGWEI
+ * @date 2019年1月30日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Configuration
+public class ExamCloudWebMvcConfigurer implements WebMvcConfigurer {
+
+	@Autowired
+	ResourceManager resourceManager;
+
+	@Autowired
+	RedisClient redisClient;
+
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");
+		registry.addInterceptor(new RpcInterceptor(resourceManager)).addPathPatterns("/**");
+
+		RequestPermissionInterceptor permissionInterceptor = new RequestPermissionInterceptor(
+				resourceManager, redisClient);
+		registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
+	}
+
+	@Override
+	public void addCorsMappings(CorsRegistry registry) {
+		registry.addMapping("/**").allowedOrigins("*").allowCredentials(false).allowedMethods("*")
+				.maxAge(3600);
+	}
+
+}

+ 47 - 0
examcloud-core-reports-starter/src/main/java/cn/com/qmth/examcloud/core/reports/starter/config/MqConsumerListenerStartup.java

@@ -0,0 +1,47 @@
+package cn.com.qmth.examcloud.core.reports.starter.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.examcloud.core.reports.api.listener.KafkaConsumerListener;
+import cn.com.qmth.examcloud.core.reports.api.listener.RocketMqConsumerListener;
+import cn.com.qmth.examcloud.reports.commons.enums.MqType;
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
+
+@Component
+@Order(999)
+public class MqConsumerListenerStartup implements ApplicationRunner {
+	private final static Logger logger = LoggerFactory.getLogger(MqConsumerListenerStartup.class);
+	private void startConsumerListener() {
+
+		String mqType = PropertyHolder.getString("$report.mq-type");
+
+		Boolean reportEnable = PropertyHolder.getBoolean("$report.enable", false);
+
+		if (reportEnable) {
+			if(MqType.KAFKA.getCode().equals(mqType)) {
+				KafkaConsumerListener.start();
+			}else if(MqType.ROCKETMQ.getCode().equals(mqType)) {
+				RocketMqConsumerListener.start();
+			}else {
+				logger.error("no $report.mq-type property config!");
+			}
+		}
+
+	}
+
+	@Override
+	public void run(ApplicationArguments args) throws Exception {
+		new Thread() {
+			@Override
+			public void run() {
+				startConsumerListener();
+			}
+			
+		}.start();
+	}
+}

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor