ソースを参照

定时任务改为30分钟执行一次

ting.yin 2 年 前
コミット
078b40ec42

+ 323 - 7
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreReportJob.java

@@ -1,9 +1,20 @@
 package cn.com.qmth.stmms.admin.thread;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -12,15 +23,28 @@ import org.springframework.core.task.AsyncTaskExecutor;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+import com.qmth.boot.tools.codec.CodecUtils;
+
+import cn.com.qmth.stmms.admin.dto.ReportSubjectRangeDTO;
 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.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.file.enums.FormatType;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.biz.report.model.ReportSubject;
+import cn.com.qmth.stmms.biz.report.model.ReportSubjectClass;
+import cn.com.qmth.stmms.biz.report.model.ReportSubjectCollege;
+import cn.com.qmth.stmms.biz.report.model.ReportSubjectGroup;
+import cn.com.qmth.stmms.biz.report.model.ReportSubjectQuestion;
+import cn.com.qmth.stmms.biz.report.model.ReportSubjectTeacher;
+import cn.com.qmth.stmms.biz.report.query.ReportSubjectQuery;
 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;
@@ -28,9 +52,12 @@ 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.ReportSubjectService;
 import cn.com.qmth.stmms.biz.report.service.ReportSubjectTeacherService;
+import cn.com.qmth.stmms.biz.report.utils.ReportContext;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
 
 /**
  * 统计定时任务
@@ -87,10 +114,18 @@ public class ScoreReportJob {
     @Autowired
     private ReportSubjectService reportSubjectService;
 
+    private ReportContext context;
+
+    private Map<String, List<ExamQuestion>> objectiveMap;
+
+    private Map<String, List<ExamQuestion>> subjectiveMap;
+
+    private Map<String, ExamSubject> subjectMap;
+
     /**
      * 定时更新成绩分析报告
      */
-    @Scheduled(cron = "0 0/30 8-22 * * ?")
+    @Scheduled(fixedDelay = 60 * 1000, initialDelay = 30 * 60 * 1000)
     public void updateReport() {
         log.info("start auto-update report");
         try {
@@ -105,7 +140,7 @@ public class ScoreReportJob {
         } catch (Exception e) {
             log.error("auto-update report error", e);
         } finally {
-            log.info("finish auto-report marker");
+            log.info("finish auto-update report");
         }
     }
 
@@ -118,10 +153,291 @@ public class ScoreReportJob {
                 subjectSet.add(subject.getCode());
             }
         }
