Parcourir la source

修改统计分析实现方案,改为模块化流式计算

luoshi il y a 6 ans
Parent
commit
6552a56a93
20 fichiers modifiés avec 596 ajouts et 107 suppressions
  1. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamDao.java
  2. 2 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamService.java
  3. 8 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamServiceImpl.java
  4. 13 12
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java
  5. 13 7
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  6. 3 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  7. 2 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/ReportService.java
  8. 57 16
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/impl/ReportServiceImpl.java
  9. 11 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/Module.java
  10. 51 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/ReportContext.java
  11. 25 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectClassModule.java
  12. 25 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectCollegeModule.java
  13. 68 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectModule.java
  14. 26 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectTeacherClassModule.java
  15. 25 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectTeacherModule.java
  16. 96 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/unit/BaseCalculatorUnit.java
  17. 27 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/unit/BaseCounter.java
  18. 34 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/unit/RangeCounter.java
  19. 30 30
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java
  20. 76 38
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java

+ 4 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamDao.java

@@ -29,5 +29,9 @@ public interface ExamDao extends PagingAndSortingRepository<Exam, Integer>, JpaS
     @Query("update Exam e set e.sliceConfig=?2 where e.id=?1")
     public void updateSliceConfig(Integer examId, String configString);
 
+    @Modifying
+    @Query("update Exam e set e.needCalculate=?2 where e.id=?1")
+    public void updateNeedCalculate(Integer examId, boolean needCalculate);
+
     public List<Exam> findBySchoolIdOrderByIdDesc(Integer schoolId);
 }

+ 2 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamService.java

@@ -17,6 +17,8 @@ public interface ExamService {
 
     void updateSliceConfig(Integer id, List<PictureConfigItem> configList);
 
+    void updateNeedCalculate(Integer id, boolean needCalculate);
+
     void deleteById(Integer id);
 
     void delete(Exam exam);

+ 8 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamServiceImpl.java

@@ -55,6 +55,14 @@ public class ExamServiceImpl extends BaseQueryService<Exam> implements ExamServi
         examDao.updateStatus(id, status);
     }
 
+    @Override
+    @Transactional
+    // @CacheEvict(value = "exam_cache", beforeInvocation = true, allEntries =
+    // true)
+    public void updateNeedCalculate(Integer id, boolean needCalculate) {
+        examDao.updateNeedCalculate(id, needCalculate);
+    }
+
     @Transactional
     // @CacheEvict(value = "exam_cache", beforeInvocation = true, allEntries =
     // true)

+ 13 - 12
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java

@@ -154,9 +154,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
                 subject.setHasPaper(false);
                 subject.setRemark(es.getRemark());
             } else {
-            	subject.setLevel(es.getLevel());
-            	subject.setCategory(es.getCategory());
-            	subject.setRemark(es.getRemark());
+                subject.setLevel(es.getLevel());
+                subject.setCategory(es.getCategory());
+                subject.setRemark(es.getRemark());
             }
             subjectService.save(subject);
         }
@@ -271,6 +271,15 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         return studentDao.findByExamId(examId, null);
     }
 
+    @Override
+    public List<ExamStudent> findByExamId(int examId, int pageNumber, int pageSize) {
+        ExamStudentSearchQuery query = new ExamStudentSearchQuery();
+        query.setPageNumber(pageNumber);
+        query.setPageSize(pageSize);
+        query.setSort(new Sort(Direction.ASC, "id"));
+        return studentDao.findByExamId(examId, query);
+    }
+
     @Override
     public List<ExamStudent> findByExamIdAndUploadAndAbsent(int examId, boolean upload, boolean absent, int pageNumber,
             int pageSize) {
@@ -699,14 +708,6 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         return countByQuery(query);
     }
 
-    @Override
-    public List<ExamStudent> findByExamId(int examId, int pageNumber, int pageSize) {
-        ExamStudentSearchQuery query = new ExamStudentSearchQuery();
-        query.setPageNumber(pageNumber);
-        query.setPageSize(pageSize);
-        return studentDao.findByExamId(examId, query);
-    }
-
     public long countByNoAbsentAndBreach(int examId, String subjectCode, boolean upload, boolean absent,
             boolean breach) {
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
@@ -932,7 +933,7 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         }
         return true;
     }
