kongzy
2024-09-14 f0f00e9ba8a755e4317e029d73b69a92ad9f9df1
update
已重命名40个文件
已修改129个文件
已添加36个文件
5096 ■■■■ 文件已修改
error.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/pom.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppCarouselController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppCourseController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppExerciseAnswerController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppLoginController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppPaperStudentController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppPhaseStudentController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppQuestionBankController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppQuestionController.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppResourceController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppStudentAnswerController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppStudentController.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppStudentStudyController.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/monitor/SysLogininforController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/monitor/SysOperLogController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/CaptchaController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCarouselController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCategoryController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCommonController.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCompanyController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysConfigController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysDictDataController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysDictTypeController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysLoginController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysNoticeController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysProfileController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysUserController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCompanyPeriodController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCourseChapterController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCourseChapterPeriodController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCourseController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCoursePhaseController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExExamPaperController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExExamRecordController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExPaperStudentController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExPhaseStudentController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExQuestionBankController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExQuestionController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExResourceController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExStatisticController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExStudentController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExStudentStudyController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/application-dev.yml 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/application-guotai.yml 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/application-prod.yml 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/application.yml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20231016001_add_user.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20231018009_dict_type.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20231018010_dict_data.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20231110001_config.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240428001_notice.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240506001_ope_log.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240510001_loginfor.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531001_student.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531002_company.sql 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531003_carousel.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531004_category.sql 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531005_course.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531006_course_chapter.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531007_course_chapter_period.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531008_resource.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531009_course_phase.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531010_student_study.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531011_phase_student.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240531012_company_period.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240603001_exam_paper.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240603002_paper_student.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240603003_question.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240603004_student_answer.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240603005_paper_question.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240604001_exam_record.sql 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240614001_question_bank.sql 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240619001_exercise_answer.sql 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240731001_change_question.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240801001_change_paper_student.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240801002_change_exam_paper.sql 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240801003_change_student_answer.sql 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240802001_change_student_answer.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/db/migration/V20240828001_change_question.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/main/resources/logback-spring.xml 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/test/java/com/gkhy/exam/admin/ExcelTest.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/test/java/com/gkhy/exam/admin/JobTest.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/test/java/com/gkhy/exam/admin/PasswordTest.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-admin/src/test/java/com/gkhy/exam/admin/QuestionTest.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/annotation/DataDesensitization.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/annotation/DataDesensitizationType.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/config/FFmpegConfig.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/constant/Constant.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/domain/TableSupport.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/domain/entity/SysUser.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/enums/PaperStudentStateEnum.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/enums/QuestionTypeEnum.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/enums/SensitiveTypeEnum.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/enums/StudentAnswerPassEnum.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/excel/StudentExcelData.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/excel/StudentExcelDataListener.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/filter/XssFilter.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/filter/XssHttpServletRequestWrapper.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/utils/DesenseUtil.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/utils/M3u8Utils.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/utils/SecurityUtils.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/utils/ServletUtils.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/utils/html/EscapeUtil.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-common/src/main/java/com/gkhy/exam/common/utils/html/HTMLFilter.java 566 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/aspectj/DataDesensitizationAspect.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/aspectj/LogAspect.java 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/config/ApplicationConfig.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/config/FastjsonConfig.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/config/FilterConfig.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/event/PaperStudentEvent.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/event/PaperStudentListener.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/exception/GlobalExceptionHandler.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/job/PaperStudentJob.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/job/UserStudyJob.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/security/SecurityConfig.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/security/handle/AuthenticationEntryPointImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/web/service/SysLoginService.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-framework/src/main/java/com/gkhy/exam/framework/web/service/TokenService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExCompanyPeriod.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExCourse.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExCoursePhase.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExExamPaper.java 99 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExExamRecord.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExExerciseAnswer.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExPaperStudent.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExPhaseStudent.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExQuestion.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExQuestionBank.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExStudent.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExStudentAnswer.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/ExStudentStudy.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/SysCategory.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/SysCompany.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/BatchPaperStudentVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyPaperStudentVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyPhaseStudentVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyPhaseVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyStatisticVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/ExPaperStudentVO.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/PaperStudentInfoVO.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/StudentStudyPeriodVO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/StudentStudyVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/TrainRecordVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/UploadObjectVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExCourseChapterPeriodMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExCourseMapper.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExExamPaperMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExPaperQuestionMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExPaperStudentMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExPhaseStudentMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExQuestionBankMapper.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExQuestionMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExResourceMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExStudentMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/SysCategoryMapper.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/mapper/SysUserMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/ExPaperStudentService.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/ExQuestionService.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/ExStudentService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/SysCommonService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/SysUserService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCourseChapterPeriodServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCourseChapterServiceImpl.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCoursePhaseServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCourseServiceImpl.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExExamPaperServiceImpl.java 107 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExExamRecordServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExExerciseAnswerServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExPaperStudentServiceImpl.java 201 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExPhaseStudentServiceImpl.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExQuestionBankServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExQuestionServiceImpl.java 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExResourceServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStatisticServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStudentAnswerServiceImpl.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStudentServiceImpl.java 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStudentStudyServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCarouselServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCategoryServiceImpl.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCommonServiceImpl.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCompanyServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysUserServiceImpl.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExCourseChapterMapper.xml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExCourseChapterPeriodMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExCourseMapper.xml 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExCoursePhaseMapper.xml 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExExamPaperMapper.xml 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExExamRecordMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExExerciseAnswerMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExPaperQuestionMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExPaperStudentMapper.xml 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExPhaseStudentMapper.xml 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExQuestionBankMapper.xml 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExQuestionMapper.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExResourceMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExStudentAnswerMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExStudentMapper.xml 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/ExStudentStudyMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/SysCarouselMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/SysCategoryMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/SysCompanyMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
exam-system/src/main/resources/mapper/system/SysUserMapper.xml 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
error.xlsx
Binary files differ
exam-admin/pom.xml
@@ -29,4 +29,35 @@
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
                <configuration>
                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <warName>${project.artifactId}</warName>
                </configuration>
            </plugin>
        </plugins>
        <finalName>${project.artifactId}</finalName>
    </build>
</project>
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppCarouselController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppCarouselController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
@@ -28,6 +28,7 @@
    @Autowired
    private SysCarouselService carouselService;
  //  @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "轮播列表(分页)")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "pageNum", dataType = "int", required = false, value = "当前页,默认1"),
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppCourseController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppCourseController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
@@ -6,6 +6,7 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -26,6 +27,7 @@
    @Autowired
    private ExCourseService courseService;
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "根据id获取课程信息")
    @GetMapping(value = { "/{courseId}" })
    public CommonResult getCompanyInfo(@PathVariable(value = "courseId", required = true) Long courseId)
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppExerciseAnswerController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppExerciseAnswerController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
@@ -22,7 +22,7 @@
 * @author kzy
 * @since 2024-06-20 09:42:25
 */
@Api(tags = "学员刷题前端控制器")
@Api(tags = "APP学员刷题前端控制器")
@RestController
@RequestMapping("/app/exercise-answer")
public class AppExerciseAnswerController {
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppLoginController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppLoginController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
@@ -30,7 +30,7 @@
    @ApiOperation(value = "用户退出")
    @PostMapping("/logout")
    public CommonResult logout(){
        sysLoginService.logout();
        return CommonResult.success();
    }
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppPaperStudentController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppPaperStudentController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
@@ -60,11 +60,10 @@
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "结束考试")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "body", name = "paperId", dataType = "long", required = true, value = "试卷id"),
            @ApiImplicitParam(paramType = "body", name = "studentId", dataType = "long", required = true, value = "学员id")
            @ApiImplicitParam(paramType = "body", name = "id", dataType = "long", required = true, value = "学员与试卷关系id")
    })
    @PostMapping(value = { "/endExam" })
    public CommonResult endExam(ExPaperStudent paperStudent)
    public CommonResult endExam(@RequestBody ExPaperStudent paperStudent)
    {
        paperStudentService.endExam(paperStudent);
        return CommonResult.success();
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppPhaseStudentController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppPhaseStudentController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppQuestionBankController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppQuestionBankController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.annotation.RepeatSubmit;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppQuestionController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppQuestionController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
@@ -32,15 +32,14 @@
    private ExQuestionService questionService;
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "刷题获取题目ID列表")
    @ApiOperation(value = "刷题根据题库id获取题目ID列表")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "bankId", dataType = "long", required = false, value = "题库id"),
            @ApiImplicitParam(paramType = "query", name = "exercistType", dataType = "int", required = false, value = "练习方式:1随机,2顺序")
            @ApiImplicitParam(paramType = "query", name = "bankId", dataType = "long", required = false, value = "题库id")
    })
    @GetMapping(value = { "/getExerciseQuestionList" })
    public CommonResult getExerciseQuestionList(@RequestParam(required = true) Long bankId,  @RequestParam(required = true)Integer exerciseType)
    public CommonResult getExerciseQuestionList(@RequestParam(required = true) Long bankId)
    {
        return CommonResult.success(questionService.getExerciseQuestionList(bankId,exerciseType));
        return CommonResult.success(questionService.getExerciseQuestionList(bankId));
    }
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
@@ -67,7 +66,7 @@
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "刷题获取错题题目ID列表")
    @ApiOperation(value = "刷题根据题库id获取错题题目ID列表")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "bankId", dataType = "long", required = false, value = "题库id")
    })
@@ -79,7 +78,7 @@
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "考试获取题目ID列表(考试完成返回错对,未完成则不返回)")
    @ApiOperation(value = "考试获取题目ID列表(考试完成返回错对,未完成则返回是否答题状态)")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "bankId", dataType = "long", required = false, value = "考卷id"),
            @ApiImplicitParam(paramType = "query", name = "viewType", dataType = "int", required = false, value = "查看方式:1考试,2查看")
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppResourceController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppResourceController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppStudentAnswerController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppStudentAnswerController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppStudentController.java
对比新文件
@@ -0,0 +1,43 @@
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.api.CommonResult;
import com.gkhy.exam.system.domain.ExStudent;
import com.gkhy.exam.system.service.ExStudentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
 * <p>
 * 学员前端控制器
 * </p>
 *
 * @author kzy
 * @since 2024-06-06 13:53:17
 */
@Api(tags = "APP学员前端控制器")
@RestController
@RequestMapping("/app/student")
public class AppStudentController {
    @Autowired
    private ExStudentService studentService;
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "根据id获取学员信息")
    @GetMapping(value = { "/{studentId}" })
    public CommonResult getStudentInfo(@PathVariable(value = "studentId", required = true) Long studentId)
    {
        return CommonResult.success(studentService.selectStudentById(studentId));
    }
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "重置密码")
    @PutMapping(value = "/resetPwd")
    public CommonResult restPwd(@RequestBody ExStudent student){
        studentService.resetUserPwd(student);
        return CommonResult.success();
    }
}
exam-admin/src/main/java/com/gkhy/exam/admin/controller/app/AppStudentStudyController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/app/AppStudentStudyController.java 修改
@@ -1,6 +1,7 @@
package com.gkhy.exam.admin.app;
package com.gkhy.exam.admin.controller.app;
import com.gkhy.exam.common.annotation.RepeatSubmit;
import com.gkhy.exam.common.api.CommonResult;
import com.gkhy.exam.system.domain.ExStudentStudy;
import com.gkhy.exam.system.service.ExStudentStudyService;
@@ -28,6 +29,7 @@
    @Autowired
    private ExStudentStudyService studentStudyService;
    @RepeatSubmit
    @PreAuthorize("hasAnyAuthority('train:exam:student')")
    @ApiOperation(value = "新增学习记录")
    @PostMapping
@@ -47,6 +49,7 @@
    })
    @PostMapping("/progress")
    public CommonResult progress(@RequestBody ExStudentStudy studentStudy){
        System.out.println("progress info:"+studentStudy.getId()+","+studentStudy.getPhaseId()+","+studentStudy.getPeriodId()+","+studentStudy.getStudentId());
        studentStudyService.progress(studentStudy);
        return CommonResult.success();
    }
