Bladeren bron

新增科目分析、日志管理

yin 3 dagen geleden
bovenliggende
commit
3a010b7d2c

+ 60 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/admin/OperationLogController.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.qmth.boot.core.collection.PageResult;
+
+import cn.com.qmth.stmms.api.controller.BaseApiController;
+import cn.com.qmth.stmms.biz.exam.model.OperationLog;
+import cn.com.qmth.stmms.biz.exam.query.OperationLogSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.OperationLogService;
+import cn.com.qmth.stmms.biz.utils.PageUtil;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.LogType;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@Api(tags = "操作日志")
+@Controller("adminOperationLogController")
+@RequestMapping("/api/admin/operation/log")
+public class OperationLogController extends BaseApiController {
+
+    protected static Logger log = LoggerFactory.getLogger(OperationLogController.class);
+
+    @Autowired
+    private OperationLogService logService;
+
+    @Autowired
+    private ExamService examService;
+
+    @ApiOperation(value = "日志列表")
+    @RequestMapping(value = "query", method = RequestMethod.POST)
+    @ResponseBody
+    public PageResult<OperationLog> query(OperationLogSearchQuery query) {
+        ApiUser user = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setSchoolId(user.getUser().getSchoolId());
+        query.orderByCreateTime();
+        query = logService.findByQuery(query);
+        return PageUtil.of(query);
+    }
+
+    @ApiOperation(value = "日志类型")
+    @RequestMapping(value = "types", method = RequestMethod.POST)
+    @ResponseBody
+    public List<LogType> examTypeList() {
+        return Arrays.asList(LogType.values());
+    }
+
+}

+ 608 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/admin/ReportController.java