-    
+
     @Override
     public long countByExamIdAndManualAbsent(int examId, boolean manualAbsent) {
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();

+ 13 - 7
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java

@@ -98,7 +98,7 @@ public class MarkServiceImpl implements MarkService {
 
     @Autowired
     private MarkLockService lockService;
-    
+
     @Autowired
     private ExamService examService;
 
@@ -236,7 +236,7 @@ public class MarkServiceImpl implements MarkService {
             // 科目总分
             subjectService.updateScore(group.getExamId(), group.getSubjectCode(), false,
                     sumTotalScore(group.getExamId(), group.getSubjectCode()));
-            //需要重新统分
+            // 需要重新统分
             Exam exam = examService.findById(group.getExamId());
             exam.setNeedCalculate(true);
             examService.save(exam);
@@ -642,7 +642,8 @@ public class MarkServiceImpl implements MarkService {
         libraryDao.updateHeaderResult(history.getStudentId(), history.getGroupNumber(), history.getUserId(),
                 history.getTotalScore(), history.getScoreList(), history.getUpdateTime(), LibraryStatus.ARBITRATED);
         updateMarkedCount(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
-        scoreCalculate(history.getExamId(), history.getSubjectCode(), history.getStudentId());
+        scoreCalculate(studentService.findById(history.getStudentId()),
+                groupDao.findByExamIdAndSubjectCode(history.getExamId(), history.getSubjectCode()));
     }
 
     /**
@@ -654,8 +655,10 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     @Transactional
-    public void scoreCalculate(Integer examId, String subjectCode, Integer studentId) {
-        List<MarkGroup> groups = groupDao.findByExamIdAndSubjectCode(examId, subjectCode);
+    public void scoreCalculate(ExamStudent student, List<MarkGroup> groups) {
+        if (student == null || groups == null || groups.isEmpty()) {
+            return;
+        }
         List<ScoreItem> scoreList = new ArrayList<ScoreItem>();
         double totalScore = 0.0;
         // 循环所有大题
@@ -663,7 +666,7 @@ public class MarkServiceImpl implements MarkService {
             if (group.getStatus().equals(MarkStatus.TRIAL)) {
                 return;
             }
-            if (calculateGroup(group, studentId)) {
+            if (calculateGroup(group, student.getId())) {
                 totalScore += group.getMarkScore();
                 scoreList.addAll(group.getMarkScoreDetail());
             } else {
@@ -672,7 +675,10 @@ public class MarkServiceImpl implements MarkService {
             }
         }
         // 全部评完,更新考生主观题得分
-        studentService.updateSubjectiveScore(studentId, totalScore, ExamStudent.buildScoreList(scoreList));
+        student.setSubjectiveScore(totalScore);
+        student.setScoreList(scoreList, false);
+        studentService.updateSubjectiveScore(student.getId(), student.getSubjectiveScore(),
+                student.getSubjectiveScoreList());
     }
 
     private boolean calculateGroup(MarkGroup group, Integer studentId) {

+ 3 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java

@@ -119,11 +119,10 @@ public interface MarkService {
     /**
      * 对某个考生某个科目进行主观题统分
      * 
-     * @param examId
-     * @param subjectCode
-     * @param studentId
+     * @param student
+     * @param groups
      */
-    void scoreCalculate(Integer examId, String subjectCode, Integer studentId);
+    void scoreCalculate(ExamStudent student, List<MarkGroup> groups);
 
     /**
      * 管理员/组长处理仲裁卷

+ 2 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/ReportService.java

@@ -13,4 +13,6 @@ public interface ReportService {
      */
     void generateReportData(Integer examId);
 
+    void deleteData(Integer examId);
+
 }

+ 57 - 16
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/impl/ReportServiceImpl.java

@@ -1,13 +1,21 @@
 package cn.com.qmth.stmms.biz.report.service.impl;
 
-import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
-import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
-import cn.com.qmth.stmms.biz.report.service.*;
+import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.util.List;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.report.service.ReportService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectClassService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectCollegeService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectGroupService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectQuestionService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectRangeService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectTeacherClassService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectTeacherService;
 
 /**
  * 报表服务 service 实现
@@ -45,11 +53,11 @@ public class ReportServiceImpl implements ReportService {
     @Autowired
     private ExamSubjectService examSubjectService;
 
-
     /**
      * 生成报表数据
      *
-     * @param examId 考试id
+     * @param examId
+     *            考试id
      */
     @Override
     public void generateReportData(Integer examId) {
@@ -65,7 +73,8 @@ public class ReportServiceImpl implements ReportService {
      *
      * @param examId
      */
-    private void deleteData(Integer examId) {
+    @Override
+    public void deleteData(Integer examId) {
 
         reportSubjectService.deleteByExamId(examId);
 
@@ -93,34 +102,66 @@ public class ReportServiceImpl implements ReportService {
     private void saveData(Integer examId) {
         List<ExamSubject> examSubjects = examSubjectService.list(examId);
 
-        //保存课程总量统计分析
+        // 保存课程总量统计分析
         reportSubjectService.saveReportSubjectData(examId);
 
-        //保存课程分段统计
+        // 保存课程分段统计
         reportSubjectRangeService.saveReportRangeSubjectData(examId);
 
         for (ExamSubject subject : examSubjects) {
             String subjectCode = subject.getCode();
-            //保存学院成绩分析
+            // 保存学院成绩分析
             reportSubjectCollegeService.saveReportSubjectCollegeData(examId, subjectCode);
 
-            //保存课程任课老师成绩分析
+            // 保存课程任课老师成绩分析
             reportSubjectTeacherService.saveReportSubjectTeacherData(examId, subjectCode);
 
-            //保存任课老师班级成绩分析
+            // 保存任课老师班级成绩分析
             reportSubjectTeacherClassService.saveReportSubjectTeacherClassData(examId, subjectCode);
 
-            //保存成班级成绩分析
+            // 保存成班级成绩分析
             reportSubjectClassService.saveReportSubjectClassData(examId, subjectCode);
 
-            //保存客观题成绩分析
+            // 保存客观题成绩分析
             reportSubjectQuestionService.saveReportSubjectQuestionData(examId, subjectCode, true);
 
-            //保存主观题成绩分析
+            // 保存主观题成绩分析
             reportSubjectQuestionService.saveReportSubjectQuestionData(examId, subjectCode, false);
 
-            //保存大题型成绩分析
+            // 保存大题型成绩分析
             reportSubjectGroupService.saveReportSubjectGroupData(examId, subjectCode);
         }
     }
+
+    public ReportSubjectService getReportSubjectService() {
+        return reportSubjectService;
+    }
+
+    public ReportSubjectRangeService getReportSubjectRangeService() {
+        return reportSubjectRangeService;
+    }
+
+    public ReportSubjectCollegeService getReportSubjectCollegeService() {
+        return reportSubjectCollegeService;
+    }
+
+    public ReportSubjectTeacherService getReportSubjectTeacherService() {
+        return reportSubjectTeacherService;
+    }
+
+    public ReportSubjectTeacherClassService getReportSubjectTeacherClassService() {
+        return reportSubjectTeacherClassService;
+    }
+
+    public ReportSubjectClassService getReportSubjectClassService() {
+        return reportSubjectClassService;
+    }
+
+    public ReportSubjectQuestionService getReportSubjectQuestionService() {
+        return reportSubjectQuestionService;
+    }
+
+    public ReportSubjectGroupService getReportSubjectGroupService() {
+        return reportSubjectGroupService;
+    }
 }

+ 11 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/Module.java

@@ -0,0 +1,11 @@
+package cn.com.qmth.stmms.biz.report.utils;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+
+public interface Module {
+
+    void process(ExamStudent student);
+
+    void save();
+
+}

+ 51 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/ReportContext.java

@@ -0,0 +1,51 @@
+package cn.com.qmth.stmms.biz.report.utils;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import cn.com.qmth.stmms.biz.exam.model.Exam;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.report.utils.module.SubjectClassModule;
+import cn.com.qmth.stmms.biz.report.utils.module.SubjectCollegeModule;
+import cn.com.qmth.stmms.biz.report.utils.module.SubjectModule;
+import cn.com.qmth.stmms.biz.report.utils.module.SubjectTeacherClassModule;
+import cn.com.qmth.stmms.biz.report.utils.module.SubjectTeacherModule;
+
+public class ReportContext {
+
+    private Exam exam;
+
+    private List<Module> modules;
+
+    public ReportContext(Exam exam) {
+        this.exam = exam;
+        this.modules = new LinkedList<Module>();
+
+        this.modules.add(new SubjectModule(this));
+        this.modules.add(new SubjectClassModule(this));
+        this.modules.add(new SubjectCollegeModule(this));
+        this.modules.add(new SubjectTeacherModule(this));
+        this.modules.add(new SubjectTeacherClassModule(this));
+    }
+
+    public void process(ExamStudent student) {
+        for (Module module : modules) {
+            module.process(student);
+        }
+    }
+
+    public void save() {
+        for (Module module : modules) {
+            module.save();
+        }
+    }
+
+    public Double getPassScore() {
+        return exam.getPassScore();
+    }
+
+    public Double getExcellentScore() {
+        return exam.getExcellentScore();
+    }
+
+}

+ 25 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectClassModule.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.stmms.biz.report.utils.module;
+
+import org.apache.commons.lang.StringUtils;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectClassService;
+import cn.com.qmth.stmms.biz.report.utils.Module;
+import cn.com.qmth.stmms.biz.report.utils.ReportContext;
+import cn.com.qmth.stmms.biz.utils.SpringContextHolder;
+
+public class SubjectClassModule extends SubjectModule implements Module {
+
+    public SubjectClassModule(ReportContext context) {
+        super(context);
+    }
+
+    public String getKey(ExamStudent student) {
+        return student.getSubjectCode() + "\t" + StringUtils.trimToEmpty(student.getClassName());
+    }
+
+    public void save() {
+        ReportSubjectClassService service = SpringContextHolder.getBean(ReportSubjectClassService.class);
+    }
+
+}

+ 25 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectCollegeModule.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.stmms.biz.report.utils.module;
+
+import org.apache.commons.lang.StringUtils;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectClassService;
+import cn.com.qmth.stmms.biz.report.utils.Module;
+import cn.com.qmth.stmms.biz.report.utils.ReportContext;
+import cn.com.qmth.stmms.biz.utils.SpringContextHolder;
+
+public class SubjectCollegeModule extends SubjectModule implements Module {
+
+    public SubjectCollegeModule(ReportContext context) {
+        super(context);
+    }
+
+    public String getKey(ExamStudent student) {
+        return student.getSubjectCode() + "\t" + StringUtils.trimToEmpty(student.getCollege());
+    }
+
+    public void save() {
+        ReportSubjectClassService service = SpringContextHolder.getBean(ReportSubjectClassService.class);
+    }
+
+}

+ 68 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectModule.java

@@ -0,0 +1,68 @@
+package cn.com.qmth.stmms.biz.report.utils.module;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectService;
+import cn.com.qmth.stmms.biz.report.utils.Module;
+import cn.com.qmth.stmms.biz.report.utils.ReportContext;
+import cn.com.qmth.stmms.biz.report.utils.unit.BaseCalculatorUnit;
+import cn.com.qmth.stmms.biz.report.utils.unit.BaseCounter;
+import cn.com.qmth.stmms.biz.utils.SpringContextHolder;
+
+public class SubjectModule implements Module {
+
+    private Map<String, BaseCalculatorUnit> units;
+
+    private Map<String, BaseCounter> counters;
+
+    private ReportContext context;
+
+    public SubjectModule(ReportContext context) {
+        this.units = new HashMap<String, BaseCalculatorUnit>();
+        this.counters = new HashMap<String, BaseCounter>();
+        this.context = context;
+    }
+
+    public void process(ExamStudent student) {
+        ExamSubject subject = student.getSubject();
+        if (subject != null) {
+            String key = getKey(student);
+            if (student.isUpload() && !student.isAbsent() && !student.isBreach()) {
+                findUnit(key).processScore(student.getTotalScore(), subject.getTotalScore(), student.getTotalScore());
+            }
+            findCounter(key).process(student);
+        }
+    }
+
+    public String getKey(ExamStudent student) {
+        return student.getSubjectCode();
+    }
+
+    public void save() {
+        ReportSubjectService service = SpringContextHolder.getBean(ReportSubjectService.class);
+    }
+
+    private BaseCalculatorUnit findUnit(String key) {
+        BaseCalculatorUnit unit = units.get(key);
+        if (unit == null) {
+            unit = new BaseCalculatorUnit();
+            unit.passScore = context.getPassScore();
+            unit.excellentScore = context.getExcellentScore();
+            units.put(key, unit);
+        }
+        return unit;
+    }
+
+    private BaseCounter findCounter(String key) {
+        BaseCounter counter = counters.get(key);
+        if (counter == null) {
+            counter = new BaseCounter();
+            counters.put(key, counter);
+        }
+        return counter;
+    }
+
+}

+ 26 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectTeacherClassModule.java

@@ -0,0 +1,26 @@
+package cn.com.qmth.stmms.biz.report.utils.module;
+
+import org.apache.commons.lang.StringUtils;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectClassService;
+import cn.com.qmth.stmms.biz.report.utils.Module;
+import cn.com.qmth.stmms.biz.report.utils.ReportContext;
+import cn.com.qmth.stmms.biz.utils.SpringContextHolder;
+
+public class SubjectTeacherClassModule extends SubjectModule implements Module {
+
+    public SubjectTeacherClassModule(ReportContext context) {
+        super(context);
+    }
+
+    public String getKey(ExamStudent student) {
+        return student.getSubjectCode() + "\t" + StringUtils.trimToEmpty(student.getTeacher()) + "\t"
+                + StringUtils.trimToEmpty(student.getClassName());
+    }
+
+    public void save() {
+        ReportSubjectClassService service = SpringContextHolder.getBean(ReportSubjectClassService.class);
+    }
+
+}

+ 25 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectTeacherModule.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.stmms.biz.report.utils.module;
+
+import org.apache.commons.lang.StringUtils;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectClassService;
+import cn.com.qmth.stmms.biz.report.utils.Module;
+import cn.com.qmth.stmms.biz.report.utils.ReportContext;
+import cn.com.qmth.stmms.biz.utils.SpringContextHolder;
+
+public class SubjectTeacherModule extends SubjectModule implements Module {
+
+    public SubjectTeacherModule(ReportContext context) {
+        super(context);
+    }
+
+    public String getKey(ExamStudent student) {
+        return student.getSubjectCode() + "\t" + StringUtils.trimToEmpty(student.getTeacher());
+    }
+
+    public void save() {
+        ReportSubjectClassService service = SpringContextHolder.getBean(ReportSubjectClassService.class);
+    }
+
+}

+ 96 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/unit/BaseCalculatorUnit.java

@@ -0,0 +1,96 @@
+package cn.com.qmth.stmms.biz.report.utils.unit;
+
+public class BaseCalculatorUnit {
+
+    public int count;
+
+    public int zeroCount;
+
+    public int fullCount;
+
+    public double fullScore;
+
+    public Double passScore;
+
+    public int passCount;
+
+    public Double excellentScore;
+
+    public int excellentCount;
+
+    public double aggScore;
+
+    public double sumScore;
+
+    public double sumScore2;
+
+    public double sumTotalScore;
+
+    public double sumTotalScore2;
+
+    public double maxScore;
+
+    public double minScore;
+
+    public double avgScore;
+
+    public double stdev;
+
+    public double tempSum;
+
+    public double difficulty;
+
+    public double coefficient;
+
+    public double discrimination;
+
+    public BaseCalculatorUnit() {
+        minScore = Double.MAX_VALUE;
+    }
+
+    public void processScore(Double score, Double fullScore, Double totalScore) {
+        if (score == null || fullScore == null || totalScore == null) {
+            return;
+        }
+        if (this.fullScore == 0) {
+            this.fullScore = fullScore;
+        }
+        count++;
+        if (score == 0) {
+            zeroCount++;
+        } else if (score.equals(fullScore)) {
+            fullCount++;
+        }
+        if (passScore != null && score >= passScore) {
+            passCount++;
+        }
+        if (excellentScore != null && score >= excellentScore) {
+            excellentCount++;
+        }
+        sumScore += score;
+        sumScore2 += (score * score);
+        aggScore += (score * totalScore);
+        sumTotalScore += totalScore;
+        sumTotalScore2 += (totalScore * totalScore);
+        maxScore = Math.max(maxScore, score);
+        minScore = Math.min(minScore, score);
+        avgScore = sumScore / count;
+        // 递归法计算标准差
+        tempSum = tempSum + 1.0 * (count - 1) / count * (score - avgScore) * (score - avgScore);
+        stdev = Math.sqrt(tempSum / (count - 1));
+        // 难度
+        difficulty = fullScore > 0 ? avgScore / fullScore : 0;
+        // 差异系数
+        coefficient = avgScore > 0 ? stdev * 100 / avgScore : 0;
+        // 区分度
+        discrimination = (aggScore - sumScore * sumTotalScore / count) / Math.sqrt(
+                (sumScore2 - sumScore * sumScore / count) * (sumTotalScore2 - sumTotalScore * sumTotalScore / count));
+    }
+
+    @Override
+    public String toString() {
+        return "count=" + count + ",zeroCount=" + zeroCount + ",fullCount=" + fullCount + ",fullScore=" + fullScore
+                + ",maxScore=" + maxScore + ",minScore=" + minScore + ",avgScore=" + avgScore + ",stdev=" + stdev
+                + ",difficulty=" + difficulty + ",coefficient=" + coefficient + ",discrimination=" + discrimination;
+    }
+}

+ 27 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/unit/BaseCounter.java

@@ -0,0 +1,27 @@
+package cn.com.qmth.stmms.biz.report.utils.unit;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+
+public class BaseCounter {
+
+    public int totalCount;
+
+    public int absentCount;
+
+    public int breachCount;
+
+    public int realityCount;
+
+    public void process(ExamStudent student) {
+        totalCount++;
+        if (student.isAbsent()) {
+            absentCount++;
+        }
+        if (student.isUpload() && !student.isAbsent()) {
+            realityCount++;
+        }
+        if (student.isBreach()) {
+            breachCount++;
+        }
+    }
+}

+ 34 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/unit/RangeCounter.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.stmms.biz.report.utils.unit;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RangeCounter {
+
+    public int totalCount;
+
+    public double interval;
+
+    public Map<Double, Integer> map;
+
+    public RangeCounter() {
+        this.interval = 1;
+        this.map = new HashMap<Double, Integer>();
+    }
+
+    public void process(double score) {
+        totalCount++;
+        // 取最接近的分段起始分数
+        double min = Math.floor(score);
+        while ((min + interval) < score) {
+            min += interval;
+        }
+        // 以起始分数为key,累加计数结果
+        Integer count = map.get(min);
+        if (count == null) {
+            count = 0;
+        }
+        count++;
+        map.put(min, count);
+    }
+}

+ 30 - 30
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java

@@ -6,8 +6,6 @@ import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import net.sf.json.JSONObject;
-
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -50,6 +48,7 @@ import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONObject;
 
 @Controller
 @RequestMapping("/admin/exam/score")
@@ -203,9 +202,8 @@ public class ScoreController extends BaseExamController {
     public ModelAndView calculate(HttpServletRequest request) {
         int examId = getSessionExamId(request);
         if (lockService.trylock(LockType.EXAM, examId)) {
-            ScoreCalculateThread thread = new ScoreCalculateThread(examId, lockService, studentService,
-                    questionService, markService, checkStudentService,
-                    reportService,examService,subjectService);
+            ScoreCalculateThread thread = new ScoreCalculateThread(examId, lockService, studentService, questionService,
+                    markService, checkStudentService, reportService, examService, subjectService, groupService);
             taskExecutor.submit(thread);
         }
         return new ModelAndView("redirect:/admin/exam/score");
@@ -217,7 +215,7 @@ public class ScoreController extends BaseExamController {
         WebUser wu = RequestUtils.getWebUser(request);
         int examId = getSessionExamId(request);
         String exporMmessage = enableExport(examId);
-        if(exporMmessage ==null && StringUtils.isNotBlank(query.getStudentCode())){
+        if (exporMmessage == null && StringUtils.isNotBlank(query.getStudentCode())) {
             exporMmessage = enableExport(examId, query.getStudentCode());
         }
         if (exporMmessage != null) {
@@ -261,16 +259,16 @@ public class ScoreController extends BaseExamController {
             return "redirect:/admin/exam/score";
         }
     }
-    
+
     @RequestMapping("/enableExport")
     @ResponseBody
     public JSONObject query(HttpServletRequest request, @RequestParam(required = false) String subjectCode) {
         int examId = getSessionExamId(request);
         JSONObject obj = new JSONObject();
         String exportMessage = null;
-        if(StringUtils.isNotBlank(subjectCode)){
+        if (StringUtils.isNotBlank(subjectCode)) {
             exportMessage = enableExport(examId, subjectCode);
-        }else{
+        } else {
             exportMessage = enableExport(examId);
         }
         if (exportMessage != null) {
@@ -348,43 +346,45 @@ public class ScoreController extends BaseExamController {
 
     private String enableExport(int examId) {
         String message = isChecked(examId);
-        if(message==null){
+        if (message == null) {
             List<ExamSubject> subjects = subjectService.list(examId);
             for (ExamSubject subject : subjects) {
-                message = enableExport(examId,subject.getCode());
-                if(message!=null){
+                message = enableExport(examId, subject.getCode());
+                if (message != null) {
                     return message;
                 }
             }
         }
         return message;
     }
-    
+
     private String isChecked(int examId) {
         String message = null;
         Exam exam = examService.findById(examId);
-//        ExamStudentSearchQuery query = new ExamStudentSearchQuery();
-//        query.setExamId(examId);
-//        query.setUpload(false);
-//        query.setManualAbsent(false);
-//        long unUploadManualAbsentCount = studentService.countByQuery(query);
+        // ExamStudentSearchQuery query = new ExamStudentSearchQuery();
+        // query.setExamId(examId);
+        // query.setUpload(false);
+        // query.setManualAbsent(false);
+        // long unUploadManualAbsentCount = studentService.countByQuery(query);
         if (exam.isNeedCalculate()) {
             message = "该考试需要统分";
-        } 
-//        else if (unUploadManualAbsentCount > 0) {
-//            message = "未上传考生必须人工指定缺考";
-//        } else if (checkStudentService.countByExamIdAndChecked(examId, false) != 0) {
-//            message = "人工确认未完成";
-//        } 
+        }
+        // else if (unUploadManualAbsentCount > 0) {
+        // message = "未上传考生必须人工指定缺考";
+        // } else if (checkStudentService.countByExamIdAndChecked(examId, false)
+        // != 0) {
+        // message = "人工确认未完成";
+        // }
         return message;
     }
-    
-    private String enableExport(int examId,String subjectCode) {
+
+    private String enableExport(int examId, String subjectCode) {
         String message = isChecked(examId);
-        if(message==null){
-            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.FORMAL,MarkStatus.TRIAL);
-            if(groups!=null && !groups.isEmpty()){
-                message = subjectCode+" 科目未评卷完成";
+        if (message == null) {
+            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.FORMAL,
+                    MarkStatus.TRIAL);
+            if (groups != null && !groups.isEmpty()) {
+                message = subjectCode + " 科目未评卷完成";
             }
         }
         return message;

+ 76 - 38
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java

@@ -8,22 +8,23 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import cn.com.qmth.stmms.biz.exam.model.CheckStudent;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.service.CheckStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.report.service.ReportService;
+import cn.com.qmth.stmms.biz.report.utils.ReportContext;
 import cn.com.qmth.stmms.biz.utils.ScoreCalculateUtil;
 import cn.com.qmth.stmms.biz.utils.ScoreInfo;
-import cn.com.qmth.stmms.common.enums.CheckType;
 import cn.com.qmth.stmms.common.enums.LockType;
 
 public class ScoreCalculateThread implements Runnable {
@@ -45,19 +46,25 @@ public class ScoreCalculateThread implements Runnable {
     private Map<String, List<ExamQuestion>> objectiveMap;
 
     private Map<String, List<ExamQuestion>> subjectiveMap;
-    
+
     private Map<String, ExamSubject> subjectMap;
 
+    private Map<String, List<MarkGroup>> groupMap;
+
     private CheckStudentService checkStudentService;
-    
+
+    private MarkGroupService groupService;
+
     private ExamSubjectService subjectService;
-    
+
     private ExamService examService;
 
+    private ReportContext context;
+
     public ScoreCalculateThread(int examId, LockService lockService, ExamStudentService studentService,
-            ExamQuestionService questionService, 
-            MarkService markService, CheckStudentService checkStudentService,
-            ReportService reportService,ExamService examService,ExamSubjectService subjectService) {
+            ExamQuestionService questionService, MarkService markService, CheckStudentService checkStudentService,
+            ReportService reportService, ExamService examService, ExamSubjectService subjectService,
+            MarkGroupService groupService) {
         this.examId = examId;
         this.lockService = lockService;
         this.studentService = studentService;
@@ -70,41 +77,51 @@ public class ScoreCalculateThread implements Runnable {
         this.objectiveMap = new HashMap<String, List<ExamQuestion>>();
         this.subjectiveMap = new HashMap<String, List<ExamQuestion>>();
         this.subjectMap = new HashMap<String, ExamSubject>();
+        this.groupMap = new HashMap<String, List<MarkGroup>>();
     }
 
     @Override
     public void run() {
         log.info("start calculate for examId=" + examId);
         try {
-//            checkStudentService.deleteByExamId(examId);
+            // 获取考试信息
+            Exam exam = examService.findById(examId);
+            // 删除原有统计数据
+            reportService.deleteData(examId);
+            context = new ReportContext(exam);
+            // checkStudentService.deleteByExamId(examId);
+
             int pageNumber = 1;
             int pageSize = 1000;
-            List<ExamStudent> list = studentService.findByExamIdAndUploadAndAbsent(examId, true, false, pageNumber,
-                    pageSize);
+            List<ExamStudent> list = studentService.findByExamId(examId, pageNumber, pageSize);
             while (list != null && list.size() > 0) {
                 for (ExamStudent student : list) {
+                    // 先统分
                     calculate(student);
+                    // 后统计
+                    statistic(student);
                 }
-
                 pageNumber++;
-                list = studentService.findByExamIdAndUploadAndAbsent(examId, true, false, pageNumber, pageSize);
+                list = studentService.findByExamId(examId, pageNumber, pageSize);
             }
 
             // 统计识别缺考的试卷
-//            List<ExamStudent> students = studentService.findByExamIdAndUploadAndAbsent(examId, true, true, 1,
-//                    Integer.MAX_VALUE);
-//            for (ExamStudent examStudent : students) {
-//                CheckStudent student = new CheckStudent(examStudent.getId(), examId, examStudent.getSubjectCode(),
-//                        CheckType.SCAN_ABSENT);
-//                checkStudentService.save(student);
-//            }
-            
+            // List<ExamStudent> students =
+            // studentService.findByExamIdAndUploadAndAbsent(examId, true, true,
+            // 1,
+            // Integer.MAX_VALUE);
+            // for (ExamStudent examStudent : students) {
+            // CheckStudent student = new CheckStudent(examStudent.getId(),
+            // examId, examStudent.getSubjectCode(),
+            // CheckType.SCAN_ABSENT);
+            // checkStudentService.save(student);
+            // }
+
             // 生成报表数据
-            reportService.generateReportData(examId);
-            //统分结束
-            Exam exam = examService.findById(examId);
-            exam.setNeedCalculate(false);
-            examService.save(exam);
+            // reportService.generateReportData(examId);
+            context.save();
+            // 统分结束修改标记
+            examService.updateNeedCalculate(examId, false);
         } catch (Exception e) {
             log.error("calculate exception for examId=" + examId, e);
         } finally {
@@ -114,9 +131,14 @@ public class ScoreCalculateThread implements Runnable {
     }
 
     private void calculate(ExamStudent student) {
+        // 未上传或缺考考生不统分
+        if (!student.isUpload() || student.isAbsent()) {
+            return;
+        }
         try {
             ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
-            ScoreInfo info = util.calculate(findQuestionList(student.getSubjectCode(), student.getPaperType(), true),null);
+            ScoreInfo info = util.calculate(findQuestionList(student.getSubjectCode(), student.getPaperType(), true),
+                    null);
 
             student.setObjectiveScore(info.getObjectiveScore());
             student.setScoreList(info.getScoreList(), true);
@@ -124,20 +146,27 @@ public class ScoreCalculateThread implements Runnable {
             studentService.save(student);
 
             // 增加主观题总分统计
-            markService.scoreCalculate(student.getExamId(), student.getSubjectCode(), student.getId());
+            markService.scoreCalculate(student, findMarkGroup(student.getSubjectCode()));
 
             // 客观题0分主观题有分的考生
-//            ExamSubject subject = findExamSubject(student.getSubjectCode()) ;
-//            if (subject.getObjectiveScore()>0 && student.getObjectiveScore() == 0 && student.getSubjectiveScore() != 0) {
-//                CheckStudent cs = new CheckStudent(student.getId(), examId, student.getSubjectCode(),
-//                        CheckType.EXCEPTION);
-//                checkStudentService.save(cs);
-//            }
+            // ExamSubject subject = findExamSubject(student.getSubjectCode()) ;
+            // if (subject.getObjectiveScore()>0 && student.getObjectiveScore()
+            // == 0 && student.getSubjectiveScore() != 0) {
+            // CheckStudent cs = new CheckStudent(student.getId(), examId,
+            // student.getSubjectCode(),
+            // CheckType.EXCEPTION);
+            // checkStudentService.save(cs);
+            // }
         } catch (Exception e) {
-            log.error("statistic error", e);
+            log.error("calculate error for studentId=" + student.getId(), e);
         }
     }
 
+    private void statistic(ExamStudent student) {
+        student.setSubject(findExamSubject(student.getSubjectCode()));
+        context.process(student);
+    }
+
     private List<ExamQuestion> findQuestionList(String subjectCode, String paperType, boolean objective) {
         if (objective) {
             String key = subjectCode + "_" + StringUtils.trimToEmpty(paperType);
@@ -157,13 +186,22 @@ public class ScoreCalculateThread implements Runnable {
             return list;
         }
     }
-    
-    private ExamSubject findExamSubject(String subjectCode){
+
+    private ExamSubject findExamSubject(String subjectCode) {
         ExamSubject subject = subjectMap.get(subjectCode);
-        if(subject==null){
+        if (subject == null) {
             subject = subjectService.find(examId, subjectCode);
             subjectMap.put(subjectCode, subject);
         }
         return subject;
     }
+
+    private List<MarkGroup> findMarkGroup(String subjectCode) {
+        List<MarkGroup> list = groupMap.get(subjectCode);
+        if (list == null) {
+            list = groupService.findByExamAndSubject(examId, subjectCode);
+            groupMap.put(subjectCode, list);
+        }
+        return list;
+    }
 }