exam-admin/src/main/java/com/gkhy/exam/admin/controller/monitor/SysLogininforController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/monitor/SysLogininforController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.monitor;
package com.gkhy.exam.admin.controller.monitor;
/**
 * 系统访问记录
exam-admin/src/main/java/com/gkhy/exam/admin/controller/monitor/SysOperLogController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/monitor/SysOperLogController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.monitor;
package com.gkhy.exam.admin.controller.monitor;
import com.gkhy.exam.common.annotation.Log;
import com.gkhy.exam.common.api.CommonResult;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/CaptchaController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/CaptchaController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.UUID;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCarouselController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysCarouselController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.Log;
@@ -48,6 +48,7 @@
        return CommonResult.success(carouselService.selectCarouselById(carouselId));
    }
    @RepeatSubmit
    @PreAuthorize("hasAnyAuthority('train:exam:system')")
    @Log(title = "轮播图管理", businessType = BusinessType.INSERT)
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCategoryController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysCategoryController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCommonController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysCommonController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.RepeatSubmit;
import com.gkhy.exam.common.api.CommonResult;
@@ -74,4 +74,11 @@
    }
    @GetMapping("/importExcel")
    public CommonResult importExcel() throws Exception {
        commonService.importStudent();
        return CommonResult.success();
    }
}
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysCompanyController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysCompanyController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysConfigController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysConfigController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
//@Api(tags = "系统配置前端控制器")
//@RestController
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysDictDataController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysDictDataController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.Log;
import com.gkhy.exam.common.annotation.RepeatSubmit;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysDictTypeController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysDictTypeController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.Log;
import com.gkhy.exam.common.annotation.RepeatSubmit;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysLoginController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysLoginController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.api.CommonResult;
@@ -30,7 +30,7 @@
    @ApiOperation(value = "用户退出")
    @PostMapping("/logout")
    public CommonResult logout(){
        sysLoginService.logout();
        return CommonResult.success();
    }
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysNoticeController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysNoticeController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.Log;
import com.gkhy.exam.common.annotation.RepeatSubmit;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysProfileController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysProfileController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import cn.hutool.core.codec.Base64;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/system/SysUserController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/system/SysUserController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.system;
package com.gkhy.exam.admin.controller.system;
import com.gkhy.exam.common.annotation.Log;
import com.gkhy.exam.common.annotation.RepeatSubmit;
@@ -24,7 +24,7 @@
  //  @PreAuthorize("hasAuthority('train:exam:company')")
 //   @PreAuthorize("hasAnyAuthority('train:exam:system','train:exam:company')")
  @PreAuthorize("hasAnyAuthority('train:exam:system','train:exam:company','train:exam:depart','train:exam:workshop','train:exam:other')")
    @PreAuthorize("hasAnyAuthority('train:exam:system','train:exam:company','train:exam:depart','train:exam:workshop','train:exam:other')")
    @ApiOperation(value = "用户列表(分页)")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "pageNum", dataType = "int", required = false, value = "当前页,默认1"),
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCompanyPeriodController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExCompanyPeriodController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCourseChapterController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExCourseChapterController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCourseChapterPeriodController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExCourseChapterPeriodController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCourseController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExCourseController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExCoursePhaseController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExCoursePhaseController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
@@ -65,7 +65,7 @@
    }
    @RepeatSubmit
    @Log(title = "批次管理", businessType = BusinessType.UPDATE)
    @Log(title = "批次管理", businessType = BusinessType.DELETE)
    @ApiOperation(value = "删除批次")
    @DeleteMapping(value = { "/{phaseId}" })
    public CommonResult delete(@PathVariable(value = "phaseId", required = true) Long phaseId){
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExExamPaperController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExExamPaperController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
@@ -12,6 +12,7 @@
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -48,6 +49,7 @@
    }
    @RepeatSubmit
    @PreAuthorize("hasAnyAuthority('train:exam:company','train:exam:depart','train:exam:workshop','train:exam:other')")
    @Log(title = "试卷管理", businessType = BusinessType.INSERT)
    @ApiOperation(value = "新增试卷")
    @PostMapping
@@ -56,6 +58,7 @@
    }
    @RepeatSubmit
    @PreAuthorize("hasAnyAuthority('train:exam:company','train:exam:depart','train:exam:workshop','train:exam:other')")
    @Log(title = "试卷管理", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "编辑试卷")
    @PutMapping
@@ -64,7 +67,8 @@
    }
    @RepeatSubmit
    @Log(title = "试卷管理", businessType = BusinessType.UPDATE)
    @PreAuthorize("hasAnyAuthority('train:exam:company','train:exam:depart','train:exam:workshop','train:exam:other')")
    @Log(title = "试卷管理", businessType = BusinessType.DELETE)
    @ApiOperation(value = "删除试卷")
    @DeleteMapping(value = { "/{paperId}" })
    public CommonResult delete(@PathVariable(value = "paperId", required = true) Long paperId){
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExExamRecordController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExExamRecordController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExPaperStudentController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExPaperStudentController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
@@ -6,6 +6,8 @@
import com.gkhy.exam.common.api.CommonResult;
import com.gkhy.exam.common.enums.BusinessType;
import com.gkhy.exam.system.domain.ExPaperStudent;
import com.gkhy.exam.system.domain.vo.BatchPaperStudentVO;
import com.gkhy.exam.system.domain.vo.ExPaperStudentVO;
import com.gkhy.exam.system.service.ExPaperStudentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@@ -17,7 +19,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -45,6 +46,13 @@
        return CommonResult.success(paperStudentService.selectPaperStudentList(paperStudent));
    }
    @ApiOperation(value = "根据id查询学员试卷信息")
    @GetMapping(value = { "/getPaperStudentById" })
    public CommonResult getPaperStudentById(@RequestParam(value = "paperStudentId", required = true) Long paperStudentId)
    {
        return CommonResult.success(paperStudentService.selectPaperStudentById(paperStudentId));
    }
    @RepeatSubmit
    @Log(title = "试卷与学员关系管理", businessType = BusinessType.INSERT)
    @ApiOperation(value = "新增学员")
@@ -55,15 +63,10 @@
    @RepeatSubmit
    @Log(title = "试卷与学员关系管理", businessType = BusinessType.INSERT)
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "body", name = "phaseIds", dataType = "array", required = false, value = "批次id列表"),
            @ApiImplicitParam(paramType = "query", name = "studentIds", dataType = "array", required = false, value = "学员id列表(批次id和学员id列表只能传一个)"),
            @ApiImplicitParam(paramType = "query", name = "paperId", dataType = "long", required = true, value = "考卷id")
    })
    @ApiOperation(value = "批量新增学员")
    @PostMapping("/batchAdd")
    public CommonResult batchAdd(@RequestBody Map<String,Object> paperStudentMap){
        return CommonResult.success(paperStudentService.batchAddPaperStudent(paperStudentMap));
    public CommonResult batchAdd(@Validated @RequestBody BatchPaperStudentVO batchPaperStudent){
        return CommonResult.success(paperStudentService.batchAddPaperStudent(batchPaperStudent));
    }
    @RepeatSubmit
@@ -78,7 +81,7 @@
    @Log(title = "试卷与学员关系管理", businessType = BusinessType.DELETE)
    @ApiOperation(value = "批量删除学员")
    @DeleteMapping(value = { "/batchDelete" })
    public CommonResult batchDelete( List<Long> paperStudentIds){
    public CommonResult batchDelete(@RequestBody List<Long> paperStudentIds){
        paperStudentService.batchDeletePaperStudent(paperStudentIds);
        return CommonResult.success();
    }
@@ -90,4 +93,13 @@
        paperStudentService.checkStudentUnique(Collections.singletonList(paperStudent));
        return CommonResult.success();
    }
    @ApiOperation(value = "提交批改试卷")
    @PostMapping("/doReview")
    public CommonResult doReview(@RequestBody ExPaperStudentVO paperStudentVO)
    {
        paperStudentService.doReview(paperStudentVO);
        return CommonResult.success();
    }
}
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExPhaseStudentController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExPhaseStudentController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
@@ -69,10 +69,10 @@
    }
    @RepeatSubmit
    @Log(title = "批次与学员关系管理", businessType = BusinessType.UPDATE)
    @Log(title = "批次与学员关系管理", businessType = BusinessType.DELETE)
    @ApiOperation(value = "批量删除学员")
    @DeleteMapping(value = { "/batchDelete" })
    public CommonResult batchDelete( List<Long> phaseStudentIds){
    public CommonResult batchDelete(@RequestBody List<Long> phaseStudentIds){
        phaseStudentService.batchDeletePhaseStudent(phaseStudentIds);
        return CommonResult.success();
    }
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExQuestionBankController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExQuestionBankController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExQuestionController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExQuestionController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
@@ -34,7 +34,7 @@
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "pageNum", dataType = "int", required = false, value = "当前页,默认1"),
            @ApiImplicitParam(paramType = "query", name = "pageSize", dataType = "int", required = false, value = "每页数目,默认10"),
            @ApiImplicitParam(paramType = "query", name = "bankId", dataType = "long", required = true, value = "题库id")
            @ApiImplicitParam(paramType = "query", name = "bankId", dataType = "long", required = false, value = "题库id")
    })
    @GetMapping("/list")
    public CommonResult list(ExQuestion question){
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExResourceController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExResourceController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExStatisticController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExStatisticController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.api.CommonResult;
@@ -8,6 +8,7 @@
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@@ -40,7 +41,7 @@
            @ApiImplicitParam(paramType = "query", name = "type", dataType = "int", required = true, value = "1线上教育  2线下教育,默认1")
    })
    @GetMapping("/companyStatistic")
    public CommonResult companyStatistic(@RequestParam(required = false) Date startTime, @RequestParam(required = false) Date endTime, @RequestParam(required = false) Long companyId, @RequestParam(required = true,value = "1") Integer type) {
    public CommonResult companyStatistic(@RequestParam(required = true,defaultValue = "1") Integer type, @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime, @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")Date endTime, @RequestParam(required = false) Long companyId) {
        return CommonResult.success(statisticService.companyStatic(companyId, startTime, endTime,type));
    }
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExStudentController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExStudentController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
@@ -12,8 +12,11 @@
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
 * <p>
@@ -92,6 +95,27 @@
    @ApiOperation(value = "校验身份证是否为一")
    @PostMapping(value = "/checkIdNoUnique")
    public CommonResult checkIdNoUnique(@RequestBody ExStudent student){
        return CommonResult.success(studentService.checkIdNoUnique(student));
        return CommonResult.success(studentService.checkIdNoUnique(student.getIdNo()));
    }
    @ApiOperation(value = "学员企业归属变更")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "body", name = "studentId", dataType = "long", required = true, value = "学员id"),
            @ApiImplicitParam(paramType = "body", name = "companyId", dataType = "long", required = true, value = "公司id")
    })
    @PreAuthorize("hasAnyAuthority('train:exam:system','train:exam:company')")
    @PostMapping(value = "/changeStudentCompany")
    public CommonResult changeStudentCompany(@RequestBody Map<String,Long> bodyMap){
        studentService.changeStudentCompany(bodyMap);
        return CommonResult.success();
    }
    @ApiOperation(value = "学员培训记录")
    @GetMapping(value = "/trainRecord")
    public CommonResult trainRecord(Long studentId){
        return CommonResult.success(studentService.trainRecord(studentId));
    }
}
exam-admin/src/main/java/com/gkhy/exam/admin/controller/web/ExStudentStudyController.java
文件名从 exam-admin/src/main/java/com/gkhy/exam/admin/web/ExStudentStudyController.java 修改
@@ -1,4 +1,4 @@
package com.gkhy.exam.admin.web;
package com.gkhy.exam.admin.controller.web;
import com.gkhy.exam.common.annotation.Log;
exam-admin/src/main/resources/application-dev.yml
@@ -16,7 +16,7 @@
        password:
      initialSize: 5 #连接池初始化大小
      minIdle: 10 #最小空闲连接数
      maxActive: 20 #最大连接数
      maxActive: 50 #最大连接数
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置连接超时时间
@@ -73,9 +73,7 @@
  level:
    root: INFO
    org:
      com.nms.swspkmas_standalone: DEBUG
  file:
    name: logs/nms-kaoshi.log
      com.gkhy.exam: DEBUG
swagger:
  enabled: true
@@ -84,46 +82,4 @@
  endpoint: http://192.168.2.16:9000/ #Minio服务所在地址
  bucketName: trainexam #存储桶名称
  accessKey: JuHGtYXvgJdeaPHcu5AI #访问的key
  secretKey: mQhUsIYnD696ZFI2sJ6eQ77tmmoe9TTUavFg9Ien #访问的秘钥
version: '3.9'
services:
  kele_mysql:
    restart: always
    image: mysql:5.6
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./conf/mysql/:/etc/mysql/conf.d
    ports:
      - "3306:3306"
    environment:
      - MYSQL_DATABASE=imooc
      - MYSQL_ROOT_PASSWORD=root
  kele_nginx:
    restart: always
    image: nginx
    ports:
      - "8001:80"
    volumes:
      - ./conf/nginx/mx_nginx.conf:/etc/nginx/conf.d/mx_nginx.conf
    volumes_from:
      - kele_imooc
    links:
      - kele_imooc:web
  kele_imooc:
    restart: always
    build: .
    ports:
      - "8000:8000"
    volumes:
      - .:/imooc
    links:
      - kele_mysql:mysql
    command: uwsgi -s :8000 -w imooc.wsgi -p 3
  secretKey: mQhUsIYnD696ZFI2sJ6eQ77tmmoe9TTUavFg9Ien #访问的秘钥
exam-admin/src/main/resources/application-guotai.yml
对比新文件
@@ -0,0 +1,86 @@
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # 主库数据源
      master:
        url: jdbc:mysql://127.0.0.1:6361/train_exam?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
        username: root
        password: HZjCbHGxiXy7cek4
      # 从库数据源
      slave:
        enabled: false
        url:
        username:
        password:
      initialSize: 5 #连接池初始化大小
      minIdle: 10 #最小空闲连接数
      maxActive: 50 #最大连接数
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置连接超时时间
      connectTimeout: 30000
      # 配置网络超时时间
      socketTimeout: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一个连接在池中最大生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      web-stat-filter:
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #不统计这些请求数据
      stat-view-servlet: #访问监控网页的登录用户名和密码
        login-username: druid
        login-password: druid
  #redis 配置
  redis:
    database: 4
    host: 127.0.0.1
    port: 6379
    password: akj78avauba789a
# mybatis-plus相关配置
mybatis-plus:
  # xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  # 以下配置均有默认值,可以不设置
  global-config:
    db-config:
      #主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
      id-type: auto
      #字段策略 IGNORED:"忽略判断"  NOT_NULL:"非 NULL 判断")  NOT_EMPTY:"非空判断"
      field-strategy: NOT_EMPTY
      #数据库类型
      db-type: MYSQL
  configuration:
    # 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
    map-underscore-to-camel-case: true
    # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    root: INFO
    org:
      com.gkhy.exam: INFO
swagger:
  enabled: false
minio:
  endpoint: http://106.15.95.149:9001/ #Minio服务所在地址
  bucketName: trainexam #存储桶名称
  accessKey: U9JW4xOeeUQOSR4f #访问的key
  secretKey: iaqQV6twR9yDZiFAf2UYr5xZfESanZs3 #访问的秘钥
exam-admin/src/main/resources/application-prod.yml
对比新文件
@@ -0,0 +1,86 @@
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # 主库数据源
      master:
        url: jdbc:mysql://127.0.0.1:23306/train_exam?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Beijing&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
        username: root
        password: 2farwL3yPXfbH2AP
      # 从库数据源
      slave:
        enabled: false
        url:
        username:
        password:
      initialSize: 5 #连接池初始化大小
      minIdle: 10 #最小空闲连接数
      maxActive: 50 #最大连接数
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置连接超时时间
      connectTimeout: 30000
      # 配置网络超时时间
      socketTimeout: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一个连接在池中最大生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      web-stat-filter:
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #不统计这些请求数据
      stat-view-servlet: #访问监控网页的登录用户名和密码
        login-username: druid
        login-password: druid
  #redis 配置
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password:
# mybatis-plus相关配置
mybatis-plus:
  # xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  # 以下配置均有默认值,可以不设置
  global-config:
    db-config:
      #主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
      id-type: auto
      #字段策略 IGNORED:"忽略判断"  NOT_NULL:"非 NULL 判断")  NOT_EMPTY:"非空判断"
      field-strategy: NOT_EMPTY
      #数据库类型
      db-type: MYSQL
  configuration:
    # 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
    map-underscore-to-camel-case: true
    # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    root: INFO
    org:
      com.gkhy.exam: INFO
swagger:
  enabled: false
minio:
  endpoint: http://127.0.0.1:9000/ #Minio服务所在地址
  bucketName: trainexam #存储桶名称
  accessKey: nblxqRZXDvQ59HBH49rF #访问的key
  secretKey: TR0IFphXPo0IObQCYgcJ0JOik21s40ey2MIMU8Rh #访问的秘钥
exam-admin/src/main/resources/application.yml
@@ -2,7 +2,7 @@
  application:
    name: train_exam
  profiles:
    active: dev
    active: guotai
  servlet:
    multipart:
      enabled: true
@@ -18,6 +18,10 @@
  port: 8082
  servlet:
    context-path: /api
  tomcat:
    threads:
      min-spare: 10
      max: 200
# 用户配置
@@ -36,3 +40,13 @@
  #获取ip地址开关
  addressEnabled: false
# 防止XSS攻击
xss:
  # 过滤开关
  enabled: true
  # 排除链接(多个用逗号分隔)
  excludes:
  # 匹配链接
  urlPatterns: /system/*,/manage/*
exam-admin/src/main/resources/db/migration/V20231016001_add_user.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 系统管理用户表
-- ----------------------------
drop table if exists `train_exam`.`sys_user`;
CREATE TABLE `train_exam`.`sys_user`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号',
@@ -10,7 +9,7 @@
`phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号码',
`sex` tinyint NULL DEFAULT 2 COMMENT '用户性别(0男,1女,2未知,默认2)',
`company_id` bigint(20) NULL DEFAULT NULL COMMENT '公司id',
`parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父级账号id',
`parent_id` bigint(20) NULL DEFAULT 0 COMMENT '父级账号id',
`password` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',
`status` tinyint NULL DEFAULT 0 COMMENT '账号状态(0正常,1停用,默认0)',
`del_flag` tinyint NULL DEFAULT 0 COMMENT '删除标志(0代表存在,2代表删除,默认0)',
@@ -27,4 +26,4 @@
UNIQUE INDEX `index_username`(`username`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统管理用户表' ROW_FORMAT = Dynamic;
insert into sys_user values(1, 'admin', '国科鸿宇', 0, '15888888888', 0, null, '$2a$10$CrsAZufp851DxTmUBep7TOuUAESWdqlkrReAIR.4SHBIahl9exO6y',  0, 0, '127.0.0.1', sysdate(), sysdate(), 'admin', sysdate(), '', sysdate(), '管理员',0);
insert into sys_user values(1, 'admin', '国科鸿宇', 0, '15888888888', 0, null,0, '$2a$10$CrsAZufp851DxTmUBep7TOuUAESWdqlkrReAIR.4SHBIahl9exO6y',  0, 0, '127.0.0.1', sysdate(), sysdate(), 'admin', sysdate(), '', sysdate(), '管理员',0);
exam-admin/src/main/resources/db/migration/V20231018009_dict_type.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 字典类型表
-- ----------------------------
drop table if exists `train_exam`.`sys_dict_type`;
CREATE TABLE `train_exam`.`sys_dict_type`  (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '字典名称',
exam-admin/src/main/resources/db/migration/V20231018010_dict_data.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 字典数据表
-- ----------------------------
drop table if exists `train_exam`.`sys_dict_data`;
CREATE TABLE `train_exam`.`sys_dict_data`  (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`sort` int NOT NULL DEFAULT 0 COMMENT '字典排序',
exam-admin/src/main/resources/db/migration/V20231110001_config.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 系统配置表
-- ----------------------------
drop table if exists `train_exam`.`sys_config`;
CREATE TABLE `train_exam`.`sys_config`  (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '参数名称',
exam-admin/src/main/resources/db/migration/V20240428001_notice.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 系统通知表
-- ----------------------------
drop table if exists sys_notice;
create table sys_notice (
id        bigint NOT NULL AUTO_INCREMENT,
title      varchar(50)     not null                   comment '公告标题',
exam-admin/src/main/resources/db/migration/V20240506001_ope_log.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 操作日志表
-- ----------------------------
drop table if exists sys_oper_log;
create table sys_oper_log (
id           bigint(20)      not null auto_increment    comment '日志主键',
title             varchar(50)     default ''                 comment '模块标题',
exam-admin/src/main/resources/db/migration/V20240510001_loginfor.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 系统访问记录
-- ----------------------------
drop table if exists sys_logininfor;
create table sys_logininfor (
id           bigint(20)      not null auto_increment    comment '日志主键',
user_name      varchar(50)    default ''                comment '用户账号',
exam-admin/src/main/resources/db/migration/V20240531001_student.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 学员表
-- ----------------------------
drop table if exists `train_exam`.`ex_student`;
CREATE TABLE `train_exam`.`ex_student`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`company_id` bigint(20) NOT NULL COMMENT '企业id',
exam-admin/src/main/resources/db/migration/V20240531002_company.sql
@@ -2,16 +2,14 @@
-- ----------------------------
-- 企业表
-- ----------------------------
drop table if exists `train_exam`.`sys_company`;
CREATE TABLE `train_exam`.`sys_company`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(80) NOT NULL COMMENT '企业名称',
`credit_code` varchar(30) NOT NULL COMMENT '企业信用代码',
`major` varchar(30) NOT NULL COMMENT '负责人',
`phone` varchar(11) NOT NULL COMMENT '联系电话',
`remain_period` int NOT NULL DEFAULT 0 COMMENT '剩余课时(分)',
`spend_period` int NOT NULL DEFAULT 0 COMMENT '已用课时(分)',
`total_period` int NOT NULL DEFAULT 0 COMMENT '总课时(分)',
`remain_period` bigint(20) NOT NULL DEFAULT 0 COMMENT '剩余课时(秒)',
`total_period` bigint(20) NOT NULL DEFAULT 0 COMMENT '总课时(秒)',
`del_flag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标志(0为删除,1删除,默认0)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
exam-admin/src/main/resources/db/migration/V20240531003_carousel.sql
@@ -2,7 +2,6 @@
-- ----------------------------
-- 轮播图表
-- ----------------------------
drop table if exists `train_exam`.`sys_carousel`;
CREATE TABLE `train_exam`.`sys_carousel`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`status` tinyint NOT NULL DEFAULT 0 COMMENT '账号状态(0正常,1停用,默认0)',
exam-admin/src/main/resources/db/migration/V20240531004_category.sql
@@ -2,12 +2,12 @@
-- ----------------------------
-- 课程分类表
-- ----------------------------
drop table if exists `train_exam`.`sys_category`;
CREATE TABLE `train_exam`.`sys_category`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`parent_id` bigint(20)  NOT NULL DEFAULT 0 COMMENT '父分类id',
`name` varchar(30) NOT NULL COMMENT '分类名称',
`category_type` tinyint  NOT NULL DEFAULT 1 COMMENT '类型(1课程,2资源)',
`del_flag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标志(0为删除,1删除,默认0)',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '账号状态(0正常,1停用,默认0)',
`sort` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '排序',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
exam-admin/src/main/resources/db/migration/V20240531005_course.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 课程表
-- ----------------------------
drop table if exists `train_exam`.`ex_course`;
CREATE TABLE `train_exam`.`ex_course`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`status` tinyint NOT NULL DEFAULT 0 COMMENT '课程状态(0正常,1停用,默认0)',
@@ -10,10 +9,10 @@
`name` varchar(50)  NOT NULL  COMMENT '课程名称',
`logo` varchar(255) NOT NULL DEFAULT '' COMMENT '课程封面',
`introduce` text NULL  COMMENT '课程简介',
`state` tinyint NULL DEFAULT 1 COMMENT '审批状态(0创建,1待审核,2审批通过,3审批不通过  默认0)',
`state` tinyint NULL DEFAULT 0 COMMENT '审批状态(0创建,1待审核,2审批通过,3审批不通过  默认0)',
`message` varchar(50)  NULL  COMMENT '审批意见',
`company_id` bigint(20) NULL COMMENT '提交公司id',
`privatize` tinyint NOT NULL DEFAULT 0  COMMENT '课程公开私有属性(0私有,1公开  默认0)',
`period` bigint(20) NOT NULL DEFAULT 0 COMMENT '课时(单位秒)',
`del_flag` tinyint NULL DEFAULT 0 COMMENT '删除标志(0代表存在,2代表删除,默认0)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_by` varchar(50) NULL DEFAULT NULL COMMENT '创建人',
exam-admin/src/main/resources/db/migration/V20240531006_course_chapter.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 课程章节表
-- ----------------------------
drop table if exists `train_exam`.`ex_course_chapter`;
CREATE TABLE `train_exam`.`ex_course_chapter`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`status` tinyint NOT NULL DEFAULT 0 COMMENT '账号状态(0正常,1停用,默认0)',
exam-admin/src/main/resources/db/migration/V20240531007_course_chapter_period.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 课时信息表
-- ----------------------------
drop table if exists `train_exam`.`ex_course_chapter_period`;
CREATE TABLE `train_exam`.`ex_course_chapter_period`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`status` tinyint NOT NULL DEFAULT 0 COMMENT '账号状态(0正常,1停用,默认0)',
exam-admin/src/main/resources/db/migration/V20240531008_resource.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 课程资源表
-- ----------------------------
drop table if exists `train_exam`.`ex_resource`;
CREATE TABLE `train_exam`.`ex_resource`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`status` tinyint NOT NULL DEFAULT 0 COMMENT '账号状态(0正常,1停用,默认0)',
@@ -13,9 +12,9 @@
`company_id` bigint(20) NULL COMMENT '提交公司id',
`resource_type` tinyint NOT NULL DEFAULT 1 COMMENT '资源种类(1:视频2:音频;3:文档,默认1)',
`media_type` varchar(100)  NULL  COMMENT '资源类型',
`resource_size` bigint(20) NOT NULL  COMMENT '资源大小',
`resource_size` bigint(20) NOT NULL DEFAULT 0  COMMENT '资源大小',
`resource_uri` varchar(200) NULL DEFAULT NULL  COMMENT '资源地址(存放第三方地址)',
`resource_length` bigint(20) NULL DEFAULT NULL  COMMENT '资源时长(秒)',
`resource_length` bigint(20) NULL DEFAULT 0  COMMENT '资源时长(秒)',
`resource_path` varchar(120)  NULL DEFAULT NULL COMMENT '资源路径',
`md5` varchar(50)  NOT NULL COMMENT '文件md5',
`doc_page` int NULL DEFAULT 0 COMMENT '资源页数,单位页',
exam-admin/src/main/resources/db/migration/V20240531009_course_phase.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 课时批次表
-- ----------------------------
drop table if exists `train_exam`.`ex_course_phase`;
CREATE TABLE `train_exam`.`ex_course_phase`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`code` varchar(12) NOT NULL COMMENT '批次编号',
exam-admin/src/main/resources/db/migration/V20240531010_student_study.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 课程学习学习日志表
-- ----------------------------
drop table if exists `train_exam`.`ex_student_study`;
CREATE TABLE `train_exam`.`ex_student_study`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`phase_id` bigint(20) NOT NULL COMMENT '课时批次id',
@@ -12,7 +11,6 @@
`current_duration` bigint(20) NOT NULL DEFAULT 0 COMMENT '当前学习时长,单位秒',
`current_page` int NOT NULL DEFAULT 0 COMMENT '当前学习页数,单位页',
`progress` decimal(5, 2) NOT NULL DEFAULT 0 COMMENT '进度(百分比)',
`resource_type` varchar(10) NOT NULL  COMMENT '资源类型(PPT,MP4,PDF)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_by` varchar(50) NULL DEFAULT NULL COMMENT '创建人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
exam-admin/src/main/resources/db/migration/V20240531011_phase_student.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 课时批次与学员关系表
-- ----------------------------
drop table if exists `train_exam`.`ex_phase_student`;
CREATE TABLE `train_exam`.`ex_phase_student`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`phase_id` bigint(20) NOT NULL COMMENT '课时批次id',
exam-admin/src/main/resources/db/migration/V20240531012_company_period.sql
@@ -1,14 +1,13 @@
-- ----------------------------
-- 公司课时变更记录表
-- ----------------------------
drop table if exists `train_exam`.`ex_company_period`;
CREATE TABLE `train_exam`.`ex_company_period`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`company_id` bigint NOT NULL COMMENT '公司id',
`phase_id` bigint  NULL COMMENT '批次id',
`origin` varchar(50) NOT NULL COMMENT '变动来源',
`modify_period` int NOT NULL COMMENT '变动情况(增减课时)',
`remain_period` int NOT NULL COMMENT '变动后剩余(分)',
`modify_period` bigint(20) NOT NULL COMMENT '变动情况(增减课时)秒',
`remain_period` bigint(20) NOT NULL COMMENT '变动后剩余(秒)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_by` varchar(50) NULL DEFAULT NULL COMMENT '创建人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
exam-admin/src/main/resources/db/migration/V20240603001_exam_paper.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 考卷(组卷)表
-- ----------------------------
drop table if exists `train_exam`.`ex_exam_paper`;
CREATE TABLE `train_exam`.`ex_exam_paper`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`code` varchar(12) NOT NULL COMMENT '编号',
@@ -10,7 +9,7 @@
`company_id` bigint NOT NULL COMMENT '公司id',
`category_id` bigint(20) NOT NULL COMMENT '分类ID',
`limit_time` int NOT NULL DEFAULT 0 COMMENT '考试限制时长',
`limit` tinyint NOT NULL DEFAULT 0 COMMENT '是否现在考试时间(0否,1是,默认0)',
`limited` tinyint NOT NULL DEFAULT 0 COMMENT '是否现在考试时间(0否,1是,默认0)',
`single_num` int NOT NULL DEFAULT 0 COMMENT '单选题目数量',
`single_score` int NOT NULL DEFAULT 0 COMMENT '单选题每题分数',
`single_bank_id` bigint(20)  NULL COMMENT '单选题题库id',
@@ -25,6 +24,7 @@
`judge_method` tinyint NOT NULL DEFAULT 1 COMMENT '出题方式1随机,2顺序,默认1',
`pass_score` int NOT NULL DEFAULT 0 COMMENT '合格分数',
`del_flag` tinyint NULL DEFAULT 0 COMMENT '删除标志(0代表存在,2代表删除,默认0)',
`deadline` datetime NOT NULL  COMMENT '考试截止时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_by` varchar(50) NULL DEFAULT NULL COMMENT '创建人',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
exam-admin/src/main/resources/db/migration/V20240603002_paper_student.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 考卷与学员关系表
-- ----------------------------
drop table if exists `train_exam`.`ex_paper_student`;
CREATE TABLE `train_exam`.`ex_paper_student`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`paper_id` bigint(20) NOT NULL COMMENT '考卷id',
exam-admin/src/main/resources/db/migration/V20240603003_question.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 考题表
-- ----------------------------
drop table if exists `train_exam`.`ex_question`;
CREATE TABLE `train_exam`.`ex_question`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`question_type` tinyint NOT NULL DEFAULT 1 COMMENT '考题类型(1单选题,2多选题,3判断题 默认1)',
exam-admin/src/main/resources/db/migration/V20240603004_student_answer.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 学员与考试题目关系表
-- ----------------------------
drop table if exists `train_exam`.`ex_student_answer`;
CREATE TABLE `train_exam`.`ex_student_answer`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`paper_id` bigint(20) NOT NULL COMMENT '考卷id',
exam-admin/src/main/resources/db/migration/V20240603005_paper_question.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 试卷与题目关系表
-- ----------------------------
drop table if exists `train_exam`.`ex_paper_question`;
CREATE TABLE `train_exam`.`ex_paper_question`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`paper_id` bigint(20) NOT NULL COMMENT '考卷id',
exam-admin/src/main/resources/db/migration/V20240604001_exam_record.sql
@@ -1,14 +1,13 @@
-- ----------------------------
-- 线下教育登记表
-- ----------------------------
drop table if exists `train_exam`.`ex_exam_record`;
CREATE TABLE `train_exam`.`ex_exam_record`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`company_id` bigint(20) NOT NULL COMMENT '公司id',
`student_id` bigint(20) NOT NULL COMMENT '学员id',
`plan_name` varchar(50) NOT NULL COMMENT '计划名称',
`course_name` varchar(50) NOT NULL COMMENT '课程名称',
`level` varchar(20) NOT NULL COMMENT '培训等级',
`level` tinyint NOT NULL DEFAULT 1 COMMENT '培训等级(1公司级 2部门级 3车间级 默认1)',
`period` int NOT NULL COMMENT '要求课时(分)',
`actual_period` int NOT NULL COMMENT '实际课时(分)',
`score` int NOT NULL COMMENT '考试成绩',
exam-admin/src/main/resources/db/migration/V20240614001_question_bank.sql
@@ -1,11 +1,10 @@
-- ----------------------------
-- 题库表
-- ----------------------------
drop table if exists `train_exam`.`ex_question_bank`;
CREATE TABLE `train_exam`.`ex_question_bank`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`name` varchar(50) NOT NULL  COMMENT '题库名称',
`company_id` bigint NOT NULL COMMENT '公司id',
`company_id` bigint  NULL COMMENT '公司id',
`category_id` bigint(20) NOT NULL COMMENT '分类ID',
`del_flag` tinyint NULL DEFAULT 0 COMMENT '删除标志(0代表存在,2代表删除,默认0)',
`privatize` tinyint NOT NULL DEFAULT 0  COMMENT '课程公开私有属性(0私有,1公开  默认0)',
exam-admin/src/main/resources/db/migration/V20240619001_exercise_answer.sql
@@ -1,7 +1,6 @@
-- ----------------------------
-- 学员与练习题关系表
-- ----------------------------
drop table if exists `train_exam`.`ex_exercise_answer`;
CREATE TABLE `train_exam`.`ex_exercise_answer`  (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
`bank_id` bigint(20) NOT NULL COMMENT '题库id',
@@ -16,5 +15,5 @@
`remark` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
`version` int NULL DEFAULT 0 COMMENT '乐观锁',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `paper_student_id` (`student_id`,`question_id`)
UNIQUE KEY `question_student_id` (`student_id`,`question_id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '学员与练习题关系表' ROW_FORMAT = DYNAMIC;
exam-admin/src/main/resources/db/migration/V20240731001_change_question.sql
对比新文件
@@ -0,0 +1,2 @@
ALTER TABLE `train_exam`.`ex_question`
MODIFY COLUMN `answer` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '答案';
exam-admin/src/main/resources/db/migration/V20240801001_change_paper_student.sql
对比新文件
@@ -0,0 +1,2 @@
ALTER TABLE `train_exam`.`ex_paper_student`
ADD COLUMN `state` tinyint NOT NULL DEFAULT 0 COMMENT '状态:0待考试,1待批阅 2批阅完成';
exam-admin/src/main/resources/db/migration/V20240801002_change_exam_paper.sql
对比新文件
@@ -0,0 +1,14 @@
ALTER TABLE `train_exam`.`ex_exam_paper`
MODIFY COLUMN `single_num` int NULL DEFAULT 0 COMMENT '单选题目数量',
MODIFY COLUMN `single_score` int NULL DEFAULT 0 COMMENT '单选题每题分数',
MODIFY COLUMN `single_method` tinyint NULL DEFAULT 1 COMMENT '出题方式1随机,2顺序,默认1',
MODIFY COLUMN `multi_num` int NULL DEFAULT 0 COMMENT '多选题目数量',
MODIFY COLUMN `multi_score` int NULL DEFAULT 0 COMMENT '多选题每题分数',
MODIFY COLUMN `multi_method` tinyint NULL DEFAULT 1 COMMENT '出题方式1随机,2顺序,默认1',
MODIFY COLUMN `judge_num` int NULL DEFAULT 0 COMMENT '判断题目数量',
MODIFY COLUMN `judge_score` int NULL DEFAULT 0 COMMENT '判断题每题分数',
MODIFY COLUMN `judge_method` tinyint NULL DEFAULT 1 COMMENT '出题方式1随机,2顺序,默认1',
ADD COLUMN `easy_num` int NULL DEFAULT 0 COMMENT '简答题题目数量',
ADD COLUMN `easy_score` int NULL DEFAULT 0 COMMENT '简答题每题分数',
ADD COLUMN `easy_bank_id` bigint NULL COMMENT '简答题题库id',
ADD COLUMN `easy_method` tinyint NULL DEFAULT 1 COMMENT '出题方式1随机,2顺序,默认1';
exam-admin/src/main/resources/db/migration/V20240801003_change_student_answer.sql
对比新文件
@@ -0,0 +1,3 @@
ALTER TABLE `train_exam`.`ex_student_answer`
MODIFY COLUMN `passed` tinyint NULL DEFAULT NULL COMMENT '是否正确(0错误,1正确,2待批改)',
ADD COLUMN `score` int NULL COMMENT '得分';
exam-admin/src/main/resources/db/migration/V20240802001_change_student_answer.sql
对比新文件
@@ -0,0 +1,5 @@
ALTER TABLE `train_exam`.`ex_student_answer`
MODIFY COLUMN `answer` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '答案';
ALTER TABLE `train_exam`.`ex_exercise_answer`
MODIFY COLUMN `answer` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci  NULL COMMENT '答案';
exam-admin/src/main/resources/db/migration/V20240828001_change_question.sql
对比新文件
@@ -0,0 +1,2 @@
ALTER TABLE `train_exam`.`ex_question`
MODIFY COLUMN `company_id` bigint NULL COMMENT '公司id' AFTER `bank_id`;
exam-admin/src/main/resources/logback-spring.xml
对比新文件
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
    <!--引用默认日志配置-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <!--使用默认的控制台日志输出实现-->
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
    <!--应用名称-->
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="springBoot"/>
    <!-- 日志输出格式 -->
    <property name="log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{requestId}] [%X{clientIP}] [%X{userId}] - [%C,%M,%L] - %msg%n"/>
    <property name="log.path" value="logs"/>
    <!--日志文件保存路径-->
    <property name="LOG_FILE_PATH" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/logs}"/>
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>
    <!-- 系统日志输出 -->
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--        <file>${log.path}/zero-spring-info.log</file>-->
        <!-- 循环政策:基于时间创建日志文件 -->
        <!--        <file>-->
        <!--            logs/zero-spring.log-->
        <!--        </file>-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/${APP_NAME}-info-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <totalSizeCap>30GB</totalSizeCap>
            <!--设置日志文件大小,超过就重新生成文件,默认50M-->
            <maxFileSize>100MB</maxFileSize>
            <!-- 日志最大的历史 30天 -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--        <file>${log.path}/zero-spring-error.log</file>-->
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/${APP_NAME}-error-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <totalSizeCap>10GB</totalSizeCap>
            <!--设置日志文件大小,超过就重新生成文件,默认50M-->
            <maxFileSize>100MB</maxFileSize>
            <!-- 日志最大的历史 30天 -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!--控制框架输出日志-->
    <logger name="org.slf4j" level="INFO"/>
    <logger name="springfox" level="INFO"/>
    <logger name="io.swagger" level="INFO"/>
    <logger name="org.springframework" level="INFO"/>
    <logger name="org.hibernate.validator" level="INFO"/>
    <root level="INFO">
<!--        <appender-ref ref="console"/>-->
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
    </root>
    <root level="DEBUG">
        <appender-ref ref="console"/>
    </root>
</configuration>
exam-admin/src/test/java/com/gkhy/exam/admin/ExcelTest.java
对比新文件
@@ -0,0 +1,24 @@
package com.gkhy.exam.admin;
import com.gkhy.exam.system.service.SysCommonService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = GkhyAdminApplication.class)
@ActiveProfiles("dev")
@Slf4j
public class ExcelTest {
    @Autowired
    private SysCommonService commonService;
    @Test
    public void importTest(){
        commonService.importStudent();
    }
}
exam-admin/src/test/java/com/gkhy/exam/admin/JobTest.java
对比新文件
@@ -0,0 +1,27 @@
package com.gkhy.exam.admin;
import com.gkhy.exam.framework.job.PaperStudentJob;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = GkhyAdminApplication.class)
@ActiveProfiles("dev")
@Slf4j
public class JobTest {
    @Autowired
    private PaperStudentJob paperStudentJob;
    @Test
    public void jobTest(){
        paperStudentJob.doprogress();
    }
}
exam-admin/src/test/java/com/gkhy/exam/admin/PasswordTest.java
@@ -12,6 +12,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
@RunWith(SpringRunner.class)
@@ -65,4 +66,15 @@
        b.add("c");
        return aa;
    }
    @Test
    public void testNum(){
        Integer a=null;
        int b=0;
        if(Optional.ofNullable(a).orElse(0)>b){
            System.out.println("gggggg");
        }else{
            System.out.println("hhhhhh");
        }
    }
}
exam-admin/src/test/java/com/gkhy/exam/admin/QuestionTest.java
对比新文件
@@ -0,0 +1,198 @@
package com.gkhy.exam.admin;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.gkhy.exam.system.domain.ExQuestion;
import com.gkhy.exam.system.service.ExQuestionService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = GkhyAdminApplication.class)
@ActiveProfiles("dev")
@Slf4j
public class QuestionTest {
    @Autowired
    private ExQuestionService questionService;
    // 定义选项数量
    private static final int OPTION_COUNT = 4;
    // 随机生成器
    private static final Random random = new Random();
    @Test
    public void questionData(){
        List<ExQuestion>questions=new ArrayList<>();
        for(int i=0;i<300;i++){
            ExQuestion question=generateSingleQuestion();
            questions.add(question);
            question=generateMultiQuestion();
            questions.add(question);
            question=generateJudgeQuestion();
            questions.add(question);
            if(questions.size()>100){
                questionService.saveBatch(questions);
                questions.clear();
            }
        }
        if(questions.size()>0){
            questionService.saveBatch(questions);
            questions.clear();
        }
//        ExQuestion question=generateSingleQuestion();
//        System.out.println(question);
//        question=generateMultiQuestion();
//        System.out.println(question);
//        question=generateJudgeQuestion();
//        System.out.println(question);
    }
    // 生成单选题目
    public  ExQuestion generateSingleQuestion() {
        int a=random.nextInt(1000);
        int b=random.nextInt(1000);
        int sum=a+b;
        List<String> options=new ArrayList<>();
        options.add("A");
        options.add("B");
        options.add("C");
        options.add("D");
        int index=random.nextInt(4);
        String title="算术运算题,整数"+a+"与整数"+b+"之和,等于多少?";
        JSONObject object=new JSONObject();
        object.put("analyze","无");
        JSONArray jsonArray=new JSONArray();
        for(int i=0;i<4;i++){
            if(i==index){
                JSONObject itemObject=new JSONObject();
                itemObject.put("prefix",options.get(index));
                itemObject.put("content",sum);
                jsonArray.add(itemObject);
            }else{
                JSONObject itemObject=new JSONObject();
                itemObject.put("prefix",options.get(i));
                itemObject.put("content",randomInt(sum));
                jsonArray.add(itemObject);
            }
        }
        object.put("items",jsonArray);
        ExQuestion question=new ExQuestion();
        question.setQuestionType(1);
        question.setBankId(16L);
        question.setCompanyId(21L);
        question.setAnswer(options.get(index));
        question.setTitle(title);
        question.setPrivatize(0);
        question.setContent(JSONObject.toJSONString(object));
        return question;
    }
    // 生成多选题目
    public  ExQuestion generateMultiQuestion() {
        int a=random.nextInt(1000);
        int b=random.nextInt(1000);
        int sum=a+b;
        List<String> options=new ArrayList<>();
        options.add("A");
        options.add("B");
        options.add("C");
        options.add("D");
        options.add("E");
        int indexa=random.nextInt(5);
        int indexb=random.nextInt(5);
        String title="算术运算题,整数"+a+"与整数"+b+"之和,等于多少?";
        JSONObject object=new JSONObject();
        object.put("analyze","无");
        JSONArray jsonArray=new JSONArray();
        for(int i=0;i<5;i++){
            if(i==indexa){
                JSONObject itemObject=new JSONObject();
                itemObject.put("prefix",options.get(indexa));
                itemObject.put("content",sum);
                jsonArray.add(itemObject);
            }else if(i==indexb) {
                JSONObject itemObject=new JSONObject();
                itemObject.put("prefix",options.get(indexb));
                itemObject.put("content",sum);
                jsonArray.add(itemObject);
            }else{
                JSONObject itemObject=new JSONObject();
                itemObject.put("prefix",options.get(i));
                itemObject.put("content",randomInt(sum));
                jsonArray.add(itemObject);
            }
        }
        object.put("items",jsonArray);
        ExQuestion question=new ExQuestion();
        question.setQuestionType(2);
        question.setBankId(16L);
        question.setCompanyId(21L);
        String answer="";
        if(indexa==indexb){
            answer=options.get(indexa);
        }else{
            if(indexa>indexb){
                answer=options.get(indexb)+","+options.get(indexa);
            }else{
                answer=options.get(indexa)+","+options.get(indexb);
            }
        }
        question.setAnswer(answer);
        question.setTitle(title);
        question.setPrivatize(0);
        question.setContent(JSONObject.toJSONString(object));
        return question;
    }
    // 生成判断题目
    public  ExQuestion generateJudgeQuestion() {
        int a=random.nextInt(1000);
        int b=random.nextInt(1000);
        int sum=a+b;
        List<String> options=new ArrayList<>();
        options.add("A");
        options.add("B");
        int index=random.nextInt(2);
        String title="算术运算题,整数"+a+"与整数"+b+"之和,等于="+(index==0?sum:randomInt(sum));
        JSONObject object=new JSONObject();
        object.put("analyze","无");
        JSONArray jsonArray=new JSONArray();
        JSONObject itemObject=new JSONObject();
        itemObject.put("prefix",options.get(0));
        itemObject.put("content","是");
        jsonArray.add(itemObject);
        itemObject=new JSONObject();
        itemObject.put("prefix",options.get(1));
        itemObject.put("content","否");
        jsonArray.add(itemObject);
        object.put("items",jsonArray);
        ExQuestion question=new ExQuestion();
        question.setQuestionType(3);
        question.setBankId(16L);
        question.setCompanyId(21L);
        question.setAnswer(index==0?options.get(0):options.get(1));
        question.setTitle(title);
        question.setPrivatize(0);
        question.setContent(JSONObject.toJSONString(object));
        return question;
    }
    public String randomInt(int answer){
        int a=random.nextInt(2000);
        while (a==answer) {
            a = random.nextInt(2000);
        }
        return a+"";
    }
}
exam-common/pom.xml
@@ -104,6 +104,14 @@
            <artifactId>fastjson2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2-extension</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2-extension-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
        </dependency>
@@ -139,6 +147,11 @@
            <artifactId>jaudiotagger</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>
    </dependencies>
</project>
exam-common/src/main/java/com/gkhy/exam/common/annotation/DataDesensitization.java
对比新文件
@@ -0,0 +1,13 @@
package com.gkhy.exam.common.annotation;
import java.lang.annotation.*;
/**
 * 数据脱敏注解,方法含有该注解表示需要数据脱敏
 */
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataDesensitization {
}
exam-common/src/main/java/com/gkhy/exam/common/annotation/DataDesensitizationType.java
对比新文件
@@ -0,0 +1,19 @@
package com.gkhy.exam.common.annotation;
import com.gkhy.exam.common.enums.SensitiveTypeEnum;
import java.lang.annotation.*;
/**
 * 数据脱敏注解,字段上含有该注解表示需要数据脱敏
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataDesensitizationType {
    /**
     * 脱敏类型(规则)
     * @return
     */
    SensitiveTypeEnum type();
}
exam-common/src/main/java/com/gkhy/exam/common/config/FFmpegConfig.java
@@ -20,8 +20,11 @@
    @Bean
    public FFmpeg fFmpeg() {
        String path = System.getProperty("user.dir");
        if(path.endsWith("exam-admin")){
            path=path.replace("\\exam-admin","");
        }
        if (isLinux()){
            path+="ffmpeg/ffmpeg-linux/ffmpeg";
            path+="/ffmpeg/ffmpeg-linux/ffmpeg";
        }else if (isWindows()){
            path+="/ffmpeg/ffmpeg-win/bin/ffmpeg.exe";
        }
@@ -34,7 +37,7 @@
    public FFprobe fFprobe() {
        String path = System.getProperty("user.dir");
        if (isLinux()){
            path+="ffmpeg/ffmpeg-linux/ffprobe";
            path+="/ffmpeg/ffmpeg-linux/ffprobe";
        }else if (isWindows()){
            path+="/ffmpeg/ffmpeg-win/bin/ffprobe.exe";
        }
exam-common/src/main/java/com/gkhy/exam/common/constant/Constant.java
@@ -142,4 +142,14 @@
     */
    public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
            "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" };
    /**
     * 考试通过
     */
    public static final Integer EXAM_PASS = 1;
    /**
     * 考试未通过
     */
    public static final Integer EXAM_UNPASS = 0;
}
exam-common/src/main/java/com/gkhy/exam/common/domain/TableSupport.java
@@ -42,7 +42,11 @@
    {
        PageDomain pageDomain = new PageDomain();
        pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
        pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
        Integer pageSize=Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10);
        if(pageSize>100){
            pageSize=100;
        }
        pageDomain.setPageSize(pageSize);
        pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
        pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
        pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
exam-common/src/main/java/com/gkhy/exam/common/domain/entity/SysUser.java
@@ -39,6 +39,7 @@
    private Long id;
    @NotBlank(message = "登录账号不能为空")
    @Length(min = 2,  message = "登录账号长度不正确")
    @ApiModelProperty(value = "登录账号",required = true)
    @TableField("username")
    private String username;
@@ -73,6 +74,7 @@
    private Long companyId;
    @NotBlank(message = "密码不能为空")
    @Length(min = 2,  message = "密码长度不正确")
    @ApiModelProperty(value = "密码",required = true)
    @TableField("password")
    private String password;
@@ -106,8 +108,13 @@
    @TableField(exist = false)
    private String companyName;
    @ApiModelProperty("剩余课时(分)")
    @TableField(exist = false)
    private Long remainPeriod;
    @ApiModelProperty("父级账号名称")
    @TableField(exist = false)
    private String parentName;
}
exam-common/src/main/java/com/gkhy/exam/common/enums/PaperStudentStateEnum.java
对比新文件
@@ -0,0 +1,29 @@
package com.gkhy.exam.common.enums;
/**
 * 考试试卷状态
 */
public enum PaperStudentStateEnum {
    WAIT_EXAM(0, "待考试"),
    WAIT_REVIEW(1, "待批改"),
    DONE_REVIEW(2, "批改完成");
    private final Integer code;
    private final String info;
    PaperStudentStateEnum(Integer code, String info)
    {
        this.code = code;
        this.info = info;
    }
    public Integer getCode()
    {
        return code;
    }
    public String getInfo()
    {
        return info;
    }
}
exam-common/src/main/java/com/gkhy/exam/common/enums/QuestionTypeEnum.java
@@ -6,7 +6,8 @@
public enum QuestionTypeEnum {
    SINGLE(1, "单选题"),
    MULTI(2, "多选题"),
    JUDGE(3, "判断题");
    JUDGE(3, "判断题"),
    EASY(4, "简答题");
    private final Integer code;
    private final String info;
exam-common/src/main/java/com/gkhy/exam/common/enums/SensitiveTypeEnum.java
对比新文件
@@ -0,0 +1,10 @@
package com.gkhy.exam.common.enums;
/**
 * 脱敏类型枚举
 */
public enum SensitiveTypeEnum {
    CHINESE_NAME,
    ID_CARD,
    MOBILE_PHONE;
}
exam-common/src/main/java/com/gkhy/exam/common/enums/StudentAnswerPassEnum.java
对比新文件
@@ -0,0 +1,29 @@
package com.gkhy.exam.common.enums;
/**
 * 考试试卷状态
 */
public enum StudentAnswerPassEnum {
    ERROR(0, "错误"),
    CORRECT(1, "正确"),
    WAIT_REVIEW(2, "待批改");
    private final Integer code;
    private final String info;
    StudentAnswerPassEnum(Integer code, String info)
    {
        this.code = code;
        this.info = info;
    }
    public Integer getCode()
    {
        return code;
    }
    public String getInfo()
    {
        return info;
    }
}
exam-common/src/main/java/com/gkhy/exam/common/excel/StudentExcelData.java
对比新文件
@@ -0,0 +1,51 @@
package com.gkhy.exam.common.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class StudentExcelData {
    @ExcelProperty("用户序号")
    private String index;
    @ExcelProperty("公司名称")
    private String companyName;
    @ExcelProperty("姓名")
    private String name;
    @ExcelProperty("用户性别")
    private String sex;
    @ExcelProperty("身份证号")
    private String idNo;
    @ExcelProperty("手机号码")
    private String phone;
    @ExcelProperty("密码")
    private String password;
    @ExcelProperty("部门名称")
    private String deptName;
    @ExcelProperty("是否是部门级账号")
    private String isDept;
    @ExcelProperty("车间名称")
    private String workShopName;
    @ExcelProperty("是否是车间级账号")
    private String isWork;
    @ExcelProperty("岗位")
    private String post;
    @ExcelProperty("职务")
    private String duty;
    @ExcelProperty("备注")
    private String remark;
}
exam-common/src/main/java/com/gkhy/exam/common/excel/StudentExcelDataListener.java
对比新文件
@@ -0,0 +1,17 @@
package com.gkhy.exam.common.excel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
public class StudentExcelDataListener extends AnalysisEventListener<StudentExcelData> {
    @Override
    public void invoke(StudentExcelData studentExcelData, AnalysisContext analysisContext) {
        System.out.println(studentExcelData);
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //所有数据解析完成后的操作
        System.out.println("doAfterAllAnalysed");
    }
}
exam-common/src/main/java/com/gkhy/exam/common/filter/XssFilter.java
对比新文件
@@ -0,0 +1,70 @@
package com.gkhy.exam.common.filter;
import com.gkhy.exam.common.utils.StringUtils;
import org.springframework.http.HttpMethod;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
 * 防止XSS攻击的过滤器
 *
 */
public class XssFilter implements Filter
{
    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        if (StringUtils.isNotBlank(tempExcludes))
        {
            String[] url = tempExcludes.split(",");
            for (int i = 0; url != null && i < url.length; i++)
            {
                excludes.add(url[i]);
            }
        }
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if (handleExcludeURL(req, resp))
        {
            chain.doFilter(request, response);
            return;
        }
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }
    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
    {
        String url = request.getServletPath();
        String method = request.getMethod();
        // GET DELETE 不过滤
        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method))
        {
            return true;
        }
        return StringUtils.matches(url, excludes);
    }
    @Override
    public void destroy()
    {
    }
}
exam-common/src/main/java/com/gkhy/exam/common/filter/XssHttpServletRequestWrapper.java
对比新文件
@@ -0,0 +1,111 @@
package com.gkhy.exam.common.filter;
import com.gkhy.exam.common.utils.StringUtils;
import com.gkhy.exam.common.utils.html.EscapeUtil;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
 * XSS过滤处理
 *
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
{
    /**
     * @param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request)
    {
        super(request);
    }
    @Override
    public String[] getParameterValues(String name)
    {
        String[] values = super.getParameterValues(name);
        if (values != null)
        {
            int length = values.length;
            String[] escapesValues = new String[length];
            for (int i = 0; i < length; i++)
            {
                // 防xss攻击和过滤前后空格
                escapesValues[i] = EscapeUtil.clean(values[i]).trim();
            }
            return escapesValues;
        }
        return super.getParameterValues(name);
    }
    @Override
    public ServletInputStream getInputStream() throws IOException
    {
        // 非json类型,直接返回
        if (!isJsonRequest())
        {
            return super.getInputStream();
        }
        // 为空,直接返回
        String json = IOUtils.toString(super.getInputStream(), "utf-8");
        if (StringUtils.isBlank(json))
        {
            return super.getInputStream();
        }
        // xss过滤
        json = EscapeUtil.clean(json).trim();
        byte[] jsonBytes = json.getBytes("utf-8");
        final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes);
        return new ServletInputStream()
        {
            @Override
            public boolean isFinished()
            {
                return true;
            }
            @Override
            public boolean isReady()
            {
                return true;
            }
            @Override
            public int available() throws IOException
            {
                return jsonBytes.length;
            }
            @Override
            public void setReadListener(ReadListener readListener)
            {
            }
            @Override
            public int read() throws IOException
            {
                return bis.read();
            }
        };
    }
    /**
     * 是否是Json请求
     *
     * @param request
     */
    public boolean isJsonRequest()
    {
        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
        return StringUtils.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }
}
exam-common/src/main/java/com/gkhy/exam/common/utils/DesenseUtil.java
对比新文件
@@ -0,0 +1,78 @@
package com.gkhy.exam.common.utils;
import cn.hutool.core.util.DesensitizedUtil;
import com.gkhy.exam.common.annotation.DataDesensitizationType;
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.common.api.CommonResult;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
/**
 * 脱密工具类,(缺陷,不支持子类数据脱密)
 */