@@ -0,0 +1,608 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.qmth.boot.core.collection.PageResult;
+import com.qmth.boot.core.exception.StatusException;
+import com.qmth.boot.tools.io.ZipWriter;
+
+import cn.com.qmth.stmms.admin.thread.ScoreReportObjectiveThread;
+import cn.com.qmth.stmms.admin.thread.ScoreReportThread;
+import cn.com.qmth.stmms.api.controller.BaseApiController;
+import cn.com.qmth.stmms.biz.exam.bean.ResultMessage;
+import cn.com.qmth.stmms.biz.exam.model.Exam;
+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.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.*;
+import cn.com.qmth.stmms.biz.report.query.ReportSubjectQuery;
+import cn.com.qmth.stmms.biz.report.service.*;
+import cn.com.qmth.stmms.biz.utils.PageUtil;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.utils.Encodes;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+@Api(tags = "科目分析")
+@Controller("adminReportController")
+@RequestMapping("/api/admin/report")
+public class ReportController extends BaseApiController {
+
+    protected static Logger log = LoggerFactory.getLogger(ReportController.class);
+
+    @Autowired
+    private ReportService reportService;
+
+    @Autowired
+    private ReportSubjectService reportSubjectService;
+
+    @Autowired
+    private ReportSubjectRangeService reportSubjectRangeService;
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private LockService lockService;
+
+    @Qualifier("task-executor")
+    @Autowired
+    private AsyncTaskExecutor taskExecutor;
+
+    @Autowired
+    private FileService fileService;
+
+    @Autowired
+    private ReportSubjectCollegeService reportSubjectCollegeService;
+
+    @Autowired
+    private ReportSubjectGroupService reportSubjectGroupService;
+
+    @Autowired
+    private ReportSubjectQuestionService reportSubjectQuestionService;
+
+    @Autowired
+    private ReportSubjectTeacherClassService reportSubjectTeacherClassService;
+
+    @Autowired
+    private ReportSubjectTeacherService reportSubjectTeacherService;
+
+    @Autowired
+    private ReportSubjectClassService reportSubjectClassService;
+
+    @ApiOperation(value = "总量分析")
+    @Logging(menu = "课程总量统计", type = LogType.QUERY)
+    @RequestMapping(value = "/subject", method = RequestMethod.POST)
+    @ResponseBody
+    public PageResult<ReportSubject> subject(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        if ((wu.isSubjectHeader() || wu.isSchoolViewer()) && StringUtils.isBlank(query.getSubjectCode())) {
+            String subjectCodeIn = StringUtils.join(wu.getSubjectCodeSet(), ",");
+            query.setSubjectCodeIn(subjectCodeIn);
+        }
+        query = reportSubjectService.findByQuery(query);
+        for (ReportSubject r : query.getResult()) {
+            r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setPassRate(new BigDecimal(r.getPassRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setExcellentRate(new BigDecimal(r.getExcellentRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+        }
+        return PageUtil.of(query);
+    }
+
+    @ApiOperation(value = "总量分析导出")
+    @Logging(menu = "课程总量统计导出", type = LogType.EXPORT)
+    @RequestMapping("/subject/export")
+    public void subjectExport(ReportSubjectQuery query, HttpServletResponse response) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        if (wu.isSubjectHeader() || wu.isSchoolViewer()) {
+            String subjectCodeIn = StringUtils.join(wu.getSubjectCodeSet(), ",");
+            query.setSubjectCodeIn(subjectCodeIn);
+        }
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        query = reportSubjectService.findByQuery(query);
+        List<ReportSubject> list = query.getResult();
+        for (ReportSubject r : list) {
+            r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setPassRate(new BigDecimal(r.getPassRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+            r.setExcellentRate(new BigDecimal(r.getExcellentRate()).setScale(2, RoundingMode.HALF_UP).doubleValue());
+        }
+        String fileName = "课程总量统计.xlsx";
+        try {
+            new ExportExcel("课程总量统计", ReportSubject.class).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导出失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "分段统计")
+    @Logging(menu = "课程分段统计", type = LogType.QUERY)
+    @RequestMapping("/range")
+    public JSONArray rangeList(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        List<ExamSubject> sList = getExamSubject(examId, wu);
+        if (StringUtils.isBlank(query.getSubjectCode()) || query.getRange() == null) {
+            throw new StatusException("请选择科目和分数间隔");
+        }
+        if (query.getObjective() != null && query.getObjective()) {
+            ReportSubjectRange subjectRange = reportSubjectRangeService.findOne(examId, query.getSubjectCode());
+            if (subjectRange != null && subjectRange.getObjectiveScoreRange() != null
+                    && subjectRange.getObjectiveScore() != null && subjectRange.getRealityCount() != null) {
+                return getScoreRange(subjectRange.getObjectiveScoreRange(), subjectRange.getObjectiveScore(),
+                        subjectRange.getRealityCount(), query.getRange());
+            }
+        } else {
+            ReportSubject subject = reportSubjectService.findOne(examId, query.getSubjectCode());
+            if (subject != null && subject.getScoreRange() != null && subject.getTotalScore() != null
+                    && subject.getRealityCount() != null) {
+                return getScoreRange(subject.getScoreRange(), subject.getTotalScore(), subject.getRealityCount(),
+                        query.getRange());
+            }
+        }
+        return null;
+    }
+
+    @ApiOperation(value = "课程统计导出")
+    @Logging(menu = "课程统计导出", type = LogType.EXPORT)
+    @RequestMapping("/range/export")
+    public void rangeExport(ReportSubjectQuery query, HttpServletResponse response,
+            @RequestParam(required = false) Boolean all) {
+        int examId = getSessionExamId();
+        response.reset();
+        response.setContentType("application/octet-stream; charset=utf-8");
+        try {
+            ZipWriter writer = ZipWriter.create(response.getOutputStream());
+            if (all) {
+                Exam exam = examService.findById(examId);
+                ApiUser wu = getApiUser();
+                response.setHeader("Content-Disposition",
+                        "attachment; filename=" + Encodes.urlEncode(exam.getName() + ".zip"));
+                List<ExamSubject> list = getExamSubject(examId, wu);
+                for (ExamSubject subject : list) {
+                    String subjectName = subject.getCode() + "-" + subject.getName();
+                    addFileToZip(examId, writer, subjectName);
+                }
+            } else {
+                if (StringUtils.isBlank(query.getSubjectCode())) {
+                    throw new StatusException("导出分析失败!请先查询出结果再导出");
+                }
+                ExamSubject subject = subjectService.find(examId, query.getSubjectCode());
+                String subjectName = subject.getCode() + "-" + subject.getName();
+                response.setHeader("Content-Disposition",
+                        "attachment; filename=" + Encodes.urlEncode(subjectName + ".zip"));
+                addFileToZip(examId, writer, subjectName);
+            }
+            writer.close();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    private void addFileToZip(int examId, ZipWriter writer, String subjectName) throws Exception, IOException {
+        if (fileService.reportExist(examId, subjectName, "课程分段统计", FormatType.XLSX)) {
+            byte[] range = fileService.downloadReport(examId, subjectName, "课程分段统计", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(range), subjectName, "课程分段统计.xlsx");
+            byte[] college = fileService.downloadReport(examId, subjectName, "学院分析", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(college), subjectName, "学院分析.xlsx");
+            byte[] className = fileService.downloadReport(examId, subjectName, "班级分析", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(className), subjectName, "班级分析.xlsx");
+            byte[] teacher = fileService.downloadReport(examId, subjectName, "任课老师统计", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(teacher), subjectName, "任课老师统计.xlsx");
+            byte[] subjective = fileService.downloadReport(examId, subjectName, "主观题分析", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(subjective), subjectName, "主观题分析.xlsx");
+            byte[] group = fileService.downloadReport(examId, subjectName, "分组统计分析", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(group), subjectName, "分组统计分析.xlsx");
+        }
+        if (fileService.reportExist(examId, subjectName, "课程客观题分段统计", FormatType.XLSX)) {
+            byte[] objectiveRange = fileService.downloadReport(examId, subjectName, "课程客观题分段统计", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(objectiveRange), subjectName, "课程客观题分段统计.xlsx");
+            byte[] objective = fileService.downloadReport(examId, subjectName, "客观题分析", FormatType.XLSX);
+            writer.write(new ByteArrayInputStream(objective), subjectName, "客观题分析.xlsx");
+        }
+    }
+
+    @ApiOperation(value = "成绩分析计算")
+    @Logging(menu = "科目成绩分析计算", type = LogType.UPDATE)
+    @RequestMapping("/calculate")
+    public ResultMessage calculate(@RequestParam String subjectCode, @RequestParam Boolean objective) {
+        int examId = getSessionExamId();
+        Set<String> subjectSet = new HashSet<String>();
+        if (!lockService.isLocked(LockType.SCORE_CALCULATE, examId, subjectCode)) {
+            subjectSet.add(subjectCode);
+        }
+        if (!subjectSet.isEmpty()) {
+            Map<Integer, Set<String>> map = new HashMap<Integer, Set<String>>();
+            map.put(examId, subjectSet);
+            if (objective != null && objective) {
+                ScoreReportObjectiveThread thread = new ScoreReportObjectiveThread(map, reportService, false);
+                taskExecutor.submit(thread);
+            } else {
+                ScoreReportThread thread = new ScoreReportThread(map, reportService, false);
+                taskExecutor.submit(thread);
+            }
+        }
+        return resultOk();
+    }
+
+    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;
+            if (start > totalScore) {
+                start = (int) Math.ceil(totalScore);
+            }
+            if (end > totalScore) {
+                end = (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;
+    }
+
+    @ApiOperation(value = "课程学院分析")
+    @Logging(menu = "课程学院分析", type = LogType.QUERY)
+    @RequestMapping("/college")
+    public PageResult<ReportSubjectCollege> collegeList(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        if (StringUtils.isBlank(query.getSubjectCode())) {
+            throw new StatusException("请先选择科目");
+        }
+        List<ReportSubjectCollege> reportSubjectColleges = reportSubjectCollegeService.findByQuery(query);
+        for (ReportSubjectCollege r : reportSubjectColleges) {
+            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());
+        }
+        return PageUtil.of(reportSubjectColleges, query);
+    }
+
+    @ApiOperation(value = "课程学院分析导出")
+    @Logging(menu = "课程学院分析导出", type = LogType.EXPORT)
+    @RequestMapping("/college/export")
+    public void collegeExport(ReportSubjectQuery query, HttpServletResponse response) {
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        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());
+        }
+        String fileName = "课程学院成绩分析.xlsx";
+        try {
+            new ExportExcel("课程学院成绩分析", ReportSubjectCollege.class).setDataList(list).write(response, fileName)
+                    .dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "课程大题分析")
+    @Logging(menu = "课程大题分析", type = LogType.QUERY)
+    @RequestMapping("/group")
+    public PageResult<ReportSubjectGroup> group(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        if (StringUtils.isBlank(query.getSubjectCode())) {
+            throw new StatusException("请先选择科目");
+        }
+        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());
+        }
+        return PageUtil.of(list, query);
+    }
+
+    @ApiOperation(value = "课程大题分析导出")
+    @Logging(menu = "课程大题分析导出", type = LogType.EXPORT)
+    @RequestMapping("/group/export")
+    public void groupExport(ReportSubjectQuery query, HttpServletResponse response) {
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        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());
+        }
+        String fileName = "大题统计分析.xlsx";
+        try {
+            new ExportExcel("大题统计分析", ReportSubjectGroup.class).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "课程小题分析")
+    @Logging(menu = "课程小题分析", type = LogType.QUERY)
+    @RequestMapping("/question")
+    public PageResult<ReportSubjectQuestion> questionList(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        if (StringUtils.isBlank(query.getSubjectCode()) && query.getObjective() == null) {
+            throw new StatusException("请先选择科目");
+        }
+        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());
+        }
+        return PageUtil.of(list, query);
+    }
+
+    @ApiOperation(value = "课程小题分析导出")
+    @Logging(menu = "课程小题分析导出", type = LogType.EXPORT)
+    @RequestMapping("/question/export")
+    public void questionExport(ReportSubjectQuery query, HttpServletResponse response) {
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        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());
+        }
+        String fileName = query.getObjective() ? "客观题统计分析.xlsx" : "主观题统计分析.xlsx";
+        try {
+            new ExportExcel(query.getObjective() ? "客观题统计分析" : "主观题统计分析", ReportSubjectQuestion.class).setDataList(list)
+                    .write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "班级分析")
+    @Logging(menu = "课程班级分析", type = LogType.QUERY)
+    @RequestMapping("/class")
+    public PageResult<ReportSubjectClass> classList(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        if (StringUtils.isBlank(query.getSubjectCode())) {
+            throw new StatusException("请先选择科目");
+        }
+        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());
+        }
+        return PageUtil.of(list, query);
+    }
+
+    @ApiOperation(value = "班级分析导出")
+    @Logging(menu = "课程班级分析导出", type = LogType.EXPORT)
+    @RequestMapping("/class/export")
+    public void classExport(ReportSubjectQuery query, HttpServletResponse response) {
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        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());
+        }
+        String fileName = "课程班级成绩分析.xlsx";
+        try {
+            new ExportExcel("课程班级成绩分析", ReportSubjectClass.class).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "课程老师分析")
+    @Logging(menu = "课程老师分析", type = LogType.QUERY)
+    @RequestMapping("/teacher")
+    public PageResult<ReportSubjectTeacher> teacherList(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        if (StringUtils.isBlank(query.getSubjectCode())) {
+            throw new StatusException("请先选择科目");
+        }
+        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());
+        }
+        return PageUtil.of(list, query);
+    }
+
+    @ApiOperation(value = "课程老师分析导出")
+    @Logging(menu = "课程老师分析导出", type = LogType.EXPORT)
+    @RequestMapping("/teacher/export")
+    public void export(ReportSubjectQuery query, HttpServletResponse response) {
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        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());
+        }
+        String fileName = "课程老师成绩分析.xlsx";
+        try {
+            new ExportExcel("课程老师成绩分析", ReportSubjectTeacher.class).setDataList(list).write(response, fileName)
+                    .dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("下载失败!失败信息:" + e.getMessage());
+        }
+
+    }
+
+    @ApiOperation(value = "课程教师班级分析")
+    @Logging(menu = "课程教师班级分析", type = LogType.QUERY)
+    @RequestMapping("/teacher/class")
+    public PageResult<ReportSubjectTeacherClass> teacherClassList(ReportSubjectQuery query) {
+        ApiUser wu = getApiUser();
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        if (StringUtils.isBlank(query.getSubjectCode()) || StringUtils.isBlank(query.getTeacherName())) {
+            throw new StatusException("请先选择科目");
+        }
+        List<ReportSubjectTeacherClass> list = reportSubjectTeacherClassService.findByQuery(query);
+        for (ReportSubjectTeacherClass 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());
+        }
+        return PageUtil.of(list, query);
+    }
+
+    @ApiOperation(value = "课程教师班级分析导出")
+    @Logging(menu = "课程教师班级分析导出", type = LogType.EXPORT)
+    @RequestMapping("/teacher/class/export")
+    public void teacherClassExport(ReportSubjectQuery query, HttpServletResponse response) {
+        int examId = getSessionExamId();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        List<ReportSubjectTeacherClass> list = reportSubjectTeacherClassService.findByQuery(query);
+        for (ReportSubjectTeacherClass 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());
+        }
+        String fileName = "课程老师班级成绩分析.xlsx";
+        try {
+            new ExportExcel("课程老师班级成绩分析", ReportSubjectTeacherClass.class).setDataList(list).write(response, fileName)
+                    .dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("下载失败!失败信息:" + e.getMessage());
+        }
+    }
+}