-        ScoreReportThread thread = new ScoreReportThread(examId, subjectSet, lockService, studentService,
-                questionService, reportService, examService, subjectService, fileService, true,
-                reportSubjectQuestionService, reportSubjectClassService, reportSubjectTeacherService,
-                reportSubjectCollegeService, reportSubjectGroupService, reportSubjectService);
-        taskExecutor.submit(thread);
+
+        // 获取考试信息并判断状态
+        Exam exam = examService.findById(examId);
+        if (exam == null || exam.getStatus() != ExamStatus.START) {
+            log.info("report exception for examId=" + examId + ", exam is null or status error");
+            return;
+        }
+        log.info("start report for examId=" + examId + ", subjectCode count=" + subjectSet.size());
+
+        lockService.trylock(LockType.SCORE_CALCULATE, examId);
+        for (String subjectCode : subjectSet) {
+            // 尝试上锁,失败直接跳过
+            if (!lockService.trylock(LockType.SCORE_CALCULATE, examId, subjectCode)) {
+                log.warn("report locked for examId=" + examId + ", subjectCode=" + subjectCode);
+                continue;
+            }
+            try {
+                log.info("start report for examId=" + examId + ", subjectCode=" + subjectCode);
+                // 删除原有统计数据
+                reportService.deleteData(examId, subjectCode);
+                context = new ReportContext(exam);
+                int pageNumber = 1;
+                int pageSize = 1000;
+                List<ExamStudent> list = studentService.findByExamIdAndSubjectCode(examId, subjectCode, pageNumber,
+                        pageSize);
+                while (list != null && list.size() > 0) {
+                    for (ExamStudent student : list) {
+                        // 统计
+                        statistic(student);
+                    }
+                    pageNumber++;
+                    list = studentService.findByExamIdAndSubjectCode(examId, subjectCode, pageNumber, pageSize);
+                }
+                // 结束统计
+                context.save();
+                // 生成文件
+                reoprtExcel(examId, subjectCode);
+            } catch (Exception e) {
+                log.error("report exception for examId=" + examId + ", subjectCode=" + subjectCode, e);
+            } finally {
+                lockService.unlock(LockType.SCORE_CALCULATE, examId, subjectCode);
+                log.info("finish report for examId=" + examId + ", subjectCode=" + subjectCode);
+            }
+        }
+        lockService.unlock(LockType.SCORE_CALCULATE, examId);
+    }
+
+    private void statistic(ExamStudent student) {
+        if (SubjectiveStatus.MARKED.equals(student.getSubjectiveStatus())
+                || SubjectiveStatus.INSPECTED.equals(student.getSubjectiveStatus())) {
+            student.setSubject(findExamSubject(student.getExamId(), student.getSubjectCode()));
+            student.setObjectiveQuestionList(findQuestionList(student.getExamId(), student.getSubjectCode(),
+                    student.getPaperType(), true));
+            student.setSubjectiveQuestionList(findQuestionList(student.getExamId(), student.getSubjectCode(),
+                    student.getPaperType(), false));
+            context.process(student);
+        }
+    }
+
+    private List<ExamQuestion> findQuestionList(int examId, String subjectCode, String paperType, boolean objective) {
+        if (objective) {
+            String key = subjectCode + "_" + StringUtils.trimToEmpty(paperType);
+            List<ExamQuestion> list = objectiveMap.get(key);
+            if (list == null) {
+                list = questionService.findByExamAndSubjectAndObjectiveAndPaperType(examId, subjectCode, true,
+                        paperType);
+                objectiveMap.put(key, list);
+            }
+            return list;
+        } else {
+            List<ExamQuestion> list = subjectiveMap.get(subjectCode);
+            if (list == null) {
+                list = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
+                subjectiveMap.put(subjectCode, list);
+            }
+            return list;
+        }
+    }
+
+    private ExamSubject findExamSubject(int examId, String subjectCode) {
+        ExamSubject subject = subjectMap.get(subjectCode);
+        if (subject == null) {
+            subject = subjectService.find(examId, subjectCode);
+            subjectMap.put(subjectCode, subject);
+        }
+        return subject;
+    }
+
+    private void reoprtExcel(int examId, String subjectCode) throws IOException, Exception {
+        uploadRange(examId, subjectCode, findExamSubject(examId, subjectCode).getName(), "课程分段统计");
+        uploadCollege(examId, subjectCode, findExamSubject(examId, subjectCode).getName(), "学院分析");
+        uploadClass(examId, subjectCode, findExamSubject(examId, subjectCode).getName(), "班级分析");
+        uploadTeacher(examId, subjectCode, findExamSubject(examId, subjectCode).getName(), "任课老师统计");
+        uploadQuestion(examId, subjectCode, findExamSubject(examId, subjectCode).getName(), true, "客观题分析");
+        uploadQuestion(examId, subjectCode, findExamSubject(examId, subjectCode).getName(), false, "主观题分析");
+        uploadGroup(examId, subjectCode, findExamSubject(examId, subjectCode).getName(), "分组统计分析");
+    }
+
+    private void uploadGroup(int examId, String subjectCode, String subjectName, String name) throws IOException,
+            Exception {
+        ReportSubjectQuery query = new ReportSubjectQuery();
+        query.setExamId(examId);
+        query.setSubjectCode(subjectCode);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        List<ReportSubjectGroup> list = reportSubjectGroupService.findByQuery(query);
+        for (ReportSubjectGroup r : list) {
+            r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setScoreRate(new BigDecimal(r.getScoreRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setStdev(new BigDecimal(r.getStdev()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setCoefficient(new BigDecimal(r.getCoefficient()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        new ExportExcel(name, ReportSubjectGroup.class).setDataList(list).write(os);
+        os.flush();
+        byte[] value = os.toByteArray();
+        String md5 = CodecUtils.toHexString(CodecUtils.md5(value));
+        fileService.uploadReport(new ByteArrayInputStream(value), md5, examId, subjectCode + "-" + subjectName, name,
+                FormatType.XLSX);
+    }
+
+    private void uploadQuestion(int examId, String subjectCode, String subjectName, boolean objective, String name)
+            throws IOException, Exception {
+        ReportSubjectQuery query = new ReportSubjectQuery();
+        query.setExamId(examId);
+        query.setSubjectCode(subjectCode);
+        query.setObjective(objective);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        List<ReportSubjectQuestion> list = reportSubjectQuestionService.findByQuery(query);
+        for (ReportSubjectQuestion r : list) {
+            r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setStdev(new BigDecimal(r.getStdev()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setScoreRate(new BigDecimal(r.getScoreRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setFullScoreRate(new BigDecimal(r.getFullScoreRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        new ExportExcel(name, ReportSubjectQuestion.class).setDataList(list).write(os);
+        os.flush();
+        byte[] value = os.toByteArray();
+        String md5 = CodecUtils.toHexString(CodecUtils.md5(value));
+        fileService.uploadReport(new ByteArrayInputStream(value), md5, examId, subjectCode + "-" + subjectName, name,
+                FormatType.XLSX);
+    }
+
+    private void uploadTeacher(int examId, String subjectCode, String subjectName, String name) throws IOException,
+            Exception {
+        ReportSubjectQuery query = new ReportSubjectQuery();
+        query.setExamId(examId);
+        query.setSubjectCode(subjectCode);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        List<ReportSubjectTeacher> list = reportSubjectTeacherService.findByQuery(query);
+        for (ReportSubjectTeacher r : list) {
+            r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setExcellentRate(new BigDecimal(r.getExcellentRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setPassRate(new BigDecimal(r.getPassRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setRelativeAvgScore(new BigDecimal(r.getRelativeAvgScore()).setScale(2, RoundingMode.HALF_UP)
+                    .doubleValue());
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        new ExportExcel(name, ReportSubjectTeacher.class).setDataList(list).write(os);
+        os.flush();
+        byte[] value = os.toByteArray();
+        String md5 = CodecUtils.toHexString(CodecUtils.md5(value));
+        fileService.uploadReport(new ByteArrayInputStream(value), md5, examId, subjectCode + "-" + subjectName, name,
+                FormatType.XLSX);
+    }
+
+    private void uploadClass(int examId, String subjectCode, String subjectName, String name) throws IOException,
+            Exception {
+        ReportSubjectQuery query = new ReportSubjectQuery();
+        query.setExamId(examId);
+        query.setSubjectCode(subjectCode);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        List<ReportSubjectClass> list = reportSubjectClassService.findByQuery(query);
+        for (ReportSubjectClass r : list) {
+            r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setExcellentRate(new BigDecimal(r.getExcellentRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setPassRate(new BigDecimal(r.getPassRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        new ExportExcel(name, ReportSubjectClass.class).setDataList(list).write(os);
+        os.flush();
+        byte[] value = os.toByteArray();
+        String md5 = CodecUtils.toHexString(CodecUtils.md5(value));
+        fileService.uploadReport(new ByteArrayInputStream(value), md5, examId, subjectCode + "-" + subjectName, name,
+                FormatType.XLSX);
+    }
+
+    private void uploadCollege(int examId, String subjectCode, String subjectName, String name) throws IOException,
+            Exception {
+        ReportSubjectQuery query = new ReportSubjectQuery();
+        query.setExamId(examId);
+        query.setSubjectCode(subjectCode);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        List<ReportSubjectCollege> list = reportSubjectCollegeService.findByQuery(query);
+        for (ReportSubjectCollege r : list) {
+            r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setExcellentRate(new BigDecimal(r.getExcellentRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setPassRate(new BigDecimal(r.getPassRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        new ExportExcel(name, ReportSubjectCollege.class).setDataList(list).write(os);
+        os.flush();
+        byte[] value = os.toByteArray();
+        String md5 = CodecUtils.toHexString(CodecUtils.md5(value));
+        fileService.uploadReport(new ByteArrayInputStream(value), md5, examId, subjectCode + "-" + subjectName, name,
+                FormatType.XLSX);
+    }
+
+    private void uploadRange(int examId, String subjectCode, String subjectName, String name) throws IOException,
+            Exception {
+        List<ReportSubjectRangeDTO> list = new ArrayList<ReportSubjectRangeDTO>();
+        ReportSubject subject = reportSubjectService.findOne(examId, subjectCode);
+        if (subject != null) {
+            JSONArray array = getScoreRange(subject.getScoreRange(), subject.getTotalScore(),
+                    subject.getRealityCount(), 10);
+            for (int i = 0; i < array.size(); i++) {
+                JSONObject jsonObject = array.getJSONObject(i);
+                String score = jsonObject.getInt("score") + "-";
+                Integer rangeCount = jsonObject.getInt("rangeCount");
+                Double rangeRate = jsonObject.getDouble("rangeRate");
+                list.add(new ReportSubjectRangeDTO(score, rangeCount, rangeRate));
+            }
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        new ExportExcel(name, ReportSubjectRangeDTO.class).setDataList(list).write(os);
+        os.flush();
+        byte[] value = os.toByteArray();
+        String md5 = CodecUtils.toHexString(CodecUtils.md5(value));
+        fileService.uploadReport(new ByteArrayInputStream(value), md5, examId, subjectCode + "-" + subjectName, name,
+                FormatType.XLSX);
+    }
+
+    private JSONArray getScoreRange(String scoreRange, double totalScore, Integer totalCount, int range) {
+        JSONArray result = new JSONArray();
+        JSONObject jsonObject = JSONObject.fromObject(scoreRange);
+        int rangeCount = 0;
+        int sumCount = 0;
+        int total = (int) Math.ceil(totalScore / range);
+        for (int i = total; i >= 0; i--) {
+            int start = i * range;
+            int end = (i - 1) * range + 1;
+            if (start > totalScore) {
+                start = (int) Math.ceil(totalScore);
+            }
+            if (end < 0) {
+                end = 0;
+            }
+            rangeCount = getSumCount(jsonObject, start, end);
+            sumCount = sumCount + rangeCount;
+            result.add(getRangeJson(totalCount, rangeCount, sumCount, i * range));
+        }
+        return result;
+    }
+
+    private int getSumCount(JSONObject jsonObject, int start, int end) {
+        int sumCount = 0;
+        int currentCount = 0;
+        if (start < end) {
+            for (int i = start; i <= end; i++) {
+                currentCount = jsonObject.getInt(String.valueOf(i));
+                sumCount = sumCount + currentCount;
+            }
+        } else if (start >= end) {
+            for (int i = end; i <= start; i++) {
+                currentCount = jsonObject.getInt(String.valueOf(i));
+                sumCount = sumCount + currentCount;
+            }
+        } else {
+            sumCount = jsonObject.getInt(String.valueOf(start));
+        }
+        return sumCount;
+    }
+
+    private JSONObject getRangeJson(Integer totalCount, int rangeCount, int sumCount, int score) {
+        JSONObject value = new JSONObject();
+        value.accumulate("score", score);
+        value.accumulate("rangeCount", rangeCount);
+        value.accumulate("rangeRate", rangeCount * 100.0 / totalCount);
+        value.accumulate("sumCount", sumCount);
+        value.accumulate("sumRate", sumCount * 100.0 / totalCount);
+        return value;
     }
 }