public class DesenseUtil {
    public static void assertResult(Object data) throws ClassNotFoundException, IllegalAccessException {
        if(data instanceof CommonResult){
            CommonResult cr= (CommonResult) data;
            Object obj=cr.getData();
            if(obj instanceof CommonPage){
                assetListResult(((CommonPage)obj).getList());
            }else if (obj instanceof List){
                assetListResult(obj);
            }else{
                assetOneResult(obj);
            }
        }
    }
    public static void assetListResult(Object data) throws ClassNotFoundException, IllegalAccessException {
        List list=(List) data;
        for (Object record : list) {
            Class<?> targetClass = Class.forName(record.getClass().getName());
            Field[] fields = targetClass.getDeclaredFields();
            reflexUpdateData(record, fields);
        }
    }
    public static void assetOneResult(Object data) throws IllegalAccessException, ClassNotFoundException {
        Class<?> targetClass = Class.forName(data.getClass().getName());
        Field[] fields=targetClass.getDeclaredFields();
        reflexUpdateData(data,fields);
    }
    public static void reflexUpdateData(Object data, Field[] fields) throws IllegalAccessException {
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = field.get(data);
            if (!(value instanceof String)) {
                continue;
            }
            String valueStr = (String) value;
            Annotation[] annotations = field.getDeclaredAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof DataDesensitizationType) {
                    DataDesensitizationType targetAnnotation = (DataDesensitizationType) annotation;
                    switch (targetAnnotation.type()) {
                        case CHINESE_NAME: {
                            field.set(data, DesensitizedUtil.chineseName(valueStr));
                            break;
                        }
                        case ID_CARD: {
                            field.set(data, DesensitizedUtil.idCardNum(valueStr, 3, 3));
                            break;
                        }
                        case MOBILE_PHONE: {
                            field.set(data, DesensitizedUtil.mobilePhone(valueStr));
                            break;
                        }
                        default: {
                            break;
                        }
                    }
                }
            }
        }
    }
}
exam-common/src/main/java/com/gkhy/exam/common/utils/M3u8Utils.java
@@ -100,8 +100,9 @@
        String path = new File(System.getProperty("user.dir")).getAbsolutePath();
        path=path+filePath.getBasePath()+mainName;
        if (!FileUtil.exist(path)) {
            FileUtil.mkdir(path);
            FileUtil.del(path);
        }
        FileUtil.mkdir(path);
        String m3u8FileName = path+"/" + mainName + ".m3u8";
        //下面这一串参数别乱动,经过调优的,1G视频大概需要10秒左右,如果是大佬随意改
@@ -119,7 +120,9 @@
                .setStrict(FFmpegBuilder.Strict.STRICT)
                .setFormat("hls")
                .setPreset("ultrafast")
                .addExtraArgs("-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_wrap", "0", "-hls_time", "10", "-hls_list_size", "0", "-threads", "12")
               // .addExtraArgs("-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_wrap", "0", "-hls_time", "10", "-hls_list_size", "0", "-threads", "12")  #新版本hls_wrap参数不支持
               // .addExtraArgs("-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_flags", "delete_segments", "-hls_time", "10", "-hls_list_size", "0", "-threads", "12")
                .addExtraArgs("-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_flags", "delete_segments", "-hls_time", "10", "-hls_list_size", "0", "-threads", "6")
                .done();
        FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
exam-common/src/main/java/com/gkhy/exam/common/utils/SecurityUtils.java
@@ -79,6 +79,22 @@
    }
    /**
     * 获取用户
     **/
    public static LoginUserDetails getLoginUserWithoutError()
    {
        try
        {
            return (LoginUserDetails) getAuthentication().getPrincipal();
        }
        catch (Exception e)
        {
            return null;
        }
    }
    /**
     * 获取Authentication
exam-common/src/main/java/com/gkhy/exam/common/utils/ServletUtils.java
@@ -110,6 +110,7 @@
    {
        try
        {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
exam-common/src/main/java/com/gkhy/exam/common/utils/html/EscapeUtil.java
对比新文件
@@ -0,0 +1,167 @@
package com.gkhy.exam.common.utils.html;
import com.gkhy.exam.common.utils.StringUtils;
/**
 * 转义和反转义工具类
 *
 * @author ruoyi
 */
public class EscapeUtil
{
    public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
    private static final char[][] TEXT = new char[64][];
    static
    {
        for (int i = 0; i < 64; i++)
        {
            TEXT[i] = new char[] { (char) i };
        }
        // special HTML characters
        TEXT['\''] = "&#039;".toCharArray(); // 单引号
        TEXT['"'] = "&#34;".toCharArray(); // 双引号
        TEXT['&'] = "&#38;".toCharArray(); // &符
        TEXT['<'] = "&#60;".toCharArray(); // 小于号
        TEXT['>'] = "&#62;".toCharArray(); // 大于号
    }
    /**
     * 转义文本中的HTML字符为安全的字符
     *
     * @param text 被转义的文本
     * @return 转义后的文本
     */
    public static String escape(String text)
    {
        return encode(text);
    }
    /**
     * 还原被转义的HTML特殊字符
     *
     * @param content 包含转义符的HTML内容
     * @return 转换后的字符串
     */
    public static String unescape(String content)
    {
        return decode(content);
    }
    /**
     * 清除所有HTML标签,但是不删除标签内的内容
     *
     * @param content 文本
     * @return 清除标签后的文本
     */
    public static String clean(String content)
    {
        return new HTMLFilter().filter(content);
    }
    /**
     * Escape编码
     *
     * @param text 被编码的文本
     * @return 编码后的字符
     */
    private static String encode(String text)
    {
        if (StringUtils.isEmpty(text))
        {
            return StringUtils.EMPTY;
        }
        final StringBuilder tmp = new StringBuilder(text.length() * 6);
        char c;
        for (int i = 0; i < text.length(); i++)
        {
            c = text.charAt(i);
            if (c < 256)
            {
                tmp.append("%");
                if (c < 16)
                {
                    tmp.append("0");
                }
                tmp.append(Integer.toString(c, 16));
            }
            else
            {
                tmp.append("%u");
                if (c <= 0xfff)
                {
                    // issue#I49JU8@Gitee
                    tmp.append("0");
                }
                tmp.append(Integer.toString(c, 16));
            }
        }
        return tmp.toString();
    }
    /**
     * Escape解码
     *
     * @param content 被转义的内容
     * @return 解码后的字符串
     */
    public static String decode(String content)
    {
        if (StringUtils.isEmpty(content))
        {
            return content;
        }
        StringBuilder tmp = new StringBuilder(content.length());
        int lastPos = 0, pos = 0;
        char ch;
        while (lastPos < content.length())
        {
            pos = content.indexOf("%", lastPos);
            if (pos == lastPos)
            {
                if (content.charAt(pos + 1) == 'u')
                {
                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
                    tmp.append(ch);
                    lastPos = pos + 6;
                }
                else
                {
                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
                    tmp.append(ch);
                    lastPos = pos + 3;
                }
            }
            else
            {
                if (pos == -1)
                {
                    tmp.append(content.substring(lastPos));
                    lastPos = content.length();
                }
                else
                {
                    tmp.append(content.substring(lastPos, pos));
                    lastPos = pos;
                }
            }
        }
        return tmp.toString();
    }
    public static void main(String[] args)
    {
        String html = "<script>alert(1);</script>";
        String escape = EscapeUtil.escape(html);
        // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
        // String html = "<123";
        // String html = "123>";
        System.out.println("clean: " + EscapeUtil.clean(html));
        System.out.println("escape: " + escape);
        System.out.println("unescape: " + EscapeUtil.unescape(escape));
    }
}
exam-common/src/main/java/com/gkhy/exam/common/utils/html/HTMLFilter.java
对比新文件
@@ -0,0 +1,566 @@
package com.gkhy.exam.common.utils.html;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * HTML过滤器,用于去除XSS漏洞隐患。
 *
 * @author ruoyi
 */
public final class HTMLFilter
{
    /**
     * regex flag union representing /si modifiers in php
     **/
    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
    private static final Pattern P_END_ARROW = Pattern.compile("^>");
    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
    private static final Pattern P_AMP = Pattern.compile("&");
    private static final Pattern P_QUOTE = Pattern.compile("\"");
    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
    // @xxx could grow large... maybe use sesat's ReferenceMap
    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
    /**
     * set of allowed html elements, along with allowed attributes for each element
     **/
    private final Map<String, List<String>> vAllowed;
    /**
     * counts of open tags for each (allowable) html element
     **/
    private final Map<String, Integer> vTagCounts = new HashMap<>();
    /**
     * html elements which must always be self-closing (e.g. "<img />")
     **/
    private final String[] vSelfClosingTags;
    /**
     * html elements which must always have separate opening and closing tags (e.g. "<b></b>")
     **/
    private final String[] vNeedClosingTags;
    /**
     * set of disallowed html elements
     **/
    private final String[] vDisallowed;
    /**
     * attributes which should be checked for valid protocols
     **/
    private final String[] vProtocolAtts;
    /**
     * allowed protocols
     **/
    private final String[] vAllowedProtocols;
    /**
     * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
     **/
    private final String[] vRemoveBlanks;
    /**
     * entities allowed within html markup
     **/
    private final String[] vAllowedEntities;
    /**
     * flag determining whether comments are allowed in input String.
     */
    private final boolean stripComment;
    private final boolean encodeQuotes;
    /**
     * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
     * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
     */
    private final boolean alwaysMakeTags;
    /**
     * Default constructor.
     */
    public HTMLFilter()
    {
        vAllowed = new HashMap<>();
        final ArrayList<String> a_atts = new ArrayList<>();
        a_atts.add("href");
        a_atts.add("target");
        vAllowed.put("a", a_atts);
        final ArrayList<String> img_atts = new ArrayList<>();
        img_atts.add("src");
        img_atts.add("width");
        img_atts.add("height");
        img_atts.add("alt");
        vAllowed.put("img", img_atts);
        final ArrayList<String> no_atts = new ArrayList<>();
        vAllowed.put("b", no_atts);
        vAllowed.put("strong", no_atts);
        vAllowed.put("i", no_atts);
        vAllowed.put("em", no_atts);
        vSelfClosingTags = new String[] { "img" };
        vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
        vDisallowed = new String[] {};
        vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
        vProtocolAtts = new String[] { "src", "href" };
        vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
        vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
        stripComment = true;
        encodeQuotes = true;
        alwaysMakeTags = false;
    }
    /**
     * Map-parameter configurable constructor.
     *
     * @param conf map containing configuration. keys match field names.
     */
    @SuppressWarnings("unchecked")
    public HTMLFilter(final Map<String, Object> conf)
    {
        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
        vDisallowed = (String[]) conf.get("vDisallowed");
        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
        stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
    }
    private void reset()
    {
        vTagCounts.clear();
    }
    // ---------------------------------------------------------------
    // my versions of some PHP library functions
    public static String chr(final int decimal)
    {
        return String.valueOf((char) decimal);
    }
    public static String htmlSpecialChars(final String s)
    {
        String result = s;
        result = regexReplace(P_AMP, "&amp;", result);
        result = regexReplace(P_QUOTE, "&quot;", result);
        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
        return result;
    }
    // ---------------------------------------------------------------
    /**
     * given a user submitted input String, filter out any invalid or restricted html.
     *
     * @param input text (i.e. submitted by a user) than may contain html
     * @return "clean" version of input, with only valid, whitelisted html elements allowed
     */
    public String filter(final String input)
    {
        reset();
        String s = input;
        s = escapeComments(s);
        s = balanceHTML(s);
        s = checkTags(s);
        s = processRemoveBlanks(s);
        // s = validateEntities(s);
        return s;
    }
    public boolean isAlwaysMakeTags()
    {
        return alwaysMakeTags;
    }
    public boolean isStripComments()
    {
        return stripComment;
    }
    private String escapeComments(final String s)
    {
        final Matcher m = P_COMMENTS.matcher(s);
        final StringBuffer buf = new StringBuffer();
        if (m.find())
        {
            final String match = m.group(1); // (.*?)
            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
        }
        m.appendTail(buf);
        return buf.toString();
    }
    private String balanceHTML(String s)
    {
        if (alwaysMakeTags)
        {
            //
            // try and form html
            //
            s = regexReplace(P_END_ARROW, "", s);
            // 不追加结束标签
            s = regexReplace(P_BODY_TO_END, "<$1>", s);
            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
        }
        else
        {
            //
            // escape stray brackets
            //
            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
            //
            // the last regexp causes '<>' entities to appear
            // (we need to do a lookahead assertion so that the last bracket can
            // be used in the next pass of the regexp)
            //
            s = regexReplace(P_BOTH_ARROWS, "", s);
        }
        return s;
    }
    private String checkTags(String s)
    {
        Matcher m = P_TAGS.matcher(s);
        final StringBuffer buf = new StringBuffer();
        while (m.find())
        {
            String replaceStr = m.group(1);
            replaceStr = processTag(replaceStr);
            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
        }
        m.appendTail(buf);
        // these get tallied in processTag
        // (remember to reset before subsequent calls to filter method)
        final StringBuilder sBuilder = new StringBuilder(buf.toString());
        for (String key : vTagCounts.keySet())
        {
            for (int ii = 0; ii < vTagCounts.get(key); ii++)
            {
                sBuilder.append("</").append(key).append(">");
            }
        }
        s = sBuilder.toString();
        return s;
    }
    private String processRemoveBlanks(final String s)
    {
        String result = s;
        for (String tag : vRemoveBlanks)
        {
            if (!P_REMOVE_PAIR_BLANKS.containsKey(tag))
            {
                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
            }
            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
            if (!P_REMOVE_SELF_BLANKS.containsKey(tag))
            {
                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
            }
            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
        }
        return result;
    }
    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
    {
        Matcher m = regex_pattern.matcher(s);
        return m.replaceAll(replacement);
    }
    private String processTag(final String s)
    {
        // ending tags
        Matcher m = P_END_TAG.matcher(s);
        if (m.find())
        {
            final String name = m.group(1).toLowerCase();
            if (allowed(name))
            {
                if (!inArray(name, vSelfClosingTags))
                {
                    if (vTagCounts.containsKey(name))
                    {
                        vTagCounts.put(name, vTagCounts.get(name) - 1);
                        return "</" + name + ">";
                    }
                }
            }
        }
        // starting tags
        m = P_START_TAG.matcher(s);
        if (m.find())
        {
            final String name = m.group(1).toLowerCase();
            final String body = m.group(2);
            String ending = m.group(3);
            // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
            if (allowed(name))
            {
                final StringBuilder params = new StringBuilder();
                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
                final List<String> paramNames = new ArrayList<>();
                final List<String> paramValues = new ArrayList<>();
                while (m2.find())
                {
                    paramNames.add(m2.group(1)); // ([a-z0-9]+)
                    paramValues.add(m2.group(3)); // (.*?)
                }
                while (m3.find())
                {
                    paramNames.add(m3.group(1)); // ([a-z0-9]+)
                    paramValues.add(m3.group(3)); // ([^\"\\s']+)
                }
                String paramName, paramValue;
                for (int ii = 0; ii < paramNames.size(); ii++)
                {
                    paramName = paramNames.get(ii).toLowerCase();
                    paramValue = paramValues.get(ii);
                    // debug( "paramName='" + paramName + "'" );
                    // debug( "paramValue='" + paramValue + "'" );
                    // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
                    if (allowedAttribute(name, paramName))
                    {
                        if (inArray(paramName, vProtocolAtts))
                        {
                            paramValue = processParamProtocol(paramValue);
                        }
                        params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
                    }
                }
                if (inArray(name, vSelfClosingTags))
                {
                    ending = " /";
                }
                if (inArray(name, vNeedClosingTags))
                {
                    ending = "";
                }
                if (ending == null || ending.length() < 1)
                {
                    if (vTagCounts.containsKey(name))
                    {
                        vTagCounts.put(name, vTagCounts.get(name) + 1);
                    }
                    else
                    {
                        vTagCounts.put(name, 1);
                    }
                }
                else
                {
                    ending = " /";
                }
                return "<" + name + params + ending + ">";
            }
            else
            {
                return "";
            }
        }
        // comments
        m = P_COMMENT.matcher(s);
        if (!stripComment && m.find())
        {
            return "<" + m.group() + ">";
        }
        return "";
    }
    private String processParamProtocol(String s)
    {
        s = decodeEntities(s);
        final Matcher m = P_PROTOCOL.matcher(s);
        if (m.find())
        {
            final String protocol = m.group(1);
            if (!inArray(protocol, vAllowedProtocols))
            {
                // bad protocol, turn into local anchor link instead
                s = "#" + s.substring(protocol.length() + 1);
                if (s.startsWith("#//"))
                {
                    s = "#" + s.substring(3);
                }
            }
        }
        return s;
    }
    private String decodeEntities(String s)
    {
        StringBuffer buf = new StringBuffer();
        Matcher m = P_ENTITY.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.decode(match).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
        buf = new StringBuffer();
        m = P_ENTITY_UNICODE.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
        buf = new StringBuffer();
        m = P_ENCODE.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
        s = validateEntities(s);
        return s;
    }
    private String validateEntities(final String s)
    {
        StringBuffer buf = new StringBuffer();
        // validate entities throughout the string
        Matcher m = P_VALID_ENTITIES.matcher(s);
        while (m.find())
        {
            final String one = m.group(1); // ([^&;]*)
            final String two = m.group(2); // (?=(;|&|$))
            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
        }
        m.appendTail(buf);
        return encodeQuotes(buf.toString());
    }
    private String encodeQuotes(final String s)
    {
        if (encodeQuotes)
        {
            StringBuffer buf = new StringBuffer();
            Matcher m = P_VALID_QUOTES.matcher(s);
            while (m.find())
            {
                final String one = m.group(1); // (>|^)
                final String two = m.group(2); // ([^<]+?)
                final String three = m.group(3); // (<|$)
                // 不替换双引号为&quot;,防止json格式无效 regexReplace(P_QUOTE, "&quot;", two)
                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
            }
            m.appendTail(buf);
            return buf.toString();
        }
        else
        {
            return s;
        }
    }
    private String checkEntity(final String preamble, final String term)
    {
        return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&amp;" + preamble;
    }
    private boolean isValidEntity(final String entity)
    {
        return inArray(entity, vAllowedEntities);
    }
    private static boolean inArray(final String s, final String[] array)
    {
        for (String item : array)
        {
            if (item != null && item.equals(s))
            {
                return true;
            }
        }
        return false;
    }
    private boolean allowed(final String name)
    {
        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
    }
    private boolean allowedAttribute(final String name, final String paramName)
    {
        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/aspectj/DataDesensitizationAspect.java
对比新文件
@@ -0,0 +1,44 @@
package com.gkhy.exam.framework.aspectj;
import com.gkhy.exam.common.annotation.DataDesensitization;
import com.gkhy.exam.common.utils.DesenseUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
 * 数据脱密
 */
@Aspect
@Component
@Slf4j
public class DataDesensitizationAspect {
    @Autowired
    private HttpServletRequest request;
    @Pointcut("@annotation(com.gkhy.exam.common.annotation.DataDesensitization)")
    public void pointcut(){}
    @Around("pointcut()")
    public Object doaround(ProceedingJoinPoint point) throws Throwable {
        Object object=point.proceed();
        String uri = request.getRequestURI();
//        if(!uri.startsWith("/app/api")){
//            return object;
//        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        if(!method.isAnnotationPresent(DataDesensitization.class)){
            return object;
        }
        DesenseUtil.assertResult(object);
        return object;
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/aspectj/LogAspect.java
@@ -1,34 +1,34 @@
package com.gkhy.exam.framework.aspectj;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.gkhy.exam.common.annotation.Log;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.BusinessStatus;
import com.gkhy.exam.common.filter.PropertyPreExcludeFilter;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.common.utils.ServletUtils;
import com.gkhy.exam.common.utils.StringUtils;
import com.gkhy.exam.system.domain.SysOperLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Collections;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -42,204 +42,105 @@
    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
    private static final ThreadLocal<Long> TIME_THREADLOCAL=new NamedThreadLocal<>("Cost Time");
    /**
     * 处理请求前执行
     * @param joinPoint
     * @param controllerLog
     */
    @Before(value = "@annotation(controllerLog)")
    public void doBefore(JoinPoint joinPoint, Log controllerLog){
        TIME_THREADLOCAL.set(System.currentTimeMillis());
    @Pointcut("execution(public * com.gkhy.exam.*.controller..*.*(..))")
    public void logPointCut(){
    }
    /**
     * 处理完请求后执行
     * @param joinPoint
     * @param controllerLog
     * @param jsonResult
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)",returning ="jsonResult" )
    public void doAfterReturning(JoinPoint joinPoint,Log controllerLog,Object jsonResult){
        handleLog(joinPoint,controllerLog,null,jsonResult);
    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
        SysUser user= SecurityUtils.getLoginUserWithoutError()!=null?SecurityUtils.getLoginUserWithoutError().getUser():null;
        long startTime = System.currentTimeMillis();
        //获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        StringBuffer requestURL = request.getRequestURL();
        JSONObject webLog = new JSONObject();
        String urlStr = request.getRequestURL().toString();
        webLog.put("basePath", StringUtils.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
        webLog.put("ip", ServletUtil.getClientIP(request,null));
        webLog.put("method",request.getMethod());
        Object params=getParameter(method, joinPoint.getArgs());
        webLog.put("parameter",params);
        webLog.put("uri",request.getRequestURI());
        webLog.put("url",requestURL.toString());
        if(user!=null) {
            webLog.put("userName", user.getName());
        }
        log.info(webLog.toString());
        Object result = joinPoint.proceed();
        if (result == null) {
            //如果切到了 没有返回类型的void方法,这里直接返回
            return null;
        }
        long endTime = System.currentTimeMillis();
        webLog.put("result",StringUtils.sub(JSON.toJSONString(result),0,2000));
        webLog.put("spendTime",endTime - startTime);
        log.info(webLog.toString());
        return result;
    }
    /**
     * 拦截异常操作
     * @param joinPoint
     * @param controllerLog
     * @param e
     */
    @AfterThrowing(value = "@annotation(controllerLog)",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint,Log controllerLog,Exception e){
        handleLog(joinPoint,controllerLog,e,null);
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint,Exception e){
        //获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String urlStr = request.getRequestURL().toString();
        log.error("@AfterThrowing异常通知:url={},出错了error_message={}", urlStr,e.getMessage());
    }
    protected void handleLog(final JoinPoint joinPoint,Log controllerLog,final Exception e,Object jsonResult){
        try{
            HttpServletRequest request= ServletUtils.getRequest();
            SysUser user= SecurityUtils.getLoginUser().getUser();
            SysOperLog operLog=new SysOperLog();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            String ip= cn.hutool.extra.servlet.ServletUtil.getClientIP(request);
            operLog.setOperIp(ip);
            operLog.setOperUrl(StringUtils.sub(request.getRequestURI(),0,255));
            if(user!=null){
                operLog.setOperName(user.getUsername());
    /**
     * 根据方法和传入的参数获取请求参数
     */
    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            if(e!=null){
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.sub(e.getMessage(),0,2000));
            }
            String className=joinPoint.getTarget().getClass().getName();
            String methodName=joinPoint.getSignature().getName();
            operLog.setMethod(className+"."+methodName+"()");
            operLog.setRequestMethod(request.getMethod());
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
            operLog.setCostTime(System.currentTimeMillis()-TIME_THREADLOCAL.get());
            log.info(JSON.toJSONString(operLog));
           // AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
        }catch (Exception exp){
            log.error("异常信息:{}",exp.getMessage());
            exp.printStackTrace();
        }finally {
            TIME_THREADLOCAL.remove();
        }
    }
    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log 日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
    {
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData())
        {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog, log.excludeParamNames());
        }
        // 是否需要保存response,参数和值
        if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult))
        {
            operLog.setJsonResult(StringUtils.sub(JSON.toJSONString(jsonResult), 0, 2000));
        }
    }
    /**
     * 获取请求的参数,放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
    {
        Map<?, ?> paramsMap = getParamMap(ServletUtils.getRequest());
        String requestMethod = operLog.getRequestMethod();
        if (ObjectUtil.isEmpty(paramsMap)
                && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
        {
            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
            operLog.setOperParam(StringUtils.sub(params, 0, 2000));
        }
        else{
            operLog.setOperParam(StringUtils.sub(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
        }
    }
    /**
     * 获得所有请求参数
     *
     * @param request 请求对象{@link ServletRequest}
     * @return Map
     */
    private  Map<String, String> getParamMap(ServletRequest request)
    {
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> map = request.getParameterMap();
        for (Map.Entry<String, String[]> entry : Collections.unmodifiableMap(map).entrySet())
        {
            params.put(entry.getKey(), StringUtils.join(",",entry.getValue()));
        }
        return params;
    }
    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (Object o : paramsArray)
            {
                if (ObjectUtil.isNotNull(o) && !isFilterObject(o))
                {
                    try
                    {
                        String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                    }
            //将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>();
                String key = parameters[i].getName();
                if (StringUtils.isNotEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        return params.trim();
    }
    /**
     * 忽略敏感属性
     */
    public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
    {
        return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
    }
    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o)
    {
        Class<?> clazz = o.getClass();
        if (clazz.isArray())
        {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Object value : collection)
            {
                return value instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Object value : map.entrySet())
            {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
exam-framework/src/main/java/com/gkhy/exam/framework/config/ApplicationConfig.java
@@ -6,7 +6,6 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.cors.CorsConfiguration;
@@ -53,6 +52,13 @@
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        //是否允许请求带有验证信息
//        corsConfiguration.setAllowCredentials(true);
//        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
//        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
//        corsConfiguration.setAllowCredentials(true);
//        corsConfiguration.setMaxAge(168000L);
        //是否允许请求带有验证信息
        corsConfiguration.setAllowCredentials(true);
        // 允许访问的客户端域名
        corsConfiguration.addAllowedOriginPattern("*");
@@ -60,6 +66,7 @@
        corsConfiguration.addAllowedHeader("*");
        // 允许访问的方法名,GET POST等
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(168000L);
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
exam-framework/src/main/java/com/gkhy/exam/framework/config/FastjsonConfig.java
对比新文件
@@ -0,0 +1,68 @@
package com.gkhy.exam.framework.config;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.support.config.FastJsonConfig;
import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class FastjsonConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonConfig config=new FastJsonConfig();
        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        config.setCharset(StandardCharsets.UTF_8);
        config.setWriterFeatures(
                JSONWriter.Feature.WriteNullListAsEmpty,
                //json格式化
                JSONWriter.Feature.PrettyFormat,
                //输出map中value为null的数据
                JSONWriter.Feature.WriteMapNullValue,
                //输出boolean 为 false
                JSONWriter.Feature.WriteNullBooleanAsFalse,
                //输出list 为 []
                JSONWriter.Feature.WriteNullListAsEmpty,
                //输出number 为 0
                JSONWriter.Feature.WriteNullNumberAsZero,
                //输出字符串 为 ""
                JSONWriter.Feature.WriteNullStringAsEmpty,
                //对map进行排序
                JSONWriter.Feature.MapSortField
        );
        // 2. 添加fastjson转换器
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        // 3. 添加支持的媒体类型
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        supportedMediaTypes.add(MediaType.APPLICATION_PDF);
        supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XML);
        supportedMediaTypes.add(MediaType.IMAGE_GIF);
        supportedMediaTypes.add(MediaType.IMAGE_JPEG);
        supportedMediaTypes.add(MediaType.IMAGE_PNG);
        supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
        supportedMediaTypes.add(MediaType.TEXT_HTML);
        supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
        supportedMediaTypes.add(MediaType.TEXT_PLAIN);
        supportedMediaTypes.add(MediaType.TEXT_XML);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        //4 将convert添加到converters
        converter.setFastJsonConfig(config);
        converters.add(0,converter);
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/config/FilterConfig.java
对比新文件
@@ -0,0 +1,64 @@
package com.gkhy.exam.framework.config;
import com.gkhy.exam.common.filter.RepeatableFilter;
import com.gkhy.exam.common.filter.XssFilter;
import com.gkhy.exam.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;
/**
 * Filter配置
 *
 */
@Configuration
public class FilterConfig
{
    @Value("${xss.excludes}")
    private String excludes;
    @Value("${xss.urlPatterns}")
    private String urlPatterns;
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
    public FilterRegistrationBean xssFilterRegistration()
    {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        StringUtils.split(urlPatterns,10);
        registration.addUrlPatterns(String.join(",",StringUtils.split(urlPatterns, ",")));
        registration.setName("xssFilter");
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("excludes", excludes);
        registration.setInitParameters(initParameters);
        return registration;
    }
    /**
     * 重复请求校验
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public FilterRegistrationBean someFilterRegistration()
    {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new RepeatableFilter());
        registration.addUrlPatterns("/*");
        registration.setName("repeatableFilter");
        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
        return registration;
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/event/PaperStudentEvent.java
对比新文件
@@ -0,0 +1,16 @@
package com.gkhy.exam.framework.event;
import com.gkhy.exam.system.domain.ExPaperStudent;
import org.springframework.context.ApplicationEvent;
/**
 *
 */
public class PaperStudentEvent extends ApplicationEvent {
    private ExPaperStudent paperStudent;
    public PaperStudentEvent(ExPaperStudent paperStudent) {
        super(paperStudent);
        this.paperStudent=paperStudent;
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/event/PaperStudentListener.java
对比新文件
@@ -0,0 +1,82 @@
package com.gkhy.exam.framework.event;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gkhy.exam.common.enums.PaperStudentStateEnum;
import com.gkhy.exam.common.enums.QuestionTypeEnum;
import com.gkhy.exam.common.enums.StudentAnswerPassEnum;
import com.gkhy.exam.system.domain.ExExamPaper;
import com.gkhy.exam.system.domain.ExPaperStudent;
import com.gkhy.exam.system.domain.ExQuestion;
import com.gkhy.exam.system.domain.ExStudentAnswer;
import com.gkhy.exam.system.service.ExExamPaperService;
import com.gkhy.exam.system.service.ExPaperStudentService;
import com.gkhy.exam.system.service.ExQuestionService;
import com.gkhy.exam.system.service.ExStudentAnswerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Component
public class PaperStudentListener {
    @Autowired
    private ExExamPaperService examPaperService;
    @Autowired
    private ExStudentAnswerService studentAnswerService;
    @Autowired
    private ExQuestionService questionService;
    @Autowired
    private ExPaperStudentService paperStudentService;
    @EventListener
    public void PaperStudentListener(ExPaperStudent paperStudent){
        ExExamPaper examPaper=examPaperService.getById(paperStudent.getPaperId());
        List<ExStudentAnswer> studentAnswerList=studentAnswerService.list(Wrappers.<ExStudentAnswer>lambdaQuery()
                .eq(true,ExStudentAnswer::getStudentId,paperStudent.getStudentId())
                .eq(true,ExStudentAnswer::getPaperId,paperStudent.getPaperId()));
        List<ExQuestion> questionList=questionService.selectQuestionByPaperId(paperStudent.getPaperId());
        Map<Long, ExQuestion> collectMap = questionList.stream().collect(Collectors.toMap(ExQuestion::getId, k -> k));
        Integer totalScore=0;
        for(ExStudentAnswer studentAnswer:studentAnswerList){
            ExQuestion question=collectMap.get(studentAnswer.getQuestionId());
            if(question.getQuestionType().equals(QuestionTypeEnum.EASY.getCode())){
                studentAnswer.setPassed(StudentAnswerPassEnum.WAIT_REVIEW.getCode());
            }else{
                if(studentAnswer.getAnswer().equals(question.getAnswer())){
                    studentAnswer.setPassed(StudentAnswerPassEnum.CORRECT.getCode());
                    studentAnswer.setScore(getScore(examPaper,question.getQuestionType()));
                    totalScore=totalScore+studentAnswer.getScore();
                }else{
                    studentAnswer.setPassed(StudentAnswerPassEnum.ERROR.getCode());
                    studentAnswer.setScore(0);
                }
            }
        }
        studentAnswerService.updateBatchById(studentAnswerList);
        if(Optional.ofNullable(examPaper.getEasyNum()).orElse(0)<=0){
            //没有简答题,直接批改试卷
            paperStudent.setScore(totalScore);
            paperStudent.setState(PaperStudentStateEnum.DONE_REVIEW.getCode());
            paperStudentService.updateById(paperStudent);
        }
    }
    private Integer getScore(ExExamPaper examPaper,Integer questionType){
        if(questionType.equals(QuestionTypeEnum.SINGLE.getCode())){
            return examPaper.getSingleScore();
        }else if(questionType.equals(QuestionTypeEnum.MULTI.getCode())){
            return examPaper.getMultiScore();
        }else if(questionType.equals(QuestionTypeEnum.JUDGE.getCode())){
            return examPaper.getJudgeScore();
        }else if(questionType.equals(QuestionTypeEnum.EASY.getCode())){
            return examPaper.getEasyScore();
        }
        return 0;
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/exception/GlobalExceptionHandler.java
@@ -80,7 +80,7 @@
    public CommonResult handleRuntimeException(RuntimeException ex, HttpServletRequest request)
    {
        writeExceptionLogFile(ex);
        return CommonResult.failed(ex.getMessage());
        return CommonResult.failed("内部服务异常");
    }
    /**
exam-framework/src/main/java/com/gkhy/exam/framework/job/PaperStudentJob.java
对比新文件
@@ -0,0 +1,70 @@
package com.gkhy.exam.framework.job;
import com.gkhy.exam.system.domain.ExPaperStudent;
import com.gkhy.exam.system.mapper.ExPaperStudentMapper;
import com.gkhy.exam.system.service.ExPaperStudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
/**
 * 考生考试超时处理job
 */
@Slf4j
@Component
public class PaperStudentJob {
    @Autowired
    private ExPaperStudentMapper paperStudentMapper;
    @Autowired
    private ExPaperStudentService paperStudentService;
    /**
     * 建议:每1小时秒执行一次
     */
    @Scheduled(cron = "0 */10 * * * ?")
    public void doprogress() {
        try {
            int pageIndex = 1;
            int pageSize = 20;
            while (true) {
                List<ExPaperStudent> paperStudentList = paperStudentMapper.selectNoCompleteStudent((pageIndex - 1) * pageSize, pageSize);
                if (paperStudentList.isEmpty()) {
                    break;
                }
                for (ExPaperStudent paperStudent : paperStudentList) {
                    handle(paperStudent);
                }
                if (paperStudentList.size() < pageSize) {
                    break;
                }
                pageIndex = pageIndex + 1;
            }
        }catch (Exception e){
            log.error("PaperStudentJob doprogress error:{}",e.getMessage());
        }
    }
    public void handle(ExPaperStudent pStudent){
        Long currentDateTime = System.currentTimeMillis();
        LocalDateTime deadline = pStudent.getExamPaper().getDeadline();
        if (currentDateTime - deadline.toInstant(ZoneOffset.of("+8")).toEpochMilli() < 0) {
            if(pStudent.getExamPaper().getLimited()==1){
                if (pStudent.getStartTime() == null) {
                    return;
                }
                if(currentDateTime - pStudent.getStartTime() < pStudent.getExamPaper().getLimitTime() * 60 * 1000) {
                    return;
                }
            }else {
                return;
            }
        }
        paperStudentService.handlePaperData(pStudent);
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/job/UserStudyJob.java
@@ -8,6 +8,7 @@
import com.gkhy.exam.system.domain.ExStudentStudy;
import com.gkhy.exam.system.mapper.ExResourceMapper;
import com.gkhy.exam.system.mapper.ExStudentStudyMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@@ -18,6 +19,7 @@
/**
 * 学习进度job
 */
@Slf4j
@Component
public class UserStudyJob {
    @Autowired
@@ -31,22 +33,32 @@
     * 建议:每10秒执行一次
     */
    @Scheduled(cron = "*/10 * * * * ?")
    public void progress(){
        Set<String> keys = redisUtils.keys(CacheConstant.STUDY_PROCESS_KEY + "*");
        if(ObjectUtil.isNotEmpty(keys)){
            for(String key:keys){
                ExStudentStudy studentStudy = (ExStudentStudy) redisUtils.get(key);
                ExResource resource=resourceMapper.selectById(studentStudy.getResourceId());
                if(ResourceTypeEnum.VIDEO.getCode().equals(resource.getResourceType())||ResourceTypeEnum.AUDIO.getCode().equals(resource.getResourceType())){
                    studentStudy.setProgress(new BigDecimal(studentStudy.getCurrentDuration()).divide(new BigDecimal(resource.getResourceLength()),BigDecimal.ROUND_CEILING).multiply(BigDecimal.valueOf(100)));
                }else{
                    studentStudy.setProgress(BigDecimal.valueOf(studentStudy.getCurrentPage()).divide(BigDecimal.valueOf(resource.getDocPage()).multiply(BigDecimal.valueOf(100))));
    public void doprogress(){
        try {
            Set<String> keys = redisUtils.keys(CacheConstant.STUDY_PROCESS_KEY + "*");
            if (ObjectUtil.isNotEmpty(keys)) {
                for (String key : keys) {
                    ExStudentStudy studentStudy = (ExStudentStudy) redisUtils.get(key);
                    ExResource resource = resourceMapper.selectById(studentStudy.getResourceId());
                    if (ResourceTypeEnum.VIDEO.getCode().equals(resource.getResourceType()) || ResourceTypeEnum.AUDIO.getCode().equals(resource.getResourceType())) {
                        studentStudy.setProgress(new BigDecimal(studentStudy.getCurrentDuration()).divide(new BigDecimal(resource.getResourceLength()), BigDecimal.ROUND_CEILING).multiply(BigDecimal.valueOf(100)));
                    } else {
                        studentStudy.setProgress(BigDecimal.valueOf(studentStudy.getCurrentPage()).divide(BigDecimal.valueOf(resource.getDocPage()).multiply(BigDecimal.valueOf(100))));
                    }
                    studentStudyMapper.updateById(studentStudy);
                    redisUtils.del(key);
                }
                studentStudyMapper.updateById(studentStudy);
                redisUtils.del(key);
            }
        }catch (Exception e){
            log.error("UserStudyJob doprogress error:{}",e.getMessage());
        }
    }
    /**
     * 统计学员学习总进度
     */
    public void staticProgress(){
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/security/SecurityConfig.java
@@ -100,6 +100,7 @@
        permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
        httpSecurity
                .cors().and()
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 禁用HTTP响应标头
@@ -111,7 +112,7 @@
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                .antMatchers("/**/login", "/register", "/system/captcha/captchaImage").permitAll()
                .antMatchers("/**/login", "/register", "/system/captcha/captchaImage","/system/common/importExcel").permitAll()
                // 静态资源,可匿名访问
                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**","/**/favicon.ico","/**/images/**").permitAll()
                .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
@@ -128,6 +129,7 @@
        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }
    /**
     * 强散列哈希加密实现
     */
exam-framework/src/main/java/com/gkhy/exam/framework/security/handle/AuthenticationEntryPointImpl.java
@@ -31,7 +31,7 @@
    {
        String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        log.error(msg);
        ServletUtils.renderString(response, JSON.toJSONString(CommonResult.failed(ResultCode.FORBIDDEN,msg)));
        ServletUtils.renderString(response, JSON.toJSONString(CommonResult.failed(ResultCode.UNAUTHORIZED,msg)));
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/web/service/SysLoginService.java
@@ -25,6 +25,7 @@
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@Component
@@ -39,6 +40,8 @@
    private TokenService tokenService;
    @Autowired
    private ExStudentService studentService;
    @Autowired
    private HttpServletRequest request;
@@ -87,18 +90,18 @@
            authentication = authenticationManager.authenticate(authenticationToken);
            LoginUserDetails loginUserDetails= (LoginUserDetails) authentication.getPrincipal();
            passwordService.validate(loginUserDetails.getUser(),password);
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_SUCCESS, "登录成功"));
        //    AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_SUCCESS, "登录成功"));
            recordLoginInfo(loginUserDetails.getUser().getId(),LoginUserTagEnum.ADMIN_USER);
            return createLoginUser(loginUserDetails,LoginUserTagEnum.ADMIN_USER);
        }catch (Exception e){
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, "用户密码不匹配"));
             //   AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, "用户密码不匹配"));
                throw new ApiException("用户密码不匹配");
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, e.getMessage()));
              //  AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, e.getMessage()));
                throw new ApiException(e.getMessage());
            }
        }finally {
@@ -125,18 +128,18 @@
            authentication = authenticationManager.authenticate(authenticationToken);
            LoginUserDetails loginUserDetails= (LoginUserDetails) authentication.getPrincipal();
            passwordService.validate(loginUserDetails.getUser(),password);
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_SUCCESS, "登录成功"));
        //    AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_SUCCESS, "登录成功"));
            recordLoginInfo(loginUserDetails.getUser().getId(),LoginUserTagEnum.STUDENT_USER);
            return createLoginUser(loginUserDetails,LoginUserTagEnum.STUDENT_USER);
        }catch (Exception e){
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, "用户密码不匹配"));
               // AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, "用户密码不匹配"));
                throw new ApiException("用户密码不匹配");
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, e.getMessage()));
               // AsyncManager.me().execute(AsyncFactory.recordLoginInfo(username, Constant.LOGIN_FAIL, e.getMessage()));
                throw new ApiException(e.getMessage());
            }
        }finally {
@@ -193,6 +196,11 @@
    }
    public void logout(){
        tokenService.delTokenCache(request);
    }
}
exam-framework/src/main/java/com/gkhy/exam/framework/web/service/TokenService.java
@@ -1,12 +1,14 @@
package com.gkhy.exam.framework.web.service;
import cn.hutool.crypto.digest.DigestUtil;
import com.gkhy.exam.common.api.ResultCode;
import com.gkhy.exam.common.constant.CacheConstant;
import com.gkhy.exam.common.domain.model.LoginUser;
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.RedisUtils;
import com.gkhy.exam.common.utils.StringUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
@@ -101,16 +103,10 @@
     * 从token中获取JWT中的负载
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
        return Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.error("JWT格式验证失败:{}", token);
        }
        return claims;
    }
    /**
@@ -121,8 +117,9 @@
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        } catch (ExpiredJwtException e) {
            log.error("JWT过期:{}", token);
            throw new ApiException(ResultCode.UNAUTHORIZED);
        }
        return username;
    }
@@ -146,13 +143,13 @@
        String tagUsername = getUserNameFromToken(token);
        String username=tagUsername.substring(0,tagUsername.lastIndexOf("_"));
        if(StringUtils.isBlank(username)||!username.equals(userDetails.getUsername())){
            return false;
            throw new ApiException(ResultCode.UNAUTHORIZED);
        }
        String tokenKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+md5Encode(token));
        String userKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+username);
        String cacheToken= (String) redisUtils.get(tokenKey);
        if(StringUtils.isBlank(cacheToken)||isTokenExpired(cacheToken)){
            return false;
            throw new ApiException(ResultCode.UNAUTHORIZED);
        }
        if(isNeedUpdate(cacheToken)){
            String newToken=createToken(tagUsername);
@@ -162,6 +159,7 @@
        }
        return true;
    }
@@ -248,4 +246,17 @@
    public void delTokenCache(HttpServletRequest request){
        String token=getToken(request);
        String tokenKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+md5Encode(token));
        redisUtils.del(tokenKey);
        String tagUsername = getUserNameFromToken(token);
        String username=tagUsername.substring(0,tagUsername.lastIndexOf("_"));
        if(!StringUtils.isBlank(username)){
            String userKey=redisUtils.generateKey(CacheConstant.SYS_USER_TOKEN+username);
            redisUtils.del(userKey);
        }
    }
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExCompanyPeriod.java
@@ -48,13 +48,13 @@
    @TableField("origin")
    private String origin;
    @ApiModelProperty("变动情况(增减课时)")
    @ApiModelProperty("变动情况(增减课时)秒")
    @TableField("modify_period")
    private Integer modifyPeriod;
    private Long modifyPeriod;
    @ApiModelProperty("变动后剩余(分)")
    @ApiModelProperty("变动后剩余(秒)")
    @TableField("remain_period")
    private Integer remainPeriod;
    private Long remainPeriod;
    @ApiModelProperty("创建时间")
    @TableField("create_time")
exam-system/src/main/java/com/gkhy/exam/system/domain/ExCourse.java
@@ -13,7 +13,6 @@
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@@ -68,11 +67,10 @@
    @TableField("introduce")
    private String introduce;
    @ApiModelProperty("审批状态(0创建,1待审核,2审批通过,3审批不通过  默认0)")
    @ApiModelProperty("审批状态(0待提交,1待审核,2审批通过,3审批不通过  默认0)")
    @TableField("state")
    private Integer state;
    @NotNull(message = "提交公司id不能为空")
    @ApiModelProperty(value = "提交公司id",required = true)
    @TableField("company_id")
    private Long companyId;
@@ -81,11 +79,10 @@
    @TableField("privatize")
    private Integer privatize;
    @NotNull(message = "要求课时不能为空")
    @ApiModelProperty(value = "要求课时(秒)",required = true)
    @Min(value = 0,message = "课时格式不正确")
    @TableField("period")
    private Long period;
    @Length(min = 0, max = 50, message = "审批意见不能超过50个字符")
    @ApiModelProperty("审批意见")
    @TableField("message")
    private String message;
    @ApiModelProperty("乐观锁")
@@ -106,4 +103,9 @@
    private String categoryName;
    @ApiModelProperty(value = "要求课时(秒) 所有小结总和")
    @TableField(exist = false)
    private Long period;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExCoursePhase.java
@@ -92,7 +92,23 @@
    @ApiModelProperty("课程课时")
    @TableField(exist = false)
    private Integer coursePeriod;
    private Long coursePeriod;
    @ApiModelProperty("课程名称")
    @TableField(exist = false)
    private String courseName;
    @ApiModelProperty("学员人数")
    @TableField(exist = false)
    private Integer studentCount;
    @ApiModelProperty("公司名称")
    @TableField(exist = false)
    private String companyName;
    @ApiModelProperty("完成人数")
    @TableField(exist = false)
    private Integer finishCount;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExExamPaper.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.gkhy.exam.system.domain.vo.PaperStudentInfoVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
@@ -69,80 +70,106 @@
    private Integer limitTime;
    @ApiModelProperty("是否限制考试时间(0否,1是,默认0)")
    @TableField("limit")
    private Integer limit;
    @TableField("limited")
    private Integer limited;
    @NotNull(message = "单选题目数量不能为空")
    @Min(value = 1,message = "单选题目数量必须大于0")
//    @NotNull(message = "单选题目数量不能为空")
//    @Min(value = 1,message = "单选题目数量必须大于0")
    @ApiModelProperty(value = "单选题目数量",required = true)
    @TableField("single_num")
    private Integer singleNum;
    @NotNull(message = "单选题每题分数不能为空")
    @Min(value = 1,message = "单选题每题分数必须大于0")
//    @NotNull(message = "单选题每题分数不能为空")
//    @Min(value = 1,message = "单选题每题分数必须大于0")
    @ApiModelProperty(value = "单选题每题分数",required = true)
    @TableField("single_score")
    private Integer singleScore;
    @NotNull(message = "单选题题库不能为空")
   // @NotNull(message = "单选题题库不能为空")
    @ApiModelProperty(value = "单选题题库id",required = true)
    @TableField("single_bank_id")
    private Long singleBankId;
    @NotNull(message = "单选题出题方式不能为空")
  //  @NotNull(message = "单选题出题方式不能为空")
    @ApiModelProperty(value = "单选题出题方式1随机,2顺序,默认1",required = true)
    @TableField("single_method")
    private Integer singleMethod;
    @NotNull(message = "多选题目数量不能为空")
    @Min(value = 1,message = "多选题目数量必须大于0")
  //  @NotNull(message = "多选题目数量不能为空")
  //  @Min(value = 1,message = "多选题目数量必须大于0")
    @ApiModelProperty(value = "多选题目数量",required = true)
    @TableField("multi_num")
    private Integer multiNum;
    @NotNull(message = "多选题每题分数不能为空")
    @Min(value = 1,message = "多选题每题分数必须大于0")
 //   @NotNull(message = "多选题每题分数不能为空")
 //   @Min(value = 1,message = "多选题每题分数必须大于0")
    @ApiModelProperty(value = "多选题每题分数",required = true)
    @TableField("multi_score")
    private Integer multiScore;
    @NotNull(message = "多选题题库不能为空")
   // @NotNull(message = "多选题题库不能为空")
    @ApiModelProperty(value = "多选题题库id",required = true)
    @TableField("multi_bank_id")
    private Long multiBankId;
    @NotNull(message = "多选题出题方式不能为空")
  //  @NotNull(message = "多选题出题方式不能为空")
    @ApiModelProperty(value = "多选题出题方式1随机,2顺序,默认1",required = true)
    @TableField("multi_method")
    private Integer multiMethod;
    @NotNull(message = "判断题目数量不能为空")
    @Min(value = 1,message = "判断题目数量必须大于0")
 //   @NotNull(message = "判断题目数量不能为空")
 //   @Min(value = 1,message = "判断题目数量必须大于0")
    @ApiModelProperty(value = "判断题目数量",required = true)
    @TableField("judge_num")
    private Integer judgeNum;
    @NotNull(message = "判断题每题分数不能为空")
    @Min(value = 1,message = "判断题每题分数必须大于0")
//    @NotNull(message = "判断题每题分数不能为空")
 //   @Min(value = 1,message = "判断题每题分数必须大于0")
    @ApiModelProperty(value = "判断题每题分数",required = true)
    @TableField("judge_score")
    private Integer judgeScore;
    @NotNull(message = "判断题题库不能为空")
  //  @NotNull(message = "判断题题库不能为空")
    @ApiModelProperty(value = "判断题题库id",required = true)
    @TableField("judge_bank_id")
    private Long judgeBankId;
    @NotNull(message = "判断题出题方式不能为空")
 //   @NotNull(message = "判断题出题方式不能为空")
    @ApiModelProperty(value = "判断题出题方式1随机,2顺序,默认1",required = true)
    @TableField("judge_method")
    private Integer judgeMethod;
    @ApiModelProperty(value = "简答题题目数量",required = true)
    @TableField("easy_num")
    private Integer easyNum;
//    @NotNull(message = "简答题每题分数不能为空")
 //   @Min(value = 1,message = "简答题每题分数必须大于0")
    @ApiModelProperty(value = "简答题每题分数",required = true)
    @TableField("easy_score")
    private Integer easyScore;
//    @NotNull(message = "简答题题库不能为空")
    @ApiModelProperty(value = "简答题题库id",required = true)
    @TableField("easy_bank_id")
    private Long easyBankId;
 //   @NotNull(message = "简答题出题方式不能为空")
    @ApiModelProperty(value = "简答题出题方式1随机,2顺序,默认1",required = true)
    @TableField("easy_method")
    private Integer easyMethod;
    @NotNull(message = "合格分数不能为空")
    @Min(value = 1,message = "合格分数必须大于0")
    @ApiModelProperty(value = "合格分数",required = true)
    @TableField("pass_score")
    private Integer passScore;
    @NotNull(message = "考试截止时间不能为空")
    @ApiModelProperty("考试截止时间")
    @TableField("deadline")
    private LocalDateTime deadline;
    @ApiModelProperty("创建时间")
    @TableField("create_time")
@@ -183,26 +210,40 @@
    @ApiModelProperty("单选题是否重新出题,0否 1是(编辑时传参)")
    @TableField(exist = false)
    private Integer singleRebuild;
    private Integer singleRebuild=0;
    @ApiModelProperty("多选题是否重新出题,0否 1是(编辑时传参)")
    @TableField(exist = false)
    private Integer multiRebuild;
    private Integer multiRebuild=0;
    @ApiModelProperty("判断题是否重新出题,0否 1是(编辑时传参)")
    @TableField(exist = false)
    private Integer judgeRebuild;
    private Integer judgeRebuild=0;
    @ApiModelProperty("单选题列表")
    @ApiModelProperty("简答题是否重新出题,0否 1是(编辑时传参)")
    @TableField(exist = false)
    private List<ExQuestion> singleQuestions;
    private Integer easyRebuild=0;
    @ApiModelProperty("多选题列表")
    @ApiModelProperty("题目列表")
    @TableField(exist = false)
    private List<ExQuestion> multiQuestions;
    private List<ExQuestion> questions;
    @ApiModelProperty("判断题列表")
    @ApiModelProperty("学员统计信息")
    @TableField(exist = false)
    private List<ExQuestion> judgeQuestions;
    private PaperStudentInfoVO paperStudentInfoVO;
    @ApiModelProperty("单选题题库名称")
    @TableField(exist = false)
    private String singleBankName;
    @ApiModelProperty("多选题题库名称")
    @TableField(exist = false)
    private String multiBankName;
    @ApiModelProperty("判断题题库名称")
    @TableField(exist = false)
    private String judgeBankName;
    @ApiModelProperty("简答题题库名称")
    @TableField(exist = false)
    private String easyBankName;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExExamRecord.java
@@ -4,13 +4,18 @@
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.time.LocalDateTime;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
/**
 * <p>
@@ -24,6 +29,7 @@
@Setter
@TableName("ex_exam_record")
@ApiModel(value = "ExExamRecord对象", description = "线下教育登记表")
@JsonInclude(NON_NULL)
public class ExExamRecord implements Serializable {
    private static final long serialVersionUID = 1L;
@@ -36,34 +42,42 @@
    @TableField("company_id")
    private Long companyId;
    @ApiModelProperty("学员id")
    @NotNull(message = "学员id不能为空")
    @ApiModelProperty(value = "学员id",required = true)
    @TableField("student_id")
    private Long studentId;
    @ApiModelProperty("计划名称")
    @NotBlank(message = "计划名称不能为空")
    @ApiModelProperty(value = "计划名称",required = true)
    @TableField("plan_name")
    private String planName;
    @ApiModelProperty("课程名称")
    @NotBlank(message = "课程名称不能为空")
    @ApiModelProperty(value = "课程名称",required = true)
    @TableField("course_name")
    private String courseName;
    @ApiModelProperty("培训等级")
    @NotNull(message = "培训等级不能为空")
    @ApiModelProperty(value = "培训等级(1公司级 2部门级 3车间级 默认1)",required = true)
    @TableField("level")
    private String level;
    private Integer level;
    @ApiModelProperty("要求课时(分)")
    @NotNull(message = "要求课时不能为空")
    @ApiModelProperty(value = "要求课时(分)",required = true)
    @TableField("period")
    private Integer period;
    @ApiModelProperty("实际课时(分)")
    @NotNull(message = "实际课时不能为空")
    @ApiModelProperty(value = "实际课时(分)",required = true)
    @TableField("actual_period")
    private Integer actualPeriod;
    @NotNull(message = "考试成绩不能为空")
    @ApiModelProperty("考试成绩")
    @TableField("score")
    private Integer score;
    @NotNull(message = "是否合格不能为空")
    @ApiModelProperty("是否合格,0不合格 1合格 默认0")
    @TableField("passed")
    private Integer passed;
@@ -93,5 +107,9 @@
    @TableField(exist = false)
    private String companyName;
    @ApiModelProperty("学生对象")
    @TableField(exist = false)
    private ExStudent student;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExExerciseAnswer.java
@@ -1,6 +1,7 @@
package com.gkhy.exam.system.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
@@ -10,6 +11,8 @@
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.time.LocalDateTime;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
/**
 * <p>
@@ -23,6 +26,7 @@
@Setter
@TableName("ex_exercise_answer")
@ApiModel(value = "ExExerciseAnswer对象", description = "学员与练习题关系表")
@JsonInclude(NON_NULL)
public class ExExerciseAnswer implements Serializable {
    private static final long serialVersionUID = 1L;
exam-system/src/main/java/com/gkhy/exam/system/domain/ExPaperStudent.java
@@ -29,7 +29,7 @@
@TableName("ex_paper_student")
@ApiModel(value = "ExPaperStudent对象", description = "学员与考试题目关系表")
@JsonInclude(NON_NULL)
public class ExPaperStudent implements Serializable {
public class ExPaperStudent  implements Serializable {
    private static final long serialVersionUID = 1L;
@@ -55,9 +55,9 @@
    @TableField("passed")
    private Integer passed;
    @ApiModelProperty("是否完成考试(0否,1是 默认0)")
    @TableField("completed")
    private Integer completed;
//    @ApiModelProperty("是否完成考试(0否,1是 默认0)")
//    @TableField("completed")
//    private Integer completed;
    @ApiModelProperty("考试时长(分钟)")
    @TableField("use_time")
@@ -70,6 +70,10 @@
    @ApiModelProperty("考试结束时间(时间戳13位ms)")
    @TableField("end_time")
    private Long endTime;
    @ApiModelProperty("状态:0待考试,1待批阅 2批阅完成,默认0")
    @TableField("state")
    private Integer state;
    @ApiModelProperty("创建时间")
    @TableField("create_time")
@@ -100,17 +104,25 @@
    @TableField("create_id")
    private Long createId;
    @ApiModelProperty(value = "学生名称",required = true)
    @ApiModelProperty(value = "学生名称")
    @TableField(exist = false)
    private String studentName;
    @ApiModelProperty(value = "学生手机号",required = true)
    @ApiModelProperty(value = "学生手机号")
    @TableField(exist = false)
    private String studentPhone;
    @ApiModelProperty(value = "分配人名称",required = true)
    @ApiModelProperty(value = "分配人名称")
    @TableField(exist = false)
    private String createName;
    @ApiModelProperty(value = "公司id")
    @TableField(exist = false)
    private Long companyId;
    @ApiModelProperty(value = "公司名称")
    @TableField(exist = false)
    private String companyName;
    @ApiModelProperty("试卷对象")
    @TableField(exist = false)
@@ -120,16 +132,10 @@
    @TableField(exist = false)
    private ExStudent student;
    @ApiModelProperty("单选题列表")
    @TableField(exist = false)
    private List<ExQuestion> singleQuestions;
    @ApiModelProperty("多选题列表")
    @ApiModelProperty("题目列表")
    @TableField(exist = false)
    private List<ExQuestion> multiQuestions;
    private List<ExQuestion> questions;
    @ApiModelProperty("判断题列表")
    @TableField(exist = false)
    private List<ExQuestion> judgeQuestions;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExPhaseStudent.java
@@ -80,12 +80,17 @@
    @TableField(exist = false)
    private String createName;
    @ApiModelProperty("课时")
    @TableField(exist = false)
    private Integer period;
    @ApiModelProperty("课程对象")
    @TableField(exist = false)
    private ExCourse course;
    @ApiModelProperty("企业id")
    @TableField(exist = false)
    private Long companyId;
    @ApiModelProperty("企业名称")
    @TableField(exist = false)
    private String companyName;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExQuestion.java
@@ -39,7 +39,7 @@
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @NotNull(message = "考题类型不能为空")
    @ApiModelProperty(value = "考题类型(1单选题,2多选题,3判断题 默认1)",required = true)
    @ApiModelProperty(value = "考题类型(1单选题,2多选题,3判断题,4简单题 默认1)",required = true)
    @TableField("question_type")
    private Integer questionType;
@@ -48,7 +48,7 @@
    @TableField("bank_id")
    private Long bankId;
    @NotNull(message = "公司id不能为空")
    @ApiModelProperty("公司id")
    @TableField("company_id")
    private Long companyId;
@@ -57,7 +57,7 @@
    @TableField("status")
    private Integer status;
    @NotBlank(message = "答案不能为空")
    @NotBlank(message = "答案不能为空(判断题对传1,错误0)")
    @ApiModelProperty(value = "答案",required = true)
    @TableField("answer")
    private String answer;
@@ -75,7 +75,8 @@
    /**
     * json格式:
     * 单选和多选   {"analyze":"",items:[{"prefix":"A","content":"xxxx"},{"prefix":"B","content":"xxxx"},{"prefix":"C","content":"xxxx"},{"prefix":"D","content":"xxxx"}]}
     * 判断题   {"analyze":""}
     * 判断题   {"analyze":"",items[{"prefix":"A","content":"是"},{"prefix":"B","content":"否"}]}
     * 简答题   {"analyze":""}
     */
    @ApiModelProperty("题目内容(json字符串)")
    @TableField("content")
