1
0
yin vor 1 Woche
Ursprung
Commit
fadfa99202

+ 2 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/PageUtil.java

@@ -3,9 +3,10 @@ package cn.com.qmth.stmms.biz.utils;
 import java.util.ArrayList;
 import java.util.List;
 
-import cn.com.qmth.stmms.biz.common.BaseQuery;
 import com.qmth.boot.core.collection.PageResult;
 
+import cn.com.qmth.stmms.biz.common.BaseQuery;
+
 public class PageUtil {
 	public static <T> PageResult<T> of(BaseQuery<T> query) {
         PageResult<T> result = new PageResult<>();
@@ -17,8 +18,6 @@ public class PageUtil {
         return result;
 	}
 
-
-
 	public static <T> PageResult<T> emptyPage(){
     	PageResult<T> page=new PageResult<>();
     	page.setPageCount(0);

+ 33 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/BaseApiController.java

@@ -1,7 +1,13 @@
 package cn.com.qmth.stmms.api.controller;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import cn.com.qmth.stmms.admin.utils.SessionExamUtils;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.domain.WebUser;
 import net.sf.json.JSONObject;
 
 import org.apache.commons.lang.StringUtils;
@@ -16,6 +22,9 @@ import cn.com.qmth.stmms.biz.school.model.School;
 import cn.com.qmth.stmms.biz.school.service.SchoolService;
 import cn.com.qmth.stmms.common.controller.BaseController;
 
+import java.util.LinkedList;
+import java.util.List;
+
 public class BaseApiController extends BaseController {
 
     protected static final Logger log = LoggerFactory.getLogger(BaseApiController.class);
@@ -25,6 +34,9 @@ public class BaseApiController extends BaseController {
     @Autowired
     private SchoolService schoolService;
 
+    @Autowired
+    private ExamSubjectService subjectService;
+
     @ExceptionHandler
     public void exception(HttpServletResponse response, Exception ex) {
         log.error("api execute error", ex);
@@ -54,6 +66,13 @@ public class BaseApiController extends BaseController {
         return obj;
     }
 
+    protected JSONObject result(String message) {
+        JSONObject obj = new JSONObject();
+        obj.accumulate("success", true);
+        obj.accumulate("message", message);
+        return obj;
+    }
+
     protected JSONObject result(String key, String value) {
         JSONObject obj = new JSONObject();
         obj.accumulate(key, value);
@@ -104,4 +123,18 @@ public class BaseApiController extends BaseController {
         }
         return parentId.equals(subId) || sub.getParentId().equals(parentId);
     }
+
+    protected Integer getSessionExamId(HttpServletRequest request) {
+        return SessionExamUtils.getExamId(request);
+    }
+
+    protected List<ExamSubject> getExamSubject(Integer examId, ApiUser wu) {
+        List<ExamSubject> list = new LinkedList<ExamSubject>();
+        if (wu.isSchoolAdmin()) {
+            list = subjectService.list(examId);
+        } else if (wu.isSubjectHeader() || wu.isInspector() || wu.isSchoolViewer()) {
+            list.addAll(subjectService.list(examId, wu.getSubjectCodeSet()));
+        }
+        return list;
+    }
 }

+ 4 - 4
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/LoginController.java

@@ -54,8 +54,8 @@ public class LoginController extends BaseApiController {
     public JSONObject login(HttpServletRequest request) {
         ApiUser user = RequestUtils.getApiUser(request);
         JSONObject obj = new JSONObject();
-        obj.accumulate("userId", user.getUserData() != null ? user.getUserData().getId() : 0);
-        obj.accumulate("userName", user.getUserData() != null ? user.getUserData().getName() : "");
+        obj.accumulate("userId", user.getUser() != null ? user.getUser().getId() : 0);
+        obj.accumulate("userName", user.getUser() != null ? user.getUser().getName() : "");
         obj.accumulate("userRole", user.getRole().toString());
         obj.accumulate("campusId", 0);
         obj.accumulate("schoolId", user.getSchoolId());
@@ -64,8 +64,8 @@ public class LoginController extends BaseApiController {
         obj.accumulate("doubleTrack", systemCache.isDoubleTrack() && school.isDoubleTrack());
         obj.accumulate("yjsObjectiveEnable", systemCache.isYjsObjectiveEnable());
         JSONArray array = new JSONArray();
-        if (user.getUserData() != null && Role.SUBJECT_HEADER.equals(user.getRole())) {
-            Set<String> set = subjectUserService.findSubjectCode(user.getUserData().getId());
+        if (user.getUser() != null && Role.SUBJECT_HEADER.equals(user.getRole())) {
+            Set<String> set = subjectUserService.findSubjectCode(user.getUser().getId());
             for (String subjectCode : set) {
                 array.add(subjectCode);
             }

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/admin/ExamController.java

@@ -62,15 +62,15 @@ public class ExamController extends BaseApiController {
         return PageUtil.of(query);
     }
 
-    @RequestMapping(value = "/type/list", method = RequestMethod.POST)
+    @RequestMapping(value = "/types", method = RequestMethod.POST)
     @ResponseBody
     public List<ExamType> examTypeList() {
         return Arrays.asList(ExamType.values());
     }
 
-    @RequestMapping(value = "/status/list", method = RequestMethod.POST)
+    @RequestMapping(value = "/status", method = RequestMethod.POST)
     @ResponseBody
-    public List<ExamStatus> statusList() {
+    public List<ExamStatus> statusLlist() {
         return Arrays.asList(ExamStatus.values());
     }
 

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

@@ -0,0 +1,246 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.math.BigDecimal;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.servlet.http.HttpServletRequest;
+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.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import com.qmth.boot.core.collection.PageResult;
+import com.qmth.boot.core.exception.StatusException;
+
+import cn.com.qmth.stmms.admin.vo.ScanInfoVO;
+import cn.com.qmth.stmms.api.controller.BaseApiController;
+import cn.com.qmth.stmms.biz.exam.model.ExamPackage;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.query.ExamPackageSearchQuery;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.ExamPackageService;
+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.query.ExamSubjectSearchQuery;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.utils.PageUtil;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+
+@Controller("adminScanController")
+@RequestMapping("/api/admin/scan")
+public class ScanController extends BaseApiController {
+
+    protected static final Logger log = LoggerFactory.getLogger(ScanController.class);
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamPackageService packageService;
+
+    private FileService fileService;
+
+    @RequestMapping(value = "/subject", method = RequestMethod.POST)
+    @Logging(menu = "扫描进度-按科目查询", type = LogType.QUERY)
+    @ResponseBody
+    public PageResult<ScanInfoVO> subjectList(HttpServletRequest request, ExamSubjectSearchQuery query) {
+        int examId = getSessionExamId(request);
+        List<ScanInfoVO> list = new LinkedList<>();
+        query.setExamId(examId);
+        query = subjectService.findByQuery(query);
+        for (ExamSubject subject : query.getResult()) {
+            ScanInfoVO vo = new ScanInfoVO();
+            vo.setName(subject.getCode() + "-" + subject.getName()
+                    + (StringUtils.isNotBlank(subject.getRemark())
+                            ? ("-" + StringUtils.trimToEmpty(subject.getRemark()))
+                            : ""));
+            vo.setTotalCount(studentService.countByExamIdAndSubjectCode(examId, subject.getCode()));
+            vo.setScanCount(subject.getUploadCount());
+            long sheetCount = studentService.countSheetCountByExamIdAndSubjectCode(examId, subject.getCode());
+            long sheetCount2 = new BigDecimal(sheetCount).divide(new BigDecimal(2), 0, BigDecimal.ROUND_HALF_UP)
+                    .longValue();
+            vo.setScanSheetCount(sheetCount2);
+            ExamStudentSearchQuery studentSearchQuery = new ExamStudentSearchQuery();
+            studentSearchQuery.setExamId(examId);
+            studentSearchQuery.setSubjectCode(subject.getCode());
+            studentSearchQuery.setManualAbsent(true);
+            long manualAbsentCount = studentService.countByQuery(studentSearchQuery);
+            vo.setManualAbsentCount(manualAbsentCount);
+            list.add(vo);
+        }
+        PageResult<ScanInfoVO> pageResult = new PageResult<>();
+        pageResult.setResult(list);
+        pageResult.setPageNumber(query.getPageNumber());
+        pageResult.setPageSize(query.getPageSize());
+        pageResult.setTotalCount(query.getTotalCount());
+        pageResult.setPageCount(query.getCurrentCount());
+        return pageResult;
+    }
+
+    @Logging(menu = "扫描进度导出", type = LogType.EXPORT)
+    @RequestMapping(value = "/subject/export", method = RequestMethod.POST)
+    public void export(ExamSubjectSearchQuery query, HttpServletRequest request, HttpServletResponse response) {
+        int examId = getSessionExamId(request);
+        List<ScanInfoVO> list = new LinkedList<>();
+        query.setExamId(examId);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        query = subjectService.findByQuery(query);
+        for (ExamSubject subject : query.getResult()) {
+            ScanInfoVO vo = new ScanInfoVO();
+            vo.setName(subject.getCode() + "-" + subject.getName()
+                    + (StringUtils.isNotBlank(subject.getRemark())
+                            ? ("-" + StringUtils.trimToEmpty(subject.getRemark()))
+                            : ""));
+            vo.setTotalCount(studentService.countByExamIdAndSubjectCode(examId, subject.getCode()));
+            vo.setScanCount(studentService.countByExamIdAndSubjectCode(examId, subject.getCode(), true));
+            vo.setScanSheetCount(studentService.countSheetCountByExamIdAndSubjectCode(examId, subject.getCode()) / 2);
+            ExamStudentSearchQuery studentSearchQuery = new ExamStudentSearchQuery();
+            studentSearchQuery.setExamId(examId);
+            studentSearchQuery.setSubjectCode(subject.getCode());
+            studentSearchQuery.setManualAbsent(true);
+            long manualAbsentCount = studentService.countByQuery(studentSearchQuery);
+            vo.setManualAbsentCount(manualAbsentCount);
+            list.add(vo);
+        }
+        try {
+            String fileName = "扫描进度.xlsx";
+            new ExportExcel("扫描进度", ScanInfoVO.class).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("scan subject export error!", e);
+            throw new StatusException("导出扫描进度失败!" + e.getMessage());
+        }
+    }
+
+    @RequestMapping(value = "/package", method = RequestMethod.POST)
+    @Logging(menu = "扫描进度-按签到表查询", type = LogType.QUERY)
+    @ResponseBody
+    public PageResult<ExamPackage> packageList(HttpServletRequest request, ExamPackageSearchQuery query) {
+        int examId = getSessionExamId(request);
+        query.setExamId(examId);
+        query = packageService.findByQuery(query);
+        for (ExamPackage ep : query.getResult()) {
+            ep.setUrls(fileService.getPackageUris(ep.getExamId(), ep.getCode(), 1, ep.getPicCount()));
+        }
+        return PageUtil.of(query);
+    }
+
+    @Logging(menu = "扫描进度-按考点查询", type = LogType.QUERY)
+    @RequestMapping("/examSite")
+    @ResponseBody
+    public PageResult<ScanInfoVO> examSite(HttpServletRequest request, ExamStudentSearchQuery query) {
+        int examId = getSessionExamId(request);
+        List<ScanInfoVO> list = new LinkedList<>();
+        List<String> result = studentService.findDistinctExamSite(examId);
+        result = result.stream().filter(s -> StringUtils.isNotBlank(s)).collect(Collectors.toList());
+        if (StringUtils.isBlank(query.getExamSite())) {
+            List<?> examSiteList = cn.com.qmth.stmms.admin.utils.PageUtil.startPage(result, query.getPageNumber(),
+                    query.getPageSize());
+            if (examSiteList != null) {
+                for (Object e : examSiteList) {
+                    ScanInfoVO vo = new ScanInfoVO();
+                    String examSite = (String) e;
+                    vo.setName(examSite);
+                    vo.setTotalCount(studentService.countByExamIdAndExamSite(examId, examSite));
+                    vo.setScanCount(studentService.countByExamIdAndExamSite(examId, examSite, true));
+                    vo.setScanSheetCount(studentService.countSheetCountByExamIdAndExamSite(examId, examSite) / 2);
+                    ExamStudentSearchQuery studentSearchQuery = new ExamStudentSearchQuery();
+                    studentSearchQuery.setExamId(examId);
+                    studentSearchQuery.setExamSite(examSite);
+                    studentSearchQuery.setManualAbsent(true);
+                    long manualAbsentCount = studentService.countByQuery(studentSearchQuery);
+                    vo.setManualAbsentCount(manualAbsentCount);
+                    list.add(vo);
+                }
+                query.setTotalCount(result.size());
+                query.setTotalPage(cn.com.qmth.stmms.admin.utils.PageUtil.pageCount(result, query.getPageNumber(),
+                        query.getPageSize()));
+                query.setCurrentCount(examSiteList.size());
+            }
+        } else {
+            ScanInfoVO vo = new ScanInfoVO();
+            vo.setName(query.getExamSite());
+            vo.setTotalCount(studentService.countByExamIdAndExamSite(examId, query.getExamSite()));
+            vo.setScanCount(studentService.countByExamIdAndExamSite(examId, query.getExamSite(), true));
+            vo.setScanSheetCount(studentService.countSheetCountByExamIdAndExamSite(examId, query.getExamSite()) / 2);
+            ExamStudentSearchQuery studentSearchQuery = new ExamStudentSearchQuery();
+            studentSearchQuery.setExamId(examId);
+            studentSearchQuery.setExamSite(query.getExamSite());
+            studentSearchQuery.setManualAbsent(true);
+            long manualAbsentCount = studentService.countByQuery(studentSearchQuery);
+            vo.setManualAbsentCount(manualAbsentCount);
+            list.add(vo);
+            query.setTotalCount(1);
+            query.setTotalPage(1);
+            query.setCurrentCount(1);
+        }
+        PageResult<ScanInfoVO> pageResult = new PageResult<>();
+        pageResult.setResult(list);
+        pageResult.setPageNumber(query.getPageNumber());
+        pageResult.setPageSize(query.getPageSize());
+        pageResult.setTotalCount(query.getTotalCount());
+        pageResult.setPageCount(query.getCurrentCount());
+        return pageResult;
+    }
+
+    @Logging(menu = "扫描进度导出", type = LogType.EXPORT)
+    @RequestMapping(value = "/examSite/export", method = RequestMethod.POST)
+    public void examSiteExport(ExamStudentSearchQuery query, HttpServletRequest request, HttpServletResponse response) {
+        int examId = getSessionExamId(request);
+        List<ScanInfoVO> list = new LinkedList<>();
+        List<String> result = studentService.findDistinctExamSite(examId);
+        result = result.stream().filter(s -> StringUtils.isNotBlank(s)).collect(Collectors.toList());
+        if (StringUtils.isBlank(query.getExamSite())) {
+            for (String examSite : result) {
+                ScanInfoVO vo = new ScanInfoVO();
+                vo.setName(examSite);
+                vo.setTotalCount(studentService.countByExamIdAndExamSite(examId, examSite));
+                vo.setScanCount(studentService.countByExamIdAndExamSite(examId, examSite, true));
+                vo.setScanSheetCount(studentService.countSheetCountByExamIdAndExamSite(examId, examSite) / 2);
+                ExamStudentSearchQuery studentSearchQuery = new ExamStudentSearchQuery();
+                studentSearchQuery.setExamId(examId);
+                studentSearchQuery.setExamSite(examSite);
+                studentSearchQuery.setManualAbsent(true);
+                long manualAbsentCount = studentService.countByQuery(studentSearchQuery);
+                vo.setManualAbsentCount(manualAbsentCount);
+                list.add(vo);
+            }
+        } else {
+            ScanInfoVO vo = new ScanInfoVO();
+            vo.setName(query.getExamSite());
+            vo.setTotalCount(studentService.countByExamIdAndExamSite(examId, query.getExamSite()));
+            vo.setScanCount(studentService.countByExamIdAndExamSite(examId, query.getExamSite(), true));
+            vo.setScanSheetCount(studentService.countSheetCountByExamIdAndExamSite(examId, query.getExamSite()) / 2);
+            ExamStudentSearchQuery studentSearchQuery = new ExamStudentSearchQuery();
+            studentSearchQuery.setExamId(examId);
+            studentSearchQuery.setExamSite(query.getExamSite());
+            studentSearchQuery.setManualAbsent(true);
+            long manualAbsentCount = studentService.countByQuery(studentSearchQuery);
+            vo.setManualAbsentCount(manualAbsentCount);
+            list.add(vo);
+        }
+        try {
+            String fileName = "扫描进度.xlsx";
+            new ExportExcel("扫描进度", ScanInfoVO.class).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("scan subject export error!", e);
+            throw new StatusException("导出扫描进度失败!" + e.getMessage());
+        }
+    }
+}

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

@@ -0,0 +1,638 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.servlet.http.HttpServletRequest;
+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.data.domain.Sort;
+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 org.springframework.web.multipart.MultipartFile;
+
+import com.google.common.collect.Lists;
+import com.qmth.boot.core.collection.PageResult;
+import com.qmth.boot.core.exception.StatusException;
+
+import cn.com.qmth.stmms.admin.vo.CollationLabelVO;
+import cn.com.qmth.stmms.admin.vo.ExamStudentVO;
+import cn.com.qmth.stmms.admin.vo.UploadStudentVO;
+import cn.com.qmth.stmms.api.controller.BaseApiController;
+import cn.com.qmth.stmms.biz.config.service.impl.SystemCache;
+import cn.com.qmth.stmms.biz.exam.model.*;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.utils.PageUtil;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.enums.ExamType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import cn.com.qmth.stmms.common.utils.ImportExcel;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONObject;
+
+@Controller("adminStudentController")
+@RequestMapping("/api/admin/student")
+public class StudentController extends BaseApiController {
+
+    protected static final Logger log = LoggerFactory.getLogger(StudentController.class);
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private MarkService markService;
+
+    @Autowired
+    private ExamPackageService packageService;
+
+    @Autowired
+    private FileService fileService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Autowired
+    private CollationLabelService collationLabelService;
+
+    private static final String NULL_PAPER_TYPE_PLACEHOLDER = "#";
+
+    private static final String PAPER_TYPES_REGEX = "[a-zA-Z#]";
+
+    @RequestMapping(value = "/list", method = RequestMethod.POST)
+    @ResponseBody
+    @Logging(menu = "查询考生", type = LogType.QUERY)
+    public PageResult<ExamStudent> list(HttpServletRequest request, ExamStudentSearchQuery query) {
+        int examId = getSessionExamId(request);
+        User user = RequestUtils.getApiUser(request).getUser();
+        List<CollationLabel> collationLabel = collationLabelService.list(examId);
+        Map<String, CollationLabel> collationLabelMap = collationLabel.stream()
+                .collect(Collectors.toMap(CollationLabel::getCode, Function.identity()));
+        query.setExamId(examId);
+        if (query.getBatchCode() != null) {
+            query.setBatchCode(StringUtils.upperCase(query.getBatchCode()));
+        }
+        query.addSort("id", Sort.Direction.ASC);
+        query = studentService.findByQuery(query);
+        for (ExamStudent student : query.getResult()) {
+            student.setSubject(subjectService.find(student.getExamId(), student.getSubjectCode()));
+            buildSheetUrl(student);
+            buildPackageUrl(student);
+            if (StringUtils.isNotBlank(student.getCollationLabelCode())) {
+                student.setCollationLabel(collationLabelMap.get(student.getCollationLabelCode()).getName());
+            }
+        }
+        return PageUtil.of(query);
+    }
+
+    private void buildSheetUrl(ExamStudent student) {
+        student.setSheetUrls(
+                fileService.getSheetUris(student.getExamId(), student.getExamNumber(), 1, student.getSheetCount()));
+    }
+
+    private void buildPackageUrl(ExamStudent student) {
+        if (StringUtils.isNotBlank(student.getPackageCode())) {
+            ExamPackage ep = packageService.find(student.getExamId(), student.getPackageCode());
+            if (ep != null && ep.getPicCount() > 0) {
+                student.setPackageUrls(fileService.getPackageUris(ep.getExamId(), ep.getCode(), 1, ep.getPicCount()));
+            }
+        }
+    }
+
+    @RequestMapping(value = "/college/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<String> collegeList(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        return studentService.findDistinctCollege(examId);
+    }
+
+    @RequestMapping(value = "/className/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<String> classNameList(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        return studentService.findDistinctClassName(examId);
+    }
+
+    @RequestMapping(value = "/examSite/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<String> examSiteList(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        return studentService.findDistinctExamSite(examId);
+    }
+
+    @RequestMapping(value = "/find", method = RequestMethod.POST)
+    @ResponseBody
+    @Logging(menu = "获取考生", type = LogType.QUERY)
+    public ExamStudent findOne(HttpServletRequest request, @RequestParam Integer id) {
+        return studentService.findById(id);
+    }
+
+    @RequestMapping(value = "/save", method = RequestMethod.POST)
+    @ResponseBody
+    @Logging(menu = "新增考生", type = LogType.ADD)
+    public JSONObject save(HttpServletRequest request, ExamStudent student) {
+        int examId = getSessionExamId(request);
+        Exam exam = examService.findById(examId);
+        ExamSubject subject = subjectService.find(examId, student.getSubjectCode());
+        if (subject == null) {
+            throw new StatusException("科目不存在");
+        }
+        student.setExamId(examId);
+        student.setSchoolId(exam.getSchoolId());
+        student.setSubjectName(subject.getName());
+        student.setAbsent(false);
+        student.setUpload(false);
+        student.setException(false);
+        student.setSliceCount(0);
+        student.setSheetCount(0);
+        student.setObjectivePageCount(0);
+        student.setObjectiveScore(0d);
+        student.setSubjectiveScore(0d);
+        student.setSubjectiveStatus(SubjectiveStatus.UNMARK);
+        student.setPaperType(NULL_PAPER_TYPE_PLACEHOLDER);
+        student.setInspectCount(0);
+        ExamStudent old = checkExamNumber(student, new HashMap<String, ExamStudent>(),
+                new HashMap<String, ExamStudent>());
+        if (old != null) {
+            throw new StatusException("准考证号" + student.getExamNumber() + "已经存在");
+        } else {
+            studentService.save(student);
+            examService.updateExamUpdateTime(examId);
+        }
+        return result("考生" + student.getName() + "保存成功");
+    }
+
+    @RequestMapping(value = "/update", method = RequestMethod.POST)
+    @ResponseBody
+    @Logging(menu = "修改考生", type = LogType.UPDATE)
+    public JSONObject update(HttpServletRequest request, ExamStudent student) {
+        int examId = getSessionExamId(request);
+        Exam exam = examService.findById(examId);
+        ExamSubject subject = subjectService.find(examId, student.getSubjectCode());
+        if (subject == null) {
+            throw new StatusException("科目不存在");
+        }
+        if (student.getId() == null) {
+            throw new StatusException("考生id不能为空");
+        }
+        ExamStudent previous = studentService.findById(student.getId());
+        if (previous == null) {
+            throw new StatusException("考生不存在");
+        }
+        if (!previous.isUpload()) {
+            // 还未扫描上传的考生,才能修改准考证号、学习中心、科目
+            previous.setCampusName(student.getCampusName());
+            previous.setSubjectCode(subject.getCode());
+            previous.setSubjectName(subject.getName());
+            previous.setExamNumber(student.getExamNumber());
+        }
+        previous.setStudentCode(student.getStudentCode());
+        previous.setName(student.getName());
+        previous.setPackageCode(student.getPackageCode());
+        previous.setCollege(student.getCollege());
+        previous.setClassName(student.getClassName());
+        previous.setTeacher(student.getTeacher());
+        previous = studentService.save(previous);
+        return result("考生" + student.getName() + "保存成功");
+    }
+
+    private ExamStudent checkExamNumber(ExamStudent student, Map<String, ExamStudent> current,
+            Map<String, ExamStudent> saveMap) {
+        ExamStudent previous = saveMap.get(student.getExamNumber());
+
+        if (previous != null && previous.getId() != null) {
+            return previous;
+        }
+        if (current != null && !current.isEmpty()) {
+            previous = current.get(student.getExamNumber());
+        } else {
+            previous = studentService.findByExamIdAndExamNumber(student.getExamId(), student.getExamNumber());
+        }
+        if (previous != null) {
+            return previous;
+        }
+        current.put(student.getExamNumber(), student);
+        return null;
+    }
+
+    @RequestMapping(value = "/delete", method = RequestMethod.POST)
+    @ResponseBody
+    @Logging(menu = "删除考生", type = LogType.DELETE)
+    public JSONObject delete(HttpServletRequest request, @RequestParam Integer id) {
+        ExamStudent student = studentService.findById(id);
+        if (student == null) {
+            throw new StatusException("考生不存在");
+        }
+        studentService.deleteById(id);
+        markService.deleteByStudent(student);
+        subjectService.updateUploadCount(student.getExamId(), student.getSubjectCode(), (int) studentService
+                .countUploadedByExamIdAndSubjectCode(student.getExamId(), student.getSubjectCode()));
+        RequestUtils.setLog(request, "删除成功,id:" + id);
+        return result("删除考生成功");
+    }
+
+    @RequestMapping(value = "/template")
+    public void importFileTemplate(HttpServletResponse response) {
+        try {
+            String fileName = "考生数据导入模板.xlsx";
+            List<ExamStudent> list = Lists.newArrayList();
+            list.add(new ExamStudent());
+            new ExportExcel("考生数据", ExamStudent.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @Logging(menu = "导入考生", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/import", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject importFile(HttpServletRequest request, MultipartFile file) {
+        int examId = getSessionExamId(request);
+        Exam exam = examService.findById(examId);
+        try {
+            int successNum = 0;
+            int updateNum = 0;
+            int failureNum = 0;
+            StringBuilder failureMsg = new StringBuilder();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<ExamStudent> list = ei.getDataList(ExamStudent.class);
+            List<ExamStudent> saveList = new LinkedList<ExamStudent>();
+            List<ExamStudent> updateList = new LinkedList<ExamStudent>();
+            Map<String, ExamStudent> saveMap = new HashMap<String, ExamStudent>();
+            Map<String, ExamStudent> current = null;
+            // 当数据库中已存在考生小于一定数量,则一次性取出所有考生,用于校验准考证号是否重复
+            if (studentService.countByExamId(examId) < 10000) {
+                current = new HashMap<String, ExamStudent>();
+                List<ExamStudent> list2 = studentService.findByExamId(examId);
+                for (ExamStudent s : list2) {
+                    current.put(s.getExamNumber(), s);
+                }
+            }
+
+            for (ExamStudent student : list) {
+                if (StringUtils.isBlank(student.getExamNumber()) || StringUtils.isBlank(student.getName())
+                        || StringUtils.isBlank(student.getSubjectCode())
+                        || StringUtils.isBlank(student.getSubjectName()) || StringUtils.isBlank(student.getCollege())
+                        || StringUtils.isBlank(student.getClassName()) || StringUtils.isBlank(student.getTeacher())
+                        || StringUtils.isBlank(student.getStudentCode())) {
+                    continue;
+                }
+
+                student.setExamId(examId);
+                student.setSchoolId(exam.getSchoolId());
+                student.setAbsent(false);
+                student.setUpload(false);
+                student.setException(false);
+                student.setSliceCount(0);
+                student.setSheetCount(0);
+                student.setObjectivePageCount(0);
+                student.setObjectiveScore(0d);
+                student.setSubjectiveScore(0d);
+                student.setSubjectiveStatus(SubjectiveStatus.UNMARK);
+                student.setPaperType(NULL_PAPER_TYPE_PLACEHOLDER);
+                student.setInspectCount(0);
+                ExamStudent previous = checkExamNumber(student, current, saveMap);
+                if (previous != null && previous.getId() != null) {
+                    student = updatePrevious(student, previous);
+                    updateList.add(student);
+                } else if (previous == null) {
+                    saveList.add(student);
+                }
+                saveMap.put(student.getExamNumber(), student);
+            }
+
+            successNum = studentService.batchSave(saveList);
+            updateNum = studentService.batchUpdate(updateList);
+            examService.updateExamUpdateTime(examId);
+            if (failureNum > 0) {
+                failureMsg.insert(0, ",失败 " + failureNum + " 条用户");
+            }
+            String message = "已成功导入 " + (successNum + updateNum) + " 条用户";
+            RequestUtils.setLog(request, message);
+            return result(message + failureMsg);
+        } catch (Exception e) {
+            log.error("Batch import student error!", e);
+            e.printStackTrace();
+            throw new StatusException("导入考生失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    private ExamStudent updatePrevious(ExamStudent student, ExamStudent previous) {
+        previous.setCollege(student.getCollege());
+        previous.setClassName(student.getClassName());
+        previous.setTeacher(student.getTeacher());
+        previous.setExamRoom(student.getExamRoom());
+        previous.setExamSite(student.getExamSite());
+        previous.setRemark(student.getRemark());
+        previous.setSubjectLevel(student.getSubjectLevel());
+        previous.setSubjectCategory(student.getSubjectCategory());
+        previous.setSubjectRemark(student.getSubjectRemark());
+        return previous;
+    }
+
+    @Logging(menu = "导出考生", type = LogType.EXPORT)
+    @RequestMapping(value = "/export", method = RequestMethod.POST)
+    public void exportFile(ExamStudentSearchQuery query, HttpServletRequest request, HttpServletResponse response) {
+        try {
+            int examId = getSessionExamId(request);
+            query.setPageNumber(1);
+            query.setPageSize(Integer.MAX_VALUE);
+            query.setExamId(examId);
+            query.addSort("id", Sort.Direction.ASC);
+            query = studentService.findByQuery(query);
+            for (ExamStudent student : query.getResult()) {
+                ExamSubject subject = subjectService.find(student.getExamId(), student.getSubjectCode());
+                student.setSubjectName(subject != null ? subject.getName() : "");
+                student.setSubjectLevel(subject != null ? subject.getLevel() : "");
+                student.setSubjectCategory(subject != null ? subject.getCategory() : "");
+                student.setSubjectRemark(subject != null ? subject.getRemark() : "");
+            }
+            String fileName = "考生数据.xlsx";
+            new ExportExcel("考生数据", ExamStudent.class).setDataList(query.getResult()).write(response, fileName)
+                    .dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导出考生数据失败!" + e.getMessage());
+        }
+    }
+
+    @RequestMapping(value = "/absentTemplate")
+    public void absentTemplate(HttpServletResponse response) {
+        try {
+            String fileName = "缺考考生导入模板.xlsx";
+            List<ExamStudentVO> list = Lists.newArrayList();
+            list.add(new ExamStudentVO());
+            new ExportExcel("缺考考生", ExamStudentVO.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @Logging(menu = "导入缺考名单", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/absentImport", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject absentImportFile(HttpServletRequest request, MultipartFile file) {
+        try {
+            int examId = getSessionExamId(request);
+            int successNum = 0;
+            int failureNum = 0;
+            StringBuilder failureMsg = new StringBuilder();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<ExamStudentVO> list = ei.getDataList(ExamStudentVO.class);
+            studentService.clearManualAbsent(examId);
+            for (ExamStudentVO studentVO : list) {
+                if (StringUtils.isBlank(studentVO.getExamNumber())) {
+                    continue;
+                }
+                ExamStudent student = studentService.findByExamIdAndExamNumber(examId, studentVO.getExamNumber());
+                if (student != null) {
+                    studentService.updateManualAbsent(examId, student.getExamNumber(), true);
+                    successNum++;
+                } else {
+                    failureMsg.append("<br/>准考证号 " + studentVO.getExamNumber() + " 不存在; ");
+                    failureNum++;
+                }
+
+            }
+            if (failureNum > 0) {
+                failureMsg.insert(0, ",失败 " + failureNum + " 条用户");
+            }
+            String message = "已成功导入 " + successNum + " 条用户";
+            RequestUtils.setLog(request, message);
+            return result(message + failureMsg);
+        } catch (Exception e) {
+            log.error("Batch import absentStudent error!", e);
+            e.printStackTrace();
+            throw new StatusException("导入缺考考生失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @RequestMapping(value = "/breachTemplate")
+    public void breachTemplate(HttpServletResponse response) {
+        try {
+            String fileName = "违纪考生导入模板.xlsx";
+            List<ExamStudentVO> list = Lists.newArrayList();
+            list.add(new ExamStudentVO());
+            new ExportExcel("违纪考生", ExamStudentVO.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @Logging(menu = "导入违纪名单", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/breachImport", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject breachImportFile(HttpServletRequest request, MultipartFile file) {
+        try {
+            int examId = getSessionExamId(request);
+            int successNum = 0;
+            int failureNum = 0;
+            StringBuilder failureMsg = new StringBuilder();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<ExamStudentVO> list = ei.getDataList(ExamStudentVO.class);
+            for (ExamStudentVO studentVO : list) {
+                if (StringUtils.isBlank(studentVO.getExamNumber())) {
+                    continue;
+                }
+                ExamStudent student = studentService.findByExamIdAndExamNumber(examId, studentVO.getExamNumber());
+                if (student != null) {
+                    studentService.updateBreach(student.getId(), true);
+                    successNum++;
+                } else {
+                    failureMsg.append("<br/>准考证号 " + studentVO.getExamNumber() + " 不存在; ");
+                    failureNum++;
+                }
+
+            }
+            if (failureNum > 0) {
+                failureMsg.insert(0, ",失败 " + failureNum + " 条用户");
+            }
+            String message = "已成功导入 " + successNum + " 条用户";
+            RequestUtils.setLog(request, message);
+            return result(message + failureMsg);
+        } catch (Exception e) {
+            log.error("Batch import BreachStudent error!", e);
+            e.printStackTrace();
+            throw new StatusException("导入违纪考生失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @Logging(menu = "违纪重置", type = LogType.UPDATE)
+    @RequestMapping(value = "/updateBreach", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject updateBreach(HttpServletRequest request, Integer id) {
+        JSONObject result = new JSONObject();
+        ExamStudent student = studentService.findById(id);
+        if (student != null && studentService.updateBreach(id, false)) {
+            List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(student.getExamId(),
+                    student.getSubjectCode(), MarkStatus.FINISH);
+            for (MarkGroup markGroup : groupList) {
+                groupService.updateStatus(student.getExamId(), student.getSubjectCode(), markGroup.getNumber(),
+                        MarkStatus.FORMAL, MarkStatus.FINISH);
+            }
+            result.accumulate("message", "重置成功!");
+            result.accumulate("success", true);
+        } else {
+            result.accumulate("message", "将违纪考生信息重置为正常时出错!");
+            result.accumulate("success", false);
+        }
+        return result;
+    }
+
+    @RequestMapping(value = "/uploadTemplate")
+    public void uploadTemplate(HttpServletResponse response) {
+        try {
+            String fileName = "多媒体考生上传导入模板.xlsx";
+            List<UploadStudentVO> list = Lists.newArrayList();
+            list.add(new UploadStudentVO());
+            new ExportExcel("多媒体考生上传", UploadStudentVO.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @RequestMapping(value = "/uploadImport", method = RequestMethod.POST)
+    @Logging(menu = "导入多媒体考生", type = LogType.IMPORT_FILE)
+    @ResponseBody
+    public JSONObject uploadImportFile(HttpServletRequest request, MultipartFile file) {
+        try {
+            int examId = getSessionExamId(request);
+            Exam exam = examService.findById(examId);
+            if (exam.getType().equals(ExamType.SCAN_IMAGE)) {
+                throw new StatusException("当前考试不是多媒体阅卷类型");
+            }
+            int successNum = 0;
+            int failureNum = 0;
+            StringBuilder failureMsg = new StringBuilder();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<UploadStudentVO> list = ei.getDataList(UploadStudentVO.class);
+            for (UploadStudentVO studentVO : list) {
+                if (StringUtils.isBlank(studentVO.getExamNumber())) {
+                    continue;
+                }
+                ExamStudent student = studentService.findByExamIdAndExamNumber(examId, studentVO.getExamNumber());
+                if (student != null) {
+                    student.setUpload(true);
+                    student.setAbsent(false);
+                    student.setAnswers(null);
+                    student.setBatchCode(null);
+                    student.setSliceCount(0);
+                    student.setSheetCount(0);
+                    student.setObjectivePageCount(0);
+                    student.setInspectCount(0);
+                    if (student.getPaperType() != null && student.getPaperType().matches(PAPER_TYPES_REGEX)) {
+                        student.setPaperType(StringUtils.trimToNull(studentVO.getPaperType()));
+                    } else {
+                        student.setPaperType(NULL_PAPER_TYPE_PLACEHOLDER);
+                    }
+                    // 同步更新评卷任务
+                    if (saveUploadStudent(student)) {
+                        successNum++;
+                    }
+                } else {
+                    failureMsg.append("<br/>准考证号 " + studentVO.getExamNumber() + " 不存在; ");
+                    failureNum++;
+                }
+
+            }
+            if (failureNum > 0) {
+                failureMsg.insert(0, ",失败 " + failureNum + " 条用户");
+            }
+            return result("已成功导入 " + successNum + " 条用户" + failureMsg);
+        } catch (Exception e) {
+            log.error("Batch import BreachStudent error!", e);
+            e.printStackTrace();
+            throw new StatusException("导入上传考生失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @RequestMapping(value = "/collationLabelTemplate")
+    public void collationLabelTemplate(HttpServletResponse response) {
+        try {
+            String fileName = "整理异常导入模板.xlsx";
+            List<ExamStudentVO> list = Lists.newArrayList();
+            list.add(new ExamStudentVO());
+            new ExportExcel("整理异常", CollationLabelVO.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @Logging(menu = "导入整理异常", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/collationLabelImport", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject collationLabelImportFile(HttpServletRequest request, MultipartFile file) {
+        int examId = getSessionExamId(request);
+        try {
+            int successNum = 0;
+            int failureNum = 0;
+            StringBuilder failureMsg = new StringBuilder();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<CollationLabelVO> list = ei.getDataList(CollationLabelVO.class);
+            List<CollationLabel> collationLabel = collationLabelService.list(examId);
+            Map<String, CollationLabel> collationLabelMap = collationLabel.stream()
+                    .collect(Collectors.toMap(CollationLabel::getCode, Function.identity()));
+            studentService.clearCollationLabelCode(examId);
+            for (CollationLabelVO studentVO : list) {
+                if (StringUtils.isBlank(studentVO.getExamNumber())) {
+                    continue;
+                }
+                ExamStudent student = studentService.findByExamIdAndExamNumber(examId, studentVO.getExamNumber());
+                if (student != null && collationLabelMap.get(studentVO.getCollationLabelCode()) != null) {
+                    studentService.updateCollationLabelCode(examId, student.getExamNumber(),
+                            studentVO.getCollationLabelCode());
+                    successNum++;
+                } else {
+                    failureMsg.append("<br/>准考证号 " + studentVO.getExamNumber() + " 不存在或异常代码 "
+                            + studentVO.getCollationLabelCode() + " 不存在; ");
+                    failureNum++;
+                }
+
+            }
+            if (failureNum > 0) {
+                failureMsg.insert(0, ",失败 " + failureNum + " 条");
+            }
+            String message = "已成功导入 " + successNum + " 条";
+            RequestUtils.setLog(request, message);
+            return result(message + failureMsg);
+        } catch (Exception e) {
+            log.error("Batch import CollationLabel error!", e);
+            e.printStackTrace();
+            throw new StatusException("导入整理异常失败!失败信息:" + e.getMessage());
+        }
+    }
+}

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

@@ -0,0 +1,181 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
+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.beans.factory.annotation.Value;
+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 org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import com.qmth.boot.core.collection.PageResult;
+import com.qmth.boot.core.exception.StatusException;
+
+import cn.com.qmth.stmms.api.controller.BaseApiController;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+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.utils.PageUtil;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONObject;
+
+@Controller("adminSubjectController")
+@RequestMapping("/api/admin/subject")
+public class SubjectController extends BaseApiController {
+
+    protected static final Logger log = LoggerFactory.getLogger(SubjectController.class);
+
+    private static final String NULL_PAPER_TYPE_PLACEHOLDER = "#";
+
+    private static final String[] SUBJECTIVE_EXCEL_HEADER = new String[] { "科目代码*", "科目名称", "大题名称*", "题目昵称",
+            "大题号(只能用小写数字)*", "小题号(只能用小写数字)*", "小题满分*", "给分次数", "间隔分*", "评卷分组(只能用小写数字)*", "图片序号(用英文逗号分割)",
+            "仲裁方式(0-分组,1-小题)", "双评比例(0~1)", "仲裁阀值", "合分策略(1-平均,2-最高,3-最低)", "评卷模式(common-普通,track-轨迹)", "试评数量(0-跳过试评)",
+            "选做题数量" };
+
+    private static final String[] OBJECTIVE_EXCEL_HEADER = new String[] { "科目代码", "科目名称", "试卷类型", "大题名称", "题目昵称",
+            "大题号(只能用小写数字)", "小题号(只能用小写数字)", "标准答案", "小题满分", "题型(1-单选,2-多选,3-判断)" };
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Autowired
+    private FileService fileService;
+
+    @Qualifier("task-executor")
+    @Autowired
+    private AsyncTaskExecutor taskExecutor;
+
+    @Autowired
+    private LockService lockService;
+
+    @Autowired
+    private ReportService reportService;
+
+    @Autowired
+    private MarkService markService;
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Autowired
+    private SelectiveGroupService selectiveGroupService;
+
+    @Autowired
+    private AnswerCardService answerCardService;
+
+    @Autowired
+    private InspectHistoryService inspectHistoryService;
+
+    @Value("${file.temp}")
+    private String tempFile;
+
+    @RequestMapping(value = "/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<ExamSubject> list(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        return getExamSubject(examId, RequestUtils.getApiUser(request));
+    }
+
+    @RequestMapping(value = "/level/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<String> levelList(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        return subjectService.listLevel(examId);
+    }
+
+    @RequestMapping(value = "/category/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<String> categoryList(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        return subjectService.listCategory(examId);
+    }
+
+    @RequestMapping(value = "/query", method = RequestMethod.POST)
+    @ResponseBody
+    public PageResult<ExamSubject> list(HttpServletRequest request, ExamSubjectSearchQuery query) {
+        int examId = getSessionExamId(request);
+        query.setExamId(examId);
+        query = subjectService.findByQuery(query);
+        for (ExamSubject subject : query.getResult()) {
+            List<String> paperTypes = questionService.getPaperType(examId, subject.getCode());
+            subject.setPaperType(StringUtils.join(paperTypes, ","));
+            subject.setPaperAnswerUrl(fileService);
+            subject.setLocked(lockService.isLocked(LockType.SCORE_CALCULATE, examId, subject.getCode()));
+        }
+        return PageUtil.of(query);
+    }
+
+    @RequestMapping(value = "/find", method = RequestMethod.POST)
+    @ResponseBody
+    public ExamSubject find(HttpServletRequest request, @RequestParam Integer examId, @RequestParam String code) {
+        return subjectService.find(examId, code);
+    }
+
+    @Logging(menu = "科目设置修改", type = LogType.UPDATE)
+    @RequestMapping(value = "/update", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject save(RedirectAttributes redirectAttributes, HttpServletRequest request, ExamSubject subject) {
+        ExamSubject previous = subjectService.find(getSessionExamId(request), subject.getCode());
+        if (previous == null) {
+            throw new StatusException("科目不存在");
+        }
+        previous.setSheetConfig(subject.getSheetConfig());
+        previous.setSasConfig(subject.getSasConfig());
+        previous.setEnableSplit(subject.getEnableSplit());
+        previous.setAutoScroll(subject.getAutoScroll());
+        previous.setDisplayQuestionName(
+                subject.getDisplayQuestionName() == null ? false : subject.getDisplayQuestionName());
+        subjectService.save(previous);
+        return result(true);
+    }
+
+    @RequestMapping(value = "/statistic", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject statistic(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        List<Double> totalScoreList = subjectService.distinctTotalScore(examId);
+        List<Long> subjectCountList = new ArrayList<Long>();
+        for (Double totalScore : totalScoreList) {
+            ExamSubjectSearchQuery querys = new ExamSubjectSearchQuery();
+            querys.setExamId(examId);
+            querys.setTotalScore(totalScore);
+            subjectCountList.add(subjectService.countByQuery(querys));
+        }
+        return result;
+    }
+}

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

@@ -0,0 +1,37 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.util.Arrays;
+import java.util.List;
+
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import net.sf.json.JSONObject;
+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 cn.com.qmth.stmms.api.controller.BaseApiController;
+import cn.com.qmth.stmms.common.enums.ExamType;
+
+@Controller("sysController")
+@RequestMapping("/api/sys")
+public class SystemController extends BaseApiController {
+
+    protected static final Logger log = LoggerFactory.getLogger(SystemController.class);
+
+    @Autowired
+    private FileService fileService;
+
+    @RequestMapping(value = "/version", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject version() {
+        JSONObject result  = new JSONObject();
+        result.accumulate("fileServer", fileService.getFileServer());
+        result.accumulate("fileServer", fileService.getFileServer());
+        return result;
+    }
+
+}

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

@@ -0,0 +1,681 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.io.ByteArrayInputStream;
+import java.util.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+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.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+
+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.dto.MarkerDTO;
+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.UserStudentVo;
+import cn.com.qmth.stmms.biz.exam.model.*;
+import cn.com.qmth.stmms.biz.exam.query.MarkerSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
+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.*;
+import cn.com.qmth.stmms.common.utils.*;
+import net.sf.json.JSONObject;
+
+@Controller("adminUserController")
+@RequestMapping("/api/admin/user")
+public class UserController extends BaseApiController {
+
+    protected static final Logger log = LoggerFactory.getLogger(UserController.class);
+
+    public static Role[] ROLE_LIST = { Role.SCANNER, Role.SUBJECT_HEADER, Role.INSPECTOR, Role.MARKER,
+            Role.SCHOOL_VIEWER, Role.SCHOOL_ADMIN, Role.COLLEGE_ADMIN };
+
+    public static final String USER_PASSWORD = "123456";
+
+    public static final String SPLIT = ",";
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private SubjectUserService subjectUserService;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Autowired
+    private SystemAuthService systemAuthService;
+
+    @Autowired
+    private MarkerClassService markerClassService;
+    @Autowired
+    private UserStudentService userStudentService;
+
+    @RequestMapping(value = "/list", method = RequestMethod.POST)
+    @ResponseBody
+    @Logging(menu = "查询用户", type = LogType.QUERY)
+    public PageResult<User> list(HttpServletRequest request, UserSearchQuery query) {
+        ApiUser user = RequestUtils.getApiUser(request);
+        query.setSchoolId(user.getSchoolId());
+        query = userService.findByQuery(query);
+        return PageUtil.of(query);
+    }
+
+    @RequestMapping(value = "/role/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<Role> roleList() {
+        return Arrays.asList(ROLE_LIST);
+    }
+
+    @RequestMapping(value = "/source/list", method = RequestMethod.POST)
+    @ResponseBody
+    public List<UserSource> sourceList() {
+        return Arrays.asList(UserSource.values());
+    }
+
+    @RequestMapping(value = "/find", method = RequestMethod.POST)
+    @ResponseBody
+    public User findOne(HttpServletRequest request, @RequestParam Integer id) {
+        return userService.findById(id);
+    }
+
+    @RequestMapping(value = "/save", method = RequestMethod.POST)
+    @Logging(menu = "新增用户", type = LogType.ADD)
+    @ResponseBody
+    public JSONObject save(HttpServletRequest request, UserSearchQuery query) {
+        return result(true);
+    }
+
+    @RequestMapping(value = "/update", method = RequestMethod.POST)
+    @ResponseBody
+    @Logging(menu = "修改用户", type = LogType.UPDATE)
+    public JSONObject update(HttpServletRequest request, UserSearchQuery query) {
+        return result(true);
+    }
+
+    @Logging(menu = "导出用户", type = LogType.EXPORT)
+    @RequestMapping(value = "/export", method = RequestMethod.POST)
+    public void exportFile(UserSearchQuery query, HttpServletRequest request, HttpServletResponse response) {
+        try {
+            String fileName = "用户数据.xlsx";
+            User user = RequestUtils.getApiUser(request).getUser();
+            query.setSchoolId(user.getSchoolId());
+            query.setPageNumber(1);
+            query.setPageSize(Integer.MAX_VALUE);
+            query = userService.findByQuery(query);
+            for (User s : query.getResult()) {
+                s.setRoleName(s.getRole().getName());
+            }
+            new ExportExcel("用户数据", User.class).setDataList(query.getResult()).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导出用户数据失败");
+        }
+    }
+
+    @Logging(menu = "导出用户", type = LogType.EXPORT)
+    @RequestMapping(value = "/exportExam", method = RequestMethod.POST)
+    public void exportExam(UserSearchQuery query, HttpServletRequest request, HttpServletResponse response) {
+        try {
+            String fileName = "用户数据.xlsx";
+            User user = RequestUtils.getApiUser(request).getUser();
+            query.setSchoolId(user.getSchoolId());
+            query.setPageNumber(1);
+            query.setPageSize(Integer.MAX_VALUE);
+            int examId = getSessionExamId(request);
+            if (query.getRole() != null && query.getRole().equals(Role.MARKER)) {
+                Map<String, ExamSubject> subjectMap = new HashMap<String, ExamSubject>();
+                Map<String, MarkGroup> groupMap = new HashMap<String, MarkGroup>();
+                Map<String, String> collegeMap = new HashMap<String, String>();
+                MarkerSearchQuery mQuery = new MarkerSearchQuery();
+                mQuery.setExamId(examId);
+                mQuery.setLoginName(query.getLoginName());
+                mQuery.setName(query.getName());
+                mQuery.setPageSize(Integer.MAX_VALUE);
+                mQuery = markerService.findByQuery(mQuery);
+                List<MarkerDTO> list = new ArrayList<MarkerDTO>();
+                for (Marker m : mQuery.getResult()) {
+                    MarkerDTO s = new MarkerDTO();
+                    ExamSubject subject = subjectMap.get(m.getSubjectCode());
+                    if (subject == null) {
+                        subject = subjectService.find(examId, m.getSubjectCode());
+                        subjectMap.put(m.getSubjectCode(), subject);
+                        List<String> colleges = studentService.findDistinctCollegeBySubjectCode(examId,
+                                m.getSubjectCode());
+                        collegeMap.put(m.getSubjectCode(), StringUtils.join(colleges, SPLIT));
+                    }
+                    s.setSubjectCode(m.getSubjectCode());
+                    s.setSubjectName(subject.getName());
+                    String groupKey = m.getSubjectCode() + "/t" + m.getGroupNumber();
+                    MarkGroup group = groupMap.get(groupKey);
+                    if (group == null) {
+                        group = groupService.findOne(examId, m.getSubjectCode(), m.getGroupNumber());
+                        group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
+                                subject.getCode(), false, group.getNumber()));
+                        groupMap.put(groupKey, group);
+                    }
+                    s.setGroupName(group.getQuestionTitle());
+                    s.setGroupNumber(m.getGroupNumber());
+                    s.setRole(Role.MARKER.getName());
+                    User u = userService.findById(m.getUserId());
+                    s.setLoginName(u.getLoginName());
+                    s.setRandomPassword(u.getRandomPassword());
+                    s.setCollege(collegeMap.get(m.getSubjectCode()));
+                    list.add(s);
+                }
+                new ExportExcel("评卷员数据", MarkerDTO.class).setDataList(list).write(response, fileName).dispose();
+            }
+            if (query.getRole() != null
+                    && (query.getRole().equals(Role.SUBJECT_HEADER) || query.getRole().equals(Role.INSPECTOR))) {
+                List<SubjectUserDTO> list = new ArrayList<SubjectUserDTO>();
+                Map<String, ExamSubject> subjectMap = new HashMap<String, ExamSubject>();
+                Map<String, String> collegeMap = new HashMap<String, String>();
+                query = userService.findByQuery(query);
+                for (User u : query.getResult()) {
+                    List<SubjectUser> subjectUsers = subjectUserService.findByUserId(u.getId());
+                    if (!subjectUsers.isEmpty()) {
+                        for (SubjectUser subjectUser : subjectUsers) {
+                            SubjectUserDTO s = new SubjectUserDTO();
+                            s.setRole(query.getRole().getName());
+                            s.setLoginName(u.getLoginName());
+                            s.setRandomPassword(u.getRandomPassword());
+                            ExamSubject subject = subjectMap.get(subjectUser.getSubjectCode());
+                            if (subject == null) {
+                                subject = subjectService.find(examId, subjectUser.getSubjectCode());
+                                List<String> colleges = studentService.findDistinctCollegeBySubjectCode(examId,
+                                        subjectUser.getSubjectCode());
+                                subjectMap.put(subjectUser.getSubjectCode(), subject);
+                                collegeMap.put(subjectUser.getSubjectCode(), StringUtils.join(colleges, SPLIT));
+                            }
+                            if (subject != null) {
+                                s.setSubjectCode(subjectUser.getSubjectCode());
+                                s.setSubjectName(subject.getName());
+                                s.setCollege(collegeMap.get(subjectUser.getSubjectCode()));
+                                list.add(s);
+                            }
+                        }
+                    }
+                }
+                new ExportExcel("用户数据", SubjectUserDTO.class).setDataList(list).write(response, fileName).dispose();
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导出用户数据失败");
+        }
+    }
+
+    @Logging(menu = "导出用户", type = LogType.EXPORT)
+    @RequestMapping(value = "/exportSubject", method = RequestMethod.POST)
+    public void exportSubject(UserSearchQuery query, HttpServletRequest request, HttpServletResponse response) {
+        int examId = getSessionExamId(request);
+        ApiUser wu = RequestUtils.getApiUser(request);
+        try {
+            String fileName = "用户数据.zip";
+            response.reset();
+            response.setContentType("application/octet-stream; charset=utf-8");
+            ZipWriter writer = ZipWriter.create(response.getOutputStream());
+            response.setHeader("Content-Disposition", "attachment; filename=" + Encodes.urlEncode(fileName));
+            List<ExamSubject> subjectList = getExamSubject(examId, wu);
+            Map<String, MarkGroup> groupMap = new HashMap<String, MarkGroup>();
+            Map<String, String> collegeMap = new HashMap<String, String>();
+            for (ExamSubject subject : subjectList) {
+                String subjectName = subject.getCode() + "-" + subject.getName();
+                if (query.getRole() != null && query.getRole().equals(Role.MARKER)) {
+                    MarkerSearchQuery mQuery = new MarkerSearchQuery();
+                    mQuery.setExamId(examId);
+                    mQuery.setSubjectCode(subject.getCode());
+                    mQuery.setLoginName(query.getLoginName());
+                    mQuery.setName(query.getName());
+                    mQuery.setPageSize(Integer.MAX_VALUE);
+                    mQuery = markerService.findByQuery(mQuery);
+                    List<MarkerDTO> list = new ArrayList<MarkerDTO>();
+                    for (Marker m : mQuery.getResult()) {
+                        MarkerDTO s = new MarkerDTO();
+                        if (collegeMap.get(subject.getCode()) == null) {
+                            List<String> colleges = studentService.findDistinctCollegeBySubjectCode(examId,
+                                    m.getSubjectCode());
+                            collegeMap.put(subject.getCode(), StringUtils.join(colleges, SPLIT));
+                        }
+                        s.setSubjectCode(subject.getCode());
+                        s.setSubjectName(subject.getName());
+                        String groupKey = m.getSubjectCode() + "/t" + m.getGroupNumber();
+                        MarkGroup group = groupMap.get(groupKey);
+                        if (group == null) {
+                            group = groupService.findOne(examId, m.getSubjectCode(), m.getGroupNumber());
+                            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
+                                    subject.getCode(), false, group.getNumber()));
+                            groupMap.put(groupKey, group);
+                        }
+                        s.setGroupName(group.getQuestionTitle());
+                        s.setGroupNumber(m.getGroupNumber());
+                        s.setRole(Role.MARKER.getName());
+                        User u = userService.findById(m.getUserId());
+                        s.setLoginName(u.getLoginName());
+                        s.setRandomPassword(u.getRandomPassword());
+                        s.setCollege(collegeMap.get(m.getSubjectCode()));
+                        list.add(s);
+                    }
+                    if (list.isEmpty()) {
+                        continue;
+                    }
+                    ByteArrayOutputStream os = new ByteArrayOutputStream();
+                    new ExportExcel(query.getRole().getName() + "数据", MarkerDTO.class).setDataList(list).write(os);
+                    os.flush();
+                    byte[] bytes = os.toByteArray();
+                    writer.write(new ByteArrayInputStream(bytes),
+                            subjectName + "-" + query.getRole().getName() + ".xlsx");
+                }
+                if (query.getRole() != null
+                        && (query.getRole().equals(Role.SUBJECT_HEADER) || query.getRole().equals(Role.INSPECTOR))) {
+                    List<SubjectUserDTO> list = new ArrayList<SubjectUserDTO>();
+                    List<SubjectUser> subjectUsers = subjectUserService.findBySubjectCode(subject.getCode());
+                    for (SubjectUser subjectUser : subjectUsers) {
+                        User u = userService.findById(subjectUser.getUserId());
+                        if (!u.getRole().equals(query.getRole())) {
+                            continue;
+                        }
+                        SubjectUserDTO s = new SubjectUserDTO();
+                        s.setRole(query.getRole().getName());
+                        s.setLoginName(u.getLoginName());
+                        s.setRandomPassword(u.getRandomPassword());
+                        if (collegeMap.get(subject.getCode()) == null) {
+                            List<String> colleges = studentService.findDistinctCollegeBySubjectCode(examId,
+                                    subjectUser.getSubjectCode());
+                            collegeMap.put(subjectUser.getSubjectCode(), StringUtils.join(colleges, SPLIT));
+                        }
+                        s.setSubjectCode(subjectUser.getSubjectCode());
+                        s.setSubjectName(subject.getName());
+                        s.setCollege(collegeMap.get(subjectUser.getSubjectCode()));
+                        list.add(s);
+                    }
+                    if (list.isEmpty()) {
+                        continue;
+                    }
+                    ByteArrayOutputStream os = new ByteArrayOutputStream();
+                    new ExportExcel(query.getRole().getName() + "数据", SubjectUserDTO.class).setDataList(list).write(os);
+                    os.flush();
+                    byte[] bytes = os.toByteArray();
+                    writer.write(new ByteArrayInputStream(bytes),
+                            subjectName + "-" + query.getRole().getName() + ".xlsx");
+                }
+            }
+            writer.close();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导出用户数据失败");
+        }
+    }
+
+    @Logging(menu = "用户启用/禁用", type = LogType.UPDATE)
+    @RequestMapping("/enable")
+    @ResponseBody
+    public JSONObject toggle(HttpServletRequest request, @RequestParam List<Integer> ids,
+            @RequestParam Boolean enable) {
+        for (Integer id : ids) {
+            User user = userService.findById(id);
+            if (user == null) {
+                continue;
+            }
+            user.setEnable(enable);
+            markerService.updateEnableByUserId(id, enable);
+            userService.save(user);
+        }
+        return result(true);
+    }
+
+    @Logging(menu = "用户重置密码", type = LogType.UPDATE)
+    @RequestMapping(value = "/reset", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject reSetPassword(@RequestParam Integer[] ids, @RequestParam String password) {
+        for (Integer id : ids) {
+            User user = userService.findById(id);
+            if (user == null || UserSource.EXTERNAL.equals(user.getSource())) {
+                continue;
+            }
+            password = StringEscapeUtils.unescapeHtml(password);
+            user.setPassword(EncryptUtils.md5(password));
+            user.setRandomPassword(null);
+            user = userService.save(user);
+        }
+        return result(true);
+    }
+
+    @Logging(menu = "批量新增用户", type = LogType.ADD)
+    @RequestMapping(value = "/batchSave", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject batchSave(HttpServletRequest request, @RequestParam Role role,
+            @RequestParam(required = false) String password, @RequestParam Integer number,
+            @RequestParam String subjectCodeString, @RequestParam(required = false) Boolean random,
+            @RequestParam(required = false) String prefix) {
+        SystemAuth systemAuth = systemAuthService.findOne();
+        if (SystemAuthType.OFFLINE.equals(systemAuth.getType())) {
+            if (StringUtils.isBlank(prefix)) {
+                throw new StatusException("请填写自定义前缀");
+            }
+        } else {
+            prefix = null;
+        }
+        int examId = getSessionExamId(request);
+        if (Role.MARKER.equals(role)) {
+            int successNum = userService.batchSaveMarker(examId, getSubjectCodeSet(subjectCodeString), number, password,
+                    random == null ? false : random, prefix);
+            String message = "已成功新增 " + successNum + " 条用户";
+            return result(message);
+        } else {
+            int successNum = userService.batchSaveSubjectUser(examId, role, getSubjectCodeSet(subjectCodeString),
+                    number, password, random == null ? false : random, prefix);
+            String message = "已成功新增 " + successNum + " 条用户";
+            return result(message);
+        }
+    }
+
+    private Set<String> getSubjectCodeSet(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) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+        }
+        return set;
+    }
+
+    @RequestMapping(value = "/student/template")
+    public void importStudentTemplate(HttpServletResponse response) {
+        try {
+            String fileName = "绑定考生导入模板.xlsx";
+            String title = "绑定考生数据";
+            List<SubjectUser> list = new ArrayList<SubjectUser>();
+            list.add(new SubjectUser());
+            new ExportExcel(title, UserStudentVo.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @RequestMapping(value = "/student/clear")
+    @ResponseBody
+    public JSONObject studentClear(@RequestParam Integer id) {
+        userStudentService.clear(id);
+        return result(true);
+    }
+
+    @RequestMapping(value = "/subject/template")
+    public void importFileTemplate(HttpServletResponse response, @RequestParam Boolean isHeader) {
+        try {
+            String fileName = isHeader ? "科组长数据导入模板.xlsx" : "复核员数据导入模板.xlsx";
+            String title = isHeader ? "科组长数据" : "复核员数据";
+            List<SubjectUser> list = new ArrayList<SubjectUser>();
+            list.add(new SubjectUser());
+            new ExportExcel(title, SubjectUser.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @Logging(menu = "科组长/查询员导入", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/subject/import", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject importFile(HttpServletRequest request, MultipartFile file, @RequestParam Boolean isHeader) {
+        ApiUser wu = RequestUtils.getApiUser(request);
+        try {
+            int successNum = 0;
+            int failureNum = 0;
+            StringBuilder failureMsg = new StringBuilder();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<SubjectUser> list = ei.getDataList(SubjectUser.class);
+
+            Map<String, User> userMap = new HashMap<String, User>();
+            List<SubjectUser> saveList = new ArrayList<SubjectUser>();
+            Integer schoolId = wu.getUser().getSchoolId();
+            for (SubjectUser subjectUser : list) {
+                if (org.apache.commons.lang.StringUtils.isBlank(subjectUser.getLoginName())) {
+                    failureMsg.append("<br/> " + subjectUser.getLoginName() + "," + subjectUser.getSubjectCode()
+                            + MarkerExcelError.NAMENULL.getName());
+                    failureNum++;
+                    continue;
+                }
+                User user = userMap.get(subjectUser.getLoginName().trim());
+                if (user == null) {
+                    user = userService.findByLoginName(subjectUser.getLoginName().trim());
+                    if (user != null && !schoolId.equals(user.getSchoolId())) {
+                        failureMsg.append("<br/>用户名 " + subjectUser.getLoginName() + "," + subjectUser.getSubjectCode()
+                                + MarkerExcelError.NAMEUSED.getName());
+                        failureNum++;
+                        continue;
+                    }
+                    if (user != null && isHeader && !user.getRole().equals(Role.SUBJECT_HEADER)) {
+                        failureMsg.append(
+                                "<br/>用户名 " + subjectUser.getLoginName() + MarkerExcelError.NAMENOTHEADER.getName());
+                        failureNum++;
+                        continue;
+                    }
+                    if (user != null && !isHeader && !user.getRole().equals(Role.INSPECTOR)) {
+                        failureMsg.append(
+                                "<br/>用户名 " + subjectUser.getLoginName() + MarkerExcelError.NAMENOTHEADER.getName());
+                        failureNum++;
+                        continue;
+                    }
+                    if (user == null) {
+                        user = getUser(userMap, subjectUser.getLoginName().trim(), schoolId, isHeader);
+                    }
+                }
+                subjectUser = getSubjectUser(subjectUser.getSubjectCode(), user.getId());
+                saveList.add(subjectUser);
+            }
+            successNum = subjectUserService.batchSave(saveList);
+            if (failureNum > 0) {
+                failureMsg.insert(0, ",失败 " + failureNum + " 条记录!");
+            }
+            String message = "已成功导入 " + successNum + " 条 ";
+            RequestUtils.setLog(request, message);
+            return result(message);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入用户失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    private SubjectUser getSubjectUser(String subjectCode, Integer userId) {
+        SubjectUser subjectUser = subjectUserService.findBySubjectCodeAndUserId(subjectCode, userId);
+        if (subjectUser == null) {
+            subjectUser = new SubjectUser();
+            subjectUser.setSubjectCode(subjectCode);
+            subjectUser.setUserId(userId);
+        }
+        return subjectUser;
+    }
+
+    private User getUser(Map<String, User> saveMap, String loginName, Integer schoolId, boolean isHeader) {
+        User user = saveMap.get(loginName);
+        if (user == null) {
+            user = new User();
+            user.setLoginName(loginName);
+            user.setName(loginName);
+            user.setPassword(EncryptUtils.md5(USER_PASSWORD));
+            user.setRole(isHeader ? Role.SUBJECT_HEADER : Role.INSPECTOR);
+            user.setSchoolId(schoolId);
+            user.setEnable(true);
+            user.setCreatedTime(new Date());
+            user.setSource(UserSource.INTERNAL);
+            userService.save(user);
+            saveMap.put(loginName, user);
+        }
+        return user;
+    }
+
+    @RequestMapping(value = "/class/template")
+    public void importFileTemplate(HttpServletResponse response) {
+        try {
+            String fileName = "评卷员班级导入模板.xlsx";
+            List<MarkerClass> list = new ArrayList<MarkerClass>();
+            list.add(new MarkerClass());
+            new ExportExcel("评卷员班级数据", MarkerClass.class, 2).setDataList(list).write(response, fileName).dispose();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入模板下载失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    @Logging(menu = "评卷员班级导入", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/class/import", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject importFile(HttpServletRequest request, MultipartFile file) {
+        ApiUser wu = RequestUtils.getApiUser(request);
+        try {
+            int successNum = 0;
+            int failureNum = 0;
+            StringBuilder failureMsg = new StringBuilder();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<MarkerClass> list = ei.getDataList(MarkerClass.class);
+            Map<String, User> userMap = new HashMap<String, User>();
+            List<MarkerClass> saveList = new ArrayList<MarkerClass>();
+            Map<Integer, List<String>> classMap = new HashMap<Integer, List<String>>();
+            Map<String, MarkerClass> markerClassMap = new HashMap<String, MarkerClass>();
+            Integer schoolId = wu.getUser().getSchoolId();
+            for (MarkerClass markerClass : list) {
+                if (markerClass.getExamId() == null) {
+                    failureMsg.append("<br/>用户名 " + markerClass.getLoginName() + MarkerExcelError.EXAMNULL.getName());
+                    failureNum++;
+                    continue;
+                }
+                if (org.apache.commons.lang.StringUtils.isBlank(markerClass.getClassName())) {
+                    failureMsg.append("<br/>用户名 " + markerClass.getLoginName() + MarkerExcelError.CLASSNULL.getName());
+                    failureNum++;
+                    continue;
+                }
+                List<String> classes = classMap.get(markerClass.getExamId());
+                if (classes == null) {
+                    classes = studentService.findDistinctClassName(markerClass.getExamId());
+                    classMap.put(markerClass.getExamId(), classes);
+                }
+                if (!classes.contains(markerClass.getClassName())) {
+                    failureMsg.append("<br/>考试 " + markerClass.getExamId() + " 班级 " + markerClass.getClassName()
+                            + MarkerExcelError.EXAMCLASSNULL.getName());
+                    failureNum++;
+                    continue;
+                }
+                if (org.apache.commons.lang.StringUtils.isBlank(markerClass.getLoginName())) {
+                    failureMsg.append("<br/> " + markerClass.getLoginName() + MarkerExcelError.NAMENULL.getName());
+                    failureNum++;
+                    continue;
+                }
+                User user = userMap.get(markerClass.getLoginName().trim());
+                if (user == null) {
+                    user = userService.findByLoginName(markerClass.getLoginName().trim());
+                    if (user != null && !schoolId.equals(user.getSchoolId())) {
+                        failureMsg
+                                .append("<br/>用户名 " + markerClass.getLoginName() + MarkerExcelError.NAMEUSED.getName());
+                        failureNum++;
+                        continue;
+                    }
+                    if (user != null && !user.getRole().equals(Role.MARKER)) {
+                        failureMsg.append(
+                                "<br/>用户名 " + markerClass.getLoginName() + MarkerExcelError.NAMENOTMARKER.getName());
+                        failureNum++;
+                        continue;
+                    }
+                    if (user == null) {
+                        user = getUser(userMap, markerClass.getLoginName().trim(), schoolId);
+                    }
+                }
+                markerClass = getMarkerClass(user.getId(), markerClass.getExamId(), markerClass.getClassName(),
+                        markerClassMap);
+                if (markerClass != null) {
+                    saveList.add(markerClass);
+                }
+            }
+            successNum = markerClassService.batchSave(saveList);
+            if (failureNum > 0) {
+                failureMsg.insert(0, ",失败 " + failureNum + " 条记录!");
+            }
+            String message = "已成功导入 " + successNum + " 条 ";
+            RequestUtils.setLog(request, message);
+            return result(message);
+        } catch (Exception e) {
+            log.error("Batch import user class error!", e.getMessage());
+            e.printStackTrace();
+            throw new StatusException("导入用户班级失败!失败信息:" + e.getMessage());
+        }
+    }
+
+    private MarkerClass getMarkerClass(Integer userId, Integer examId, String className,
+            Map<String, MarkerClass> markerClassMap) {
+        MarkerClass m = markerClassMap.get(userId + "_" + examId + "_" + className);
+        if (m != null) {
+            return null;
+        }
+        m = markerClassService.findByUserIdAndExamIdAndClassName(userId, examId, className);
+        if (m == null) {
+            m = new MarkerClass(userId, examId, className);
+        }
+        markerClassMap.put(userId + "_" + examId + "_" + className, m);
+        return m;
+    }
+
+    private User getUser(Map<String, User> saveMap, String loginName, Integer schoolId) {
+        User user = saveMap.get(loginName);
+        if (user == null) {
+            user = new User();
+            user.setLoginName(loginName);
+            user.setName(loginName);
+            user.setPassword(EncryptUtils.md5(USER_PASSWORD));
+            user.setRole(Role.MARKER);
+            user.setSchoolId(schoolId);
+            user.setEnable(true);
+            user.setCreatedTime(new Date());
+            user.setSource(UserSource.INTERNAL);
+            userService.save(user);
+            saveMap.put(loginName, user);
+        }
+        return user;
+    }
+}

+ 34 - 4
stmms-web/src/main/java/cn/com/qmth/stmms/common/domain/ApiUser.java

@@ -4,16 +4,20 @@ import cn.com.qmth.stmms.biz.school.model.School;
 import cn.com.qmth.stmms.biz.user.model.User;
 import cn.com.qmth.stmms.common.enums.Role;
 
+import java.util.Set;
+
 public class ApiUser {
 
     private Integer schoolId;
 
-    private User userData;
+    private User user;
 
     private School schoolData;
 
     private Role role;
 
+    private Set<String> subjectCodeSet;
+
     public ApiUser(School school) {
         schoolData = school;
         schoolId = school.getId();
@@ -21,7 +25,7 @@ public class ApiUser {
     }
 
     public ApiUser(User user) {
-        userData = user;
+        this.user = user;
         schoolId = user.getSchoolId();
         role = user.getRole();
     }
@@ -34,12 +38,38 @@ public class ApiUser {
         return role;
     }
 
-    public User getUserData() {
-        return userData;
+    public User getUser() {
+        return user;
     }
 
     public School getSchoolData() {
         return schoolData;
     }
 
+    public Set<String> getSubjectCodeSet() {
+        return subjectCodeSet;
+    }
+
+    public void setSubjectCodeSet(Set<String> subjectCodeSet) {
+        this.subjectCodeSet = subjectCodeSet;
+    }
+    public boolean isSubjectHeader() {
+        return (role == Role.SUBJECT_HEADER) || (role == Role.COLLEGE_ADMIN);
+    }
+
+    public boolean isSchoolAdmin() {
+        return role == Role.SCHOOL_ADMIN;
+    }
+
+    public boolean isSchoolViewer() {
+        return role == Role.SCHOOL_VIEWER;
+    }
+
+    public boolean isMarker() {
+        return role == Role.MARKER;
+    }
+
+    public boolean isInspector() {
+        return role == Role.INSPECTOR;
+    }
 }

+ 6 - 5
stmms-web/src/main/java/cn/com/qmth/stmms/common/interceptor/LogInterceptor.java

@@ -5,6 +5,7 @@ import java.util.Date;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import cn.com.qmth.stmms.common.enums.Role;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -66,13 +67,13 @@ public class LogInterceptor extends HandlerInterceptorAdapter {
         log.setCreateTime(new Date());
         log.setMenu(logging.menu());
         log.setType(logging.type());
-        log.setMarker(false);
-        log.setOperatorId(wu.getUserData().getId());
-        log.setIpAddress(wu.getUserData().getLastLoginIp());
-        log.setLoginName(wu.getUserData().getLoginName());
+        log.setMarker((wu.getUser().getRole().equals(Role.MARKER)));
+        log.setOperatorId(wu.getUser().getId());
+        log.setIpAddress(wu.getUser().getLastLoginIp());
+        log.setLoginName(wu.getUser().getLoginName());
+        log.setSchoolId(wu.getSchoolId());
         int examId = SessionExamUtils.getExamId(request);
         log.setExamId(examId > 0 ? examId : null);
-        log.setSchoolId(wu.getSchoolId());
         log.setDescription(RequestUtils.getLog(request));
         logService.save(log);
     }