+ 183 - 9
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/admin/UserController.java

@@ -6,9 +6,6 @@ import java.util.*;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import cn.com.qmth.stmms.biz.exam.bean.ResultMessage;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
 import org.apache.commons.io.output.ByteArrayOutputStream;
 import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -31,6 +28,8 @@ import cn.com.qmth.stmms.admin.dto.SubjectUserDTO;
 import cn.com.qmth.stmms.api.controller.BaseApiController;
 import cn.com.qmth.stmms.biz.config.model.SystemAuth;
 import cn.com.qmth.stmms.biz.config.service.SystemAuthService;
+import cn.com.qmth.stmms.biz.exam.bean.AddStudentInfo;
+import cn.com.qmth.stmms.biz.exam.bean.ResultMessage;
 import cn.com.qmth.stmms.biz.exam.bean.UserStudentVo;
 import cn.com.qmth.stmms.biz.exam.model.*;
 import cn.com.qmth.stmms.biz.exam.query.MarkerSearchQuery;
@@ -43,6 +42,8 @@ import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.domain.ApiUser;
 import cn.com.qmth.stmms.common.enums.*;
 import cn.com.qmth.stmms.common.utils.*;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import net.sf.json.JSONObject;
 
 @Api(tags = "用户管理")
@@ -85,9 +86,13 @@ public class UserController extends BaseApiController {
 
     @Autowired
     private MarkerClassService markerClassService;
+
     @Autowired
     private UserStudentService userStudentService;
 
+    @Autowired
+    private UserExamService userExamService;
+
     @ApiOperation(value = "用户查询")
     @RequestMapping(value = "/query", method = RequestMethod.POST)
     @ResponseBody
@@ -105,6 +110,7 @@ public class UserController extends BaseApiController {
     public List<Role> roleList() {
         return Arrays.asList(ROLE_LIST);
     }
+
     @ApiOperation(value = "用户来源下拉列表")
     @RequestMapping(value = "/source/list", method = RequestMethod.POST)
     @ResponseBody
@@ -118,20 +124,179 @@ public class UserController extends BaseApiController {
     public User findOne(HttpServletRequest request, @RequestParam Integer id) {
         return userService.findById(id);
     }
+
     @ApiOperation(value = "新增用户")
     @RequestMapping(value = "/save", method = RequestMethod.POST)
     @Logging(menu = "新增用户", type = LogType.ADD)
     @ResponseBody
-    public ResultMessage save(HttpServletRequest request, UserSearchQuery query) {
+    public ResultMessage save(User user, @RequestParam(required = false) String subjectCodeString,
+            @RequestParam(required = false) String examIdString, @RequestParam(required = false) UserSource querySource,
+            @RequestParam(required = false) MultipartFile studentFile) {
+        User current = getApiUser().getUser();
+        int examId = getSessionExamId();
+        User previous = null;
+        if (user.getId() != null) {
+            previous = userService.findById(user.getId());
+        }
+        AddStudentInfo suc = null;
+        String message = validate(user, subjectCodeString, examIdString);
+        if (message != null) {
+            throw new StatusException(message);
+        }
+        String password = StringEscapeUtils.unescapeHtml(user.getPassword());
+        user.setPassword(EncryptUtils.md5(password));
+        user.setSchoolId(current.getSchoolId());
+        user.setSource(UserSource.INTERNAL);
+        user.setCreatedTime(new Date());
+        user.setUpdatedTime(new Date());
+        userService.save(user);
+        if (user.getRole() == Role.SUBJECT_HEADER || user.getRole() == Role.INSPECTOR) {
+            subjectUserService.updateByUserId(user.getId(), getSubjectSet(subjectCodeString));
+        }
+        if (user.getRole() == Role.SCHOOL_VIEWER) {
+            userExamService.updateByUserId(user.getId(), getExamIdSet(examIdString));
+            subjectUserService.updateByUserId(user.getId(), getSubjectSet(subjectCodeString));
+        }
+        if (user.getRole() == Role.COLLEGE_ADMIN) {
+            subjectUserService.updateByUserId(user.getId(), getSubjectCodeSetByCollege(examId, user.getDescription()));
+            userService.save(user);
+        }
+        if (user.getRole() == Role.SCHOOL_VIEWER && !studentFile.isEmpty()) {
+            suc = addStudent(studentFile, user.getSchoolId(), user.getId());
+        }
         return resultOk();
     }
+
+    private Set<String> getSubjectSet(String subjectCodeString) {
+        Set<String> set = new HashSet<>();
+        try {
+            String[] values = StringUtils.split(subjectCodeString, SPLIT);
+            for (String value : values) {
+                value = StringUtils.trimToNull(value);
+                if (value != null) {
+                    set.add(value);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return set;
+    }
+
+    private Set<Integer> getExamIdSet(String examIdString) {
+        Set<Integer> set = new HashSet<>();
+        try {
+            String[] values = StringUtils.split(examIdString, SPLIT);
+            for (String value : values) {
+                value = StringUtils.trimToNull(value);
+                if (value != null) {
+                    set.add(Integer.parseInt(value));
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return set;
+    }
+
+    private Set<String> getSubjectCodeSetByCollege(int examId, String college) {
+        Set<String> set = new HashSet<>();
+        try {
+            List<String> subject = studentService.findDistinctSubjectCodeByCollege(examId, college);
+            for (String value : subject) {
+                value = StringUtils.trimToNull(value);
+                if (value != null) {
+                    set.add(value);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return set;
+    }
+
+    private AddStudentInfo addStudent(MultipartFile file, Integer schoolId, Integer userId) {
+        try {
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<UserStudentVo> list = ei.getDataList(UserStudentVo.class);
+            return userStudentService.addStudent(list, schoolId, userId);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @ApiOperation(value = "修改用户")
     @RequestMapping(value = "/update", method = RequestMethod.POST)
     @ResponseBody
     @Logging(menu = "修改用户", type = LogType.UPDATE)
-    public ResultMessage update(HttpServletRequest request, UserSearchQuery query) {
+    public ResultMessage update(User user, @RequestParam(required = false) String subjectCodeString,
+            @RequestParam(required = false) String examIdString,
+            @RequestParam(required = false) MultipartFile studentFile) {
+        User current = getApiUser().getUser();
+        int examId = getSessionExamId();
+        User previous = userService.findById(user.getId());
+        AddStudentInfo suc = null;
+        previous.setLoginName(user.getLoginName());
+        previous.setName(user.getName());
+        if (StringUtils.isNotBlank(user.getPassword())) {
+            String password = StringEscapeUtils.unescapeHtml(user.getPassword());
+            previous.setPassword(EncryptUtils.md5(password));
+            previous.setRandomPassword(null);
+        }
+        previous.setEmpno(user.getEmpno());
+        previous.setEnable(user.isEnable());
+        previous.setDescription(user.getDescription());
+        String message = validate(previous, subjectCodeString, examIdString);
+        if (message != null) {
+            throw new StatusException(message);
+        }
+        if (previous.getRole() == Role.SUBJECT_HEADER || user.getRole() == Role.INSPECTOR) {
+            previous.refreshAccessToken();
+            subjectUserService.updateByUserId(previous.getId(), getSubjectCodeSet(subjectCodeString));
+        }
+        if (user.getRole() == Role.SCHOOL_VIEWER) {
+            previous.refreshAccessToken();
+            userExamService.updateByUserId(user.getId(), getExamIdSet(examIdString));
+            subjectUserService.updateByUserId(previous.getId(), getSubjectCodeSet(subjectCodeString));
+        }
+        if (user.getRole() == Role.COLLEGE_ADMIN) {
+            previous.refreshAccessToken();
+            subjectUserService.updateByUserId(user.getId(), getSubjectCodeSetByCollege(examId, user.getDescription()));
+        }
+        if (user.getRole() == Role.MARKER) {
+            markerService.updateEnableByUserId(previous.getId(), previous.isEnable());
+        }
+        previous.setUpdatedTime(new Date());
+        userService.save(previous);
+        if (previous.getRole() == Role.SCHOOL_VIEWER && !studentFile.isEmpty()) {
+            suc = addStudent(studentFile, previous.getSchoolId(), previous.getId());
+        }
         return resultOk();
     }
+
+    private String validate(User user, String subjectCodeString, String examIdString) {
+        String message = null;
+        if (StringUtils.isBlank(user.getLoginName())) {
+            message = "登录名不能为空";
+        } else if (!checkLoginName(user)) {
+            message = "登录名不能重复";
+        } else if (StringUtils.isBlank(user.getName())) {
+            message = "名称不能为空";
+        } else if (StringUtils.isBlank(user.getPassword())) {
+            message = "密码不能为空";
+        } else if (user.getRole() == Role.SUBJECT_HEADER && StringUtils.isBlank(subjectCodeString)) {
+            message = "科组长必须绑定科目代码";
+        } else if (user.getRole() == Role.INSPECTOR && StringUtils.isBlank(subjectCodeString)) {
+            message = "复核员必须绑定科目代码";
+        } else if (user.getRole() == Role.COLLEGE_ADMIN && StringUtils.isBlank(user.getDescription())) {
+            message = "学院管理员必须绑定学院";
+        } else if (user.getRole() == Role.SCHOOL_VIEWER
+                && (StringUtils.isBlank(subjectCodeString) || StringUtils.isBlank(examIdString))) {
+            message = "学校查询员必须绑定考试ID和科目代码";
+        }
+        return message;
+    }
+
     @ApiOperation(value = "导出用户")
     @Logging(menu = "导出用户", type = LogType.EXPORT)
     @RequestMapping(value = "/export", method = RequestMethod.POST)
@@ -150,9 +315,10 @@ public class UserController extends BaseApiController {
         } catch (Exception e) {
             log.error(e.getMessage());
             e.printStackTrace();
-            throw new StatusException("导出数据失败!失败信息:"+e.getMessage());
+            throw new StatusException("导出数据失败!失败信息:" + e.getMessage());
         }
     }
+
     @ApiOperation(value = "按考试导出用户")
     @Logging(menu = "导出用户", type = LogType.EXPORT)
     @RequestMapping(value = "/exportExam", method = RequestMethod.POST)
@@ -242,7 +408,7 @@ public class UserController extends BaseApiController {
         } catch (Exception e) {
             log.error(e.getMessage());
             e.printStackTrace();
-            throw new StatusException("导出数据失败!失败信息:"+e.getMessage());
+            throw new StatusException("导出数据失败!失败信息:" + e.getMessage());
         }
     }
 
@@ -346,12 +512,13 @@ public class UserController extends BaseApiController {
         } catch (Exception e) {
             log.error(e.getMessage());
             e.printStackTrace();
-            throw new StatusException("导出用户数据失败"+e.getMessage());
+            throw new StatusException("导出用户数据失败" + e.getMessage());
         }
     }
+
     @ApiOperation(value = "用户启用-禁用")
     @Logging(menu = "用户启用/禁用", type = LogType.UPDATE)
-    @RequestMapping(value="/enable", method = RequestMethod.POST)
+    @RequestMapping(value = "/enable", method = RequestMethod.POST)
     @ResponseBody
     public ResultMessage toggle(HttpServletRequest request, @RequestParam List<Integer> ids,
             @RequestParam Boolean enable) {
@@ -366,6 +533,7 @@ public class UserController extends BaseApiController {
         }
         return resultOk();
     }
+
     @ApiOperation(value = "重置密码")
     @Logging(menu = "用户重置密码", type = LogType.UPDATE)
     @RequestMapping(value = "/reset", method = RequestMethod.POST)
@@ -430,6 +598,7 @@ public class UserController extends BaseApiController {
         }
         return set;
     }
+
     @ApiOperation(value = "绑定考生导入模板")
     @RequestMapping(value = "/student/template", method = RequestMethod.POST)
     public void importStudentTemplate(HttpServletResponse response) {
@@ -445,6 +614,7 @@ public class UserController extends BaseApiController {
             throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
         }
     }
+
     @ApiOperation(value = "绑定考生清空")
     @RequestMapping(value = "/student/clear", method = RequestMethod.POST)
     @ResponseBody
@@ -452,6 +622,7 @@ public class UserController extends BaseApiController {
         userStudentService.clear(id);
         return resultOk();
     }
+
     @ApiOperation(value = "科组长-复核员导入模版")
     @RequestMapping(value = "/subject/template", method = RequestMethod.POST)
     public void importFileTemplate(HttpServletResponse response, @RequestParam Boolean isHeader) {
@@ -467,6 +638,7 @@ public class UserController extends BaseApiController {
             throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
         }
     }
+
     @ApiOperation(value = "科组长-复核员导入")
     @Logging(menu = "科组长/复核员导入", type = LogType.IMPORT_FILE)
     @RequestMapping(value = "/subject/import", method = RequestMethod.POST)
@@ -559,6 +731,7 @@ public class UserController extends BaseApiController {
         }
         return user;
     }
+
     @ApiOperation(value = "评卷员班级导入模版")
     @RequestMapping(value = "/class/template", method = RequestMethod.POST)
     public void importFileTemplate(HttpServletResponse response) {
@@ -573,6 +746,7 @@ public class UserController extends BaseApiController {
             throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
         }
     }
+
     @ApiOperation(value = "评卷员班级导入")
     @Logging(menu = "评卷员班级导入", type = LogType.IMPORT_FILE)
     @RequestMapping(value = "/class/import", method = RequestMethod.POST)