@@ -114,6 +115,10 @@
    @TableField(exist = false)
    private ExExerciseAnswer exExerciseAnswer;
    @ApiModelProperty("题库名称")
    @TableField(exist = false)
    private String bankName;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExQuestionBank.java
@@ -92,12 +92,32 @@
    @TableField(exist = false)
    private Long studentId;
    @ApiModelProperty("题目数量")
    @ApiModelProperty("题目总数量")
    @TableField(exist = false)
    private Integer totalCount;
    @ApiModelProperty("单选题数量")
    @TableField(exist = false)
    private Integer singleCount;
    @ApiModelProperty("多选题数量")
    @TableField(exist = false)
    private Integer multiCount;
    @ApiModelProperty("判断题数量")
    @TableField(exist = false)
    private Integer judgeCount;
    @ApiModelProperty("学员练习数量")
    @TableField(exist = false)
    private Integer exerciseCount;
    @ApiModelProperty("分类名称")
    @TableField(exist = false)
    private String categoryName;
    @ApiModelProperty("最新刷题的题目id")
    @TableField(exist = false)
    private Long questionId;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExStudent.java
@@ -5,6 +5,10 @@
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.gkhy.exam.common.annotation.DataDesensitizationType;
import com.gkhy.exam.common.domain.BaseEntity;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.SensitiveTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
@@ -15,7 +19,6 @@
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.time.LocalDateTime;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@@ -34,7 +37,7 @@
@Accessors(chain = true)
@ApiModel(value = "ExStudent对象", description = "学员表")
@JsonInclude(NON_NULL)
public class ExStudent implements Serializable {
public class ExStudent extends BaseEntity {
    private static final long serialVersionUID = 1L;
@@ -75,6 +78,7 @@
    @TableField("phone")
    private String phone;
    @DataDesensitizationType(type = SensitiveTypeEnum.ID_CARD)
    @NotBlank(message = "身份证号不能为空")
    @Length(min = 18, max = 18, message = "身份证号只能为18位")
    @ApiModelProperty(value = "身份证号",required = true)
@@ -97,26 +101,6 @@
    @TableField("create_id")
    private Long createId;
    @ApiModelProperty("创建时间")
    @TableField("create_time")
    private LocalDateTime createTime;
    @ApiModelProperty("创建人")
    @TableField("create_by")
    private String createBy;
    @ApiModelProperty("更新时间")
    @TableField("update_time")
    private LocalDateTime updateTime;
    @ApiModelProperty("更新人")
    @TableField("update_by")
    private String updateBy;
    @ApiModelProperty("备注")
    @TableField("remark")
    private String remark;
    @ApiModelProperty("乐观锁")
    @TableField("version")
    private Integer version;
@@ -137,5 +121,9 @@
    @TableField(exist = false)
    private SysCompany company;
    @ApiModelProperty("创建人信息")
    @TableField(exist = false)
    private SysUser createUser;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/ExStudentAnswer.java
@@ -58,7 +58,11 @@
    @TableField("answer")
    private String answer;
    @ApiModelProperty("是否正确(0错误,1正确)")
    @ApiModelProperty("得分")
    @TableField("score")
    private Integer score;
    @ApiModelProperty("是否正确(0错误,1正确,2待批改)")
    @TableField("passed")
    private Integer passed;
exam-system/src/main/java/com/gkhy/exam/system/domain/ExStudentStudy.java
@@ -72,9 +72,6 @@
    @TableField("progress")
    private BigDecimal progress;
    @ApiModelProperty("资源类型(PPT,MP4,PDF)")
    @TableField("resource_type")
    private String resourceType;
    @ApiModelProperty("创建时间")
    @TableField("create_time")
exam-system/src/main/java/com/gkhy/exam/system/domain/SysCategory.java
@@ -46,6 +46,10 @@
    @TableField("name")
    private String name;
    @ApiModelProperty("删除标志,0未删除,1删除,默认0")
    @TableField("del_flag")
    private Integer delFlag;
//    @ApiModelProperty("类型(1课程,2资源)")
//    @TableField("category_type")
//    private Integer categoryType;
exam-system/src/main/java/com/gkhy/exam/system/domain/SysCompany.java
@@ -10,6 +10,7 @@
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@@ -61,21 +62,19 @@
    private String phone;
    @ApiModelProperty("删除标志,0未删除,1删除,默认0")
    @TableField("delFlag")
    @TableField("del_flag")
    private Integer delFlag;
    @ApiModelProperty("剩余课时(分)")
    @ApiModelProperty("剩余课时(秒)")
    @TableField("remain_period")
    private Integer remainPeriod;
    private Long remainPeriod;
    @ApiModelProperty("已用课时(分)")
    @TableField("spend_period")
    private Integer spendPeriod;
    @NotNull(message = "总课时不能为空")
    @ApiModelProperty(value = "总课时(分)",required = true)
    @Max(value = 9999999999L,message = "总课时长度不能超过10位")
    @ApiModelProperty(value = "总课时(秒)",required = true)
    @TableField("total_period")
    private Integer totalPeriod;
    private Long totalPeriod;
    @Version
    @ApiModelProperty("乐观锁")
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/BatchPaperStudentVO.java
对比新文件
@@ -0,0 +1,29 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.util.List;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)  //链式写法
@ApiModel(value = "试卷批量绑定学员对象", description = "试卷批量绑定学员对象")
@JsonInclude(NON_NULL)
public class BatchPaperStudentVO {
    @ApiModelProperty("批次id列表")
    private List<Long> phaseIds;
    @ApiModelProperty("学员id列表")
    private List<Long> studentIds;
    @NotNull(message = "考卷id不能为空")
    @ApiModelProperty(value = "考卷id",required = true)
    private Long paperId;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyPaperStudentVO.java
@@ -1,13 +1,17 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)
@JsonInclude(NON_NULL)
public class CompanyPaperStudentVO {
    @ApiModelProperty("公司id")
    private Long companyId;
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyPhaseStudentVO.java
@@ -1,13 +1,17 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)
@JsonInclude(NON_NULL)
public class CompanyPhaseStudentVO {
    @ApiModelProperty("公司id")
    private Long companyId;
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyPhaseVO.java
@@ -1,13 +1,19 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)
@ApiModel(value = "CompanyPhaseVO对象", description = "公司批次统计对象")
@JsonInclude(NON_NULL)
public class CompanyPhaseVO {
    @ApiModelProperty("公司id")
    private Long companyId;
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/CompanyStatisticVO.java
@@ -1,15 +1,19 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)
@ApiModel(value = "CompanyStatisticVO对象", description = "公司数据统计对象")
@JsonInclude(NON_NULL)
public class CompanyStatisticVO {
    @ApiModelProperty("公司id")
    private Long companyId;
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/ExPaperStudentVO.java
对比新文件
@@ -0,0 +1,60 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.util.List;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)
@ApiModel(value = "ExPaperStudent对象", description = "学员与考试题目关系表")
@JsonInclude(NON_NULL)
public class ExPaperStudentVO{
    @NotNull(message = "学员与试卷关系id")
    @ApiModelProperty(value = "学员与试卷关系id",required = true)
    private Long id;
    @NotNull(message = "考卷id不能为空")
    @ApiModelProperty(value = "考卷id",required = true)
    private Long paperId;
    @NotNull(message = "学员id不能为空")
    @ApiModelProperty(value = "学员id",required = true)
    private Long studentId;
    @NotNull(message = "简答题题目列表不能为空")
    @ApiModelProperty("题目列表")
    private List<QuestionVO> questions;
    @Getter
    @Setter
    @NoArgsConstructor
    public static class QuestionVO {
        @NotNull(message = "题目id不能为空")
        @ApiModelProperty(value = "题目id",required = true)
        private Long questionId;
        @NotNull(message = "得分不能为空")
        @ApiModelProperty("得分")
        private Integer score;
        @NotNull(message = "题目分数不能为空")
        @ApiModelProperty("题目分数")
        private Integer questionScore;
    }
}
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/PaperStudentInfoVO.java
对比新文件
@@ -0,0 +1,34 @@
package com.gkhy.exam.system.domain.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)  //链式写法
@ApiModel(value = "试卷学员统计信息对象", description = "试卷学员统计信息对象")
@JsonInclude(NON_NULL)
public class PaperStudentInfoVO {
    @ApiModelProperty("学员人数")
    @TableField(exist = false)
    private Integer studentCount;
    @ApiModelProperty("合格人数")
    @TableField(exist = false)
    private Integer passStudentCount;
    @ApiModelProperty("平均分数")
    @TableField(exist = false)
    private String avgScore;
    @ApiModelProperty("完成人数")
    @TableField(exist = false)
    private Integer finishCount;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/StudentStudyPeriodVO.java
@@ -1,5 +1,6 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
@@ -9,10 +10,13 @@
import java.math.BigDecimal;
import java.time.LocalDateTime;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Getter
@Setter
@Accessors(chain = true)
@ApiModel(description = "课程用户学习日志")
@JsonInclude(NON_NULL)
public class StudentStudyPeriodVO {
    @ApiModelProperty(value = "课时id")
    private Long periodId;
@@ -37,4 +41,8 @@
    @ApiModelProperty(value = "资源id")
    private Long resourceId;
    @ApiModelProperty(value = "资源种类(1:视频2:音频;3:文档)")
    private Integer resourceType;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/StudentStudyVO.java
@@ -1,5 +1,6 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
@@ -9,10 +10,13 @@
import java.io.Serializable;
import java.util.List;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Setter
@Getter
@Accessors(chain = true)
@ApiModel(description = "课程用户学习日志")
@JsonInclude(NON_NULL)
public class StudentStudyVO implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "章节ID")
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/TrainRecordVO.java
对比新文件
@@ -0,0 +1,32 @@
package com.gkhy.exam.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
@Setter
@Getter
@Accessors(chain = true)
@ApiModel(description = "培训记录对象")
@JsonInclude(NON_NULL)
public class TrainRecordVO {
    @ApiModelProperty(value = "学员id")
    private Long studentId;
    @ApiModelProperty(value = "培训类型(1批次学习 2考试)")
    private Integer trainType;
    @ApiModelProperty(value = "批次或者考试名称")
    private String name;
    @ApiModelProperty(value = "培训时间")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "公司id")
    private Long companyId;
    @ApiModelProperty(value = "公司名称")
    private String companyName;
}
exam-system/src/main/java/com/gkhy/exam/system/domain/vo/UploadObjectVO.java
@@ -17,6 +17,10 @@
    @ApiModelProperty("文件保存相对路径")
    private String path;
    @ApiModelProperty("文件访问链接")
    private String  url;
    @ApiModelProperty("文件原始名称")
    private String originName;
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExCourseChapterPeriodMapper.java
@@ -47,4 +47,11 @@
     * @return
     */
    int deletePeriodByChapterId(Long chapterId);
    /**
     * 根据课程id获取课时数量
     * @param courseId
     * @return
     */
    int selectCountByCourseId(Long courseId);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExCourseMapper.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gkhy.exam.system.domain.ExCourse;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -36,7 +37,7 @@
     * @param name
     * @return
     */
    ExCourse checkNameUnique(String name);
    ExCourse checkNameUnique(@Param("name") String name, @Param("companyId") Long companyId);
    /**
     * 根据id删除
@@ -52,6 +53,13 @@
     */
    int selectCourseState(Long courseId);
    /**
     * 根据课程id查询课程绑定数量
     * @param categoryId
     * @return
     */
    int selectCountByCategoryId(Long categoryId);
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExExamPaperMapper.java
@@ -51,6 +51,6 @@
     * @param name
     * @return
     */
    ExExamPaper checkNameUnique(String name);
    ExExamPaper checkNameUnique(@Param("name") String name,@Param("companyId") Long companyId);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExPaperQuestionMapper.java
