From e8fd79f539301c5cd790366166a67e39ae8207b9 Mon Sep 17 00:00:00 2001 From: Mlxa0324 Date: Sun, 27 Nov 2022 14:21:30 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=98=E7=9B=AE=E7=9A=84=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=88=A4=E6=96=AD=EF=BC=8C=E9=98=B2=E6=AD=A2=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E5=8E=9F=E5=9B=A0=E5=AF=BC=E8=87=B4=E6=8F=90=E4=BA=A4=E7=9A=84?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=E6=9C=80=E6=96=B0=E7=AD=94=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/sql/mysql/tianze-pro-update.sql | 2 + .../java/cn/jlw/util/excel/ExcelUtil.java | 15 +++ .../ibeetl/jlw/dao/GeneralQuestionLogDao.java | 11 ++ .../dao/TeacherOpenCourseQuestionLogDao.java | 11 ++ .../ibeetl/jlw/entity/GeneralQuestionLog.java | 5 + ...acherOpenCourseMergeResourcesQuestion.java | 8 +- ...penCourseMergeResourcesQuestionImport.java | 106 ++++++++++++++++++ .../entity/TeacherOpenCourseQuestionLog.java | 5 + .../jlw/entity/dto/QuestionLogAddDTO.java | 57 ++++++++++ .../jlw/enums/QuestionLogAddTypeEnum.java | 22 ++++ .../service/GeneralQuestionLogService.java | 70 ++++++++---- .../TeacherOpenCourseQuestionLogService.java | 75 +++++++++---- .../jlw/web/GeneralQuestionLogController.java | 15 +-- ...ourseMergeResourcesQuestionController.java | 12 +- ...eacherOpenCourseQuestionLogController.java | 15 +-- .../web/query/GeneralQuestionLogQuery.java | 4 + .../TeacherOpenCourseQuestionLogQuery.java | 4 + .../resources/sql/jlw/generalQuestionLog.md | 17 ++- .../sql/jlw/teacherOpenCourseQuestionLog.md | 16 +++ 19 files changed, 396 insertions(+), 74 deletions(-) create mode 100644 web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestionImport.java create mode 100644 web/src/main/java/com/ibeetl/jlw/entity/dto/QuestionLogAddDTO.java create mode 100644 web/src/main/java/com/ibeetl/jlw/enums/QuestionLogAddTypeEnum.java diff --git a/doc/sql/mysql/tianze-pro-update.sql b/doc/sql/mysql/tianze-pro-update.sql index b55362e5..15ff0a5c 100644 --- a/doc/sql/mysql/tianze-pro-update.sql +++ b/doc/sql/mysql/tianze-pro-update.sql @@ -369,6 +369,7 @@ ALTER TABLE teacher_open_course_question_log ADD COLUMN teacher_open_course_ques ALTER TABLE teacher_open_course_question_log ADD COLUMN teacher_open_course_question_log_update_time datetime COMMENT '修改时间'; ALTER TABLE teacher_open_course_question_log ADD COLUMN teacher_open_course_question_log_finish_time int(10) default 1 COMMENT '总用时(分钟)'; ALTER TABLE teacher_open_course_question_log ADD COLUMN teacher_open_course_question_log_reply varchar(300) COMMENT '评语'; +ALTER TABLE teacher_open_course_question_log ADD COLUMN question_log_add_type varchar(50) COMMENT '题目添加类型(枚举 QuestionLogAddTypeEnum)'; -- 通用的题目配置 drop table if exists general_question_setting; @@ -445,6 +446,7 @@ CREATE TABLE `general_question_log` ( `general_question_log_update_time` datetime COMMENT '修改时间', `general_question_log_finish_time` int(10) default 1 COMMENT '总用时(分钟)', `general_question_log_reply` varchar(300) default 1 COMMENT '评语', + `question_log_add_type` varchar(50) COMMENT '题目添加类型(枚举 QuestionLogAddTypeEnum)', `org_id` bigint(20) DEFAULT NULL COMMENT '组织ID', `user_id` bigint(20) DEFAULT NULL COMMENT '用户ID', PRIMARY KEY (`general_question_log_id`) USING BTREE diff --git a/web/src/main/java/cn/jlw/util/excel/ExcelUtil.java b/web/src/main/java/cn/jlw/util/excel/ExcelUtil.java index ecc7ded1..3a969f0b 100644 --- a/web/src/main/java/cn/jlw/util/excel/ExcelUtil.java +++ b/web/src/main/java/cn/jlw/util/excel/ExcelUtil.java @@ -26,6 +26,9 @@ import java.lang.reflect.Field; import java.net.URLEncoder; import java.util.List; import java.util.Map; +import java.util.Objects; + +import static cn.jlw.util.excel.ExcelSelector.ID_SEPARATOR; @Slf4j public class ExcelUtil { @@ -311,4 +314,16 @@ public class ExcelUtil { } return selectedMap; } + + public static T longValuesOf(String text) { + return (T) Long.valueOf(Objects.requireNonNull(text.split(ID_SEPARATOR)[1], text + "字段,必须要有___ID:的分隔符")); + } + + public static T bigDecimalValuesOf(String text) { + return (T) Long.valueOf(Objects.requireNonNull(text.split(ID_SEPARATOR)[1], text + "字段,必须要有___ID:的分隔符")); + } + + public static T integerValuesOf(String text) { + return (T) Integer.valueOf(Objects.requireNonNull(text.split(ID_SEPARATOR)[1], text + "字段,必须要有___ID:的分隔符")); + } } diff --git a/web/src/main/java/com/ibeetl/jlw/dao/GeneralQuestionLogDao.java b/web/src/main/java/com/ibeetl/jlw/dao/GeneralQuestionLogDao.java index 6b6cb7ab..46234daf 100644 --- a/web/src/main/java/com/ibeetl/jlw/dao/GeneralQuestionLogDao.java +++ b/web/src/main/java/com/ibeetl/jlw/dao/GeneralQuestionLogDao.java @@ -10,6 +10,7 @@ import org.beetl.sql.mapper.annotation.SqlResource; import org.beetl.sql.mapper.annotation.Update; import org.springframework.stereotype.Repository; +import java.util.Date; import java.util.List; import java.util.Map; @@ -55,5 +56,15 @@ public interface GeneralQuestionLogDao extends BaseMapper { * @return */ PageQuery getQuestionLogScoreDetailsInfo(PageQuery query); + + /** + * 验证前端传递过来的添加时间是否是最新的 + * @param questionSettingId 题目配置ID + * @param questionSnapshotIds 题目快照ID + * @param studentId 学生ID + * @param addTime 前端传递的添加时间 + * @return + */ + boolean validateQuestionLogAddTimeLatest(Long questionSettingId, String questionSnapshotIds, Long studentId, Date addTime); } diff --git a/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseQuestionLogDao.java b/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseQuestionLogDao.java index dcc39f88..690a273a 100644 --- a/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseQuestionLogDao.java +++ b/web/src/main/java/com/ibeetl/jlw/dao/TeacherOpenCourseQuestionLogDao.java @@ -9,6 +9,7 @@ import org.beetl.sql.mapper.annotation.SqlResource; import org.beetl.sql.mapper.annotation.Update; import org.springframework.stereotype.Repository; +import java.util.Date; import java.util.List; /** @@ -60,4 +61,14 @@ public interface TeacherOpenCourseQuestionLogDao extends BaseMapper getQuestionLogScoreDetailsInfo(PageQuery query); + + /** + * 验证前端传递过来的添加时间是否是最新的 + * @param questionSettingId 题目配置ID + * @param questionSnapshotIds 题目快照ID + * @param studentId 学生ID + * @param addTime 前端传递的添加时间 + * @return + */ + boolean validateQuestionLogAddTimeLatest(Long questionSettingId, String questionSnapshotIds, Long studentId, Date addTime); } diff --git a/web/src/main/java/com/ibeetl/jlw/entity/GeneralQuestionLog.java b/web/src/main/java/com/ibeetl/jlw/entity/GeneralQuestionLog.java index 0465420d..5bdf354f 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/GeneralQuestionLog.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/GeneralQuestionLog.java @@ -4,6 +4,7 @@ import com.ibeetl.admin.core.annotation.Dict; import com.ibeetl.admin.core.entity.BaseEntity; import com.ibeetl.admin.core.util.ValidateConfig; import com.ibeetl.jlw.enums.GeneralResourcesQuestionLogTypeEnum; +import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum; import lombok.*; import lombok.experimental.Accessors; import org.beetl.sql.annotation.entity.AssignID; @@ -59,6 +60,10 @@ public class GeneralQuestionLog extends BaseEntity{ @Dict(type="global_status") private Integer generalQuestionLogStatus ; + + // 题目提交类型 + + private QuestionLogAddTypeEnum questionLogAddType; //学生ID diff --git a/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestion.java b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestion.java index fd83e355..db108cda 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestion.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestion.java @@ -7,8 +7,9 @@ import com.alibaba.excel.annotation.write.style.HeadFontStyle; import com.ibeetl.admin.core.annotation.Dict; import com.ibeetl.admin.core.entity.BaseEntity; import com.ibeetl.admin.core.util.ValidateConfig; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.EqualsAndHashCode; -import lombok.ToString; import org.beetl.sql.annotation.entity.AssignID; import javax.validation.constraints.NotNull; @@ -20,11 +21,12 @@ import static cn.jlw.util.excel.ExcelSelector.ID_SEPARATOR; * 开课-题库管理-离线 * gen by Spring Boot2 Admin 2022-10-26 */ -@ToString +@Builder +@AllArgsConstructor @ExcelIgnoreUnannotated @EqualsAndHashCode(callSuper=false) @HeadFontStyle(fontName = "仿宋", fontHeightInPoints = 12) -public class TeacherOpenCourseMergeResourcesQuestion extends BaseEntity{ +public class TeacherOpenCourseMergeResourcesQuestion extends BaseEntity { //ID @NotNull(message = "ID不能为空", groups =ValidateConfig.UPDATE.class) diff --git a/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestionImport.java b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestionImport.java new file mode 100644 index 00000000..3f028241 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseMergeResourcesQuestionImport.java @@ -0,0 +1,106 @@ +package com.ibeetl.jlw.entity; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.ibeetl.admin.core.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import static cn.jlw.util.excel.ExcelUtil.*; + +/** + * 开课-题库管理-离线 导入实体 + * @author lx + */ +@Data +@ExcelIgnoreUnannotated +@EqualsAndHashCode(callSuper=false) +public class TeacherOpenCourseMergeResourcesQuestionImport extends BaseEntity { + + //ID + + private String teacherOpenCourseMergeResourcesQuestionId ; + + //开课ID + + private String teacherOpenCourseId ; + + //课程章节ID + + private String teacherOpenCourseMergeCourseInfoId ; + + //题型(1单选 2多选 3判断 4填空 5分析) + + private String questionType ; + + //分值 + + private String questionScore ; + + //题干 + + private String questionStem ; + + //选项A + + private String questionOptionA ; + + //选项B + + private String questionOptionB ; + + //选项C + + private String questionOptionC ; + + //选项D + + private String questionOptionD ; + + //选项E + + private String questionOptionE ; + + //答案(单选是一个 多选是多个 判断是对错) + + private String questionAnswer ; + + //解析 + + private String questionAnalysis ; + + //题目状态 (1上架, 2下架) + + private String questionStatus ; + + //后台用户ID + + private String userId ; + + //组织机构ID + + private String orgId ; + + /** + * 类型转换 + * @param importPojo + * @return + */ + public static TeacherOpenCourseMergeResourcesQuestion pojo(TeacherOpenCourseMergeResourcesQuestionImport importPojo) { + return TeacherOpenCourseMergeResourcesQuestion.builder() + .teacherOpenCourseMergeCourseInfoId(longValuesOf(importPojo.getTeacherOpenCourseMergeCourseInfoId())) + .teacherOpenCourseId(longValuesOf(importPojo.getTeacherOpenCourseId())) + .questionType(integerValuesOf(importPojo.getTeacherOpenCourseId())) + .questionScore(bigDecimalValuesOf(importPojo.getTeacherOpenCourseId())) + .questionStem(importPojo.getQuestionStem()) + .questionOptionA(importPojo.getQuestionOptionA()) + .questionOptionB(importPojo.getQuestionOptionB()) + .questionOptionC(importPojo.getQuestionOptionC()) + .questionOptionD(importPojo.getQuestionOptionD()) + .questionOptionE(importPojo.getQuestionOptionE()) + .questionAnswer(importPojo.getQuestionAnswer()) + .questionAnalysis(importPojo.getQuestionAnalysis()) + .questionStatus(1) + .build(); + } + +} diff --git a/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseQuestionLog.java b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseQuestionLog.java index 8d8e85fe..abf7d021 100644 --- a/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseQuestionLog.java +++ b/web/src/main/java/com/ibeetl/jlw/entity/TeacherOpenCourseQuestionLog.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil; import com.ibeetl.admin.core.annotation.Dict; import com.ibeetl.admin.core.entity.BaseEntity; import com.ibeetl.admin.core.util.ValidateConfig; +import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum; import com.ibeetl.jlw.enums.ResourcesQuestionTypeEnum; import lombok.Data; import lombok.EqualsAndHashCode; @@ -57,6 +58,10 @@ public class TeacherOpenCourseQuestionLog extends BaseEntity { @Dict(type="global_status") private Integer teacherOpenCourseQuestionLogStatus ; + + // 题目提交类型 + + private QuestionLogAddTypeEnum questionLogAddType; //学生ID diff --git a/web/src/main/java/com/ibeetl/jlw/entity/dto/QuestionLogAddDTO.java b/web/src/main/java/com/ibeetl/jlw/entity/dto/QuestionLogAddDTO.java new file mode 100644 index 00000000..73b060b1 --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/entity/dto/QuestionLogAddDTO.java @@ -0,0 +1,57 @@ +package com.ibeetl.jlw.entity.dto; + +import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum; +import lombok.Data; + +import javax.annotation.Nullable; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.Map; +import java.util.TreeSet; + +import static com.ibeetl.jlw.enums.QuestionLogAddTypeEnum.PRE_SUBMIT; + +/** + *

+ * 通用的General、TeacherOpenCourse + * 统一题目提交实体 + *

+ * + * @author mlx + * @date 2022/11/27 + * @modified + */ +@Data +public class QuestionLogAddDTO { + + /** + * 题目配置ID 简单理解,区分属于哪套卷子:章节测试、考试、作业 目前支持这三种类型。作业中,又区分题目作业,和附件作业。 + */ + @NotNull(message = "题目配置ID") + private Long questionSettingId; + + /** + * <题目快照ID, [数组:前端传递过来不需要考虑排序和重复问题]> 例: {"10086": ["D", "A", "B", "A"]} + * 附件上传:需要传递questionLogMap参数的value值,支持多个文件上传。Key值传不传无所谓,后台不取这个值。 + */ + @NotEmpty(message = "提交答案不能为空") + private Map> questionLogMap; + + /** + * 提交时间(前端传) + */ + @NotNull(message = "提交时间不能为空") + private Date addTime; + + /** + * 提交类型(默认 预提交) 可为空 + */ + @Nullable + private QuestionLogAddTypeEnum questionLogAddType; + + public QuestionLogAddDTO() { + // 默认值 + setQuestionLogAddType(PRE_SUBMIT); + } +} diff --git a/web/src/main/java/com/ibeetl/jlw/enums/QuestionLogAddTypeEnum.java b/web/src/main/java/com/ibeetl/jlw/enums/QuestionLogAddTypeEnum.java new file mode 100644 index 00000000..fa39b89b --- /dev/null +++ b/web/src/main/java/com/ibeetl/jlw/enums/QuestionLogAddTypeEnum.java @@ -0,0 +1,22 @@ +package com.ibeetl.jlw.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.beetl.sql.annotation.entity.EnumMapping; + +/** + * 题目提交业务类型 + * @author lx + */ + +@Getter +@AllArgsConstructor +@EnumMapping("name") +public enum QuestionLogAddTypeEnum { + + FINALLY_SUBMIT("最终提交"), + PRE_SUBMIT("预提交"); + + private String text; + +} \ No newline at end of file diff --git a/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java b/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java index 689633fb..38e54217 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java @@ -18,6 +18,8 @@ import com.ibeetl.admin.core.web.JsonReturnCode; import com.ibeetl.jlw.dao.GeneralQuestionLogDao; import com.ibeetl.jlw.dao.StudentDao; import com.ibeetl.jlw.entity.*; +import com.ibeetl.jlw.entity.dto.QuestionLogAddDTO; +import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum; import com.ibeetl.jlw.web.query.GeneralQuestionLogQuery; import com.ibeetl.jlw.web.query.GeneralQuestionSettingQuery; import org.apache.commons.lang3.StringUtils; @@ -473,12 +475,12 @@ public class GeneralQuestionLogService extends CoreBaseService 例: {"10086": ["D", "A", "B", "A"]} - * @param questionSettingId 题目配置ID + * @param questionLogAddDTO 题目信息 */ - public void insertQuestionLog( - @NotEmpty(message = "请上传题目快照ID和答案!") Map> questionLogMap, - @NotNull(message = "通用题目配置ID不能为空!") final Long questionSettingId) { + public void insertQuestionLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO) { + + Long questionSettingId = questionLogAddDTO.getQuestionSettingId(); + Map> questionLogMap = questionLogAddDTO.getQuestionLogMap(); // 查询学生身份 final Student student = getStudent(); @@ -487,7 +489,7 @@ public class GeneralQuestionLogService extends CoreBaseService> questionLogMap, Long questionSettingId, Student student) { + private void addQuestionRelatedLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO, Student student) { + + // 参数获取 + QuestionLogAddTypeEnum addType = questionLogAddDTO.getQuestionLogAddType(); + Date addTime = questionLogAddDTO.getAddTime(); Long studentId = student.getStudentId(); + Long questionSettingId = questionLogAddDTO.getQuestionSettingId(); + Map> questionLogMap = questionLogAddDTO.getQuestionLogMap(); // 查询符合条件的日志表 String questionSnapshotIds = join(questionLogMap.keySet().toArray(), ","); @@ -547,31 +554,50 @@ public class GeneralQuestionLogService extends CoreBaseService updateList = new ArrayList<>(); // 处理答案和分数 logList.forEach(questionLog -> { - // 这里判断题目只允许做一次,禁止二次提交 - Assert.isNull(questionLog.getGeneralQuestionLogUpdateTime(), - "题目名称:{} 题目禁止重复提交!", questionLog.getQuestionStem()); - + // 验证最新的提交时间,防止网络延迟 + if (!validateQuestionLogAddTimeLatest(questionSettingId, questionLog.getGeneralResourcesQuestionSnapshotId().toString(), studentId, addTime)) { + return; + } + Assert.isTrue(!questionLog.getQuestionLogAddType().equals(addType), "无法再次提交答案,题目已完成!"); // 学生提交的答案 String answersText = join(questionLogMap.get(questionLog.getGeneralResourcesQuestionSnapshotId()).toArray(), ","); - questionLog.setGeneralQuestionLogAnswer(answersText); - // 是否是正确答案 Boolean isCorrectAnswer = questionLog.getQuestionAnswer().equalsIgnoreCase(answersText); - // 计算该题目学生的得分情况 questionLog.setStudentScore(isCorrectAnswer ? questionLog.getQuestionScore() : BigDecimal.valueOf(0)); + // 完成时间 + long finishTime = DateUtil.between(questionLog.getGeneralQuestionLogAddTime(), now, MINUTE); + // 填充属性 questionLog.setGeneralQuestionLogUpdateTime(now); - long finishTime = DateUtil.between(questionLog.getGeneralQuestionLogAddTime(), now, MINUTE); questionLog.setGeneralQuestionLogFinishTime(finishTime); + questionLog.setGeneralQuestionLogAnswer(answersText); + questionLog.setQuestionLogAddType(addType); + + updateList.add(questionLog); }); // 学生做的题目的答案与日志关联 - updateBatchTemplate(logList); + updateBatchTemplate(updateList); + } + + /** + * 验证前端传递过来的添加时间是否是最新的 + * @param questionSettingId 题目配置ID + * @param questionSnapshotIds 题目快照ID + * @param studentId 学生ID + * @param addTime 前端传递的添加时间 + * @return + */ + private boolean validateQuestionLogAddTimeLatest(@NotNull(message = "开课题目配置ID不能为空!") Long questionSettingId, + @NotBlank(message = "题目快照IDs不能为空!") String questionSnapshotIds, + @NotNull(message = "学生ID不能为空!") Long studentId, + @NotNull(message = "提交时间不能为空!") Date addTime) { + return generalQuestionLogDao.validateQuestionLogAddTimeLatest(questionSettingId, questionSnapshotIds, studentId, addTime); } /** diff --git a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java index a773a01d..cd570126 100644 --- a/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java +++ b/web/src/main/java/com/ibeetl/jlw/service/TeacherOpenCourseQuestionLogService.java @@ -17,6 +17,8 @@ import com.ibeetl.jlw.dao.ResourcesQuestionSnapshotDao; import com.ibeetl.jlw.dao.StudentDao; import com.ibeetl.jlw.dao.TeacherOpenCourseQuestionLogDao; import com.ibeetl.jlw.entity.*; +import com.ibeetl.jlw.entity.dto.QuestionLogAddDTO; +import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum; import com.ibeetl.jlw.web.query.TeacherOpenCourseQuestionLogQuery; import com.ibeetl.jlw.web.query.TeacherOpenCourseQuestionSettingQuery; import lombok.extern.slf4j.Slf4j; @@ -42,6 +44,7 @@ import static cn.hutool.core.date.DateUnit.MINUTE; import static cn.hutool.core.util.ArrayUtil.join; import static cn.jlw.util.CacheUserUtil.getStudent; import static com.ibeetl.admin.core.util.user.CacheUserUtil.getUserId; +import static com.ibeetl.jlw.enums.QuestionLogAddTypeEnum.FINALLY_SUBMIT; import static com.ibeetl.jlw.enums.ResourcesQuestionSnapshotFromTypeEnum.EXAM; import static java.util.stream.Collectors.groupingBy; @@ -275,12 +278,12 @@ public class TeacherOpenCourseQuestionLogService extends CoreBaseService 例: {"10086": ["D", "A", "B", "A"]} - * @param questionSettingId 题目配置ID + * @param questionLogAddDTO 题目信息 */ - public void insertQuestionLog( - @NotEmpty(message = "请上传题目快照ID和答案!") Map> questionLogMap, - @NotNull(message = "开课题目配置ID不能为空!") final Long questionSettingId) { + public void insertQuestionLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO) { + + Long questionSettingId = questionLogAddDTO.getQuestionSettingId(); + Map> questionLogMap = questionLogAddDTO.getQuestionLogMap(); // 查询学生身份 final Student student = getStudent(); @@ -289,7 +292,7 @@ public class TeacherOpenCourseQuestionLogService extends CoreBaseService> questionLogMap, Long questionSettingId, Student student) { + private void addQuestionRelatedLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO, final Student student) { + + // 参数获取 + QuestionLogAddTypeEnum addType = questionLogAddDTO.getQuestionLogAddType(); + Date addTime = questionLogAddDTO.getAddTime(); Long studentId = student.getStudentId(); + Long questionSettingId = questionLogAddDTO.getQuestionSettingId(); + Map> questionLogMap = questionLogAddDTO.getQuestionLogMap(); + // 查询符合条件的日志表 String questionSnapshotIds = join(questionLogMap.keySet().toArray(), ","); @@ -350,33 +361,55 @@ public class TeacherOpenCourseQuestionLogService extends CoreBaseService updateList = new ArrayList<>(); // 处理答案和分数 logList.forEach(questionLog -> { - // 这里判断题目只允许做一次,禁止二次提交 - Assert.isNull(questionLog.getTeacherOpenCourseQuestionLogUpdateTime(), - "题目名称:{} 题目禁止重复提交!", questionLog.getQuestionStem()); - + // 验证最新的提交时间,防止网络延迟 + if (!validateQuestionLogAddTimeLatest(questionSettingId, questionLog.getResourcesQuestionSnapshotId().toString(), studentId, addTime)) { + return; + } + Assert.isTrue(!questionLog.getQuestionLogAddType().equals(addType), "无法再次提交答案,题目已完成!"); // 学生提交的答案 String answersText = join(questionLogMap.get(questionLog.getResourcesQuestionSnapshotId()).toArray(), ","); - questionLog.setTeacherOpenCourseQuestionLogAnswer(answersText); - // 是否是正确答案 Boolean isCorrectAnswer = questionLog.getQuestionAnswer().equalsIgnoreCase(answersText); + // 完成时间 + long finishTime = DateUtil.between(questionLog.getTeacherOpenCourseQuestionLogAddTime(), now, MINUTE); + // 填充属性 + questionLog.setTeacherOpenCourseQuestionLogAnswer(answersText); // 计算该题目学生的得分情况 questionLog.setStudentScore(isCorrectAnswer ? questionLog.getQuestionScore() : BigDecimal.valueOf(0)); questionLog.setTeacherOpenCourseQuestionLogUpdateTime(now); - long finishTime = DateUtil.between(questionLog.getTeacherOpenCourseQuestionLogAddTime(), now, MINUTE); questionLog.setTeacherOpenCourseQuestionLogFinishTime(finishTime); + questionLog.setQuestionLogAddType(addType); + + // 只添加可以更新的数据 + updateList.add(questionLog); }); // 批量插入错题集(错题库),方法内部自带分数判断。内部方法更新log中的错题标记。 teacherOpenCourseQuestionLogWrongService.insertBatchByQuestionLogList(logList); // 学生做的题目的答案与日志关联 - updateBatchTemplate(logList); + updateBatchTemplate(updateList); + } + + + /** + * 验证前端传递过来的添加时间是否是最新的 + * @param questionSettingId 题目配置ID + * @param questionSnapshotIds 题目快照ID + * @param studentId 学生ID + * @param addTime 前端传递的添加时间 + * @return + */ + private boolean validateQuestionLogAddTimeLatest(@NotNull(message = "开课题目配置ID不能为空!") Long questionSettingId, + @NotBlank(message = "题目快照IDs不能为空!") String questionSnapshotIds, + @NotNull(message = "学生ID不能为空!") Long studentId, + @NotNull(message = "提交时间不能为空!") Date addTime) { + return teacherOpenCourseQuestionLogDao.validateQuestionLogAddTimeLatest(questionSettingId, questionSnapshotIds, studentId, addTime); } /** diff --git a/web/src/main/java/com/ibeetl/jlw/web/GeneralQuestionLogController.java b/web/src/main/java/com/ibeetl/jlw/web/GeneralQuestionLogController.java index f14bf776..23f84c1c 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/GeneralQuestionLogController.java +++ b/web/src/main/java/com/ibeetl/jlw/web/GeneralQuestionLogController.java @@ -18,6 +18,7 @@ import com.ibeetl.admin.core.web.query.PageParam; import com.ibeetl.jlw.entity.FileEntity; import com.ibeetl.jlw.entity.GeneralQuestionLog; import com.ibeetl.jlw.entity.Student; +import com.ibeetl.jlw.entity.dto.QuestionLogAddDTO; import com.ibeetl.jlw.service.GeneralQuestionLogService; import com.ibeetl.jlw.web.query.GeneralQuestionLogQuery; import com.ibeetl.jlw.web.query.GeneralQuestionLogScoreDetailsInfoQuery; @@ -107,23 +108,15 @@ public class GeneralQuestionLogController{ * 学生端-和题目有关的提交方法 (章节测试、考试、作业) *
  • 支持附件上传
  • * - * @param questionLogMap - * <题目快照ID, [数组:前端传递过来不需要考虑排序和重复问题]> 例: {"10086": ["D", "A", "B", "A"]} - * 附件上传:需要传递questionLogMap参数的value值,支持多个文件上传。Key值传不传无所谓,后台不取这个值。 - * @param questionSettingId 题目配置ID 简单理解,区分属于哪套卷子:章节测试、考试、作业 目前支持这三种类型。作业中,又区分题目作业,和附件作业。 + * @param questionLogAddDTO 提交的题目信息 * @param coreUser 用来验证用户登录,为了解耦不会传递到Service层 * @return */ @PostMapping(API + "/addQuestionLog.do") @ResponseBody - public JsonResult addQuestionLog( - @RequestBody - Map> questionLogMap, - Long questionSettingId, - @SCoreUser - CoreUser coreUser) { + public JsonResult addQuestionLog(@RequestBody QuestionLogAddDTO questionLogAddDTO, @SCoreUser CoreUser coreUser) { Assert.isTrue(coreUser.isStudent(), "非学生身份,无法提交!"); - generalQuestionLogService.insertQuestionLog(questionLogMap, questionSettingId); + generalQuestionLogService.insertQuestionLog(questionLogAddDTO); return JsonResult.success(); } diff --git a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseMergeResourcesQuestionController.java b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseMergeResourcesQuestionController.java index 8fb4d12f..8d1432a1 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseMergeResourcesQuestionController.java +++ b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseMergeResourcesQuestionController.java @@ -9,7 +9,6 @@ import cn.jlw.Interceptor.RFile; import cn.jlw.Interceptor.SCoreUser; import cn.jlw.util.ToolUtils; import cn.jlw.util.excel.ExcelUtil; -import cn.jlw.util.excel.MyExcelCellDataListener; import cn.jlw.validate.ValidateConfig; import com.ibeetl.admin.core.annotation.Function; import com.ibeetl.admin.core.entity.CoreUser; @@ -18,6 +17,7 @@ import com.ibeetl.admin.core.util.TimeTool; import com.ibeetl.admin.core.web.JsonResult; import com.ibeetl.jlw.entity.FileEntity; import com.ibeetl.jlw.entity.TeacherOpenCourseMergeResourcesQuestion; +import com.ibeetl.jlw.entity.TeacherOpenCourseMergeResourcesQuestionImport; import com.ibeetl.jlw.service.TeacherOpenCourseMergeResourcesQuestionService; import com.ibeetl.jlw.web.query.TeacherOpenCourseMergeResourcesQuestionQuery; import lombok.SneakyThrows; @@ -51,6 +51,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static cn.jlw.util.CacheUserUtil.getStudent; import static com.ibeetl.admin.core.util.user.CacheUserUtil.getUser; @@ -505,11 +506,12 @@ public class TeacherOpenCourseMergeResourcesQuestionController extends BaseContr Assert.notNull(getUser(), "请登录后再操作"); Assert.isNull(getStudent(), "学生无法访问该接口"); - MyExcelCellDataListener myExcelCellDataListener = new MyExcelCellDataListener(); // 输出 Excel - ExcelUtil.readExcelNotContainHeader(file, TeacherOpenCourseMergeResourcesQuestion.class, myExcelCellDataListener); - - List options = myExcelCellDataListener.getData(); + List list = + ExcelUtil.readExcelNotContainHeader(file, TeacherOpenCourseMergeResourcesQuestionImport.class); + // 类型转换 + List options = list.stream() + .map(TeacherOpenCourseMergeResourcesQuestionImport::pojo).collect(Collectors.toList()); for (Object option : options) { System.out.println(option); diff --git a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java index 16e76d44..e5198d4e 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java +++ b/web/src/main/java/com/ibeetl/jlw/web/TeacherOpenCourseQuestionLogController.java @@ -13,6 +13,7 @@ import com.ibeetl.admin.core.web.JsonResult; import com.ibeetl.admin.core.web.query.PageParam; import com.ibeetl.jlw.entity.Student; import com.ibeetl.jlw.entity.TeacherOpenCourseQuestionLog; +import com.ibeetl.jlw.entity.dto.QuestionLogAddDTO; import com.ibeetl.jlw.service.TeacherOpenCourseQuestionLogService; import com.ibeetl.jlw.web.query.TeacherOpenCourseQuestionLogQuery; import com.ibeetl.jlw.web.query.TeacherOpenCourseQuestionLogScoreDetailsInfoQuery; @@ -109,23 +110,15 @@ public class TeacherOpenCourseQuestionLogController { * 学生端-和题目有关的提交方法 (章节测试、考试、作业) *
  • 支持附件上传
  • * - * @param questionLogMap - * <题目快照ID, [数组:前端传递过来不需要考虑排序和重复问题]> 例: {"10086": ["D", "A", "B", "A"]} - * 附件上传:需要传递questionLogMap参数的value值,支持多个文件上传。Key值传不传无所谓,后台不取这个值。 - * @param questionSettingId 题目配置ID 简单理解,区分属于哪套卷子:章节测试、考试、作业 目前支持这三种类型。作业中,又区分题目作业,和附件作业。 + * @param questionLogAddDTO 提交的题目信息 * @param coreUser 用来验证用户登录,为了解耦不会传递到Service层 * @return */ @PostMapping(API + "/addQuestionLog.do") @ResponseBody - public JsonResult addQuestionLog( - @RequestBody - Map> questionLogMap, - Long questionSettingId, - @SCoreUser - CoreUser coreUser) { + public JsonResult addQuestionLog(@RequestBody QuestionLogAddDTO questionLogAddDTO, @SCoreUser CoreUser coreUser) { Assert.isTrue(coreUser.isStudent(), "非学生身份,无法提交!"); - teacherOpenCourseQuestionLogService.insertQuestionLog(questionLogMap, questionSettingId); + teacherOpenCourseQuestionLogService.insertQuestionLog(questionLogAddDTO); return JsonResult.success(); } diff --git a/web/src/main/java/com/ibeetl/jlw/web/query/GeneralQuestionLogQuery.java b/web/src/main/java/com/ibeetl/jlw/web/query/GeneralQuestionLogQuery.java index a135e6cd..534841c4 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/query/GeneralQuestionLogQuery.java +++ b/web/src/main/java/com/ibeetl/jlw/web/query/GeneralQuestionLogQuery.java @@ -5,6 +5,7 @@ import com.ibeetl.admin.core.annotation.Query; import com.ibeetl.admin.core.web.query.PageParam; import com.ibeetl.jlw.entity.GeneralQuestionLog; import com.ibeetl.jlw.enums.GeneralResourcesQuestionLogTypeEnum; +import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum; import lombok.*; import lombok.experimental.Accessors; @@ -42,6 +43,8 @@ public class GeneralQuestionLogQuery extends PageParam { private Date generalQuestionLogAddTime; @Query(name = "状态(1正常 2删除)", display = true,type=Query.TYPE_DICT,dict="global_status") private Integer generalQuestionLogStatus; + @Query(name = "题目提交类型", display = false) + private QuestionLogAddTypeEnum questionLogAddType; @Query(name = "学生ID", display = false) private Long studentId; @Query(name = "学生得分", display = false) @@ -121,6 +124,7 @@ public class GeneralQuestionLogQuery extends PageParam { pojo.setGeneralQuestionLogUpdateTime(this.getGeneralQuestionLogUpdateTime()); pojo.setGeneralQuestionLogFinishTime(this.getGeneralQuestionLogFinishTime()); pojo.setGeneralQuestionLogReply(this.getGeneralQuestionLogReply()); + pojo.setQuestionLogAddType(this.getQuestionLogAddType()); pojo.setOrgId(this.getOrgId()); pojo.setUserId(this.getUserId()); return pojo; diff --git a/web/src/main/java/com/ibeetl/jlw/web/query/TeacherOpenCourseQuestionLogQuery.java b/web/src/main/java/com/ibeetl/jlw/web/query/TeacherOpenCourseQuestionLogQuery.java index c64329ec..5877883c 100644 --- a/web/src/main/java/com/ibeetl/jlw/web/query/TeacherOpenCourseQuestionLogQuery.java +++ b/web/src/main/java/com/ibeetl/jlw/web/query/TeacherOpenCourseQuestionLogQuery.java @@ -4,6 +4,7 @@ import cn.jlw.validate.ValidateConfig; import com.ibeetl.admin.core.annotation.Query; import com.ibeetl.admin.core.web.query.PageParam; import com.ibeetl.jlw.entity.TeacherOpenCourseQuestionLog; +import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum; import lombok.*; import lombok.experimental.Accessors; @@ -36,6 +37,8 @@ public class TeacherOpenCourseQuestionLogQuery extends PageParam { private Date teacherOpenCourseQuestionLogAddTime; @Query(name = "状态(1正常 2删除)", display = true,type=Query.TYPE_DICT,dict="global_status") private Integer teacherOpenCourseQuestionLogStatus; + @Query(name = "题目提交类型", display = false) + private QuestionLogAddTypeEnum questionLogAddType; @Query(name = "学生ID", display = false) private Long studentId; @Query(name = "组织ID", display = false) @@ -149,6 +152,7 @@ public class TeacherOpenCourseQuestionLogQuery extends PageParam { pojo.setTeacherOpenCourseQuestionLogUpdateTime(this.getTeacherOpenCourseQuestionLogUpdateTime()); pojo.setTeacherOpenCourseQuestionLogFinishTime(this.getTeacherOpenCourseQuestionLogFinishTime()); pojo.setTeacherOpenCourseQuestionLogReply(this.getTeacherOpenCourseQuestionLogReply()); + pojo.setQuestionLogAddType(this.getQuestionLogAddType()); return pojo; } diff --git a/web/src/main/resources/sql/jlw/generalQuestionLog.md b/web/src/main/resources/sql/jlw/generalQuestionLog.md index 31809ac4..45042de3 100644 --- a/web/src/main/resources/sql/jlw/generalQuestionLog.md +++ b/web/src/main/resources/sql/jlw/generalQuestionLog.md @@ -1221,4 +1221,19 @@ getQuestionLogScoreDetailsInfo ta.student_sn, ta.class_id, tb.class_name - ) tz \ No newline at end of file + ) tz + +validateQuestionLogAddTimeLatest +=== +* 验证前端传递过来的添加时间是否是最新的 + select + if(t.general_question_log_update_time is null, true, t.general_question_log_update_time < #addTime#) + from + general_question_log t + where 1 = 1 + and t.general_question_setting_id = #questionSettingId# + and FIND_IN_SET(t.general_resources_question_snapshot_id, #questionSnapshotIds#) + and t.student_id = #studentId# + and t.general_question_log_status = 1 + order by general_question_log_add_time desc + limit 1 \ No newline at end of file diff --git a/web/src/main/resources/sql/jlw/teacherOpenCourseQuestionLog.md b/web/src/main/resources/sql/jlw/teacherOpenCourseQuestionLog.md index 7381ff26..4f26f0dc 100644 --- a/web/src/main/resources/sql/jlw/teacherOpenCourseQuestionLog.md +++ b/web/src/main/resources/sql/jlw/teacherOpenCourseQuestionLog.md @@ -732,3 +732,19 @@ getQuestionLogScoreDetailsInfo ta.class_id, tb.class_name ) tz + + +validateQuestionLogAddTimeLatest +=== +* 验证前端传递过来的添加时间是否是最新的 + select + if(t.teacher_open_course_question_log_update_time is null, true, t.teacher_open_course_question_log_update_time < #addTime#) + from + teacher_open_course_question_log t + where 1 = 1 + and t.teacher_open_course_question_setting_id = #questionSettingId# + and FIND_IN_SET(t.resources_question_snapshot_id, #questionSnapshotIds#) + and t.student_id = #studentId# + and t.teacher_open_course_question_log_status = 1 + order by teacher_open_course_question_log_add_time desc + limit 1