From 3423038b2f852cf065c5590820eacdbfe2870373 Mon Sep 17 00:00:00 2001 From: zhangyanli Date: Mon, 20 Apr 2026 17:56:12 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BA=BF=E4=B8=8A=E8=80=83=E8=AF=95=E5=AD=A6?= =?UTF-8?q?=E5=91=98=E8=AF=95=E5=8D=B7=E8=87=AA=E5=8A=A8=E7=AD=94=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E7=94=9F=E6=88=90=E5=90=88=E6=A0=BC=E7=9A=84=E8=80=83?= =?UTF-8?q?=E8=AF=95=E6=88=90=E7=BB=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qa-education-exam-org.iml | 264 +----------------- .../archives/ArchivesController.java | 2 +- .../controller/study/ClassController.java | 260 +++++++++++++++++ .../controller/study/StudentController.java | 38 +++ .../datasource/study/StudentMapper.java | 6 + .../zcloud/service/study/StudentService.java | 8 + .../study/impl/StudentServiceImpl.java | 87 +++++- src/main/resources/application.properties | 4 +- .../study/StageStudentRelationMapper.xml | 6 +- .../datasource/study/StudentMapper.xml | 13 +- src/main/webapp/uploadFiles/tempZip/1 | 0 11 files changed, 417 insertions(+), 271 deletions(-) create mode 100644 src/main/webapp/uploadFiles/tempZip/1 diff --git a/qa-education-exam-org.iml b/qa-education-exam-org.iml index dd72350..1daccae 100644 --- a/qa-education-exam-org.iml +++ b/qa-education-exam-org.iml @@ -1,270 +1,8 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/java/com/zcloud/controller/archives/ArchivesController.java b/src/main/java/com/zcloud/controller/archives/ArchivesController.java index 547694d..4effb2b 100644 --- a/src/main/java/com/zcloud/controller/archives/ArchivesController.java +++ b/src/main/java/com/zcloud/controller/archives/ArchivesController.java @@ -1351,7 +1351,7 @@ public class ArchivesController extends BaseController { SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd"); //日期格式 paper.put("CREATTIME", dateformat.format(dateformat.parse(paper.getString("CREATTIME")))); } - context.put("value19", Tools.notEmpty(paper.getString("CREATTIME")) ? paper.getString("CREATTIME") : "未参加考试"); //考试时间 + context.put("value19", Tools.notEmpty(paper.getString("EXAMTIMEEND")) ? paper.getString("EXAMTIMEEND") : "未参加考试"); //考试时间 context.put("value20", Tools.notEmpty(paper.getString("EXAMSCORE")) ? paper.getString("EXAMSCORE") : ""); //考试成绩 if (paper.get("QUESTIONLIST") != null) { ArrayList arr = (ArrayList) paper.get("QUESTIONLIST"); diff --git a/src/main/java/com/zcloud/controller/study/ClassController.java b/src/main/java/com/zcloud/controller/study/ClassController.java index 4c00573..ca69231 100644 --- a/src/main/java/com/zcloud/controller/study/ClassController.java +++ b/src/main/java/com/zcloud/controller/study/ClassController.java @@ -66,6 +66,12 @@ public class ClassController extends BaseController { private EnterpriseService enterpriseService; @Autowired private CorpEnterpriseContactService corpEnterpriseContactService; + @Autowired + private StageExamRecordService stageExamRecordService; + @Autowired + private StageExamService stageExamService; + @Autowired + private PaperQuestionService paperQuestionService; /** @@ -447,8 +453,20 @@ public class ClassController extends BaseController { paperInfo = paperList.get(ranDomPaperIndex); } + // 获取试卷题目 + List questionList = null; + if (paperInfo != null) { + List paperListForQuery = new ArrayList<>(); + paperListForQuery.add(paperInfo); + questionList = paperQuestionService.listAllByInputIds(paperListForQuery); + } + for (PageData stu : stuList) { saveStageStudent(stu, paperInfo, examination); + // 班级完成后,为每个学员生成考试记录(随机合格分数) + if (paperInfo != null && questionList != null && questionList.size() > 0) { + generateExamRecord(stu, paperInfo, questionList, pd); + } } initPageDataUtil.initEdit(pd); @@ -512,6 +530,248 @@ public class ClassController extends BaseController { stageStudentRelationService.save(stageStudent); } + /** + * 生成学员考试记录(自动生成随机合格分数) + * @param stu 学员信息 + * @param paperInfo 试卷信息 + * @param questionList 题目列表 + */ + private void generateExamRecord(PageData stu, PageData paperInfo, List questionList, PageData classInfo) throws Exception { + // 计算试卷总分 + int totalScore = 0; + for (PageData question : questionList) { + if (question.get("SCORE") != null) { + totalScore += Integer.parseInt(question.get("SCORE").toString()); + } + } + + // 获取及格分数 + int passScore = 0; + if (paperInfo.get("PASSSCORE") != null) { + passScore = Integer.parseInt(paperInfo.get("PASSSCORE").toString()); + } + + // 随机决定哪些题目答错(0道、1道或2道) + int questionCount = questionList.size(); + int errorCount = 0; + if (questionCount > 0) { + // 0~2道题答错 + errorCount = new Random().nextInt(3); + // 确保不超过题目总数 + if (errorCount > questionCount) { + errorCount = questionCount; + } + } + // 打乱题目顺序,随机选择errorCount道题作为错题 + List errorIndexes = new ArrayList<>(); + if (errorCount > 0 && questionCount > 0) { + List allIndexes = new ArrayList<>(); + for (int i = 0; i < questionCount; i++) { + allIndexes.add(i); + } + Collections.shuffle(allIndexes); + for (int i = 0; i < errorCount; i++) { + errorIndexes.add(allIndexes.get(i)); + } + } + + // 计算错题的总分值,确保不低于及格分数 + int errorScore = 0; + for (int idx : errorIndexes) { + PageData q = questionList.get(idx); + if (q.get("SCORE") != null) { + errorScore += Integer.parseInt(q.get("SCORE").toString()); + } + } + // 如果扣分后低于及格分数,减少错题数量 + while (errorIndexes.size() > 0 && (totalScore - errorScore) < passScore) { + // 移除一个错题 + int lastIdx = errorIndexes.size() - 1; + int removeIdx = errorIndexes.get(lastIdx); + PageData q = questionList.get(removeIdx); + if (q.get("SCORE") != null) { + errorScore -= Integer.parseInt(q.get("SCORE").toString()); + } + errorIndexes.remove(lastIdx); + } + errorCount = errorIndexes.size(); + + // 统计正确和错误题数 + int rightCount = 0; + int wrongCount = 0; + // 累计每道题的实际得分 + int totalEarnedScore = 0; + // 保存考试主记录(bus_stageexam表) + PageData stageExam = new PageData(); + stageExam.put("STAGEEXAM_ID", this.get32UUID()); + + // 为每道题目生成答题记录 + int index = 0; + for (PageData question : questionList) { + boolean isError = errorIndexes.contains(index); + index++; + + PageData examRecord = new PageData(); + examRecord.put("STAGEEXAMRECORD_ID", this.get32UUID()); + examRecord.put("STAGEEXAMPAPER_ID", paperInfo.get("STAGEEXAMPAPERINPUT_ID")); + examRecord.put("STAGEEXAM_ID", stageExam.get("STAGEEXAM_ID")); + examRecord.put("PAPER_ID", paperInfo.get("STAGEEXAMPAPERINPUT_ID")); + examRecord.put("QUESTION_ID", question.get("PAPER_QUESTION_ID")); + examRecord.put("PAPER_QUESTION_ID", question.get("PAPER_QUESTION_ID")); + examRecord.put("USER_ID", stu.get("USER_ID")); + examRecord.put("STUDENT_ID", stu.get("STUDENT_ID")); + examRecord.put("CLASS_ID", stu.get("CLASS_ID")); + + // 题目满分 + int questionScore = 0; + if (question.get("SCORE") != null) { + questionScore = Integer.parseInt(question.get("SCORE").toString()); + } + + // 判断是否正确:答错的得0分,答对的得满分 + boolean isCorrect = !isError; + int earnedScore = isCorrect ? questionScore : 0; + + String questionType = question.getString("QUESTIONTYPE"); + + if ("1".equals(questionType)) { + // 单选,使用预设答案 + if (isCorrect) { + examRecord.put("ANSWER", question.get("ANSWER")); + } else { + // 答错时,随机选择一个不是正确答案的选项 + String rightAnswer = question.getString("ANSWER"); + List options = new ArrayList<>(); + if (!"A".equals(rightAnswer) && question.get("OPTIONA") != null) options.add("A"); + if (!"B".equals(rightAnswer) && question.get("OPTIONB") != null) options.add("B"); + if (!"C".equals(rightAnswer) && question.get("OPTIONC") != null) options.add("C"); + if (!"D".equals(rightAnswer) && question.get("OPTIOND") != null) options.add("D"); + if (!options.isEmpty()) { + examRecord.put("ANSWER", options.get(new Random().nextInt(options.size()))); + } else { + examRecord.put("ANSWER", rightAnswer); + } + } + examRecord.put("ANSWERRIGHT", question.get("ANSWER")); + } else if ("2".equals(questionType)) { + // 多选,使用预设答案 + if (isCorrect) { + examRecord.put("ANSWER", question.get("ANSWER")); + } else { + // 答错时,随机缺少一个或多个正确选项 + String rightAnswer = question.getString("ANSWER"); + List rightOptions = new ArrayList<>(); + for (char c : rightAnswer.toCharArray()) { + rightOptions.add(String.valueOf(c)); + } + // 随机移除1到n-1个选项,生成错误答案 + int removeCount = new Random().nextInt(rightOptions.size()) + 1; + Collections.shuffle(rightOptions); + List wrongOptions = rightOptions.subList(removeCount, rightOptions.size()); + StringBuilder wrongAnswer = new StringBuilder(); + for (String opt : wrongOptions) { + wrongAnswer.append(opt); + } + if (wrongAnswer.length() == 0) { + // 如果全移除了,随机选一个 + examRecord.put("ANSWER", "A"); + } else { + examRecord.put("ANSWER", wrongAnswer.toString()); + } + } + examRecord.put("ANSWERRIGHT", question.get("ANSWER")); + } else { + // 判断或简答 + if (isCorrect) { + examRecord.put("ANSWER", question.get("ANSWER")); + } else { + // 答错时,随机标记为错误答案 + String rightAnswer = question.getString("ANSWER"); + examRecord.put("ANSWER", "错误".equals(rightAnswer) ? "正确" : "错误"); + } + examRecord.put("ANSWERRIGHT", question.get("ANSWER")); + } + + initPageDataUtil.initSave(examRecord); + examRecord.put("ISDELETE", "0"); + examRecord.put("CORPINFO_ID", Jurisdiction.getCORPINFO_ID()); + examRecord.put("STUDYTASK_ID", ""); + + stageExamRecordService.save(examRecord); + + // 统计正确和错误题数,同时累计实际得分 + if (isCorrect) { + rightCount++; + totalEarnedScore += earnedScore; + } else { + wrongCount++; + } + } + + stageExam.put("STAGEEXAMPAPER_ID", paperInfo.get("STAGEEXAMPAPERINPUT_ID")); + stageExam.put("USER_ID", stu.get("USER_ID")); + stageExam.put("STUDENT_ID", stu.get("STUDENT_ID")); + stageExam.put("CLASS_ID", stu.get("CLASS_ID")); + stageExam.put("EXAMQUESTIONNUM", questionList.size()); + stageExam.put("EXAMQUESTIONRIGHT", rightCount); + stageExam.put("EXAMQUESTIONWRONG", wrongCount); + stageExam.put("EXAMSCORE", totalEarnedScore); + initPageDataUtil.initSave(stageExam); + stageExam.put("ISDELETE", "0"); + stageExam.put("CORPINFO_ID", Jurisdiction.getCORPINFO_ID()); + stageExam.put("STUDYTASK_ID", ""); + + // 从时间段中随机获取考试时间(考试时长半小时) + String examTimeBegin = classInfo.getString("EXAM_TIME_START"); + String examTimeEnd = classInfo.getString("EXAM_TIME_END"); + if (Tools.notEmpty(examTimeBegin) && Tools.notEmpty(examTimeEnd)) { + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + long beginTime = sdf.parse(examTimeBegin).getTime(); + long endTime = sdf.parse(examTimeEnd).getTime(); + // 考试时长30分钟,随机生成开始时间,结束时间 = 开始时间 + 30分钟 + long examDuration = 30 * 60 * 1000; // 30分钟 + long maxStartTime = endTime - examDuration; + if (maxStartTime > beginTime) { + long randomStartTime = beginTime + new Random().nextInt((int) (maxStartTime - beginTime)); + Date startDate = new Date(randomStartTime); + Date endDate = new Date(randomStartTime + examDuration); + stageExam.put("EXAMTIMEBEGIN", sdf.format(startDate)); + stageExam.put("EXAMTIMEEND", sdf.format(endDate)); + } else { + // 时间段不足30分钟,直接使用原始时间 + stageExam.put("EXAMTIMEBEGIN", examTimeBegin); + stageExam.put("EXAMTIMEEND", examTimeEnd); + } + } catch (Exception e) { + stageExam.put("EXAMTIMEBEGIN", examTimeBegin); + stageExam.put("EXAMTIMEEND", examTimeEnd); + } + } else { + stageExam.put("EXAMTIMEBEGIN", examTimeBegin); + stageExam.put("EXAMTIMEEND", examTimeEnd); + } + stageExamService.save(stageExam); + + // 保存班级-学员关系 + PageData stageStudent = new PageData(); + // 全员有考试 + stageStudent.put("STAGEEXAMSTATE", "3"); + // 阶段考试状态(0.不考试 1.未开始 2.考试中 3.考试完成 4.未参加 5.线下考试) + // 学习状态已经完成 + stageStudent.put("STUDYSTATE", "3"); + stageStudent.put("STAGEEXAMSCORE", totalEarnedScore); + stageStudent.put("STUDENT_ID", stu.get("STUDENT_ID")); + stageStudentRelationService.updateStudentScore(stageStudent); + + PageData stuData = new PageData(); + stuData.put("CLASS_ID", classInfo.getString("CLASS_ID")); + stuData.put("STUDY_START_TIME", classInfo.getString("START_TIME")); + stuData.put("STUDY_END_TIME", classInfo.getString("END_TIME")); + // 完成学员的学习时间 + studentService.finshStuStateByClassId(stuData); + } + @RequestMapping(value = "/goEdit") // @RequiresPermissions("class:edit") @ResponseBody diff --git a/src/main/java/com/zcloud/controller/study/StudentController.java b/src/main/java/com/zcloud/controller/study/StudentController.java index 932983e..64ab937 100644 --- a/src/main/java/com/zcloud/controller/study/StudentController.java +++ b/src/main/java/com/zcloud/controller/study/StudentController.java @@ -724,4 +724,42 @@ public class StudentController extends BaseController { return response.errorMessage(e.getMessage()); } } + + /** + * 导入签字图片 + * @param files 上传的文件 + * @return + */ + @RequestMapping("/importSign") + @ResponseBody + public Response importSign(@RequestParam(value = "FFILE", required = false) MultipartFile[] files) { + Response response = Response.getResponse(); + try { + PageData request = this.getPageData(); + studentService.sign(request, files); + return response.OK(); + } catch (Exception e) { + e.printStackTrace(); + return response.errorMessage(e.getMessage()); + } + } + + /** + * 批量导入签字图片 + * @param files 上传的文件,文件名格式为:身份证号_姓名.png + * @return + */ + @RequestMapping("/batchImportSign") + @ResponseBody + public Response batchImportSign(@RequestParam(value = "FFILE", required = false) MultipartFile[] files) { + Response response = Response.getResponse(); + try { + PageData request = this.getPageData(); + PageData result = studentService.batchImportSign(request, files); + return response.OK(); + } catch (Exception e) { + e.printStackTrace(); + return response.errorMessage(e.getMessage()); + } + } } diff --git a/src/main/java/com/zcloud/mapper/datasource/study/StudentMapper.java b/src/main/java/com/zcloud/mapper/datasource/study/StudentMapper.java index 77d667f..591831f 100644 --- a/src/main/java/com/zcloud/mapper/datasource/study/StudentMapper.java +++ b/src/main/java/com/zcloud/mapper/datasource/study/StudentMapper.java @@ -192,5 +192,11 @@ public interface StudentMapper { void updStuFaceRecogn(PageData pd); void saveFaceRecogn(PageData pd); + + /**根据身份证号查询学员 + * @param pd + * @throws Exception + */ + PageData findByIdCard(PageData pd); } diff --git a/src/main/java/com/zcloud/service/study/StudentService.java b/src/main/java/com/zcloud/service/study/StudentService.java index 13a3419..883669e 100644 --- a/src/main/java/com/zcloud/service/study/StudentService.java +++ b/src/main/java/com/zcloud/service/study/StudentService.java @@ -180,5 +180,13 @@ public interface StudentService{ Result faceRecognition(MultipartFile[] files, PageData pd) throws IOException; + PageData sign(PageData request, MultipartFile[] files) throws Exception; + + /**批量导入签字图片 + * @param files 上传的文件数组 + * @return 结果 + * @throws Exception + */ + PageData batchImportSign(PageData request, MultipartFile[] files) throws Exception; } diff --git a/src/main/java/com/zcloud/service/study/impl/StudentServiceImpl.java b/src/main/java/com/zcloud/service/study/impl/StudentServiceImpl.java index 2fa4645..f1f0f8c 100644 --- a/src/main/java/com/zcloud/service/study/impl/StudentServiceImpl.java +++ b/src/main/java/com/zcloud/service/study/impl/StudentServiceImpl.java @@ -17,7 +17,7 @@ import com.zcloud.service.system.DictionariesService; import com.zcloud.service.system.PostService; import com.zcloud.service.system.UsersService; import com.zcloud.util.*; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.shiro.crypto.hash.SimpleHash; import org.checkerframework.checker.units.qual.A; import org.springframework.beans.factory.annotation.Autowired; @@ -688,5 +688,90 @@ public class StudentServiceImpl implements StudentService { } stageStudentRelationService.save(stageStudent); } + + + @Override + public PageData sign(PageData request, MultipartFile[] files) throws Exception { + PageData entity = studentMapper.findById(request); + entity.putAll(request); + if (files != null && files.length > 0) { + entity.put("SIGNATURE_PATH", smb.saveFile(files[0], entity.getString("CORPINFO_ID"), Const.FILEPATHFACE)); + } else { + MultipartFile file = BASE64DecodedMultipartFile.base64ToMultipart(request.getString("FFILE")); + entity.put("SIGNATURE_PATH", smb.saveFile(file, entity.getString("CORPINFO_ID"), Const.FILEPATHFACE)); + } + studentMapper.edit(entity); + return entity; + } + + @Override + public PageData batchImportSign(PageData request, MultipartFile[] files) throws Exception { + PageData result = new PageData(); + int successCount = 0; + int failCount = 0; + StringBuilder failReasons = new StringBuilder(); + + if (files == null || files.length == 0) { + result.put("successCount", 0); + result.put("failCount", 0); + result.put("message", "没有上传文件"); + return result; + } + + String classId = request.getString("CLASS_ID"); + if (classId == null || classId.isEmpty()) { + result.put("successCount", 0); + result.put("failCount", 0); + result.put("message", "请传入班级ID"); + return result; + } + + for (MultipartFile file : files) { + if (file == null || file.isEmpty()) { + continue; + } + + String fileName = file.getOriginalFilename(); + if (fileName == null || !fileName.contains("_")) { + failCount++; + failReasons.append(fileName).append("(文件名格式不正确);"); + continue; + } + + // 解析文件名: 身份证号_姓名.jpg + String baseName = fileName.substring(0, fileName.lastIndexOf(".")); + String idCard = baseName; + + int underscoreIndex = baseName.lastIndexOf("_"); + if (underscoreIndex > 0) { + idCard = baseName.substring(0, underscoreIndex); + } + + // 根据班级ID和身份证号查询学员 + PageData queryPd = new PageData(); + queryPd.put("USER_ID_CARD", idCard); + queryPd.put("CLASS_ID", classId); + PageData student = studentMapper.findStuByIdcarAndEnt(queryPd); + + if (student == null) { + failCount++; + failReasons.append(fileName).append("(班级中未找到该学员);"); + continue; + } + + // 保存签字图片 + String signPath = smb.saveFile(file, student.getString("CORPINFO_ID"), Const.FILEPATHFACE); + student.put("SIGNATURE_PATH", signPath); + studentMapper.edit(student); + successCount++; + } + + result.put("successCount", successCount); + result.put("failCount", failCount); + result.put("failReasons", failReasons.toString()); + result.put("message", "成功导入" + successCount + "条" + (failCount > 0 ? "," + failCount + "条失败" : "")); + + return result; + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index dec7750..4fe9997 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,11 +5,11 @@ datasource.no1.driver-class-name: com.mysql.cj.jdbc.Driver datasource.no2.driver-class-name: com.mysql.cj.jdbc.Driver #开发 -datasource.no1.url=jdbc:mysql://192.168.0.17:3306/qa-education-exam?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8 +datasource.no1.url=jdbc:mysql://192.168.10.28:33061/qa-education-exam?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8 datasource.no1.username=root datasource.no1.password=root -datasource.no2.url=jdbc:mysql://192.168.0.17:3306/qa-education-exam?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8 +datasource.no2.url=jdbc:mysql://192.168.10.28:33061/qa-education-exam?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8 datasource.no2.username=root datasource.no2.password=root diff --git a/src/main/resources/mybatis/datasource/study/StageStudentRelationMapper.xml b/src/main/resources/mybatis/datasource/study/StageStudentRelationMapper.xml index c880933..106c01b 100644 --- a/src/main/resources/mybatis/datasource/study/StageStudentRelationMapper.xml +++ b/src/main/resources/mybatis/datasource/study/StageStudentRelationMapper.xml @@ -267,12 +267,12 @@ OPERATOR = #{OPERATOR}, OPERATTIME = #{OPERATTIME} - where + where ISDELETE = '0' - STAGESTUDENTRELATION_ID = #{STAGESTUDENTRELATION_ID} + and STAGESTUDENTRELATION_ID = #{STAGESTUDENTRELATION_ID} - STUDENT_ID = #{STUDENT_ID} + and STUDENT_ID = #{STUDENT_ID} diff --git a/src/main/resources/mybatis/datasource/study/StudentMapper.xml b/src/main/resources/mybatis/datasource/study/StudentMapper.xml index cb940fd..95e20ee 100644 --- a/src/main/resources/mybatis/datasource/study/StudentMapper.xml +++ b/src/main/resources/mybatis/datasource/study/StudentMapper.xml @@ -47,7 +47,6 @@ f.STUDY_END_TIME, f.FILE_NUMBER - @@ -1133,4 +1132,16 @@ + + diff --git a/src/main/webapp/uploadFiles/tempZip/1 b/src/main/webapp/uploadFiles/tempZip/1 new file mode 100644 index 0000000..e69de29