@@ -3,6 +3,7 @@
import com.gkhy.exam.system.domain.ExPaperQuestion;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -31,7 +32,7 @@
    /**
     * 删除考卷下试题
     * @param paperId
     * @param code
     * @param questionType
     */
    void deletePaperQuestion(Long paperId, Integer code);
    void deletePaperQuestion(@Param("paperId") Long paperId, @Param("questionType") Integer questionType);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExPaperStudentMapper.java
@@ -66,4 +66,29 @@
     * @return
     */
    ExPaperStudent selectByPaperStudentId(ExPaperStudent paperStudent);
    /**
     * 根据学员id获取学员分配的考试列表
     * @param studentId
     * @return
     */
    List<ExPaperStudent> selectByStudentId(Long studentId);
    /**
     * 分页获取未完成考试学生列表
     * @param startIndex
     * @param pageSize
     * @return
     */
    List<ExPaperStudent> selectNoCompleteStudent(int startIndex,int pageSize);
    /**
     * 批量更新完成状态
     * @param paperStudentIds
     * @param completed
     */
    void batchUpdateComplete(@Param("paperStudentIds") List<Long> paperStudentIds,@Param("completed") Integer completed);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExPhaseStudentMapper.java
@@ -52,4 +52,11 @@
     * @return
     */
    Integer selectCountByPhaseStudentId(@Param("phaseId") Long phaseId, @Param("studentId")Long studentId);
    /**
     * 根据学员id获取学员批次分配列表
     * @param studentId
     * @return
     */
    public List<ExPhaseStudent> selectPhaseStudentByStudentId(Long studentId);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExQuestionBankMapper.java
@@ -60,4 +60,26 @@
     * @return
     */
    ExQuestionBank selectQuestionBankByIdForStudent(@Param("bankId") Long bankId,@Param("studentId") Long studentId);
    /**
     * 查询题库题目数量
     * @param bankId
     * @return
     */
    int selectCountByBankId(Long bankId);
    /**
     * 根据课程id查询题库绑定数量
     * @param categoryId
     * @return
     */
    int selectCountByCategoryId(Long categoryId);
    /**
     * 根据id列表查询题库
     * @param bankIds
     * @return
     */
    List<ExQuestionBank> selectQuestionBankByIds(List<Long> bankIds);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExQuestionMapper.java
@@ -27,6 +27,13 @@
    Integer selectCountByBankId(@Param("companyId") Long companyId,@Param("bankId") Long bankId, @Param("questionType") Integer questionType);
    /**
     * 根据id获取题目信息
     * @param questionId
     * @return
     */
    ExQuestion selectByQuestionId(Long questionId);
    /**
     * 顺序获取指定数量的考题
     * @param bankId
     * @param questionType
@@ -53,10 +60,9 @@
    /**
     * 获取题库下面所有题目id列表
     * @param bankId
     * @param exerciseType
     * @return
     */
    List<Map> getExerciseQuestionList(@Param("bankId") Long bankId, @Param("exerciseType") Integer exerciseType,@Param("studentId") Long StundentId);
    List<Map> getExerciseQuestionList(@Param("bankId") Long bankId,@Param("studentId") Long StundentId);
    /**
     * 刷题模式下获取题目
@@ -76,36 +82,43 @@
    /**
     * 获取考卷下面所有题目id列表
     * @param paperId
     * @param completed
     * @param userId
     * @param state
     * @param studentId
     * @return
     */
    List<Map> getPaperQuestionList(@Param("paperId") Long paperId, @Param("completed") Integer completed, @Param("userId")Long userId);
    List<Map> getPaperQuestionList(@Param("paperId") Long paperId, @Param("state") Integer state, @Param("studentId")Long studentId, @Param("viewType")Integer viewType);
    /**
     * 根据id获取考卷下题目详情
     * @param questionId
     * @param completed
     * @param userId
     * @param state
     * @param studentId
     * @return
     */
    ExQuestion getPaperQuestionById(@Param("paperId")Long paperId,@Param("questionId")Long questionId, @Param("completed")Integer completed, @Param("userId")Long userId);
    ExQuestion getPaperQuestionById(@Param("paperId")Long paperId,@Param("questionId")Long questionId, @Param("state")Integer state, @Param("studentId")Long studentId);
    /**
     * 根据id列表批量获取考卷下题目详情
     * @param paperId
     * @param questionIds
     * @param completed
     * @param userId
     * @param state
     * @param studentId
     * @return
     */
    List<ExQuestion> getPaperQuestionByIds(@Param("paperId") Long paperId, @Param("questionIds")List<Long> questionIds, @Param("completed")Integer completed, @Param("userId")Long userId);
    List<ExQuestion> getPaperQuestionByIds(@Param("paperId") Long paperId, @Param("questionIds")List<Long> questionIds, @Param("state")Integer state, @Param("studentId")Long studentId);
    /**
     * 获取错题题目id
     * @param bankId
     * @param userId
     * @param studentId
     * @return
     */
    List<Long> getExerciseErrorQuestionList(@Param("bankId") Long bankId, @Param("userId") Long userId);
    List<Long> getExerciseErrorQuestionList(@Param("bankId") Long bankId, @Param("studentId") Long studentId);
    /**
     * 根据试卷id查询题目列表
     * @param paperId
     * @return
     */
    List<ExQuestion> selectQuestionByPaperId(Long paperId);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExResourceMapper.java
@@ -45,4 +45,11 @@
     * @return
     */
    ExResource selectResourceByPeriodId(Long periodId);
    /**
     * 查看资源是否被分配
     * @param resourceId
     * @return
     */
    int checkResourceAssign(Long resourceId);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/ExStudentMapper.java
@@ -57,4 +57,7 @@
     * @return
     */
    int deleteByStudentId(Long studentId);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/SysCategoryMapper.java
@@ -34,17 +34,9 @@
    SysCategory checkNameUnique(@Param("name") String name, @Param("parentId")Long parentId);
    /**
     * 根据课程id查询课程绑定数量
     * 根据id删除分类
     * @param categoryId
     * @return
     */
    int selectCountOfCoure(Long categoryId);
    /**
     * 根据课程id查询题库绑定数量
     * @param categoryId
     * @return
     */
    int selectCountOfBank(Long categoryId);
    int deleteByCategoryId(Long categoryId);
}
exam-system/src/main/java/com/gkhy/exam/system/mapper/SysUserMapper.java
@@ -66,4 +66,11 @@
     * @return
     */
    SysUser checkPhoneUnique(String phone);
    /**
     * 根据部门账号id获取车间级账号id列表
     * @param departUserId
     * @return
     */
    List<Long> selectWorkshopUserIds(Long departUserId);
}
exam-system/src/main/java/com/gkhy/exam/system/service/ExPaperStudentService.java
@@ -3,9 +3,10 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.system.domain.ExPaperStudent;
import com.gkhy.exam.system.domain.vo.BatchPaperStudentVO;
import com.gkhy.exam.system.domain.vo.ExPaperStudentVO;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -26,10 +27,10 @@
    /**
     * 批量新增考卷与学员关系
     * @param paperStudentMap
     * @param batchPaperStudentVO
     * @return
     */
    public int batchAddPaperStudent(Map<String,Object> paperStudentMap);
    public int batchAddPaperStudent(BatchPaperStudentVO batchPaperStudentVO);
    /**
     * 分页获取学员的试卷列表
@@ -89,4 +90,16 @@
     */
    public void checkStudentUnique(List<ExPaperStudent> paperStudents);
    /**
     * 批改试卷
     * @param paperStudentVO
     */
    public void doReview(ExPaperStudentVO paperStudentVO);
    /**
     * 计算试卷分数
     * @param paperStudent
     */
    public void handlePaperData(ExPaperStudent paperStudent);
}
exam-system/src/main/java/com/gkhy/exam/system/service/ExQuestionService.java
@@ -60,10 +60,9 @@
    /**
     * 获取题目ID列表
     * @param bankId
     * @param exerciseType
     * @return
     */
    List<Map> getExerciseQuestionList(Long bankId, Integer exerciseType);
    List<Map> getExerciseQuestionList(Long bankId);
    /**
     * 刷题模式下根据id获取题目
@@ -108,4 +107,11 @@
     * @return
     */
    List<Long> getExerciseErrorQuestionList(Long bankId);
    /**
     * 根据考卷获取题目列表
     * @param paperId
     * @return
     */
    List<ExQuestion> selectQuestionByPaperId(Long paperId);
}
exam-system/src/main/java/com/gkhy/exam/system/service/ExStudentService.java
@@ -3,6 +3,10 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.system.domain.ExStudent;
import com.gkhy.exam.system.domain.vo.TrainRecordVO;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -80,15 +84,28 @@
    public boolean checkIdNoUnique(ExStudent student);
    /**
     * 校验身份证号是否存在,存在则放回公司信息
     * 校验身份证号是否存在,供前端校验使用
     * @param idNo
     * @return
     */
    public ExStudent checkIdNoUnique(String idNo);
    public Map checkIdNoUnique(String idNo);
    /**
     * 重置密码
     * @param student
     */
    boolean resetUserPwd(ExStudent student);
    /**
     * 变更学员所属公司
     * @param bodyMap
     */
    void changeStudentCompany(Map<String, Long> bodyMap);
    /**
     * 获取学员培训记录
     * @param studentId
     * @return
     */
    List<TrainRecordVO> trainRecord(Long studentId);
}
exam-system/src/main/java/com/gkhy/exam/system/service/SysCommonService.java
@@ -60,4 +60,7 @@
     * @param path
     */
    void removeMinioFile(Long resourceId,String path);
    void importStudent();
}
exam-system/src/main/java/com/gkhy/exam/system/service/SysUserService.java
@@ -107,4 +107,5 @@
     */
    SysUser checkUserDataScope(Long userId);
}
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCourseChapterPeriodServiceImpl.java
@@ -101,12 +101,15 @@
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(courseChapterPeriod.getCompanyId())){
        if(courseChapterPeriod.getCompanyId()!=null&&!currentUser.getCompanyId().equals(courseChapterPeriod.getCompanyId())){
            throw new ApiException("没有权限操作其他企业课程");
        }
        int state=courseMapper.selectCourseState(courseChapterPeriod.getCourseId());
        if(state== ApproveStatusEnum.APPROVED.getCode()){
            throw new ApiException("已审批的课程不能再修改");
            throw new ApiException("已审批的课程不能再操作");
        }
        if(state== ApproveStatusEnum.APPROVING.getCode()){
            throw new ApiException("待审批的课程不能再操作");
        }
    }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCourseChapterServiceImpl.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.common.config.MinioConfig;
import com.gkhy.exam.common.constant.UserConstant;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.ApproveStatusEnum;
@@ -10,6 +11,8 @@
import com.gkhy.exam.common.utils.PageUtils;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.system.domain.ExCourseChapter;
import com.gkhy.exam.system.domain.ExCourseChapterPeriod;
import com.gkhy.exam.system.domain.ExResource;
import com.gkhy.exam.system.mapper.ExCourseChapterMapper;
import com.gkhy.exam.system.mapper.ExCourseChapterPeriodMapper;
import com.gkhy.exam.system.mapper.ExCourseMapper;
@@ -34,6 +37,9 @@
    private ExCourseChapterPeriodMapper courseChapterPeriodMapper;
    @Autowired
    private ExCourseMapper courseMapper;
    @Autowired
    private MinioConfig minioConfig;
    @Override
    public CommonPage selectChapterList(ExCourseChapter chapter) {
@@ -116,7 +122,19 @@
    @Override
    public List<ExCourseChapter> selectChapterByCourseId(Long courseId,Integer status) {
        return baseMapper.selectChapterByCourseId(courseId,null);
        List<ExCourseChapter> courseChapterList= baseMapper.selectChapterByCourseId(courseId,null);
        for(ExCourseChapter courseChapter:courseChapterList){
            if(courseChapter.getChapterPeriods()!=null&& !courseChapter.getChapterPeriods().isEmpty()){
                List<ExCourseChapterPeriod> courseChapterPeriodList=courseChapter.getChapterPeriods();
                for(ExCourseChapterPeriod courseChapterPeriod:courseChapterPeriodList){
                    if(courseChapterPeriod.getResource()!=null){
                        ExResource resource=courseChapterPeriod.getResource();
                        resource.setResourcePath(minioConfig.getEndpoint()+minioConfig.getBucketName()+"/"+resource.getResourcePath());
                    }
                }
            }
        }
        return courseChapterList;
    }
@@ -128,12 +146,16 @@
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(courseChapter.getCompanyId())){
        if(courseChapter.getCompanyId()!=null&&!currentUser.getCompanyId().equals(courseChapter.getCompanyId())){
            throw new ApiException("没有权限操作其他企业课程");
        }
        int state=courseMapper.selectCourseState(courseChapter.getCourseId());
        if(state==ApproveStatusEnum.APPROVED.getCode()){
            throw new ApiException("已审批的课程不能再修改");
            throw new ApiException("已审批的课程不能再操作");
        }
        if(state==ApproveStatusEnum.APPROVING.getCode()){
            throw new ApiException("待审批的课程不能再操作");
        }
    }
}
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCoursePhaseServiceImpl.java
@@ -4,12 +4,15 @@
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.common.constant.UserConstant;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.ApproveStatusEnum;
import com.gkhy.exam.common.enums.CodeTypeEnum;
import com.gkhy.exam.common.enums.UserTypeEnum;
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.PageUtils;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.system.domain.ExCourse;
import com.gkhy.exam.system.domain.ExCoursePhase;
import com.gkhy.exam.system.mapper.ExCourseMapper;
import com.gkhy.exam.system.mapper.ExCoursePhaseMapper;
import com.gkhy.exam.system.mapper.ExPhaseStudentMapper;
import com.gkhy.exam.system.service.ExCoursePhaseService;
@@ -31,6 +34,8 @@
public class ExCoursePhaseServiceImpl extends ServiceImpl<ExCoursePhaseMapper, ExCoursePhase> implements ExCoursePhaseService {
    @Autowired
    private ExPhaseStudentMapper phaseStudentMapper;
    @Autowired
    private ExCourseMapper courseMapper;
@@ -64,6 +69,7 @@
    @Override
    public int insertCoursePhase(ExCoursePhase coursePhase) {
        checkUserAllowed(coursePhase);
        checkCourseStatus(coursePhase);
        if(!checkNameUnique(coursePhase)){
            throw new ApiException("批次名称已存在");
        }
@@ -79,6 +85,7 @@
    @Override
    public int updateCoursePhase(ExCoursePhase coursePhase) {
        checkUserAllowed(coursePhase);
        checkCourseStatus(coursePhase);
        if(!checkNameUnique(coursePhase)){
            throw new ApiException("批次名称已存在");
        }
@@ -98,7 +105,7 @@
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(coursePhase.getCompanyId())){
        if(coursePhase.getCompanyId()!=null&&!currentUser.getCompanyId().equals(coursePhase.getCompanyId())){
            throw new ApiException("没有权限操作其他企业批次");
        }
        int level=coursePhase.getLevel();
@@ -110,6 +117,18 @@
        }
    }
    private void checkCourseStatus(ExCoursePhase coursePhase){
        Long courseId=coursePhase.getCourseId();
        ExCourse course= courseMapper.selectById(courseId);
        if(course.getStatus().equals(UserConstant.DISENABLE)){
            throw new ApiException("课程已禁用,不能分配");
        }
        if(!course.getState().equals(ApproveStatusEnum.APPROVED.getCode())){
            throw new ApiException("课程审批未通过,不能分配");
        }
    }
    @Override
    public int deleteCoursePhaseById(Long phaseId) {
        checkUserAllowed(baseMapper.selectById(phaseId));
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExCourseServiceImpl.java
@@ -10,6 +10,7 @@
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.PageUtils;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.common.utils.StringUtils;
import com.gkhy.exam.system.domain.ExCourse;
import com.gkhy.exam.system.mapper.ExCourseChapterMapper;
import com.gkhy.exam.system.mapper.ExCourseMapper;
@@ -78,7 +79,7 @@
            course.setState(ApproveStatusEnum.APPROVED.getCode());
            course.setPrivatize(PrivatizeEnum.PUBLIC.getCode());
        }else{
            course.setState(ApproveStatusEnum.APPROVING.getCode());
            course.setState(ApproveStatusEnum.TEMPORARY.getCode());
        }
        int row =baseMapper.insert(course);
        if(row<1){
@@ -112,8 +113,11 @@
            throw new ApiException("没有权限操作其他企业课程");
        }
        if(course.getId()!=null){
            if(course.getState().equals(ApproveStatusEnum.APPROVING.getCode())){
                throw new ApiException("课程待审批状态不能再操作");
            }
            if(course.getState().equals(ApproveStatusEnum.APPROVED.getCode())){
                throw new ApiException("已审批的课程不能再修改");
                throw new ApiException("已审批的课程不能再操作");
            }
        }
    }
