package cn.com.qmth.scancentral.controller.admin; import java.io.File; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; 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.RestController; import org.springframework.web.multipart.MultipartFile; import com.qmth.boot.api.annotation.Aac; import com.qmth.boot.api.constant.ApiConstant; import com.qmth.boot.core.collection.PageResult; import com.qmth.boot.core.concurrent.service.ConcurrentService; import com.qmth.boot.core.exception.ParameterException; import com.qmth.boot.core.exception.ReentrantException; import com.qmth.boot.core.exception.StatusException; import com.qmth.boot.tools.excel.ExcelWriter; import com.qmth.boot.tools.excel.enums.ExcelType; import com.qmth.boot.tools.excel.model.DataMap; import com.qmth.boot.tools.iterator.PageListIterator; import com.qmth.boot.tools.iterator.SingletonIterator; import com.qmth.boot.tools.uuid.FastUUID; import cn.com.qmth.scancentral.bean.AbsentQueryDomain; import cn.com.qmth.scancentral.bean.AnswerQueryDomain; import cn.com.qmth.scancentral.controller.BaseController; import cn.com.qmth.scancentral.entity.ExamEntity; import cn.com.qmth.scancentral.entity.SubjectEntity; import cn.com.qmth.scancentral.enums.AsyncTaskStatus; import cn.com.qmth.scancentral.enums.ExamStatus; import cn.com.qmth.scancentral.enums.ExamStatusCheckMode; import cn.com.qmth.scancentral.enums.GroupType; import cn.com.qmth.scancentral.enums.LockType; import cn.com.qmth.scancentral.exception.ParameterExceptions; import cn.com.qmth.scancentral.model.ManualAbsentImportDTO; import cn.com.qmth.scancentral.service.AsyncTaskService; import cn.com.qmth.scancentral.service.ExamService; import cn.com.qmth.scancentral.service.StudentService; import cn.com.qmth.scancentral.service.SubjectService; import cn.com.qmth.scancentral.task.thread.ExamStatusImportThread; import cn.com.qmth.scancentral.task.thread.ExamStatusResetThread; import cn.com.qmth.scancentral.util.ResouceUtil; import cn.com.qmth.scancentral.vo.AbsentInfoVo; import cn.com.qmth.scancentral.vo.AbsentManualImportVo; import cn.com.qmth.scancentral.vo.AbsentQueryVo; import cn.com.qmth.scancentral.vo.ExamStatusSaveVo; import cn.com.qmth.scancentral.vo.UpdateTimeVo; import cn.com.qmth.scancentral.vo.answerquery.AnswerQueryParam; import cn.com.qmth.scancentral.vo.answerquery.AnswerQueryVo; import cn.com.qmth.scancentral.vo.asynctask.ExamStatusImportTaskVo; import cn.com.qmth.scancentral.vo.asynctask.ExamStatusResetTaskVo; import cn.com.qmth.scancentral.vo.student.StudentExamRoomVo; import cn.com.qmth.scancentral.vo.student.StudentVo; import cn.com.qmth.scancentral.vo.subject.TaskIdVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @RestController @Api(tags = "缺考接口") @RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/admin/check") @Aac(strict = false, auth = true) public class CheckController extends BaseController { @Autowired private StudentService studentService; @Resource private ConcurrentService concurrentService; @Autowired private ExamService examService; @Autowired private AsyncTaskExecutor taskExecutor; @Autowired private AsyncTaskService asyncTaskService; @Autowired private SubjectService subjectService; @ApiOperation(value = "缺考数据汇总") @PostMapping("absent/info") public AbsentInfoVo absentInfo(@RequestParam Long examId, @RequestParam(required = false) GroupType groupType, @RequestParam(required = false) String groupName) { return studentService.absentInfo(examId, groupType, groupName); } @ApiOperation(value = "查询缺考数据") @RequestMapping(value = "absent/query", method = RequestMethod.POST) public PageResult absentQuery(@Validated AbsentQueryDomain query) { return studentService.absentQuery(query); } @ApiOperation(value = "查询缺考数据标识") @RequestMapping(value = "absent/summary", method = RequestMethod.POST) public List absentSummary(@Validated AbsentQueryDomain query) { return studentService.absentSummary(query); } @ApiOperation(value = "导出缺考数据") @RequestMapping(value = "absent/export", method = RequestMethod.POST) public void absentExport(@Validated AbsentQueryDomain query, HttpServletResponse response) throws IOException { String fileName = URLEncoder.encode("缺考数据", "UTF-8"); response.setHeader("Content-Disposition", "inline; filename=" + fileName + ".xlsx"); response.setContentType("application/vnd.ms-excel"); Map subjectMap = new HashMap<>(); PageListIterator iterator = new PageListIterator(100) { @Override public Collection getPageList(int pageNumber, int pageSize) { query.setPageNumber(pageNumber); query.setPageSize(pageSize); List list = studentService.absentExportList(query); List ret = new ArrayList<>(); if (CollectionUtils.isNotEmpty(list)) { for (AbsentQueryVo vo : list) { DataMap map = new DataMap(); if (GroupType.SUBJECT.equals(query.getGroupType())) { map.put("科目代码", vo.getGroupName()); map.put("科目名称", getSubjectName(subjectMap, vo.getGroupName(), query.getExamId())); } else { map.put(query.getGroupType().getName(), vo.getGroupName()); } map.put("考生总数", getStringVal(vo.getTotalCount())); map.put("已扫描", getStringVal(vo.getScannedCount())); map.put("未扫描", getStringVal(vo.getUnexistCount())); map.put("指定缺考", getStringVal(vo.getManualAbsentCount())); map.put("识别缺考", getStringVal(vo.getOmrAbsentCount())); map.put("缺考异常", getStringVal(vo.getAbsentSuspectCount())); ret.add(map); } } return ret; } }; String[] head; if (GroupType.SUBJECT.equals(query.getGroupType())) { head = new String[] { "科目代码", "科目名称", "考生总数", "已扫描", "未扫描", "指定缺考", "识别缺考", "缺考异常" }; } else { head = new String[] { query.getGroupType().getName(), "考生总数", "已扫描", "未扫描", "指定缺考", "识别缺考", "缺考异常" }; } ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX); writer.writeDataMaps("缺考核对", null, head, iterator); writer.output(response.getOutputStream()); } private String getSubjectName(Map subMap, String subjectCode, Long examId) { SubjectEntity subject = subMap.get(subjectCode); if (subMap.get(subjectCode) == null) { subject = subjectService.findByExamIdAndCode(examId, subjectCode); if (subject == null) { throw new StatusException("未找到科目信息:" + subjectCode); } subMap.put(subjectCode, subject); } return subject.getName(); } private String getStringVal(Integer val) { if (val == null) { return "0"; } return val.toString(); } @PostMapping("absent/manual/import") @ApiOperation(value = "导入指定缺考名单") public AbsentManualImportVo absentManualImport(@RequestParam Long examId, @RequestParam MultipartFile file) { return studentService.absentManualImport(examId, file); } @ApiOperation(value = "下载导入指定缺考名单模板") @PostMapping("absent/manual/template") public void getImportTemplate() throws IOException { ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX); writer.writeObjects("指定缺考名单", null, ManualAbsentImportDTO.class, new SingletonIterator<>(new ManualAbsentImportDTO())); exportFile("指定缺考名单导入模板.xlsx", writer); } @ApiOperation(value = "更新为指定缺考") @PostMapping("absent/manual/update") public UpdateTimeVo absentManualUpdate(@RequestParam Long examId, @RequestParam String subjectCode, @RequestParam String examNumber) { return studentService.absentManualUpdate(examId, subjectCode, examNumber); } @ApiOperation(value = "取消缺考标记嫌疑") @PostMapping("absent/suspect/remove") public UpdateTimeVo absentSuspectRemove(@RequestParam Long examId, @RequestParam String subjectCode, @RequestParam String examNumber) { return studentService.absentSuspectUpdate(examId, subjectCode, examNumber, false); } @ApiOperation(value = "缺考校验列表") @RequestMapping(value = "exam-status/list", method = RequestMethod.POST) public PageResult examStatusList(@Validated AnswerQueryParam query) { query.setWithOmrDetail(true); return studentService.query(query); } @ApiOperation(value = "缺考校验按考生导出") @PostMapping(value = "exam-status/student/export") public void studentExport(@Validated AnswerQueryDomain query, HttpServletResponse response) throws IOException { String fileName = URLEncoder.encode("按考生导出", "UTF-8"); response.setHeader("Content-Disposition", "inline; filename=" + fileName + ".xlsx"); response.setContentType("application/vnd.ms-excel"); ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX); PageListIterator iterator = new PageListIterator(5000) { @Override public Collection getPageList(int pageNumber, int pageSize) { query.setPageNumber(pageNumber); query.setPageSize(pageSize); return studentService.studentExportList(query); } }; writer.writeObjects("按考生导出", null, StudentVo.class, iterator); writer.output(response.getOutputStream()); } @ApiOperation(value = "缺考校验按考场导出") @PostMapping(value = "exam-status/exam-room/export") public void studentExamRoomExport(@Validated AnswerQueryDomain query, HttpServletResponse response) throws IOException { String fileName = URLEncoder.encode("按考场导出", "UTF-8"); response.setHeader("Content-Disposition", "inline; filename=" + fileName + ".xlsx"); response.setContentType("application/vnd.ms-excel"); ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX); PageListIterator iterator = new PageListIterator(5000) { @Override public Collection getPageList(int pageNumber, int pageSize) { query.setPageNumber(pageNumber); query.setPageSize(pageSize); return studentService.studentExamRoomExportList(query); } }; writer.writeObjects("按考场导出", null, StudentExamRoomVo.class, iterator); writer.output(response.getOutputStream()); } @ApiOperation(value = "缺考校验提交") @RequestMapping(value = "exam-status/save", method = RequestMethod.POST) public ExamStatusSaveVo examStatusSave(@RequestParam Long id, @RequestParam ExamStatus examStatus) { studentService.updateExamStatus(id, examStatus); return ExamStatusSaveVo.create(examStatus); } @ApiOperation(value = "缺考校验导入模版下载") @PostMapping("exam-status/template") public void breachTemplate(HttpServletResponse response) { exportFile("缺考校验导入模板.txt", ResouceUtil.getStream("templates/absent-import.txt")); } @ApiOperation(value = "缺考校验导入") @RequestMapping(value = "exam-status/import", method = RequestMethod.POST) public TaskIdVo examStatusImportFile(@RequestParam Long examId, @RequestParam ExamStatusCheckMode mode, @RequestParam MultipartFile file) throws IOException { ExamEntity exam = examService.getById(examId); if (exam == null) { throw ParameterExceptions.EXAM_NOT_FOUND; } if (!concurrentService.isLocked(LockType.EXAM_STATUS_RESET + "-" + examId)) { if (!file.getOriginalFilename().toLowerCase().endsWith(".txt")) { throw new ParameterException("只能是txt文件"); } String taskId = FastUUID.get(); // 暂存临时文件 File temDir = new File("temp/" + taskId + "/"); temDir.mkdirs(); File txt = new File(temDir.getAbsolutePath() + "/data.txt"); try { file.transferTo(txt); List list = FileUtils.readLines(txt, "utf-8"); if (CollectionUtils.isEmpty(list) || list.size() <= 1) { throw new ParameterException("文件内容为空"); } ExamStatusImportTaskVo vo = new ExamStatusImportTaskVo(); vo.setTaskId(taskId); vo.setTotalCount(list.size() - 1); vo.setExamId(examId); vo.setMode(mode); vo.setStatus(AsyncTaskStatus.RUNNING); vo.setProgress(0.0); vo.setTempDir(temDir); vo.setFile(txt); taskExecutor.submit(new ExamStatusImportThread(vo, studentService, concurrentService)); asyncTaskService.addTask(vo); return TaskIdVo.create(vo.getTaskId()); } catch (Exception e) { throw new ParameterException("系统错误", e); } } else { throw new ParameterException("正在导入,请稍后再试"); } } // @ApiOperation(value = "缺考校验导入状态") // @RequestMapping(value = "exam-status/import/status", method = // RequestMethod.POST) // public Map examStatusImportStatus(@RequestParam Long // examId) { // ExamEntity exam = examService.getById(examId); // if (exam == null) { // throw ParameterExceptions.EXAM_NOT_FOUND; // } // // Map result = new HashMap(); // result.put("synching", // concurrentService.isLocked(LockType.EXAM_STATUS_RESET + "-" + examId)); // return result; // } @ApiOperation(value = "缺考校验重新生成") @RequestMapping(value = "exam-status/reset", method = RequestMethod.POST) public TaskIdVo examStatusReset(@RequestParam Long examId, @RequestParam Integer examNumberFillCount) { ExamEntity exam = examService.getById(examId); if (exam == null) { throw ParameterExceptions.EXAM_NOT_FOUND; } if (!concurrentService.isLocked(LockType.EXAM_STATUS_RESET + "-" + examId)) { String taskId = FastUUID.get(); try { int total = studentService.getCountByExam(examId); if (total == 0) { throw new ParameterException("没有要处理的数据"); } ExamStatusResetTaskVo vo = new ExamStatusResetTaskVo(); vo.setTaskId(taskId); vo.setTotalCount(total); vo.setExamId(examId); vo.setExamNumberFillCount(examNumberFillCount); vo.setStatus(AsyncTaskStatus.RUNNING); vo.setProgress(0.0); taskExecutor.submit(new ExamStatusResetThread(vo, studentService, concurrentService)); asyncTaskService.addTask(vo); return TaskIdVo.create(vo.getTaskId()); } catch (Exception e) { throw new ParameterException("系统错误", e); } } else { throw new ReentrantException("正在重新生成,请稍后再试"); } } // @ApiOperation(value = "缺考校验重新生成状态") // @RequestMapping(value = "exam-status/reset/status", method = // RequestMethod.POST) // public Map examStatusResetStatus(@RequestParam Long // examId) { // ExamEntity exam = examService.getById(examId); // if (exam == null) { // throw ParameterExceptions.EXAM_NOT_FOUND; // } // // Map result = new HashMap(); // result.put("synching", // concurrentService.isLocked(LockType.EXAM_STATUS_RESET + "-" + examId)); // return result; // } @ApiOperation(value = "缺考校验查询概要") @RequestMapping(value = "exam-status/summary", method = RequestMethod.POST) public List examStatusSummary(@Validated AnswerQueryDomain query) { return studentService.summary(query); } }