@@ -139,8 +143,9 @@
    @Override
    public boolean checkNameUnique(ExCourse course) {
        SysUser user=SecurityUtils.getLoginUser().getUser();
        Long courseId=course.getId()==null?-1L:course.getId();
        ExCourse cou= baseMapper.checkNameUnique(course.getName());
        ExCourse cou= baseMapper.checkNameUnique(course.getName(),user.getCompanyId());
        if(cou!=null&&cou.getId().longValue()!=courseId.longValue()){
            return UserConstant.NOT_UNIQUE;
        }
@@ -149,8 +154,22 @@
    @Override
    public int doApprove(ExCourse course) {
        ExCourse existCourse=baseMapper.selectById(course.getId());
        checkUserAllowed(existCourse);
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        ExCourse dbCourse=getById(course.getId());
        if(!currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
                throw new ApiException("没有权限操作");
            }
            if(!currentUser.getCompanyId().equals(dbCourse.getCompanyId())){
                throw new ApiException("没有权限操作其他企业课程");
            }
            if(dbCourse.getState().equals(ApproveStatusEnum.APPROVED.getCode())){
                throw new ApiException("已审批的课程不能再操作");
            }
        }
        if(StringUtils.isBlank(course.getMessage())){
            course.setMessage("");
        }
        int row=baseMapper.updateById(course);
        if(row<1){
            throw new ApiException("审批失败");
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExExamPaperServiceImpl.java
@@ -14,20 +14,15 @@
import com.gkhy.exam.system.domain.ExExamPaper;
import com.gkhy.exam.system.domain.ExPaperQuestion;
import com.gkhy.exam.system.domain.ExQuestion;
import com.gkhy.exam.system.mapper.ExExamPaperMapper;
import com.gkhy.exam.system.mapper.ExPaperQuestionMapper;
import com.gkhy.exam.system.mapper.ExPaperStudentMapper;
import com.gkhy.exam.system.mapper.ExQuestionMapper;
import com.gkhy.exam.system.domain.ExQuestionBank;
import com.gkhy.exam.system.mapper.*;
import com.gkhy.exam.system.service.ExExamPaperService;
import com.gkhy.exam.system.utils.SequenceUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -47,6 +42,8 @@
    private ExPaperQuestionMapper paperQuestionMapper;
    @Autowired
    private ExQuestionMapper questionMapper;
    @Autowired
    private ExQuestionBankMapper questionBankMapper;
    @Autowired
    private SequenceUtils sequenceUtils;
@@ -65,6 +62,27 @@
    @Override
    public ExExamPaper selectExamPaperById(Long paperId) {
        ExExamPaper examPaper= baseMapper.selectExamPaperById(paperId);
        //获取题库名称
        List<Long> bankIds=new ArrayList<>();
        bankIds.add(examPaper.getSingleBankId());
        bankIds.add(examPaper.getMultiBankId());
        bankIds.add(examPaper.getJudgeBankId());
        bankIds.add(examPaper.getEasyBankId());
        List<ExQuestionBank> questionBanks=questionBankMapper.selectQuestionBankByIds(bankIds);
        questionBanks.forEach(item -> {
            if(Objects.equals(item.getId(), examPaper.getSingleBankId())){
                examPaper.setSingleBankName(item.getName());
            }
            if(Objects.equals(item.getId(), examPaper.getMultiBankId())){
                examPaper.setMultiBankName(item.getName());
            }
            if(Objects.equals(item.getId(), examPaper.getJudgeBankId())){
                examPaper.setJudgeBankName(item.getName());
            }
            if(Objects.equals(item.getId(), examPaper.getEasyBankId())){
                examPaper.setEasyBankName(item.getName());
            }
        });
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        if(currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            return examPaper;
@@ -79,6 +97,7 @@
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public int insertExamPaper(ExExamPaper examPaper) {
        validatedData(examPaper);
        checkUserAllowed(examPaper);
        if(!checkNameUnique(examPaper)){
            throw new ApiException("试卷名称已存在");
@@ -87,18 +106,27 @@
        examPaper.setCode(sequenceUtils.getNextSequence(CodeTypeEnum.EXAM_PAPER.getCode()));
        examPaper.setCreateBy(SecurityUtils.getUsername());
        if(examPaper.getLimitTime()>0){
            examPaper.setLimit(1);
            examPaper.setLimited(1);
        }else{
            examPaper.setLimit(0);
            examPaper.setLimited(0);
        }
        int row=baseMapper.insert(examPaper);
        if(row<1){
            throw new ApiException("新增试卷失败");
        }
        //分配试题
        assignSingleQuestion(examPaper);
        assignMultiQuestion(examPaper);
        assignJudgeQuestion(examPaper);
        if(Optional.ofNullable(examPaper.getSingleNum()).orElse(0)>0) {
            assignSingleQuestion(examPaper);
        }
        if(Optional.ofNullable(examPaper.getMultiNum()).orElse(0)>0) {
            assignMultiQuestion(examPaper);
        }
        if(Optional.ofNullable(examPaper.getJudgeNum()).orElse(0)>0) {
            assignJudgeQuestion(examPaper);
        }
        if(Optional.ofNullable(examPaper.getEasyNum()).orElse(0)>0) {
            assignEasyQuestion(examPaper);
        }
        return row;
    }
@@ -110,8 +138,37 @@
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(examPaper.getCompanyId())){
        if(examPaper.getCompanyId()!=null&&!currentUser.getCompanyId().equals(examPaper.getCompanyId())){
            throw new ApiException("没有权限操作其他企业试卷");
        }
    }
    public void validatedData(ExExamPaper examPaper){
        if(Optional.ofNullable(examPaper.getSingleNum()).orElse(0)<=0
        &&Optional.ofNullable(examPaper.getMultiNum()).orElse(0)<=0
        &&Optional.ofNullable(examPaper.getJudgeNum()).orElse(0)<=0
        &&Optional.ofNullable(examPaper.getEasyNum()).orElse(0)<=0){
            throw new ApiException("试卷题目数量不能为空");
        }
        if(Optional.ofNullable(examPaper.getSingleNum()).orElse(0)>0){
            if(Optional.ofNullable(examPaper.getSingleScore()).orElse(0)<=0 || examPaper.getSingleMethod()==null||examPaper.getSingleBankId()==null){
                throw new ApiException("单选题参数错误");
            }
        }
        if(Optional.ofNullable(examPaper.getMultiNum()).orElse(0)>0){
            if(Optional.ofNullable(examPaper.getMultiScore()).orElse(0)<=0 || examPaper.getMultiMethod()==null||examPaper.getMultiBankId()==null){
                throw new ApiException("多选题参数错误");
            }
        }
        if(Optional.ofNullable(examPaper.getJudgeNum()).orElse(0)>0){
            if(Optional.ofNullable(examPaper.getJudgeScore()).orElse(0)<=0 || examPaper.getJudgeMethod()==null||examPaper.getJudgeBankId()==null){
                throw new ApiException("判断题参数错误");
            }
        }
        if(Optional.ofNullable(examPaper.getEasyNum()).orElse(0)>0){
            if(Optional.ofNullable(examPaper.getEasyScore()).orElse(0)<=0 || examPaper.getEasyMethod()==null||examPaper.getEasyBankId()==null){
                throw new ApiException("简答题参数错误");
            }
        }
    }
@@ -136,6 +193,13 @@
        paperQuestionMapper.batchInsert(paperQuestionList);
    }
    //分配简答题
    public void assignEasyQuestion(ExExamPaper examPaper){
        List<ExPaperQuestion> paperQuestionList = getPaperQuestions(examPaper.getId(), examPaper.getEasyBankId(),
                examPaper.getEasyMethod(),examPaper.getEasyNum(), examPaper.getEasyScore(),QuestionTypeEnum.EASY);
        paperQuestionMapper.batchInsert(paperQuestionList);
    }
    private List<ExPaperQuestion> getPaperQuestions(Long paperId,Long bankId,Integer questionMethod,Integer questionCount,Integer questionScore, QuestionTypeEnum questionTypeEnum) {
        SysUser currentUser=SecurityUtils.getLoginUser().getUser();
        Integer totalQuestionCount=questionMapper.selectCountByBankId(currentUser.getCompanyId(), bankId, questionTypeEnum.getCode());
@@ -146,7 +210,7 @@
        if(Objects.equals(questionMethod, QuestionAssignEnum.RANDOM.getCode())){//随机分配
            questions=questionMapper.selectRandomQuestion(currentUser.getCompanyId(), bankId,questionTypeEnum.getCode(), questionCount);
        }else{//顺序分配
            int totalPage=questionCount/questionMethod;//不能整除,忽略最后一页
            int totalPage=totalQuestionCount/questionCount;//不能整除,忽略最后一页
            Random random=new Random();
            int startIndex=random.nextInt(totalPage)+1;
            questions=questionMapper.selectQuestionWithLimit(currentUser.getCompanyId(), bankId,questionTypeEnum.getCode(),startIndex, questionCount);
@@ -164,6 +228,7 @@
    @Override
    public int updateExamPaper(ExExamPaper examPaper) {
        validatedData(examPaper);
        checkUserAllowed(examPaper);
        if(!checkNameUnique(examPaper)){
            throw new ApiException("试卷名称已存在");
@@ -174,9 +239,10 @@
        }
        examPaper.setCode(null);//编号不能修改
        if(examPaper.getLimitTime()>0){
            examPaper.setLimit(1);
            examPaper.setLimited(1);
        }else{
            examPaper.setLimit(0);
            examPaper.setLimited(0);
            examPaper.setLimitTime(0);
        }
        int row=baseMapper.updateById(examPaper);
        if(row<1){
@@ -194,6 +260,10 @@
        if(examPaper.getJudgeRebuild()==1) {
           deletePaperQuestion(examPaper.getId(),QuestionTypeEnum.JUDGE);
           assignJudgeQuestion(examPaper);
        }
        if(examPaper.getEasyRebuild()==1) {
            deletePaperQuestion(examPaper.getId(),QuestionTypeEnum.EASY);
            assignEasyQuestion(examPaper);
        }
        return row;
    }
@@ -229,8 +299,9 @@
    @Override
    public boolean checkNameUnique(ExExamPaper examPaper) {
        SysUser currentUser=SecurityUtils.getLoginUser().getUser();
        Long paperId=examPaper.getId()==null?-1L:examPaper.getId();
        ExExamPaper paper= baseMapper.checkNameUnique(examPaper.getName());
        ExExamPaper paper= baseMapper.checkNameUnique(examPaper.getName(),currentUser.getCompanyId());
        if(paper!=null&&paper.getId().longValue()!=paperId.longValue()){
            return UserConstant.NOT_UNIQUE;
        }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExExamRecordServiceImpl.java
@@ -46,6 +46,8 @@
    @Override
    public int insertExamRecord(ExExamRecord examRecord) {
        checkUserAllowed(examRecord);
        examRecord.setCompanyId(SecurityUtils.getLoginUser().getUser().getCompanyId());
        examRecord.setCreateBy(SecurityUtils.getUsername());
        int row=baseMapper.insert(examRecord);
        if(row<1){
            throw new ApiException("新增登记记录失败");
@@ -56,6 +58,7 @@
    @Override
    public int updateExamRecord(ExExamRecord examRecord) {
        checkUserAllowed(examRecord);
        examRecord.setUpdateBy(SecurityUtils.getUsername());
        int row=baseMapper.updateById(examRecord);
        if(row<1){
            throw new ApiException("更新登记记录失败");
@@ -72,12 +75,12 @@
    public void checkUserAllowed(ExExamRecord examRecord) {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        if(currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            return;
            throw new ApiException("管理员没有权限操作");
        }
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(examRecord.getCompanyId())){
        if(examRecord.getCompanyId()!=null&&!currentUser.getCompanyId().equals(examRecord.getCompanyId())){
            throw new ApiException("没有权限操作其他企业登记记录");
        }
    }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExExerciseAnswerServiceImpl.java
@@ -1,7 +1,9 @@
package com.gkhy.exam.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gkhy.exam.common.enums.StudentAnswerPassEnum;
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.system.domain.ExExerciseAnswer;
import com.gkhy.exam.system.domain.ExQuestion;
import com.gkhy.exam.system.domain.ExStudentAnswer;
@@ -27,11 +29,12 @@
    @Override
    public ExExerciseAnswer addExerciseAnswer(ExExerciseAnswer exerciseAnswer) {
        int row=0;
        exerciseAnswer.setStudentId(SecurityUtils.getUserId());
        ExQuestion question=questionMapper.selectById(exerciseAnswer.getQuestionId());
        if(exerciseAnswer.getAnswer().equals(question.getAnswer())){
            exerciseAnswer.setPassed(1);
            exerciseAnswer.setPassed(StudentAnswerPassEnum.CORRECT.getCode());
        }else{
            exerciseAnswer.setPassed(0);
            exerciseAnswer.setPassed(StudentAnswerPassEnum.ERROR.getCode());
        }
        ExExerciseAnswer existAnswer= baseMapper.getExerciseAnswer(exerciseAnswer);
        if(existAnswer!=null){
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExPaperStudentServiceImpl.java
@@ -4,18 +4,22 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.common.constant.Constant;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.PaperStudentStateEnum;
import com.gkhy.exam.common.enums.QuestionTypeEnum;
import com.gkhy.exam.common.enums.StudentAnswerPassEnum;
import com.gkhy.exam.common.enums.UserTypeEnum;
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.PageUtils;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.system.domain.ExExamPaper;
import com.gkhy.exam.system.domain.ExPaperStudent;
import com.gkhy.exam.system.domain.ExPhaseStudent;
import com.gkhy.exam.system.domain.ExStudent;
import com.gkhy.exam.common.utils.StringUtils;
import com.gkhy.exam.system.domain.*;
import com.gkhy.exam.system.domain.vo.BatchPaperStudentVO;
import com.gkhy.exam.system.domain.vo.ExPaperStudentVO;
import com.gkhy.exam.system.mapper.*;
import com.gkhy.exam.system.service.ExPaperStudentService;
import com.gkhy.exam.system.service.ExStudentAnswerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -36,11 +40,15 @@
    @Autowired
    private ExExamPaperMapper examPaperMapper;
    @Autowired
    private ExStudentAnswerService studentAnswerService;
    @Autowired
    private ExStudentAnswerMapper studentAnswerMapper;
    @Autowired
    private ExStudentMapper studentMapper;
    @Autowired
    private ExPhaseStudentMapper phaseStudentMapper;
    @Autowired
    private ExQuestionMapper questionMapper;
    @Override
@@ -78,26 +86,38 @@
    }
    @Override
    public int batchAddPaperStudent(Map<String,Object> paperStudentMap) {
        List<Long> phaseIds= (List<Long>) paperStudentMap.get("phaseIds");
        List<Long> studentIds= (List<Long>) paperStudentMap.get("studentIds");
    public int batchAddPaperStudent(BatchPaperStudentVO batchPaperStudentVO) {
        List<Long> phaseIds= batchPaperStudentVO.getPhaseIds();
        List<Long> studentIds= batchPaperStudentVO.getStudentIds();
        Long paperId=batchPaperStudentVO.getPaperId();
        if(ObjectUtil.isEmpty(phaseIds) && ObjectUtil.isEmpty(studentIds)){
            throw new ApiException("批次id与学员id不能同时为空");
        }
        Long paperId= (Long) paperStudentMap.get("paperId");
        if(paperId==null){
            throw new ApiException("考卷id不能为空");
        }
        List<Long> allStudentIds=new ArrayList<>();
        //按批次绑定用户
        if(phaseIds.size()>0){
        if(phaseIds!=null&&!phaseIds.isEmpty()){
            List<ExPhaseStudent> phaseStudentList=phaseStudentMapper.selectList( Wrappers.<ExPhaseStudent>lambdaQuery()
                    .in(ExPhaseStudent::getPhaseId, phaseIds));
            studentIds=phaseStudentList.stream().map(item -> item.getStudentId()).distinct().collect(Collectors.toList());
            allStudentIds=phaseStudentList.stream().map(item -> item.getStudentId()).distinct().collect(Collectors.toList());
        }
        List<ExPaperStudent> paperStudents=studentIds.stream().map(item -> {
            return new ExPaperStudent().setPaperId(paperId).setStudentId(item);
        if(studentIds!=null&&!studentIds.isEmpty()){
            allStudentIds.addAll(studentIds);
        }
        List<ExPaperStudent> paperStudentList=baseMapper.selectList( Wrappers.<ExPaperStudent>lambdaQuery()
                .eq(true,ExPaperStudent::getPaperId, paperId)
                .in(true,ExPaperStudent::getStudentId,allStudentIds));
        if(paperStudentList.size()>0) {
            List<Long> existStudentIds = paperStudentList.stream().map(item -> item.getStudentId()).distinct().collect(Collectors.toList());
            allStudentIds.removeAll(existStudentIds);
        }
        if(allStudentIds.isEmpty()){
            throw new ApiException("未匹配到学员");
        }
        allStudentIds= allStudentIds.stream().distinct().collect(Collectors.toList());
        List<ExPaperStudent> paperStudents=allStudentIds.stream().map(item -> {
            return new ExPaperStudent().setPaperId(paperId).setStudentId(item).setCreateId(SecurityUtils.getUserId());
        }).collect(Collectors.toList());
        checkStudentUnique(paperStudents);
      //  checkStudentUnique(paperStudents);
        int row=baseMapper.batchInsert(paperStudents);
        if(row<1){
            throw new ApiException("批量分配学员失败");
@@ -115,6 +135,57 @@
            }
        }
    }
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void doReview(ExPaperStudentVO paperStudentVO) {
        checkUserAllowed(new ExPaperStudent().setPaperId(paperStudentVO.getPaperId()));
        ExPaperStudent paperStudent=baseMapper.selectById(paperStudentVO.getId());
        ExExamPaper examPaper=examPaperMapper.selectById(paperStudent.getPaperId());
        if(paperStudent.getState().equals(PaperStudentStateEnum.WAIT_EXAM.getCode())){
            throw new ApiException("待考试状态的试卷不能批改!");
        }
        List<ExStudentAnswer> studentAnswerList=studentAnswerMapper.selectList(Wrappers.<ExStudentAnswer>lambdaQuery()
                .eq(true,ExStudentAnswer::getStudentId,paperStudent.getStudentId())
                .eq(true,ExStudentAnswer::getPaperId,paperStudent.getPaperId()));
        Map<Long, ExStudentAnswer> collectMap = studentAnswerList.stream().collect(Collectors.toMap(ExStudentAnswer::getQuestionId, k -> k));
        List<ExPaperStudentVO.QuestionVO> questionVOList=paperStudentVO.getQuestions();
        List<ExStudentAnswer> updateStudentAnswerList=new ArrayList<>();
        for (ExPaperStudentVO.QuestionVO questionVO:questionVOList){
            ExStudentAnswer sa=collectMap.get(questionVO.getQuestionId());
            if(sa!=null){
                int score=questionVO.getScore();
                sa.setScore(score);
                if(score>examPaper.getEasyScore()){
                    throw new ApiException("得分不能超过题目的最高分!");
                }else if(score==examPaper.getEasyScore()){
                    sa.setPassed(StudentAnswerPassEnum.CORRECT.getCode());
                }else{
                    sa.setPassed(StudentAnswerPassEnum.ERROR.getCode());
                }
                updateStudentAnswerList.add(sa);
            }
        }
        int totalScore=0;
        for(ExStudentAnswer studentAnswer:studentAnswerList){
            if(studentAnswer.getScore()!=null) {
                totalScore = totalScore + studentAnswer.getScore();
            }
        }
        paperStudent.setState(PaperStudentStateEnum.DONE_REVIEW.getCode());
        paperStudent.setScore(totalScore);
        if(totalScore<examPaper.getPassScore()){
            paperStudent.setPassed(Constant.EXAM_UNPASS);
        }else{
            paperStudent.setPassed(Constant.EXAM_PASS);
        }
        paperStudent.setUpdateBy(SecurityUtils.getUsername());
        baseMapper.updateById(paperStudent);
        if(!updateStudentAnswerList.isEmpty()){
            studentAnswerService.updateBatchById(updateStudentAnswerList);
        }
    }
    @Override
    public CommonPage selectPaperStudentList(ExPaperStudent paperStudent) {
@@ -166,32 +237,96 @@
    }
    @Override
    public void endExam(ExPaperStudent paperStudent) {
    public void endExam(ExPaperStudent pStudent) {
        if(pStudent.getId()==null){
            throw new ApiException("学员与试卷关系id不能为空");
        }
        ExPaperStudent existPs=baseMapper.selectById(pStudent.getId());
        if(existPs==null){
            throw new ApiException("学员与试卷关系不存在");
        }
        Long endTime=System.currentTimeMillis();
        //异步计算
        existPs.setEndTime(endTime);
        handlePaperData(existPs);
    }
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void handlePaperData(ExPaperStudent paperStudent){
        //计算考试成绩
        ExExamPaper paper=examPaperMapper.selectById(paperStudent.getPaperId());
        int singleCount=studentAnswerMapper.selectPassCount(paperStudent.getPaperId(),paperStudent.getStudentId(), QuestionTypeEnum.SINGLE.getCode());
        int multiCount=studentAnswerMapper.selectPassCount(paperStudent.getPaperId(),paperStudent.getStudentId(), QuestionTypeEnum.MULTI.getCode());
        int judgeCount=studentAnswerMapper.selectPassCount(paperStudent.getPaperId(),paperStudent.getStudentId(), QuestionTypeEnum.JUDGE.getCode());
        int score=singleCount*paper.getSingleScore()+multiCount*paper.getMultiScore()+judgeCount*paper.getJudgeScore();
        ExPaperStudent exPaperStudent = new ExPaperStudent()
                .setPaperId(paperStudent.getPaperId())
                .setStudentId(paperStudent.getStudentId())
                .setCompleted(1)
                .setEndTime(endTime)
                .setScore(score);
        if(score>=paper.getPassScore()){
            exPaperStudent.setPassed(1);
        }else{
            exPaperStudent.setPassed(0);
        paperStudent.setState(PaperStudentStateEnum.WAIT_REVIEW.getCode());
        List<ExStudentAnswer> studentAnswerList=studentAnswerMapper.selectList(Wrappers.<ExStudentAnswer>lambdaQuery()
                .eq(true,ExStudentAnswer::getStudentId,paperStudent.getStudentId())
                .eq(true,ExStudentAnswer::getPaperId,paperStudent.getPaperId()));
        List<ExQuestion> questionList=questionMapper.selectQuestionByPaperId(paperStudent.getPaperId());
        Map<Long, ExStudentAnswer> collectMap = studentAnswerList.stream().collect(Collectors.toMap(ExStudentAnswer::getQuestionId, k -> k));
        List<ExStudentAnswer> updateStudentAnswers=new ArrayList<>();
        int totalScore=0;
        boolean easyViewFlag=false;
        for(ExQuestion question:questionList){
            ExStudentAnswer sa=collectMap.get(question.getId());
            if(sa!=null){
                if(question.getQuestionType().equals(QuestionTypeEnum.EASY.getCode())) {
                    if(StringUtils.isBlank(sa.getAnswer())){
                        sa.setPassed(StudentAnswerPassEnum.ERROR.getCode());
                        sa.setScore(0);
                    }else{
                        sa.setPassed(StudentAnswerPassEnum.WAIT_REVIEW.getCode());
                        easyViewFlag=true;
                    }
                }else{
                    if (sa.getAnswer().equals(question.getAnswer())) {
                        sa.setPassed(StudentAnswerPassEnum.CORRECT.getCode());
                        sa.setScore(getScore(paper, question.getQuestionType()));
                        totalScore = totalScore + sa.getScore();
                    }else {
                        sa.setPassed(StudentAnswerPassEnum.ERROR.getCode());
                        sa.setScore(0);
                    }
                }
            }else{
                sa=new ExStudentAnswer();
                sa.setPassed(StudentAnswerPassEnum.ERROR.getCode());
                sa.setScore(0);
                sa.setStudentId(paperStudent.getStudentId());
                sa.setPaperId(paperStudent.getPaperId());
                sa.setQuestionId(question.getId());
            }
            updateStudentAnswers.add(sa);
        }
        int row=baseMapper.updateById(exPaperStudent);
        studentAnswerService.saveOrUpdateBatch(updateStudentAnswers);
        paperStudent.setScore(totalScore);
        if(!easyViewFlag){
            //没有简答题,直接批改试卷
            paperStudent.setState(PaperStudentStateEnum.DONE_REVIEW.getCode());
            if(paper.getPassScore()>totalScore){
                paperStudent.setPassed(Constant.EXAM_UNPASS);
            }else{
                paperStudent.setPassed(Constant.EXAM_PASS);
            }
        }
        int row=baseMapper.updateById(paperStudent);
        if(row<1){
            throw new ApiException("提交考试失败");
        }
    }
    private Integer getScore(ExExamPaper examPaper,Integer questionType){
        if(questionType.equals(QuestionTypeEnum.SINGLE.getCode())){
            return examPaper.getSingleScore();
        }else if(questionType.equals(QuestionTypeEnum.MULTI.getCode())){
            return examPaper.getMultiScore();
        }else if(questionType.equals(QuestionTypeEnum.JUDGE.getCode())){
            return examPaper.getJudgeScore();
        }else if(questionType.equals(QuestionTypeEnum.EASY.getCode())){
            return examPaper.getEasyScore();
        }
        return 0;
    }
}
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExPhaseStudentServiceImpl.java
@@ -11,10 +11,7 @@
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.system.domain.*;
import com.gkhy.exam.system.domain.vo.StudentStudyVO;
import com.gkhy.exam.system.mapper.ExCoursePhaseMapper;
import com.gkhy.exam.system.mapper.ExPhaseStudentMapper;
import com.gkhy.exam.system.mapper.ExStudentMapper;
import com.gkhy.exam.system.mapper.ExStudentStudyMapper;
import com.gkhy.exam.system.mapper.*;
import com.gkhy.exam.system.service.ExCompanyPeriodService;
import com.gkhy.exam.system.service.ExPhaseStudentService;
import com.gkhy.exam.system.service.ExStudentStudyService;
@@ -23,6 +20,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -50,6 +48,8 @@
    private SysCompanyService companyService;
    @Autowired
    private ExCompanyPeriodService companyPeriodService;
    @Autowired
    private ExCourseChapterPeriodMapper courseChapterPeriodMapper;
    @Override
@@ -72,6 +72,9 @@
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public int batchAddPhaseStudent(List<ExPhaseStudent> phaseStudents) {
        if(phaseStudents==null|| phaseStudents.isEmpty()){
            throw new ApiException("学员数据不能为空");
        }
        checkUserAllowed(phaseStudents.get(0));
        checkStudentUnique(phaseStudents);
        for(ExPhaseStudent phaseStudent:phaseStudents){
@@ -96,7 +99,7 @@
            throw new ApiException("没有权限操作");
        }
        ExCoursePhase coursePhase=coursePhaseMapper.selectById(phaseStudent.getPhaseId());
        if(!currentUser.getCompanyId().equals(coursePhase.getCompanyId())){
        if(!coursePhase.getCompanyId().equals(currentUser.getCompanyId())){
            throw new ApiException("没有权限操作其他企业批次");
        }
@@ -110,6 +113,17 @@
        }
        PageUtils.startPage();
        List<ExPhaseStudent> phaseStudentList=baseMapper.selectPhaseStudentList(phaseStudent);
        if(phaseStudentList.size()>0) {
            //获取课程所有课时数量
            int count = courseChapterPeriodMapper.selectCountByCourseId(phaseStudentList.get(0).getCourse().getId());
            if(count>0) {
                for (ExPhaseStudent ps : phaseStudentList) {
                    if (ps.getTotalProgress() != null) {
                        ps.setTotalProgress(ps.getTotalProgress().divide(BigDecimal.valueOf(count), BigDecimal.ROUND_UP));
                    }
                }
            }
        }
        return CommonPage.restPage(phaseStudentList);
    }
@@ -129,7 +143,7 @@
    public int deletePhaseStudent(Long phaseStudentId) {
        ExPhaseStudent phaseStudent=baseMapper.selectPhaseStudentById(phaseStudentId);
        if(ObjectUtil.isNull(phaseStudent)){
            throw new ApiException(String.format("该批次下不存在该学员<>",phaseStudent.getStudentName()));
            throw new ApiException("该批次下不存在该学员id");
        }
        checkUserAllowed(phaseStudent);
        int studentStudyCount=studentStudyMapper.countByPhaseId(phaseStudent.getPhaseId(),phaseStudent.getStudentId());
@@ -171,13 +185,14 @@
        ExCoursePhase coursePhase= coursePhaseMapper.selectCoursePhaseById(phaseId);
        ExCompanyPeriod companyPeriod=new ExCompanyPeriod();
        companyPeriod.setPhaseId(phaseId);
        companyPeriod.setModifyPeriod(-1*studentCount*coursePhase.getCoursePeriod());
        companyPeriod.setModifyPeriod(-1L*studentCount*coursePhase.getCoursePeriod());
        companyPeriod.setOrigin("\""+coursePhase.getName()+"\"新增学员消耗");
        SysCompany company=companyService.getById(coursePhase.getCompanyId());
        int remainPeriod=company.getTotalPeriod()+companyPeriod.getModifyPeriod();
        Long remainPeriod=company.getRemainPeriod()+companyPeriod.getModifyPeriod();
        if(remainPeriod<0){
            throw new ApiException("企业剩余课时不足");
        }
        companyPeriod.setCompanyId(company.getId());
        companyPeriod.setRemainPeriod(remainPeriod);
        companyPeriodService.save(companyPeriod);
        company.setRemainPeriod(companyPeriod.getRemainPeriod());
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExQuestionBankServiceImpl.java
@@ -69,6 +69,7 @@
            questionBank.setPrivatize(PrivatizeEnum.PUBLIC.getCode());
        }else{
            questionBank.setCompanyId(user.getCompanyId());
            questionBank.setPrivatize(PrivatizeEnum.PRIVATE.getCode());
        }
        int row =baseMapper.insert(questionBank);
        if(row<1){
@@ -98,7 +99,7 @@
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(questionBank.getCompanyId())){
        if(questionBank.getCompanyId()!=null&&!currentUser.getCompanyId().equals(questionBank.getCompanyId())){
            throw new ApiException("没有权限操作其他企业课程");
        }
    }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExQuestionServiceImpl.java
@@ -6,6 +6,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.PaperStudentStateEnum;
import com.gkhy.exam.common.enums.PrivatizeEnum;
import com.gkhy.exam.common.enums.QuestionTypeEnum;
import com.gkhy.exam.common.enums.UserTypeEnum;
@@ -25,9 +26,12 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * <p>
@@ -48,17 +52,21 @@
    @Override
    public CommonPage selectQuestionList(ExQuestion question) {
        if(question.getBankId()==null){
            throw new ApiException("题库id不能为空");
        }
        ExQuestionBank questionBank=questionBankMapper.selectById(question.getBankId());
        if(!questionBank.getPrivatize().equals(PrivatizeEnum.PUBLIC.getCode())){
            SysUser currentUser=SecurityUtils.getLoginUser().getUser();
            if(!currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
                if(!question.getCompanyId().equals(currentUser.getCompanyId())){
                    throw new ApiException("无权限查看其它企业题目");
                }
            }
//        if(question.getBankId()==null){
//            throw new ApiException("题库id不能为空");
//        }
//        ExQuestionBank questionBank=questionBankMapper.selectById(question.getBankId());
//        if(!questionBank.getPrivatize().equals(PrivatizeEnum.PUBLIC.getCode())){
//            SysUser currentUser=SecurityUtils.getLoginUser().getUser();
//            if(!currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
//                if(!question.getCompanyId().equals(currentUser.getCompanyId())){
//                    throw new ApiException("无权限查看其它企业题目");
//                }
//            }
//        }
        SysUser currentUser=SecurityUtils.getLoginUser().getUser();
        if(!currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            question.setCompanyId(currentUser.getCompanyId());
        }
        PageUtils.startPage();
        List<ExQuestion> questionList=baseMapper.selectQuestionList(question);
@@ -67,7 +75,7 @@
    @Override
    public ExQuestion selectQuestionById(Long questionId) {
        ExQuestion question= baseMapper.selectById(questionId);
        ExQuestion question= baseMapper.selectByQuestionId(questionId);
        if(question.getPrivatize().equals(PrivatizeEnum.PUBLIC.getCode())){
            return question;
        }
@@ -85,15 +93,22 @@
    public int insertQuestion(ExQuestion question) {
        checkUserAllowed(question);
        SysUser user= SecurityUtils.getLoginUser().getUser();
        int totalCount=questionBankMapper.selectCountByBankId(question.getBankId());
        if(totalCount>1000){
            throw new ApiException("题库题目数量超过1000,不能再新增");
        }
        //公开的题库新增题目,题目也是公开
        ExQuestionBank questionBank=questionBankMapper.selectById(question.getBankId());
        if(user.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())||questionBank.getPrivatize().equals(PrivatizeEnum.PUBLIC.getCode())){
        if(user.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())&&questionBank.getPrivatize().equals(PrivatizeEnum.PRIVATE.getCode())){
            throw new ApiException("管理员不允许在私有题库创建题目");
        }else if (user.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())&&questionBank.getPrivatize().equals(PrivatizeEnum.PUBLIC.getCode())){
            question.setPrivatize(PrivatizeEnum.PUBLIC.getCode());
        }else{
            question.setCompanyId(user.getCompanyId());
            question.setPrivatize(PrivatizeEnum.PRIVATE.getCode());
        }
        validData(question);
        question.setCreateBy(user.getUsername());
        int row=baseMapper.insert(question);
        if(row<1){
            throw new ApiException("新增题目失败");
@@ -105,6 +120,7 @@
    public int updateQuestion(ExQuestion question) {
        validData(question);
        checkUserAllowed(question);
        question.setUpdateBy(SecurityUtils.getUsername());
        int row=baseMapper.updateById(question);
        if(row<1){
            throw new ApiException("编辑题目失败");
@@ -113,7 +129,7 @@
    }
    public void validData(ExQuestion question){
        if(!question.getQuestionType().equals(QuestionTypeEnum.JUDGE.getCode())){
        if(!question.getQuestionType().equals(QuestionTypeEnum.JUDGE.getCode())&&!question.getQuestionType().equals(QuestionTypeEnum.EASY.getCode())){
            if(StringUtils.isBlank(question.getContent())){
                throw new ApiException("题目内容不能为空");
            }
@@ -141,7 +157,7 @@
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(question.getCompanyId())){
        if(question.getCompanyId()!=null&&!currentUser.getCompanyId().equals(question.getCompanyId())){
            throw new ApiException("没有权限操作其他企业题目");
        }
    }
@@ -153,8 +169,8 @@
    }
    @Override
    public List<Map> getExerciseQuestionList(Long bankId, Integer exerciseType) {
        return baseMapper.getExerciseQuestionList(bankId,exerciseType,SecurityUtils.getUserId());
    public List<Map> getExerciseQuestionList(Long bankId) {
        return baseMapper.getExerciseQuestionList(bankId,SecurityUtils.getUserId());
    }
    @Override
@@ -164,6 +180,12 @@
    @Override
    public List<ExQuestion> getExerciseQuestionByIds(List<Long> questionIds) {
        if(questionIds==null|| questionIds.isEmpty()){
            throw new ApiException("题目id列表不能为空");
        }
        if(questionIds.size()>30){
            throw new ApiException("批量id数量超过阀值");
        }
        return baseMapper.getExeriseQuestionByIds(questionIds,SecurityUtils.getUserId());
    }
@@ -176,28 +198,46 @@
        if(paperStudent==null){
            throw new ApiException("您分配的试卷未查询到");
        }
        Long currentDateTime=System.currentTimeMillis();
        if(viewType==1){//更新考试开始时间
            Long startTime=System.currentTimeMillis();
            if(paperStudent.getCompleted()==1){
            if(paperStudent.getState()!= PaperStudentStateEnum.WAIT_EXAM.getCode()){
                throw new ApiException("考试已完成,不能重复考试");
            }
            if(paperStudent.getStartTime()!=null) {//判断考卷是否已完成
                ExExamPaper examPaper = examPaperMapper.selectById(paperStudent.getPaperId());
                if(examPaper.getLimit()==1) {
                    Long currentDateTime = System.currentTimeMillis();
                    if (currentDateTime-paperStudent.getStartTime()>=examPaper.getLimitTime()*60*1000){
                        paperStudent.setCompleted(1);
                        paperStudentMapper.updateById(paperStudent);
                        throw new ApiException("考试已超时,不能再考试");
                    }
            ExExamPaper examPaper = examPaperMapper.selectById(paperStudent.getPaperId());
            if(examPaper.getLimited()==1){
                if(paperStudent.getStartTime()!=null && (currentDateTime-paperStudent.getStartTime()>=examPaper.getLimitTime()*60*1000)){
//                    paperStudent.setCompleted(1);
//                    paperStudentMapper.updateById(paperStudent);
                    throw new ApiException("考试已超时,不能再考试");
                }
                LocalDateTime deadline = examPaper.getDeadline();
                if(currentDateTime-deadline.toInstant(ZoneOffset.of("+8")).toEpochMilli()>0){
//                    paperStudent.setCompleted(1);
//                    paperStudentMapper.updateById(paperStudent);
                    throw new ApiException("已过考试截止时间,不能再考试");
                }
            }
            int row=paperStudentMapper.updateById(new ExPaperStudent().setPaperId(paperStudent.getPaperId()).setStudentId(paperStudent.getStudentId()).setStartTime(startTime));
            if(row<1){
                throw new ApiException("设置考试开始时间失败");
            if(paperStudent.getStartTime()==null) {
                int row = paperStudentMapper.updateById(new ExPaperStudent().setId(paperStudent.getId()).setPaperId(paperStudent.getPaperId()).setStudentId(paperStudent.getStudentId()).setStartTime(currentDateTime));
                if (row < 1) {
                    throw new ApiException("设置考试开始时间失败");
                }
            }
        }else{
            if(!Objects.equals(paperStudent.getState(), PaperStudentStateEnum.DONE_REVIEW.getCode())){
//                if(paperStudent.getStartTime()!=null) {//判断考卷是否已完成
//                    ExExamPaper examPaper = examPaperMapper.selectById(paperStudent.getPaperId());
//                    if(examPaper.getLimited()==1) {
//                        if (currentDateTime-paperStudent.getStartTime()>=examPaper.getLimitTime()*60*1000){
//                            paperStudent.setCompleted(1);
//                            paperStudentMapper.updateById(paperStudent);
//                        }
//                    }
//                }
                throw new ApiException("试卷未审批完,无法查看");
            }
        }
        return baseMapper.getPaperQuestionList(paperId,paperStudent.getCompleted(),SecurityUtils.getUserId());
        return baseMapper.getPaperQuestionList(paperId,paperStudent.getState(),SecurityUtils.getUserId(),viewType);
    }
    @Override
@@ -209,11 +249,14 @@
        if(paperStudent==null){
            throw new ApiException("您分配的试卷未查询到");
        }
        return baseMapper.getPaperQuestionById(paperId,questionId,paperStudent.getCompleted(),SecurityUtils.getUserId());
        return baseMapper.getPaperQuestionById(paperId,questionId,paperStudent.getState(),SecurityUtils.getUserId());
    }
    @Override
    public List<ExQuestion> getPaperQuestionByIds(Long paperId, List<Long> questionIds) {
        if(questionIds.size()>30){
            throw new ApiException("批量id数量超过阀值");
        }
        LambdaQueryWrapper<ExPaperStudent> lambdaQueryWrapper = Wrappers.<ExPaperStudent>lambdaQuery()
                .eq(ExPaperStudent::getPaperId, paperId)
                .eq(ExPaperStudent::getStudentId, SecurityUtils.getUserId());
@@ -221,11 +264,16 @@
        if(paperStudent==null){
            throw new ApiException("您分配的试卷未查询到");
        }
        return baseMapper.getPaperQuestionByIds(paperId,questionIds,paperStudent.getCompleted(),SecurityUtils.getUserId());
        return baseMapper.getPaperQuestionByIds(paperId,questionIds,paperStudent.getState(),SecurityUtils.getUserId());
    }
    @Override
    public List<Long> getExerciseErrorQuestionList(Long bankId) {
        return baseMapper.getExerciseErrorQuestionList(bankId,SecurityUtils.getUserId());
    }
    @Override
    public List<ExQuestion> selectQuestionByPaperId(Long paperId) {
        return baseMapper.selectQuestionByPaperId(paperId);
    }
}
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExResourceServiceImpl.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gkhy.exam.common.api.CommonPage;
import com.gkhy.exam.common.config.MinioConfig;
import com.gkhy.exam.common.constant.UserConstant;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.PrivatizeEnum;
@@ -32,6 +33,12 @@
public class ExResourceServiceImpl extends ServiceImpl<ExResourceMapper, ExResource> implements ExResourceService {
    @Autowired
    private SysCommonService commonService;
    @Autowired
    private MinioConfig minioConfig;
    @Override
    public CommonPage selectResourseList(ExResource resource) {
        SysUser currentUser = SecurityUtils.getLoginUser().getUser();
@@ -40,6 +47,9 @@
        }
        PageUtils.startPage();
        List<ExResource> resourceList=baseMapper.selectResourceList(resource);
        resourceList.forEach(item -> {
            item.setResourcePath(minioConfig.getEndpoint()+minioConfig.getBucketName()+"/"+item.getResourcePath());
        });
        return CommonPage.restPage(resourceList);
    }
@@ -49,6 +59,7 @@
        if(resource==null){
            return resource;
        }
        resource.setResourcePath(minioConfig.getEndpoint()+minioConfig.getBucketName()+"/"+resource.getResourcePath());
        if(resource.getPrivatize().equals(PrivatizeEnum.PUBLIC.getCode())){
            return resource;
        }
@@ -59,6 +70,7 @@
        if(!resource.getCompanyId().equals(currentUser.getCompanyId())){
            throw new ApiException("无权限查看其它企业资源");
        }
        return resource;
    }
@@ -68,6 +80,7 @@
        if(resource==null){
            return resource;
        }
        resource.setResourcePath(minioConfig.getEndpoint()+minioConfig.getBucketName()+"/"+resource.getResourcePath());
        if(resource.getPrivatize().equals(PrivatizeEnum.PUBLIC.getCode())){
            return resource;
        }
@@ -126,6 +139,11 @@
        if(!checkNameUnique(resource)){
            throw new ApiException("资源名称已存在");
        }
        String resourcePath=resource.getResourcePath();
        if(resourcePath.startsWith(minioConfig.getEndpoint()+minioConfig.getBucketName()+"/")) {
            resourcePath = resourcePath.replace(minioConfig.getEndpoint()+minioConfig.getBucketName() + "/", "");
            resource.setResourcePath(resourcePath);
        }
        int row=baseMapper.updateById(resource);
        if(row<1){
            throw new ApiException("更新资源失败");
@@ -141,7 +159,7 @@
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(resource.getCompanyId())){
        if(resource.getCompanyId()!=null&&!currentUser.getCompanyId().equals(resource.getCompanyId())){
            throw new ApiException("没有权限操作其他企业资源");
        }
    }
@@ -149,8 +167,12 @@
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public int deleteResourceById(Long resourceId) {
        //校验资源是否绑定
        checkUserAllowed(baseMapper.selectById(resourceId));
        //校验资源是否绑定
        int count= baseMapper.checkResourceAssign(resourceId);
        if(count>0){
            throw new ApiException("资源已跟课时关联,不能删除");
        }
        ExResource resource=getById(resourceId);
        int row=baseMapper.deleteById(resourceId);
        if(row<1){
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStatisticServiceImpl.java
@@ -33,17 +33,27 @@
        if(!user.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            companyId=user.getCompanyId();
        }
        if(startTime==null && endTime==null){
            endTime=new Date();
            startTime=DateUtil.offsetDay(endTime,-7);
        }else if(startTime!=null&&endTime==null){
            endTime=DateUtil.offsetDay(startTime,7);
        }else if(startTime==null&&endTime!=null){
            startTime=DateUtil.offsetDay(endTime,-7);
        }
        PageUtils.startPage();
        List<SysCompany> companyList=companyMapper.selectCompanyList(new SysCompany().setId(companyId));
        CommonPage commonPage= CommonPage.restPage(companyList);
        List<CompanyStatisticVO>companyStatisticVOList=staticData(companyList, DateUtil.formatDateTime(DateUtil.beginOfDay(startTime)),DateUtil.formatDateTime(DateUtil.endOfDay(endTime)),type);
        commonPage.setList(companyStatisticVOList);
        if(companyList.size()>0) {
            List<CompanyStatisticVO> companyStatisticVOList = staticData(companyList, DateUtil.formatDateTime(DateUtil.beginOfDay(startTime)), DateUtil.formatDateTime(DateUtil.endOfDay(endTime)), type);
            commonPage.setList(companyStatisticVOList);
        }
        return commonPage;
    }
    public List<CompanyStatisticVO> staticData(List<SysCompany> companyList,String startTime,String endTime,Integer type){
        List<Long> companyIds=companyList.stream().map(item -> item.getId()).collect(Collectors.toList());
        //统计公司批次数据
        List<CompanyPhaseVO> companyPhaseVOList=null;
        if(type==1) {
            companyPhaseVOList=companyMapper.getOnlineCompanyPhaseCount(companyIds, startTime, endTime);
@@ -51,14 +61,15 @@
            companyPhaseVOList=companyMapper.getOfflineCompanyPhaseCount(companyIds, startTime, endTime);
        }
        Map<Long,List<CompanyPhaseVO>> companyPhaseVOMap=companyPhaseVOList.stream().collect(Collectors.groupingBy(CompanyPhaseVO::getCompanyId, LinkedHashMap::new,Collectors.toList()));
        List<CompanyPhaseStudentVO> companyPhaseStudentVOList=null;
        if(type==1) {
            companyPhaseStudentVOList=companyMapper.getOnlineCompanyPhaseStudentCount(companyIds, startTime, endTime);
        }else{
            companyPhaseStudentVOList=companyMapper.getOfflineCompanyPhaseStudentCount(companyIds, startTime, endTime);
        }
        Map<Long,List<CompanyPhaseStudentVO>> companyPhaseStudentVOMap=companyPhaseStudentVOList.stream().collect(Collectors.groupingBy(CompanyPhaseStudentVO::getCompanyId,LinkedHashMap::new,Collectors.toList()));
        Map<Long,List<CompanyPhaseStudentVO>> companyPhaseStudentVOMap=companyPhaseStudentVOList.stream().collect(Collectors.groupingBy(CompanyPhaseStudentVO::getCompanyId,LinkedHashMap::new,Collectors.toList()));
        List<CompanyPaperStudentVO> companyPaperStudentVOList=null;
        if(type==1) {
            companyPaperStudentVOList=companyMapper.getOnlineCompanyPaperStudentCount(companyIds, startTime, endTime);
@@ -73,11 +84,11 @@
            List<CompanyPhaseVO> companyPhaseVOs=companyPhaseVOMap.get(item.getId());
            if(companyPhaseVOs!=null&&companyPhaseVOs.size()>0){
                companyPhaseVOs.forEach(cp -> {
                    if(cp.getLevel().equals(PhaseLevelEnum.COMPANY)){
                    if(cp.getLevel().equals(PhaseLevelEnum.COMPANY.getCode())){
                        companyStatisticVO.setLevel1PhaseCount(cp.getPhaseCount());
                    }else if(cp.getLevel().equals(PhaseLevelEnum.DEPART)){
                    }else if(cp.getLevel().equals(PhaseLevelEnum.DEPART.getCode())){
                        companyStatisticVO.setLevel2PhaseCount(cp.getPhaseCount());
                    }else if(cp.getLevel().equals(PhaseLevelEnum.WORkSHOP)) {
                    }else if(cp.getLevel().equals(PhaseLevelEnum.WORkSHOP.getCode())) {
                        companyStatisticVO.setLevel3PhaseCount(cp.getPhaseCount());
                    }
                });
@@ -86,11 +97,11 @@
            List<CompanyPhaseStudentVO> companyPhaseStudentVOs=companyPhaseStudentVOMap.get(item.getId());
            if(companyPhaseStudentVOs!=null&&companyPhaseStudentVOs.size()>0){
                companyPhaseStudentVOs.forEach(cp -> {
                    if(cp.getLevel().equals(PhaseLevelEnum.COMPANY)){
                    if(cp.getLevel().equals(PhaseLevelEnum.COMPANY.getCode())){
                        companyStatisticVO.setLevel1StudentCount(cp.getPhaseStudentCount());
                    }else if(cp.getLevel().equals(PhaseLevelEnum.DEPART)){
                    }else if(cp.getLevel().equals(PhaseLevelEnum.DEPART.getCode())){
                        companyStatisticVO.setLevel2StudentCount(cp.getPhaseStudentCount());
                    }else if(cp.getLevel().equals(PhaseLevelEnum.WORkSHOP)) {
                    }else if(cp.getLevel().equals(PhaseLevelEnum.WORkSHOP.getCode())) {
                        companyStatisticVO.setLevel3StudentCount(cp.getPhaseStudentCount());
                    }
                });
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStudentAnswerServiceImpl.java
@@ -1,9 +1,9 @@
package com.gkhy.exam.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gkhy.exam.common.enums.PaperStudentStateEnum;
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.system.domain.ExPaperStudent;
import com.gkhy.exam.system.domain.ExQuestion;
import com.gkhy.exam.system.domain.ExStudentAnswer;
import com.gkhy.exam.system.mapper.ExPaperStudentMapper;
import com.gkhy.exam.system.mapper.ExQuestionMapper;
@@ -11,6 +11,8 @@
import com.gkhy.exam.system.service.ExStudentAnswerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
 * <p>
@@ -30,12 +32,18 @@
    public int addStudentAnswer(ExStudentAnswer studentAnswer) {
        int row=0;
        validData(studentAnswer);
        ExQuestion question=questionMapper.selectById(studentAnswer.getQuestionId());
        if(studentAnswer.getAnswer().equals(question.getAnswer())){
            studentAnswer.setPassed(1);
        }else{
            studentAnswer.setPassed(0);
        }
//        ExQuestion question=questionMapper.selectById(studentAnswer.getQuestionId());
//        if(question.getQuestionType().equals(QuestionTypeEnum.EASY.getCode())){
//            studentAnswer.setPassed(StudentAnswerPassEnum.WAIT_REVIEW.getCode());
//        }else{
//            if(studentAnswer.getAnswer().equals(question.getAnswer())){
//                studentAnswer.setPassed(StudentAnswerPassEnum.CORRECT.getCode());
//                studentAnswer.setScore();
//            }else{
//                studentAnswer.setPassed(StudentAnswerPassEnum.ERROR.getCode());
//                studentAnswer.setScore(0);
//            }
//        }
        ExStudentAnswer existAnswer= baseMapper.getStudentAnswer(studentAnswer);
        if(existAnswer!=null){
            studentAnswer.setId(existAnswer.getId());
@@ -51,7 +59,7 @@
    public void validData(ExStudentAnswer studentAnswer){
        ExPaperStudent paperStudent=paperStudentMapper.selectByPaperStudentId(new ExPaperStudent().setPaperId(studentAnswer.getPaperId()).setStudentId(studentAnswer.getStudentId()));
        if(paperStudent.getCompleted()==1){
        if(!Objects.equals(paperStudent.getState(), PaperStudentStateEnum.WAIT_EXAM.getCode())){
            throw new ApiException("考试已完成,不能再作答");
        }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStudentServiceImpl.java
@@ -12,16 +12,24 @@
import com.gkhy.exam.common.utils.PageUtils;
import com.gkhy.exam.common.utils.RedisUtils;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.common.utils.StringUtils;
import com.gkhy.exam.system.domain.ExPaperStudent;
import com.gkhy.exam.system.domain.ExPhaseStudent;
import com.gkhy.exam.system.domain.ExStudent;
import com.gkhy.exam.system.domain.SysCompany;
import com.gkhy.exam.system.domain.vo.TrainRecordVO;
import com.gkhy.exam.system.mapper.ExPaperStudentMapper;
import com.gkhy.exam.system.mapper.ExPhaseStudentMapper;
import com.gkhy.exam.system.mapper.ExStudentMapper;
import com.gkhy.exam.system.mapper.SysUserMapper;
import com.gkhy.exam.system.service.ExStudentService;
import com.gkhy.exam.system.service.SysCompanyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * <p>
@@ -37,12 +45,34 @@
    private SysCompanyService companyService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ExPhaseStudentMapper phaseStudentMapper;
    @Autowired
    private ExPaperStudentMapper paperStudentMapper;
    @Autowired
    private SysUserMapper userMapper;
    @Override
    public CommonPage selectStudentList(ExStudent student) {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        if(!currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            student.setCompanyId(currentUser.getCompanyId());
            Map<String,Object> paramsMap=new HashMap<>();
            if(currentUser.getUserType().equals(UserTypeEnum.DEPART_USER.getCode())) {//部门级用户
                List<Long> workshopUserIds=userMapper.selectWorkshopUserIds(currentUser.getId());
                if(workshopUserIds==null){
                    workshopUserIds=new ArrayList<>();
                }
                workshopUserIds.add(currentUser.getId());
                paramsMap.put("createIds",workshopUserIds);
                student.setParams(paramsMap);
            }else if(currentUser.getUserType().equals(UserTypeEnum.WORKSHOP_USER.getCode())){//车间级用户
                List<Long> workshopUserIds=new ArrayList<>();
                workshopUserIds.add(currentUser.getId());
                workshopUserIds.add(currentUser.getParentId());
                paramsMap.put("createIds",workshopUserIds);
                student.setParams(paramsMap);
            }
        }
        PageUtils.startPage();
        List<ExStudent> studentList=baseMapper.selectStudentList(student);
@@ -73,6 +103,10 @@
        SysUser currentUser=SecurityUtils.getLoginUser().getUser();
        if(currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            return student;
        }else if (currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            if(!Objects.equals(studentId, currentUser.getId())){
                throw new ApiException("无权查看其它学员信息");
            }
        }
        if(!student.getCompanyId().equals(currentUser.getCompanyId())){
            throw new ApiException("无权限查看其它企业学员");
@@ -84,6 +118,7 @@
    @Override
    public int insertStudent(ExStudent student) {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        student.setCompanyId(currentUser.getCompanyId());
        checkUserAllowed(student);
        if(!checkPhoneUnique(student)){
            throw new ApiException("手机号已存在");
@@ -91,7 +126,19 @@
        if(!checkIdNoUnique(student)){
            throw new ApiException("身份证号已存在");
        }
        student.setCreateId(currentUser.getId());
        if(currentUser.getUserType().equals(UserTypeEnum.COMPANY_USER.getCode())){
            if(student.getCreateId()==null){
                throw new ApiException("部门id为空");
            }
        }else if(currentUser.getUserType().equals(UserTypeEnum.DEPART_USER.getCode())){
            student.setCreateId(currentUser.getId());
        }else{//当前用户为车间级用户
            if(currentUser.getParentId()==null){
                throw new ApiException("当前用户部门id为空");
            }
            student.setCreateId(currentUser.getParentId());
        }
        student.setPassword(SecurityUtils.encryptPassword(Base64.decodeStr(student.getPassword())));
        int row=baseMapper.insert(student);
        if(row<0){
            throw new ApiException("新增学员失败");
@@ -109,6 +156,7 @@
            throw new ApiException("身份证号已存在");
        }
        ExStudent existStudent=checkUserDataScope(student.getId());
        student.setPassword(null);
        int row=baseMapper.updateById(student);
        if(row<0){
            throw new ApiException("更新学员失败");
@@ -150,13 +198,30 @@
    }
    @Override
    public ExStudent checkIdNoUnique(String idNo) {
        ExStudent stu= baseMapper.checkIdNoUnique(idNo);
        if(stu!=null){
            SysCompany company=companyService.selectCompanyById(stu.getCompanyId());
            stu.setCompany(company);
    public Map checkIdNoUnique(String idNo) {
        if(StringUtils.isBlank(idNo)){
            throw new ApiException("身份证号不能为空");
        }
        return stu;
        SysUser currentUser=SecurityUtils.getLoginUser().getUser();
        ExStudent stu= baseMapper.checkIdNoUnique(idNo);
        Map<String,Object> resMap=new HashMap<>();
        Integer status=0;//默认不存在
        if(stu!=null){
            status=1; //存在,且同一个公司
            resMap.put("studentId",stu.getId());
            resMap.put("studentName",stu.getName());
            if(stu.getCompanyId()!=currentUser.getCompanyId()){
                status=2;  //存在,不同公司
                SysCompany company=companyService.selectCompanyById(stu.getCompanyId());
                if(company==null){
                    throw new ApiException("学员公司不存在");
                }
                resMap.put("companyId",company.getId());
                resMap.put("companyName",company.getName());
            }
        }
        resMap.put("status",status);
        return resMap;
    }
    @Override
@@ -167,6 +232,62 @@
        su.setUpdateBy(SecurityUtils.getUsername());
        delCacheByPhone(existStudent.getPhone());
        return updateById(su);
    }
    @Override
    public void changeStudentCompany(Map<String, Long> bodyMap) {
        Long studentId=bodyMap.get("studentId");
        Long companyId=bodyMap.get("companyId");
        if(studentId==null||companyId==null){
            throw new ApiException("学员id或者公司id不能为空");
        }
        ExStudent student = baseMapper.selectById(studentId);
        if(student==null){
            throw new ApiException("学员不存在");
        }
        SysCompany company=companyService.selectCompanyById(companyId);
        if(company==null){
            throw new ApiException("公司不存在");
        }
        ExStudent stu=new ExStudent().setId(studentId).setCompanyId(companyId);
        stu.setUpdateBy(SecurityUtils.getUsername());
        baseMapper.updateById(stu);
    }
    @Override
    public List<TrainRecordVO> trainRecord(Long studentId) {
        List<TrainRecordVO> trainRecordVOList=new ArrayList<>();
        //查询培训记录
        List<ExPhaseStudent> phaseStudentList=phaseStudentMapper.selectPhaseStudentByStudentId(studentId);
        if(phaseStudentList.size()>0){
            trainRecordVOList.addAll(phaseStudentList.stream().map(item -> {
                TrainRecordVO trainRecordVO=new TrainRecordVO();
                trainRecordVO.setStudentId(item.getStudentId());
                trainRecordVO.setTrainType(1);
                trainRecordVO.setName(item.getPhaseName());
                trainRecordVO.setCreateTime(item.getCreateTime());
                trainRecordVO.setCompanyId(item.getCompanyId());
                trainRecordVO.setCompanyName(item.getCompanyName());
                return trainRecordVO;
            }).collect(Collectors.toList()));
        }
        //查询考试记录
        List<ExPaperStudent> paperStudentList=paperStudentMapper.selectByStudentId(studentId);
        if(paperStudentList.size()>0){
            trainRecordVOList.addAll(paperStudentList.stream().map(item -> {
                TrainRecordVO trainRecordVO=new TrainRecordVO();
                trainRecordVO.setStudentId(item.getStudentId());
                trainRecordVO.setTrainType(2);
                trainRecordVO.setName(item.getExamPaper().getName());
                trainRecordVO.setCreateTime(item.getCreateTime());
                trainRecordVO.setCompanyId(item.getCompanyId());
                trainRecordVO.setCompanyName(item.getCompanyName());
                return trainRecordVO;
            }).collect(Collectors.toList()));
        }
        //排序
        trainRecordVOList=trainRecordVOList.stream().sorted(Comparator.comparing(TrainRecordVO::getCreateTime)).collect(Collectors.toList());;
        return trainRecordVOList;
    }
    public ExStudent checkUserDataScope(Long studentId) {
@@ -183,13 +304,27 @@
    public void checkUserAllowed(ExStudent student) {
        SysUser currentUser= SecurityUtils.getLoginUser().getUser();
        if(currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            throw new ApiException("系统管理员没有权限操作");
        if(student.getId()!=null){
            if(currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
                return;
            }
            if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode()) ){
                if(!Objects.equals(currentUser.getId(), student.getId())){
                    throw new ApiException("没有权限操作");
                }else{
                    return;
                }
            }
        }else{
            if(currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
                throw new ApiException("系统管理员没有权限操作");
            }
            if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
                throw new ApiException("没有权限操作");
            }
        }
        if(currentUser.getUserType().equals(UserTypeEnum.STUDENT.getCode())){
            throw new ApiException("没有权限操作");
        }
        if(!currentUser.getCompanyId().equals(student.getCompanyId())){
        if(student.getCompanyId()!=null&&!currentUser.getCompanyId().equals(student.getCompanyId())){
            throw new ApiException("没有权限操作其他企业学员");
        }
    }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/ExStudentStudyServiceImpl.java
@@ -51,10 +51,11 @@
            row=baseMapper.updateById(existStudentStudy);
        }else{
            row=baseMapper.insert(studentStudy);
            if(row<1){
                throw new ApiException("新增学习记录失败");
            }
        }
        if(row<1){
            throw new ApiException("新增学习记录失败");
        }
        System.out.println("student_study id:"+studentStudy.getId());
        return studentStudy.getId();
    }
@@ -82,6 +83,7 @@
                studentStudyPeriodVO.setPeriodId(courseChapterPeriod.getId());
                studentStudyPeriodVO.setPeriod(courseChapterPeriod.getPeriod());
                studentStudyPeriodVO.setResourceId(courseChapterPeriod.getResourceId());
                studentStudyPeriodVO.setResourceType(courseChapterPeriod.getResource()!=null?courseChapterPeriod.getResource().getResourceType():null);
                ExStudentStudy exStudentStudy=studentStudyMap.get(courseChapterPeriod.getId());
                if(exStudentStudy!=null){
                    studentStudyPeriodVO.setProgress(exStudentStudy.getProgress());
@@ -100,7 +102,7 @@
    @Override
    public void progress(ExStudentStudy studentStudy) {
        if(studentStudy.getPhaseId()==null||studentStudy.getPeriodId()==null||studentStudy.getStudentId()==null||studentStudy.getResourceId()==null){
        if(studentStudy.getId()==null||studentStudy.getPhaseId()==null||studentStudy.getPeriodId()==null||studentStudy.getStudentId()==null||studentStudy.getResourceId()==null){
            throw new ApiException("参数传参错误");
        }
        ExResource resource=resourceMapper.selectById(studentStudy.getResourceId());
@@ -112,10 +114,12 @@
            }
        } else if (ResourceTypeEnum.DOC.getCode().equals(resource.getResourceType())) {
            // 文档类型处理
            if (studentStudy.getCurrentPage().compareTo(resource.getDocPage()) >= 0) {
                // 学习完成
                 completeStudy(studentStudy);
            }
//            if (studentStudy.getCurrentPage().compareTo(resource.getDocPage()) >= 0) {
//                // 学习完成
//                 completeStudy(studentStudy);
//            }
            // 学习完成
            completeStudy(studentStudy);
        }
        redisUtils.set(CacheConstant.STUDY_PROCESS_KEY + studentStudy.getId(), studentStudy, 1, TimeUnit.DAYS);
    }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCarouselServiceImpl.java
@@ -24,9 +24,9 @@
    @Override
    public CommonPage selectCarouselList(SysCarousel carousel) {
        PageUtils.startPage();
        List<SysCarousel> carousels=baseMapper.selectCarouselList(carousel);
        return CommonPage.restPage(carousels);
            PageUtils.startPage();
            List<SysCarousel> carousels = baseMapper.selectCarouselList(carousel);
            return CommonPage.restPage(carousels);
    }
    @Override
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCategoryServiceImpl.java
@@ -7,8 +7,11 @@
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.system.domain.SysCategory;
import com.gkhy.exam.system.mapper.ExCourseMapper;
import com.gkhy.exam.system.mapper.ExQuestionBankMapper;
import com.gkhy.exam.system.mapper.SysCategoryMapper;
import com.gkhy.exam.system.service.SysCategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Comparator;
@@ -26,6 +29,10 @@
 */
@Service
public class SysCategoryServiceImpl extends ServiceImpl<SysCategoryMapper, SysCategory> implements SysCategoryService {
    @Autowired
    private ExCourseMapper courseMapper;
    @Autowired
    private ExQuestionBankMapper questionBankMapper;
    @Override
    public List<SysCategory> selectCategoryList(SysCategory category) {
@@ -82,15 +89,15 @@
    public int deleteCategoryById(Long categoryId) {
        //校验课程分类是否存在课程或者题目
        checkUserAllowed();
        int courseCount=baseMapper.selectCountOfCoure(categoryId);
        int courseCount=courseMapper.selectCountByCategoryId(categoryId);
        if(courseCount>0){
            throw new ApiException("已绑定课程,无法删除");
        }
        int bankCount=baseMapper.selectCountOfBank(categoryId);
        int bankCount=questionBankMapper.selectCountByCategoryId(categoryId);
        if(bankCount>0){
            throw new ApiException("已绑定题库,无法删除");
        }
        int row=baseMapper.deleteById(categoryId);
        int row=baseMapper.deleteByCategoryId(categoryId);
        if(row<1){
            throw new ApiException("删除课程分类失败");
        }
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCommonServiceImpl.java
@@ -2,22 +2,35 @@
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gkhy.exam.common.config.FilePathConfig;
import com.gkhy.exam.common.config.MinioConfig;
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.ResourceTypeEnum;
import com.gkhy.exam.common.enums.UserTypeEnum;
import com.gkhy.exam.common.excel.StudentExcelData;
import com.gkhy.exam.common.excel.StudentExcelDataListener;
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.*;
import com.gkhy.exam.system.domain.ExResource;
import com.gkhy.exam.system.domain.ExStudent;
import com.gkhy.exam.system.domain.SysCompany;
import com.gkhy.exam.system.domain.vo.UploadObjectVO;
import com.gkhy.exam.system.mapper.ExResourceMapper;
import com.gkhy.exam.system.service.ExStudentService;
import com.gkhy.exam.system.service.SysCommonService;
import com.gkhy.exam.system.service.SysCompanyService;
import com.gkhy.exam.system.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
@@ -33,6 +46,9 @@
@Slf4j
public class SysCommonServiceImpl implements SysCommonService {
    @Autowired
    private MinioConfig minioConfig;
    @Value("${image.upload_image}")
    private String uploadPath;
    @Autowired
@@ -43,9 +59,15 @@
    private ExResourceMapper resourceMapper;
    @Resource
    private FilePathConfig filePathConfig;
    @Autowired
    private SysCompanyService companyService;
    @Autowired
    private ExStudentService studentService;
    @Autowired
    private SysUserService userService;
    // 使用HashSet存储允许的文件格式,提高查找效率
    private final HashSet<String> FORMATSET = new HashSet<>(Arrays.asList(".mp4", ".mp3", ".xls", ".xlsx", ".doc", ".docx", ".ppt", ".pptx", ".pdf"));
    private final HashSet<String> FORMATSET = new HashSet<>(Arrays.asList(".mp4",".doc", ".docx", ".ppt", ".pptx", ".pdf"));
    @Resource(name = "threadPoolTaskExecutor")
@@ -202,6 +224,7 @@
            log.warn("临时目录 {}已删除", new File(localPath).getParent());
        });
        uploadObjectVO.setPath(minioPath);
        uploadObjectVO.setUrl(minioConfig.getEndpoint()+minioConfig.getBucketName()+"/"+minioPath);
        return uploadObjectVO;
    }
@@ -236,6 +259,87 @@
        }
    }
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void importStudent() {
        String path="/home/java/train_exam/back/安全教育学员模板.xlsx";
      //  String path="F:/kzy/乱七八糟/安全教育学员模板.xlsx";
        List<StudentExcelData> studentExcelDataList=EasyExcel.read(path, StudentExcelData.class,new StudentExcelDataListener()).sheet().doReadSync();
        List<ExStudent> students=new ArrayList<>();
        List<StudentExcelData> errorStudents=new ArrayList<>();
        SysCompany company=companyService.getOne(Wrappers.<SysCompany>lambdaQuery().eq(SysCompany::getName,studentExcelDataList.get(0).getCompanyName())
                .eq(SysCompany::getDelFlag,0));
        if(company==null){
            throw new ApiException("公司不存在");
        }
        for(StudentExcelData studentExcelData:studentExcelDataList){
            String errorMessage=validateData(studentExcelData);
            if(StringUtils.isNotBlank(errorMessage)){
                studentExcelData.setRemark(errorMessage);
                errorStudents.add(studentExcelData);
                continue;
            }
            ExStudent dbStudent=studentService.getOne(Wrappers.<ExStudent>lambdaQuery().eq(ExStudent::getPhone,studentExcelData.getPhone()));
            if(dbStudent!=null){
                if(!dbStudent.getName().equals(studentExcelData.getName())){
                    studentExcelData.setRemark("序号"+studentExcelData.getIndex()+"学员用户已存在");
                    errorStudents.add(studentExcelData);
                }
                continue;
            }
            ExStudent student=new ExStudent();
            BeanUtils.copyProperties(studentExcelData,student,new String[]{"sex"});
            if("男".equals(studentExcelData.getSex())){
                student.setSex(0);
            }else{
                student.setSex(1);
            }
            String departName=studentExcelData.getDeptName();
            List<SysUser> users=userService.list(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getName,departName).eq(SysUser::getUserType, UserTypeEnum.DEPART_USER.getCode()).eq(SysUser::getDelFlag,0).last(" limit 1"));
            if(users.isEmpty()){
                studentExcelData.setRemark("序号"+studentExcelData.getIndex()+"未找到相应的部门账号");
                errorStudents.add(studentExcelData);
                continue;
            }
            student.setPassword(SecurityUtils.encryptPassword("123456@a"));
            student.setCompanyId(company.getId());
            student.setCreateId(users.get(0).getId());
            students.add(student);
        }
        if(!errorStudents.isEmpty()){
            EasyExcel.write("error.xlsx").head(StudentExcelData.class)
                    .sheet("异常用户列表")
                    .doWrite(errorStudents);
        }
        studentService.saveBatch(students);
    }
    public String validateData(StudentExcelData studentExcelData){
        if(StringUtils.isBlank(studentExcelData.getName())){
            return "序号"+studentExcelData.getIndex()+"姓名为空";
        }
        if(StringUtils.isBlank(studentExcelData.getSex())){
            return "序号"+studentExcelData.getIndex()+"性别为空";
        }
        if(StringUtils.isBlank(studentExcelData.getIdNo())||studentExcelData.getIdNo().length()!=18){
            return "序号"+studentExcelData.getIndex()+"身份证为空或者长度不正确";
        }
        if(StringUtils.isBlank(studentExcelData.getPhone())||studentExcelData.getPhone().length()!=11){
            return "序号"+studentExcelData.getIndex()+"手机号为空或者长度不正确";
        }
        if(StringUtils.isBlank(studentExcelData.getDeptName())){
            return "序号"+studentExcelData.getIndex()+"部门为空";
        }
        return "";
    }
    public UploadObjectVO mergeFile(String fileMd5,String fileName){
        String subfix = fileName.substring(fileName.lastIndexOf("."));
        String systemDir=System.getProperty("user.dir");
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysCompanyServiceImpl.java
@@ -49,6 +49,7 @@
            throw new ApiException("公司名称已存在");
        }
        company.setCreateBy(SecurityUtils.getUsername());
        company.setRemainPeriod(company.getTotalPeriod());
        int row= baseMapper.insert(company);
        if(row<1){
            throw new ApiException("新增公司失败");
exam-system/src/main/java/com/gkhy/exam/system/service/impl/SysUserServiceImpl.java
@@ -9,7 +9,10 @@
import com.gkhy.exam.common.domain.entity.SysUser;
import com.gkhy.exam.common.enums.UserTypeEnum;
import com.gkhy.exam.common.exception.ApiException;
import com.gkhy.exam.common.utils.*;
import com.gkhy.exam.common.utils.PageUtils;
import com.gkhy.exam.common.utils.RedisUtils;
import com.gkhy.exam.common.utils.SecurityUtils;
import com.gkhy.exam.common.utils.StringUtils;
import com.gkhy.exam.system.mapper.SysUserMapper;
import com.gkhy.exam.system.service.SysConfigService;
import com.gkhy.exam.system.service.SysUserService;
@@ -17,6 +20,7 @@
import org.springframework.stereotype.Service;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -44,14 +48,17 @@
    @Override
    public CommonPage<SysUser> selectUserList(SysUser user) {
        SysUser currentUser=SecurityUtils.getLoginUser().getUser();
        Map<String,Object> paramsMap=new HashMap<>();
        paramsMap.put("userType",currentUser.getUserType());
        user.setParams(paramsMap);
        if(!currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
            user.setCompanyId(currentUser.getCompanyId());
        List<SysUser> users=new ArrayList<>();
        if(!currentUser.getUserType().equals(UserTypeEnum.WORKSHOP_USER.getCode())){
            if(!currentUser.getUserType().equals(UserTypeEnum.SYSTEM_USER.getCode())){
                user.setCompanyId(currentUser.getCompanyId());
                Map<String,Object> paramsMap=new HashMap<>();
                paramsMap.put("userType",currentUser.getUserType());
                user.setParams(paramsMap);
            }
            PageUtils.startPage();
            users=baseMapper.userList(user);
        }
        PageUtils.startPage();
        List<SysUser> users=baseMapper.userList(user);
        return CommonPage.restPage(users);
    }
@@ -109,6 +116,7 @@
        checkRequestData(user);
        checkUserAllowed(user);
        user.setUpdateBy(SecurityUtils.getUsername());
        user.setPassword(null);
        int row=baseMapper.updateById(user);
        if(row<1){
            throw new ApiException("更新用户信息失败");
@@ -182,7 +190,11 @@
        Integer currentUserType=currentUser.getUserType();
        Integer userType=user.getUserType();
        //校验权限,规则:上一级用户可以增加下一级用户类型的用户
        if(!currentUserType.equals(UserTypeEnum.SYSTEM_USER.getCode())){
        if(currentUserType.equals(UserTypeEnum.SYSTEM_USER.getCode())){
            if( !userType.equals(UserTypeEnum.SYSTEM_USER.getCode())&&!userType.equals(UserTypeEnum.OTHER_USER.getCode()) &&!userType.equals(UserTypeEnum.COMPANY_USER.getCode())){
                throw new ApiException("管理员只能操作管理员、企业级和其他类型的用户");
            }
        }else{
            if(userType.equals(UserTypeEnum.OTHER_USER.getCode())){
                throw new ApiException("没有权限操作或者更新上级用户类型的用户");
            }
exam-system/src/main/resources/mapper/system/ExCourseChapterMapper.xml
@@ -40,6 +40,9 @@
        <result property="id"       column="resource_id"       />
        <result property="name"       column="resource_name"       />
        <result property="resourcePath"       column="resource_path"       />
        <result property="resourceType"       column="resource_type"       />
        <result property="resourceLength"       column="period"       />
        <result property="docPage"       column="doc_page"       />
    </resultMap>
    <sql id="selectChapterVo">
@@ -85,8 +88,8 @@
    </select>
    <select id="getChapterPeriodByChapterId" resultMap="ExPeriodResult">
        select a.id,a.name,a.course_id,a.chapter_id,a.status,a.company_id,a.resource_id,
               b.resource_length as period,b.name as resource_name,b.resource_path from ex_course_chapter_period a
        select a.id,a.name,a.course_id,a.chapter_id,a.status,a.company_id,a.resource_id,a.sort,
               b.resource_length as period,b.name as resource_name,b.resource_path,b.doc_page,b.resource_type from ex_course_chapter_period a
        left join ex_resource b on b.id=a.resource_id
        where a.chapter_id=#{chapterId}
        <if test="status!=null">
exam-system/src/main/resources/mapper/system/ExCourseChapterPeriodMapper.xml
@@ -40,5 +40,9 @@
        select * from ex_course_chapter_period where chapter_id=#{chapterId}
    </select>
    <select id="selectCountByCourseId" resultType="java.lang.Integer" parameterType="java.lang.Long">
        select count(1) from ex_course_chapter_period where course_id=#{courseId} and status=0
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExCourseMapper.xml
@@ -13,7 +13,7 @@
        <result property="delFlag"         column="del_flag"          />
        <result property="companyId"         column="company_id"          />
        <result property="privatize"         column="privatize"          />
        <result property="period"         column="period"          />
        <result property="message"         column="message"          />
        <result property="version"         column="version"          />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
@@ -22,11 +22,12 @@
        <result property="remark"         column="remark"          />
        <result property="companyName"         column="company_name"          />
        <result property="categoryName"         column="category_name"          />
        <association property="period" javaType="java.lang.Long"  select="getCoursePeriod" column="{courseId=id}"/>
    </resultMap>
    <sql id="selectCourseVo">
        select a.id, a.name, a.category_id, a.status, a.logo,a.sort,a.introduce,a.state,a.company_id,
               a.privatize,a.period,a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as company_name,c.name as category_name
        select a.id, a.name, a.category_id, a.status, a.logo,a.sort,a.introduce,a.state,a.company_id,a.message,
               a.privatize,a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as company_name,c.name as category_name
        from ex_course a
        left join sys_company b on b.id=a.company_id
        left join sys_category c on c.id=a.category_id
@@ -68,10 +69,24 @@
    </select>
    <select id="checkNameUnique" resultType="com.gkhy.exam.system.domain.ExCourse">
        select id,name from ex_course where name=#{name} and company_id=#{companyId} and del_flag=0 limit 1
        select id,name from ex_course where name=#{name} and del_flag=0
        <if test="companyId!=null">
            and company_id=#{companyId}
        </if>
        limit 1
    </select>
    <select id="selectCourseState" resultType="java.lang.Integer">
        select state from ex_course where id=#{courseId}
    </select>
    <select id="selectCountByCategoryId" resultType="java.lang.Integer">
        select count(1) from ex_course where category_id=#{categoryId} and del_flag=0
    </select>
    <select id="getCoursePeriod" resultType="java.lang.Long">
        select sum(b.resource_length) from ex_course_chapter_period a
        inner join ex_resource b on a.resource_id=b.id
        where a.course_id=#{courseId}
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExCoursePhaseMapper.xml
@@ -15,11 +15,16 @@
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
        <result property="coursePeriod"         column="course_period"          />
        <result property="courseName"         column="course_name"          />
        <result property="companyName"         column="company_name"          />
        <result property="studentCount"         column="student_count"          />
        <result property="finishCount"         column="finish_count"          />
        <association property="coursePeriod" javaType="java.lang.Long"  select="getCoursePeriod" column="{courseId=course_id}"/>
        <association property="finishCount" javaType="java.lang.Integer"  select="getFinishStudentCount" column="{courseId=course_id,phaseId=id}"/>
    </resultMap>
    <sql id="selectCoursePhaseVo">
        select a.id, a.name, a.code, a.company_id, a.course_id,a.level,a.del_flag,a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.period as course_period
        select a.id, a.name, a.code, a.company_id, a.course_id,a.level,a.del_flag,a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as course_name
        from ex_course_phase a
        left join ex_course b on b.id=a.course_id
    </sql>
@@ -29,7 +34,12 @@
    </update>
    <select id="selectCoursePhaseList" resultMap="ExCoursePhaseResult">
        <include refid="selectCoursePhaseVo"/>
        select a.id, a.name, a.code, a.company_id, a.course_id,a.level,a.del_flag,a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,
               b.name as course_name,c.name as company_name,
               (select count(1) from ex_phase_student where phase_id=a.id) as student_count
        from ex_course_phase a
        left join ex_course b on b.id=a.course_id
        left join sys_company c on c.id=a.company_id
        <where>
            and a.del_flag=0
            <if test="name != null and name != ''">
@@ -43,6 +53,9 @@
            </if>
            <if test="courseId != null and courseId != ''">
                AND a.course_id =#{courseId}
            </if>
            <if test="level != null">
                AND a.level =#{level}
            </if>
        </where>
        order by a.create_time desc
@@ -61,4 +74,14 @@
        select count(1) from ex_course_phase where del_flag=0 and courde_id=#{courseId}
    </select>
    <select id="getCoursePeriod" resultType="java.lang.Long">
        select sum(b.resource_length) from ex_course_chapter_period a
        inner join ex_resource b on a.resource_id=b.id
        where a.course_id=#{courseId}
    </select>
    <select id="getFinishStudentCount" resultType="java.lang.Integer">
        select count(1) from (select student_id,count(1) as study_count from ex_student_study  where phase_id=#{phaseId} group by student_id) as a
        where a.study_count=(select count(1) as period_count from ex_course_chapter_period  where course_id=#{courseId})
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExExamPaperMapper.xml
@@ -9,7 +9,7 @@
        <result property="companyId"         column="company_id"          />
        <result property="categoryId"         column="category_id"          />
        <result property="limitTime"         column="limit_time"          />
        <result property="limit"         column="limit"          />
        <result property="limited"         column="limited"          />
        <result property="singleNum"         column="single_num"          />
        <result property="singleScore"         column="single_score"          />
        <result property="singleBankId"         column="single_bank_id"          />
@@ -22,28 +22,38 @@
        <result property="judgeScore"         column="judge_score"          />
        <result property="judgeBankId"         column="judge_bank_id"          />
        <result property="judgeMethod"         column="judge_method"          />
        <result property="easyNum"         column="easy_num"          />
        <result property="easyScore"         column="easy_score"          />
        <result property="easyBankId"         column="easy_bank_id"          />
        <result property="easyMethod"         column="easy_method"          />
        <result property="passScore"         column="pass_score"          />
        <result property="version"         column="version"          />
        <result property="delFlag"         column="del_flag"          />
        <result property="createBy"       column="create_by"       />
        <result property="deadline"     column="deadline"     />
        <result property="createTime"     column="create_time"     />
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
        <result property="companyName"         column="company_name"          />
        <collection property="singleQuestions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=id,questionType=1}"/>
        <collection property="multiQuestions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=id,questionType=2}"/>
        <collection property="judgeQuestions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=id,questionType=3}"/>
        <result property="categoryName"         column="category_name"          />
        <association property="paperStudentInfoVO" javaType="com.gkhy.exam.system.domain.vo.PaperStudentInfoVO"  select="getPaperStudentInfoByPaperId" column="{paperId=id}"/>
    </resultMap>
    <resultMap type="com.gkhy.exam.system.domain.ExExamPaper" id="ExamPaperResult2" extends="ExamPaperResult">
        <collection property="questions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=id}"/>
    </resultMap>
    <sql id="selectExamPaperVo">
        select a.id, a.name, a.code, a.status, a.company_id,a.category_id,a.limit_time,a.limit,a.single_num,a.single_score,a.single_bank_id,
        select a.id, a.name, a.code, a.status, a.company_id,a.category_id,a.deadline,a.limit_time,a.limited,a.single_num,a.single_score,a.single_bank_id,
               a.single_method,a.multi_num,a.multi_score,
               a.multi_bank_id,a.multi_method,a.judge_num,a.judge_score,a.judge_bank_id,a.judge_method,a.pass_score,
               a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as company_name
               a.easy_num,a.easy_score,a.easy_bank_id,a.easy_method,
               a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as company_name,c.name as category_name
        from ex_exam_paper a
        left join sys_company b on b.id=a.company_id
        left join sys_category c on c.id=a.category_id
    </sql>
    <update id="deletePaperById">
@@ -64,11 +74,14 @@
            <if test="companyId != null and companyId != ''">
                AND a.company_id= #{companyId}
            </if>
            <if test="categoryId != null and categoryId != ''">
                AND a.category_id= #{categoryId}
            </if>
        </where>
        order by a.id desc
    </select>
    <select id="selectExamPaperById" resultMap="ExamPaperResult">
    <select id="selectExamPaperById" resultMap="ExamPaperResult2">
        <include refid="selectExamPaperVo"/>
        where a.id=#{paperId}
    </select>
@@ -78,16 +91,28 @@
    </select>
    <select id="checkNameUnique" resultType="com.gkhy.exam.system.domain.ExExamPaper">
        select id ,name from ex_exam_paper where name=#{name} limit 1
        select id ,name from ex_exam_paper where name=#{name} and del_flag=0
        <if test="companyId!=null">
            and company_id=#{companyId}
        </if>
        limit 1
    </select>
    <select id="getQuestionByPaperId" resultType="com.gkhy.exam.system.domain.ExQuestion">
        select a.* from ex_question a
        inner join ex_paper_question b on a.id=b.question_id
        where b.paper_id=#{paperId} and a.question_type=#{questionType}
        order by a.id asc
        where b.paper_id=#{paperId}
        order by a.question_type asc,a.id asc
    </select>
    <select id="getPaperStudentInfoByPaperId" resultType="com.gkhy.exam.system.domain.vo.PaperStudentInfoVO">
        select count(1) as student_count,ifnull(sum(passed),0) pass_student_count,ROUND(ifnull(avg(score),0),2) as avg_score,
               (select count(1) from ex_paper_student where paper_id=#{paperId} and state!=0) as finish_count
        from ex_paper_student  where paper_id=#{paperId}
    </select>
exam-system/src/main/resources/mapper/system/ExExamRecordMapper.xml
@@ -19,13 +19,23 @@
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
        <result property="companyName"         column="company_name"          />
        <association property="student" javaType="com.gkhy.exam.system.domain.ExStudent" resultMap="exStudentResult" />
    </resultMap>
    <resultMap id="exStudentResult" type="com.gkhy.exam.system.domain.ExStudent">
        <result property="id"       column="student_id"       />
        <result property="name"       column="student_name"       />
        <result property="phone"       column="student_phone"       />
        <result property="idNo"       column="student_idno"       />
    </resultMap>
    <sql id="selectExamRecordVo">
        select a.id, a.company_id, a.student_id, a.plan_name, a.course_name,a.level,a.period,a.actual_period,a.score,
               a.company_id,a.passed, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as company_name
               a.company_id,a.passed, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as company_name,
               c.name as student_name,c.id_no as student_idno,c.phone as student_phone
        from ex_exam_record a
        left join sys_company b on b.id=a.company_id
        left join ex_student c on c.id=a.student_id
    </sql>
    <select id="selectExamRecordList" resultMap="ExamRecordResult">
exam-system/src/main/resources/mapper/system/ExExerciseAnswerMapper.xml
@@ -6,6 +6,6 @@
    </delete>
    <select id="getExerciseAnswer" resultType="com.gkhy.exam.system.domain.ExExerciseAnswer">
        select * from ex_exercise_answer where bank_id=#{bankId} and qustion_id=#{questionId} and student_id=#{studentId} limit 1
        select * from ex_exercise_answer where bank_id=#{bankId} and question_id=#{questionId} and student_id=#{studentId} limit 1
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExPaperQuestionMapper.xml
@@ -13,11 +13,12 @@
    </delete>
    <delete id="deletePaperQuestion">
        delete from ex_paper_question
        delete a from ex_paper_question as a
        inner join ex_question as b on b.id=a.question_id
        <where>
            and paper_id=#{paperId}
            and a.paper_id=#{paperId}
           <if test="questionType!=null">
               and question_type=#{questionType}
               and b.question_type=#{questionType}
           </if>
        </where>
    </delete>
exam-system/src/main/resources/mapper/system/ExPaperStudentMapper.xml
@@ -8,7 +8,7 @@
        <result property="score"     column="score"     />
        <result property="passed"     column="passed"     />
        <result property="useTime"     column="use_time"     />
        <result property="completed"     column="completed"     />
        <result property="state"     column="state"     />
        <result property="version"         column="version"          />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
@@ -18,10 +18,8 @@
        <result property="createName"         column="create_name"          />
        <association property="student" javaType="com.gkhy.exam.system.domain.ExStudent" resultMap="exStudentResult" />
        <association property="examPaper" javaType="com.gkhy.exam.system.domain.ExExamPaper" resultMap="exExamPaperResult" />
        <collection property="singleQuestions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=paper_id,studentId=student_id,completed=completed,questionType=1}"/>
        <collection property="multiQuestions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=paper_id,studentId=student_id,completed=completed,questionType=2}"/>
        <collection property="judgeQuestions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=paper_id,studentId=student_id,completed=completed,questionType=3}"/>
    </resultMap>
        <collection property="questions" ofType="com.gkhy.exam.system.domain.ExQuestion" select="getQuestionByPaperId" column="{paperId=paper_id,studentId=student_id,state=state}"/>
     </resultMap>
    <resultMap type="com.gkhy.exam.system.domain.ExPaperStudent" id="SimplePaperStudentResult">
@@ -31,7 +29,8 @@
        <result property="score"     column="score"     />
        <result property="passed"     column="passed"     />
        <result property="useTime"     column="use_time"     />
        <result property="completed"     column="completed"     />
        <result property="startTime"     column="start_time"     />
        <result property="state"     column="state"     />
        <result property="version"         column="version"          />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
@@ -39,12 +38,23 @@
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
        <result property="createName"         column="create_name"          />
        <result property="companyId"         column="company_id"          />
        <result property="companyName"         column="company_name"          />
        <association property="student" javaType="com.gkhy.exam.system.domain.ExStudent" resultMap="exStudentResult" />
        <association property="examPaper" javaType="com.gkhy.exam.system.domain.ExExamPaper" resultMap="exExamPaperResult" />
    </resultMap>
    <resultMap type="com.gkhy.exam.system.domain.ExQuestion" id="ExQuestionResult">
        <result property="id"       column="id"       />
        <result property="questionType"    column="question_type"    />
        <result property="bankId"     column="bank_id"     />
        <result property="status"         column="status"          />
        <result property="companyId"         column="company_id"          />
        <result property="status"         column="status"          />
        <result property="answer"         column="answer"          />
        <result property="title"         column="title"          />
        <result property="privatize"         column="privatize"          />
        <result property="content"         column="content"          />
        <association property="studentAnswer" javaType="com.gkhy.exam.system.domain.ExStudentAnswer" resultMap="studentAnswerResult" />
    </resultMap>
@@ -55,6 +65,7 @@
        <result property="questionId"       column="answer_question_id"       />
        <result property="answer"       column="answer_answer"       />
        <result property="passed"       column="answer_passed"       />
        <result property="score"       column="answer_score"       />
    </resultMap>
    <resultMap id="exExamPaperResult" type="com.gkhy.exam.system.domain.ExExamPaper">
@@ -62,6 +73,17 @@
        <result property="code"       column="paper_code"       />
        <result property="name"       column="paper_name"       />
        <result property="categoryName"       column="category_name"       />
        <result property="limited"       column="limited"       />
        <result property="limitTime"       column="limit_time"       />
        <result property="deadline"       column="deadline"       />
        <result property="singleNum"       column="single_num"       />
        <result property="multiNum"       column="multi_num"       />
        <result property="judgeNum"       column="judge_num"       />
        <result property="easyNum"       column="easy_num"       />
        <result property="singleScore"       column="single_score"       />
        <result property="multiScore"       column="multi_score"       />
        <result property="judgeScore"       column="judge_score"       />
        <result property="easyScore"       column="easy_score"       />
    </resultMap>
    <resultMap id="exStudentResult" type="com.gkhy.exam.system.domain.ExStudent">
@@ -71,11 +93,19 @@
    </resultMap>
    <insert id="batchInsert">
        insert into ex_paper_student(paper_id,student_id) values
        insert into ex_paper_student(paper_id,student_id,create_id) values
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.paperId},#{item.studentId})
            (#{item.paperId},#{item.studentId},#{item.createId})
        </foreach>
    </insert>
    <update id="batchUpdateComplete">
        update ex_paper_student set completed=#{completed}
        where id in
        <foreach collection="paperStudentIds" item="item" open="(" separator="," close=")">
            #{item}
        </foreach>
    </update>
    <select id="countByPaperId" resultType="java.lang.Integer">
        select count(1) from ex_paper_student where paper_id=#{paperId}
@@ -86,7 +116,11 @@
    </select>
    <select id="selectPaperStudentList" resultMap="SimplePaperStudentResult">
        select a.*,e.name as create_name,b.phone as student_phone,b.name as student_name,c.name as paper_name,c.id as paper_id,c.code as paper_code,d.name as category_name  from ex_paper_student a
        select a.*,e.name as create_name,b.phone as student_phone,b.name as student_name,c.name as paper_name,c.code as paper_code,c.limited,c.limit_time,c.deadline,d.name as category_name
        <if test="studentId!=null">
        ,(select question_id from ex_student_answer where paper_id=a.paper_id and student_id=#{studentId} order by id desc limit 1) as question_id
        </if>
        from ex_paper_student a
        left join ex_student b on a.student_id=b.id
        left join ex_exam_paper c on c.id=a.paper_id
        left join sys_category d on d.id=c.category_id
@@ -105,12 +139,15 @@
            <if test="studentId!=null">
                and a.student_id = #{studentId}
            </if>
            <if test="state!=null">
                and a.state = #{state}
            </if>
        </where>
        order by a.id desc
        order by a.passed desc,a.id desc
    </select>
    <select id="selectPaperStudentById" resultMap="ExPaperStudentResult">
        select a.*,b.id as student_id,b.phone as student_phone,b.name as student_name,c.name as paper_name from ex_paper_student a
        select a.*,b.id as student_id,b.phone as student_phone,b.name as student_name,c.name as paper_name,c.single_num,c.multi_num,c.judge_num,c.easy_num,c.single_score,c.multi_score,c.judge_score,c.easy_score from ex_paper_student a
        left join ex_student b on a.student_id=b.id
        left join ex_exam_paper c on c.id=a.paper_id
        where a.id=#{paperStudentId}
@@ -130,19 +167,31 @@
        where a.paper_id=#{paperId} and a.student_id=#{studentId}
    </select>
    <select id="getQuestionByPaperId" resultType="com.gkhy.exam.system.domain.ExQuestion">
    <select id="getQuestionByPaperId" resultMap="ExQuestionResult">
        select a.id,a.question_type,a.bank_id,a.company_id,a.status,
               <if test="completed!=null and completed=1">
               a.answer,
               <if test="state!=null and state!=0">
               a.answer,c.passed as answer_passed,c.score as answer_score,
               </if>
               a.title,a.privatize,a.content,
               c.id as answer_id,c.paper_id as answer_paper_id,c.student_id as answer_student_id,c.question_id as answer_question_id,c.answer as answer_answer,
                <if test="completed!=null and completed=1">
               c.passed as answer_passed
                </if>
               c.id as answer_id,c.paper_id as answer_paper_id,c.student_id as answer_student_id,c.question_id as answer_question_id,c.answer as answer_answer
        from ex_question a
        inner join ex_paper_question b on a.id=b.question_id
        left join ex_student_answer c on c.question_id=a.id and c.student_id=#{studentId} and c.paper_id=#{paperId}
        where b.paper_id=#{paperId} and a.question_type=#{questionType}
        where b.paper_id=#{paperId}
        order by a.question_type asc,a.id desc
    </select>
    <select id="selectByStudentId" resultMap="SimplePaperStudentResult">
        select a.*,b.name as paper_name,c.id as company_id,c.name as company_name from ex_paper_student a
        left join ex_exam_paper b on b.id=a.paper_id
        left join sys_company c on c.id=b.company_id
    </select>
    <select id="selectNoCompleteStudent" resultMap="SimplePaperStudentResult">
        select a.*, b.name as paper_name,b.limited,b.limit_time,b.deadline,b.single_num,b.multi_num,b.judge_num,b.easy_num
        from ex_paper_student a
        inner join ex_exam_paper b on b.id=a.paper_id
        where a.state=0 limit #{startIndex},#{pageSize}
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExPhaseStudentMapper.xml
@@ -11,7 +11,6 @@
        <result property="studentPhone"     column="student_phone"     />
        <result property="phaseName"     column="phase_name"     />
        <result property="createName"     column="create_name"     />
        <result property="period"     column="period"     />
        <association property="course" javaType="com.gkhy.exam.system.domain.ExCourse" resultMap="courseResult" />
        <collection property="totalProgress" ofType="java.math.BigDecimal" select="getTotalProgress" column="{phaseId=phase_id,studentId=student_id}"/>
        <collection property="startTime" ofType="java.time.LocalDateTime" select="getStartTime" column="{phaseId=phase_id,studentId=student_id}"/>
@@ -21,20 +20,21 @@
        <id     property="id"       column="course_id"        />
        <result property="name"     column="course_name"      />
        <result property="logo"     column="course_logo"      />
        <association property="period" javaType="java.lang.Long"  select="getCoursePeriod" column="{courseId=course_id}"/>
    </resultMap>
    <insert id="batchInsert" parameterType="java.util.List">
        insert into ex_phase_student(phase_id,student_id) values
        insert into ex_phase_student(phase_id,student_id,create_id) values
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.phaseId},#{item.studentId})
            (#{item.phaseId},#{item.studentId},#{item.createId})
        </foreach>
    </insert>
    <select id="countByPhaseId" resultType="java.lang.Integer">
        select count(1) from ex_phase_student where phase_id#{phaseId}
        select count(1) from ex_phase_student where phase_id=#{phaseId}
    </select>
    <select id="selectPhaseStudentById" resultMap="ExPhaseStudentResult">
    <select id="selectPhaseStudentById" resultType="com.gkhy.exam.system.domain.ExPhaseStudent">
        select a.*,b.phone as student_phone,b.name as student_name from ex_phase_student a
        left join ex_student b on a.student_id=b.id
        where a.id=#{phaseStudentId}
@@ -49,7 +49,7 @@
    </select>
    <select id="selectPhaseStudentList" resultMap="ExPhaseStudentResult">
        select a.*,b.phone as student_phone,b.name as student_name,c.name as phase_name,d.period,e.name as create_name,d.id as course_id,d.logo as course_logo,d.name as course_name from ex_phase_student a
        select a.*,b.phone as student_phone,b.name as student_name,c.name as phase_name,e.name as create_name,d.id as course_id,d.logo as course_logo,d.name as course_name from ex_phase_student a
        left join ex_student b on a.student_id=b.id
        left join ex_course_phase c on c.id=a.phase_id
        left join ex_course d on d.id=c.course_id
@@ -77,4 +77,19 @@
    </select>
    <select id="getCoursePeriod" resultType="java.lang.Long">
        select sum(b.resource_length) from ex_course_chapter_period a
        inner join ex_resource b on a.resource_id=b.id
        where a.course_id=#{courseId}
    </select>
    <select id="selectPhaseStudentByStudentId" resultType="com.gkhy.exam.system.domain.ExPhaseStudent"
            parameterType="java.lang.Long">
        select a.*,b.name as phase_name,c.id as company_id,c.name as company_name from ex_phase_student a
        left join ex_course_phase b on b.id=a.phase_id
        left join sys_company c on c.id=b.company_id
        where a.student_id=#{studentId}
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExQuestionBankMapper.xml
@@ -15,14 +15,23 @@
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
        <result property="singleCount"         column="single_count"          />
        <result property="multiCount"         column="multi_count"          />
        <result property="judgeCount"         column="judge_count"          />
        <result property="totalCount"         column="total_count"          />
        <result property="exerciseCount"         column="exercise_count"          />
        <result property="categoryName"         column="category_name"          />
        <result property="questionId"         column="question_id"          />
    </resultMap>
    <sql id="selectQuestionBankVo">
        select id, name, category_id, status, del_flag,company_id,privatize,version, create_by, create_time, update_by, update_time, remark,
               (select count(1) from ex_question where bank_id=a.id) as total_count
        from ex_question_bank
        select a.id, a.name, a.category_id, a.status, a.del_flag,a.company_id,a.privatize,a.version,
               a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as category_name,
               (select count(1) from ex_question where bank_id=a.id and question_type=1) as single_count,
               (select count(1) from ex_question where bank_id=a.id and question_type=2) as multi_count,
               (select count(1) from ex_question where bank_id=a.id and question_type=3) as judge_count
        from ex_question_bank a
        left join sys_category b on b.id=a.category_id
    </sql>
    <update id="deleteByBankId">
@@ -40,43 +49,55 @@
    <select id="selectQuestionBankList" resultMap="ExQuestionBankResult">
        <include refid="selectQuestionBankVo"/>
        <where>
            and del_flag=0
            and a.del_flag=0
            <if test="name != null and name != ''">
                AND name like concat('%', #{name}, '%')
                AND a.name like concat('%', #{name}, '%')
            </if>
            <if test="categoryId != null ">
                AND categoryId =#{categoryId}
                AND a.category_id =#{categoryId}
            </if>
            <if test="status != null ">
                AND status =#{status}
                AND a.status =#{status}
            </if>
            <if test="companyId != null ">
                AND (company_id =#{companyId} or privatize=1)
            </if>
        </where>
        order by id desc
    </select>
    <select id="selectQuestionBankListForStudent" resultType="com.gkhy.exam.system.domain.ExQuestionBank">
        select a.*,
        (select count(1) from ex_question where bank_id=a.id) as total_count,
        b.exe_count as exercise_count
        from ex_question_bank a
        left join (select bank_id,count(*) as exe_count from ex_exercise_answer where student_id=#{studentId} group by bank_id) b on b.bank_id=a.id
        <where>
            and (a.company_id=#{companyId} or a.privatize=1) and a.del_flag=0
            <if test="name!=null and name!=''">
                a.name like concat('%',#{name},'%')
                AND (a.company_id =#{companyId} or a.privatize=1)
            </if>
        </where>
        order by a.id desc
    </select>
    <select id="selectQuestionBankListForStudent" resultType="com.gkhy.exam.system.domain.ExQuestionBank">
        select a.*,
        (select count(1) from ex_question where bank_id=a.id ) as total_count,
        (select count(1)  from ex_exercise_answer where bank_id=a.id and student_id=#{studentId}) as exercise_count,
        (select question_id from ex_exercise_answer where bank_id=a.id and student_id=#{studentId} order by id desc limit 1) as question_id
        from ex_question_bank a
        where a.del_flag=0 and (a.company_id=#{companyId} or a.privatize=1)
        order by a.id desc
    </select>
    <select id="selectQuestionBankByIdForStudent" resultType="com.gkhy.exam.system.domain.ExQuestionBank">
        select a.*,
               (select count(1) from ex_question where bank_id=a.id) as total_count,
               (select count(1) from ex_question where bank_id=a.id and question_type=1) as single_count,
               (select count(1) from ex_question where bank_id=a.id and question_type=2) as multi_count,
               (select count(1) from ex_question where bank_id=a.id and question_type=3) as judge_count,
               (select count(1)  from ex_exercise_answer where bank_id=#{bankId} and student_id=#{studentId}) as exercise_count
        from ex_question_bank a
        where a.bank_id=#{bankId}
    </select>
    <select id="selectCountByBankId" resultType="java.lang.Integer" parameterType="java.lang.Long">
        select count(1) from ex_question_bank where del_flag=0 and id=#{bankId}
    </select>
    <select id="selectCountByCategoryId" resultType="java.lang.Integer">
        select count(1) from ex_question_bank where category_id=#{categoryId} and del_flag=0
    </select>
    <select id="selectQuestionBankByIds" resultType="com.gkhy.exam.system.domain.ExQuestionBank">
        select * from ex_question_bank where del_flag=0 and id in
        <foreach collection="bankIds" item="bankId" index="index" separator="," open="(" close=")">
            #{bankId}
        </foreach>
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExQuestionMapper.xml
@@ -18,6 +18,7 @@
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
        <result property="bankName"         column="bank_name"          />
        <association property="exExerciseAnswer" javaType="com.gkhy.exam.system.domain.ExExerciseAnswer" resultMap="ExerciseAnswerResult" />
        <association property="studentAnswer" javaType="com.gkhy.exam.system.domain.ExStudentAnswer" resultMap="StudentAnswerResult" />
    </resultMap>
@@ -51,32 +52,33 @@
    </select>
    <select id="selectQuestionWithLimit" resultType="com.gkhy.exam.system.domain.ExQuestion">
        select id,title from ex_question where bank_id=#{bankId} and question_type=#{questionType} and (company_id=#{companyId} or privatize=1) limit #{startIndex},{questionCount}
        select id,title from ex_question where bank_id=#{bankId} and question_type=#{questionType} and (company_id=#{companyId} or privatize=1) limit #{startIndex},#{questionCount}
    </select>
    <select id="selectRandomQuestion" resultType="com.gkhy.exam.system.domain.ExQuestion">
        select id,title from ex_question where bank_id=#{bankId} and question_type=#{questionType} and (company_id=#{companyId} or privatize=1) order by RAND()
        limit {questionCount}
        limit #{questionCount}
    </select>
    <select id="selectQuestionList" resultType="com.gkhy.exam.system.domain.ExQuestion">
        select id,question_type, bank_id, status, company_id,answer,title,privatize from ex_question
        select a.id,a.question_type, a.bank_id, a.status, a.company_id,a.title,a.privatize,b.name as bank_name from ex_question a
        left join ex_question_bank b on b.id=a.bank_id
        <where>
            <if test="title!=null and title!=''">
            title like concat(#{title},"%")
                and a.title like concat(#{title},"%")
            </if>
            <if test="bankId!=null">
                bank_id=#{bankId}
                and a.bank_id=#{bankId}
            </if>
            <if test="companyId!=null">
                company_id=#{companyId}
                and (a.company_id=#{companyId} or a.privatize=1)
            </if>
            <if test="questionType!=null">
                question_type=#{questionType}
                and a.question_type=#{questionType}
            </if>
            <if test="privatize!=null">
                privatize=#{privatize}
            </if>
<!--            <if test="privatize!=null">-->
<!--                and privatize=#{privatize}-->
<!--            </if>-->
        </where>
        order by id desc
    </select>
@@ -85,12 +87,7 @@
        select a.id,b.passed from ex_question a
        left join ex_exercise_answer b on b.question_id=a.id and b.student_id=#{studentId}
        where a.bank_id=#{bankId}
        <if test="exerciseType=2">
            order by a.question_type asc,a.id asc
        </if>
        <if test="exerciseType=1">
            order by a.id asc
        </if>
        order by a.question_type asc,a.id asc
    </select>
    <select id="getExeriseQuestionById" resultMap="ExQuestionResult">
@@ -106,18 +103,25 @@
        <foreach collection="questionIds" item="questionId" open="(" separator="," close=")">
            #{questionId}
        </foreach>
        ORDER BY FIELD(a.id, <foreach collection="questionIds" item="questionId" separator=",">#{questionId}</foreach>)
    </select>
    <select id="getPaperQuestionList" resultType="java.util.Map">
        select a.id
             <if test="completed=1">
             ,c.passed
             <if test="viewType==1">
                ,
                 case when c.answer is null then 0
                 else 1
                 end as state
             </if>
            <if test="viewType==2">
                <if test="state!=0">
                    ,c.passed
                </if>
            </if>
        from ex_question a
        inner join ex_paper_question b on b.question_id=a.id
        <if test="completed=1">
        left join ex_student_answer c on c.question_id=a.id and c.student_id=#{studentId} and c.paper_id=#{paperId}
        </if>
        where b.paper_id=#{paperId}
        order by a.question_type asc,a.id asc
    </select>
@@ -125,7 +129,7 @@
    <select id="getPaperQuestionById" resultMap="ExQuestionResult">
        select a.id,a.question_type,a.bank_id,a.company_id,a.title,a.content,a.privatize,
               b.id as student_answer_id, b.answer as student_answer,b.question_id,b.student_id
               <if test="completed=1">
               <if test="state!=0">
                   ,a.answer,b.passed as student_passed
               </if>
        from ex_question a
@@ -133,10 +137,10 @@
        where a.id=#{questionId}
    </select>
    <select id="getPaperQuestionByIds" resultType="com.gkhy.exam.system.domain.ExQuestion">
    <select id="getPaperQuestionByIds" resultMap="ExQuestionResult">
        select a.id,a.question_type,a.bank_id,a.company_id,a.title,a.content,a.privatize,
        b.id as student_answer_id, b.answer as student_answer,b.question_id,b.student_id
        <if test="completed=1">
        <if test="state!=0">
            ,a.answer,b.passed as student_passed
        </if>
        from ex_question a
@@ -145,14 +149,28 @@
        <foreach collection="questionIds" item="questionId" open="(" separator="," close=")">
            #{questionId}
        </foreach>
        ORDER BY FIELD(a.id, <foreach collection="questionIds" item="questionId" separator=",">#{questionId}</foreach>)
    </select>
    <select id="getExerciseErrorQuestionList" resultType="java.lang.Long">
        select a.id from ex_question a
        inner join ex_exercise_answer b on b.question_id=a.id
        where a.bank_id=#{bankId} and b.student_id=#{studentId}
        where a.bank_id=#{bankId} and b.student_id=#{studentId} and b.passed=0
        order by a.question_type asc,a.id asc
    </select>
    <select id="selectByQuestionId" resultType="com.gkhy.exam.system.domain.ExQuestion">
        select a.*,b.name as bank_name from ex_question a
        left join ex_question_bank b on b.id=a.bank_id
        where a.id=#{questionId}
    </select>
    <select id="selectQuestionByPaperId" resultType="com.gkhy.exam.system.domain.ExQuestion"
            parameterType="java.lang.Long">
        select a.* from ex_question a
        inner join ex_paper_question b on b.question_id=a.id
        where b.paper_id=#{paperId}
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExResourceMapper.xml
@@ -65,5 +65,10 @@
        where b.id=#{periodId}
    </select>
    <select id="checkResourceAssign" resultType="java.lang.Integer" parameterType="java.lang.Long">
        select count(1) from ex_course_chapter_period a
        where a.resource_id=#{resourceId}
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExStudentAnswerMapper.xml
@@ -9,7 +9,7 @@
    <select id="selectPassCount" resultType="java.lang.Integer">
        select count(1) from ex_student_answer a
        inner join ex_question b on b.id=a.question_id
        where a.paper_id=#{paperId} and a.student_id=#{studentId} and b.questionType=#{questionType} and a.passed=1
        where a.paper_id=#{paperId} and a.student_id=#{studentId} and b.question_type=#{questionType} and a.passed=1
    </select>
    <select id="getStudentAnswer" resultType="com.gkhy.exam.system.domain.ExStudentAnswer">
exam-system/src/main/resources/mapper/system/ExStudentMapper.xml
@@ -27,18 +27,25 @@
        <result property="remark"         column="remark"          />
        <association property="company" javaType="com.gkhy.exam.system.domain.SysCompany" resultMap="companyResult" />
        <association property="createUser" javaType="com.gkhy.exam.common.domain.entity.SysUser" resultMap="userResult" />
    </resultMap>
    <resultMap id="companyResult" type="com.gkhy.exam.system.domain.SysCompany">
        <id     property="id"       column="company_id"        />
        <result property="name"     column="company_name"      />
    </resultMap>
    <resultMap id="userResult" type="com.gkhy.exam.common.domain.entity.SysUser">
        <id     property="id"       column="create_id"        />
        <result property="name"     column="create_name"      />
    </resultMap>
    <sql id="selectStudentVo">
        select s.id, s.name, s.company_id, s.empno, s.phone,s.password,s.status,s.sex,s.id_no,s.post,s.duty,
        select s.id, s.name, s.company_id, s.empno, s.phone,s.status,s.sex,s.id_no,s.post,s.duty,
               s.create_id,s.del_flag,s.version, s.create_by, s.create_time, s.update_by, s.update_time, s.remark,
                c.id as company_id,c.name as company_name
                c.id as company_id,c.name as company_name,d.name as create_name
        from ex_student s
        left join sys_company c on c.id=s.company_id
        left join sys_user d on d.id=s.create_id
    </sql>
    <update id="deleteByStudentId">
@@ -62,6 +69,15 @@
            <if test="idNo != null and idNo != ''">
                AND s.id_no like concat('%', #{idNo}, '%')
            </if>
            <if test="createId != null">
                AND s.create_id =#{createId}
            </if>
            <if test="params.createIds != null and params.createIds != ''">
                AND s.create_id in
                <foreach collection="params.createIds" item="createId" open="(" separator="," close=")">
                    #{createId}
                </foreach>
            </if>
        </where>
        order by s.id desc
    </select>
@@ -79,7 +95,7 @@
    </select>
    <select id="selectStudentByPhone" resultType="com.gkhy.exam.system.domain.ExStudent">
        select * from ex_student where phone=#{phone}  limit 1
        select * from ex_student where phone=#{phone} and del_flag=0  limit 1
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/ExStudentStudyMapper.xml
@@ -11,7 +11,6 @@
        <result property="currentDuration"         column="current_duration"          />
        <result property="currentPage"         column="current_page"          />
        <result property="progress"         column="progress"          />
        <result property="resourceType"         column="resource_type"          />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
        <result property="updateBy"       column="update_by"       />
@@ -24,7 +23,7 @@
    </resultMap>
    <sql id="selectStudentStudyVo">
        select a.id, a.phase_id, a.course_id, a.chapter_id, a.period_id,a.student_id,a.current_duration,a.current_page,a.progress,a.resource_type,
        select a.id, a.phase_id, a.course_id, a.chapter_id, a.period_id,a.student_id,a.current_duration,a.current_page,a.progress,
               a.version, a.create_by, a.create_time, a.update_by, a.update_time, a.remark,b.name as course_name,d.name as chapter_mame,c.name as period_name
        from ex_student_study a
        left join ex_course b on b.id=a.course_id
@@ -45,6 +44,6 @@
    </select>
    <select id="selectStudyByObject" resultType="com.gkhy.exam.system.domain.ExStudentStudy">
        select * from ex_student_study where paper_id=#{paperId} and  period_id=#{periodId} and student_id=#{studentId}
        select * from ex_student_study where phase_id=#{phaseId} and  period_id=#{periodId} and student_id=#{studentId} limit 1
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/SysCarouselMapper.xml
@@ -32,7 +32,7 @@
                AND status =#{status}
            </if>
        </where>
        order by create_time desc
        order by id desc
    </select>
    <select id="selectCarouselById" resultMap="SysCarouselResult">
exam-system/src/main/resources/mapper/system/SysCategoryMapper.xml
@@ -7,6 +7,7 @@
        <result property="parentId"     column="parent_id"     />
        <result property="status"         column="status"          />
        <result property="sort"         column="sort"          />
        <result property="delFlag"         column="del_flag"          />
        <result property="version"         column="version"          />
        <result property="createBy"       column="create_by"       />
        <result property="createTime"     column="create_time"     />
@@ -16,13 +17,18 @@
    </resultMap>
    <sql id="selectCategoryVo">
        select id, name, parent_id, category_type, status,sort,version, create_by, create_time, update_by, update_time, remark
        select id, name, parent_id, category_type, status,sort,del_flag,version, create_by, create_time, update_by, update_time, remark
        from sys_category
    </sql>
    <update id="deleteByCategoryId" parameterType="java.lang.Long">
        update sys_category set del_flag=1 where id=#{categoryId}
    </update>
    <select id="selectCategoryList" resultMap="SysCategoryResult">
        <include refid="selectCategoryVo"/>
        <where>
            and del_flag=0
            <if test="name != null and name != ''">
                AND name like concat('%', #{name}, '%')
            </if>
@@ -34,15 +40,7 @@
    </select>
    <select id="checkNameUnique" resultType="com.gkhy.exam.system.domain.SysCategory">
        select id,name,parent_id from sys_category where name=#{name} and parent_id=#{parentId} limit 1
    </select>
    <select id="selectCountOfCoure" resultType="java.lang.Integer">
        select count(1) from ex_course where category_id=#{categoryId} and del_flag=0
    </select>
    <select id="selectCountOfBank" resultType="java.lang.Integer">
        select count(1) from ex_question_bank where category_id=#{categoryId} and del_flag=0
        select id,name,parent_id from sys_category where name=#{name} and parent_id=#{parentId} and del_flag=0 limit 1
    </select>
</mapper>
exam-system/src/main/resources/mapper/system/SysCompanyMapper.xml
@@ -9,7 +9,6 @@
        <result property="phone"         column="phone"          />
        <result property="delFlag"         column="del_flag"          />
        <result property="remainPeriod"         column="remain_period"          />
        <result property="spendPeriod"         column="spend_period"          />
        <result property="totalPeriod"         column="total_period"          />
        <result property="version"         column="version"          />
        <result property="createBy"       column="create_by"       />
@@ -20,7 +19,7 @@
    </resultMap>
    <sql id="selectCompanyVo">
        select id, name, credit_code, major, phone,remain_period,spend_period,total_period,version, create_by, create_time, update_by, update_time, remark
        select id, name, credit_code, major, phone,remain_period,total_period,version, create_by, create_time, update_by, update_time, remark
        from sys_company
    </sql>
exam-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -24,6 +24,7 @@
        <result property="remark"        column="remark"          />
        <result property="version"        column="version"          />
        <result property="companyName"        column="company_name"          />
        <result property="remainPeriod"        column="remain_period"          />
        <result property="parentName"        column="parent_name"          />
    </resultMap>
@@ -35,12 +36,12 @@
    <select id="getUserByUsername" resultMap="SysUserResult">
        select id,username,name,password,user_type,company_id,status,del_flag from sys_user
        where username=#{username} limit 1
        select id,username,name,password,user_type,company_id,status,del_flag,parent_id from sys_user
        where username=#{username} and del_flag=0 limit 1
    </select>
    <select id="userList"  resultMap="SysUserResult">
        select u.id,u.username,u.name,u.user_type,u.phone,u.parent_id,u.company_id,u.sex,u.status,u.del_flag,
        select u.id,u.username,u.name,u.user_type,u.phone,u.parent_id,u.company_id,u.sex,u.status,u.del_flag,u.version,
               u.login_ip,u.login_date,u.create_by,u.create_time,u.remark,c.name as company_name,su.name as parent_name
        from sys_user u
        left join sys_company c on c.id=u.company_id
@@ -49,6 +50,9 @@
            and u.del_flag = 0
            <if test="username != null and username != ''">
                AND u.username like concat('%', #{username}, '%')
            </if>
            <if test="name != null and name != ''">
                AND u.name like concat('%', #{name}, '%')
            </if>
            <if test="status != null and status != ''">
                AND u.status = #{status}
@@ -68,10 +72,17 @@
            <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
                AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
            </if>
            <if test="params.userType != null">
                AND u.user_type >#{params.userType}
            <if test="params.userType != null and (params.userType==4 or params.userType==1)">
                AND u.user_type in (2,3)
            </if>
            <if test="params.userType != null and params.userType==2">
                AND u.user_type in (3)
            </if>
            <if test="params.userType != null and params.userType==0">
                AND u.user_type in (1,2,3,4)
            </if>
        </where>
        order by u.id desc
    </select>
@@ -82,7 +93,7 @@
    </select>
    <select id="getUserById" resultMap="SysUserResult">
        select u.id,u.username,u.user_type,u.name,u.phone,u.parent_id,u.company_id,u.status,u.sex,u.del_flag,c.name as company_name,su.name as parent_name
        select u.id,u.username,u.user_type,u.name,u.phone,u.parent_id,u.company_id,u.status,u.sex,u.del_flag,u.version,c.name as company_name,c.remain_period,su.name as parent_name
        from sys_user u
       left join sys_company c on c.id=u.company_id
       left join sys_user su on su.id=u.parent_id and u.parent_id!=0
@@ -97,4 +108,8 @@
        select id,phone from sys_user where phone=#{phone} and del_flag=0 limit 1
    </select>
    <select id="selectWorkshopUserIds" resultType="java.lang.Long" parameterType="java.lang.Long">
        select id from sys_user where parent_id=#{departUserId} and del_flag=0 and user_type=3
    </select>
</mapper>
pom.xml
@@ -35,7 +35,7 @@
        <mybatis-plus.version>3.5.1</mybatis-plus.version>
        <mysql-connector.version>8.0.29</mysql-connector.version>
        <jwt.version>0.9.1</jwt.version>
        <fastjson.version>2.0.48</fastjson.version>
        <fastjson.version>2.0.52</fastjson.version>
        <caffeine.version>2.9.3</caffeine.version>
        <minio.version>8.4.5</minio.version>
        <kaptcha.version>2.3.3</kaptcha.version>
@@ -44,6 +44,8 @@
        <poi.version>5.2.3</poi.version>
        <pdfbox.version>2.0.27</pdfbox.version>
        <jaudiotagger.version>2.0.1</jaudiotagger.version>
        <easyexcel.version>4.0.2</easyexcel.version>
    </properties>
    <dependencyManagement>
        <dependencies>
@@ -130,6 +132,16 @@
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2-extension</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2-extension-spring5</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.ben-manes.caffeine</groupId>
                <artifactId>caffeine</artifactId>
                <version>${caffeine.version}</version>
@@ -187,6 +199,11 @@
                <version>${jaudiotagger.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>${easyexcel.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>