|
|
package com.ibeetl.jlw.service;
|
|
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
import cn.hutool.core.lang.Assert;
|
|
|
import cn.hutool.core.map.MapUtil;
|
|
|
import cn.hutool.core.util.*;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
import cn.jlw.util.ToolUtils;
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.ibeetl.admin.core.service.CoreBaseService;
|
|
|
import com.ibeetl.admin.core.service.CoreDictService;
|
|
|
import com.ibeetl.admin.core.util.PlatformException;
|
|
|
import com.ibeetl.admin.core.web.JsonResult;
|
|
|
import com.ibeetl.admin.core.web.JsonReturnCode;
|
|
|
import com.ibeetl.jlw.dao.ResourcesQuestionSnapshotDao;
|
|
|
import com.ibeetl.jlw.dao.StudentDao;
|
|
|
import com.ibeetl.jlw.dao.TeacherOpenCourseQuestionLogDao;
|
|
|
import com.ibeetl.jlw.dao.TeacherOpenCourseQuestionSettingDao;
|
|
|
import com.ibeetl.jlw.entity.*;
|
|
|
import com.ibeetl.jlw.entity.dto.QuestionLogAddDTO;
|
|
|
import com.ibeetl.jlw.entity.dto.TeacherOpenCourseHomeWorkLogDTO;
|
|
|
import com.ibeetl.jlw.entity.vo.FileQuestionLogListVO;
|
|
|
import com.ibeetl.jlw.entity.vo.TeacherOpenCourseHomeWorkLogExportVO;
|
|
|
import com.ibeetl.jlw.entity.vo.TeacherOpenCourseQuestionLogAnswerLockVO;
|
|
|
import com.ibeetl.jlw.enums.GlobalStatusEnum;
|
|
|
import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum;
|
|
|
import com.ibeetl.jlw.enums.ResourcesQuestionSnapshotFromTypeEnum;
|
|
|
import com.ibeetl.jlw.web.query.QuestionLogSummaryQuery;
|
|
|
import com.ibeetl.jlw.web.query.TeacherOpenCourseQuestionLogQuery;
|
|
|
import com.ibeetl.jlw.web.query.TeacherOpenCourseQuestionSettingQuery;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.beetl.sql.core.SqlId;
|
|
|
import org.beetl.sql.core.engine.PageQuery;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.context.annotation.Lazy;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.MultiValueMapAdapter;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
import javax.annotation.Nullable;
|
|
|
import javax.validation.constraints.NotBlank;
|
|
|
import javax.validation.constraints.NotEmpty;
|
|
|
import javax.validation.constraints.NotNull;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.util.*;
|
|
|
import java.util.function.Function;
|
|
|
import java.util.function.Predicate;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import static cn.hutool.core.date.DateUnit.SECOND;
|
|
|
import static cn.hutool.core.util.ArrayUtil.isAllNotEmpty;
|
|
|
import static cn.hutool.core.util.ArrayUtil.join;
|
|
|
import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
|
|
|
import static cn.jlw.util.CacheUserUtil.getStudent;
|
|
|
import static com.ibeetl.admin.core.util.StreamUtils.listJoin;
|
|
|
import static com.ibeetl.admin.core.util.user.CacheUserUtil.getUserId;
|
|
|
import static com.ibeetl.jlw.enums.QuestionBusinessTypeEnum.FROM_OPEN_COURSE;
|
|
|
import static com.ibeetl.jlw.enums.QuestionLogAddTypeEnum.*;
|
|
|
import static com.ibeetl.jlw.enums.ResourcesQuestionSnapshotFromTypeEnum.CHAPTER_EXERCISE;
|
|
|
import static com.ibeetl.jlw.enums.ResourcesQuestionSnapshotFromTypeEnum.HOMEWORK_FILE;
|
|
|
import static com.ibeetl.jlw.enums.ResourcesQuestionTypeEnum.isOneQuestion;
|
|
|
import static java.lang.Math.abs;
|
|
|
import static java.lang.Math.toIntExact;
|
|
|
import static java.math.BigDecimal.ZERO;
|
|
|
import static java.util.stream.Collectors.groupingBy;
|
|
|
import static java.util.stream.Collectors.joining;
|
|
|
|
|
|
/**
|
|
|
* 题目日志 Service
|
|
|
* 当分布式ID开启后请勿使用insert(*,true)
|
|
|
*/
|
|
|
|
|
|
@Service
|
|
|
@Transactional
|
|
|
@Validated
|
|
|
@Slf4j
|
|
|
public class TeacherOpenCourseQuestionLogService extends CoreBaseService<TeacherOpenCourseQuestionLog> implements DeleteResourcesBy{
|
|
|
|
|
|
@Autowired private TeacherOpenCourseQuestionLogDao teacherOpenCourseQuestionLogDao;
|
|
|
@Autowired private StudentDao studentDao;
|
|
|
@Autowired private ResourcesQuestionSnapshotDao resourcesQuestionSnapshotDao;
|
|
|
@Autowired @Lazy
|
|
|
private TeacherOpenCourseQuestionSettingService teacherOpenCourseQuestionSettingService;
|
|
|
@Autowired
|
|
|
private TeacherOpenCourseQuestionSettingDao teacherOpenCourseQuestionSettingDao;
|
|
|
@Autowired private TeacherOpenCourseQuestionLogWrongService teacherOpenCourseQuestionLogWrongService;
|
|
|
@Autowired private QuestionLogSummaryService questionLogSummaryService;
|
|
|
@Autowired private CoreDictService coreDictService;
|
|
|
public PageQuery<TeacherOpenCourseQuestionLog>queryByCondition(PageQuery query){
|
|
|
PageQuery ret = teacherOpenCourseQuestionLogDao.queryByCondition(query);
|
|
|
queryListAfter(ret.getList());
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
public PageQuery<TeacherOpenCourseQuestionLog>queryByConditionQuery(PageQuery query){
|
|
|
PageQuery ret = teacherOpenCourseQuestionLogDao.queryByConditionQuery(query);
|
|
|
List<TeacherOpenCourseQuestionLog> list = ret.getList();
|
|
|
queryListAfter(list);
|
|
|
|
|
|
Map<Long, List<Student>> studentMap = new HashMap<>();
|
|
|
if (ObjectUtil.isNotEmpty(list)) {
|
|
|
String studentIds = list.stream().map(TeacherOpenCourseQuestionLog::getStudentId).map(Object::toString).distinct().collect(joining(","));
|
|
|
List<Student> studentList = studentDao.getByIds(studentIds);
|
|
|
dictParser(studentList);
|
|
|
if (ObjectUtil.isNotEmpty(studentList)) {
|
|
|
studentMap.putAll(studentList.stream().collect(groupingBy(Student::getStudentId)));
|
|
|
}
|
|
|
|
|
|
list.forEach(item -> {
|
|
|
List<Student> students = studentMap.get(item.getStudentId());
|
|
|
if (ObjectUtil.isNotEmpty(students)) {
|
|
|
Student student = students.get(0);
|
|
|
// 学生编号
|
|
|
item.set("studentSn", student.getStudentSn());
|
|
|
item.set("studentName", student.getStudentName());
|
|
|
// 班级
|
|
|
item.set("className", defaultIfNull(student.get("classIdText"), "班级不存在"));
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
public void deleteByList(List list){
|
|
|
String ids = "";
|
|
|
ToolUtils.deleteNullList(list);
|
|
|
for(int i=0;null != list && i<list.size();i++){
|
|
|
ids += list.get(i).toString()+(i==list.size()-1?"":",");
|
|
|
}
|
|
|
if(StringUtils.isNotBlank(ids)){
|
|
|
teacherOpenCourseQuestionLogDao.deleteByIds(ids);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void deleteTeacherOpenCourseQuestionLog(String ids){
|
|
|
try {
|
|
|
teacherOpenCourseQuestionLogDao.deleteTeacherOpenCourseQuestionLogByIds(ids);
|
|
|
} catch (Exception e) {
|
|
|
throw new PlatformException("批量删除题目日志失败", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据题目配置ID 批量真删除
|
|
|
* @param settingIds
|
|
|
*/
|
|
|
public void deleteBySettingIds(String settingIds) {
|
|
|
try {
|
|
|
teacherOpenCourseQuestionLogDao.deleteBySettingIds(settingIds);
|
|
|
} catch (Exception e) {
|
|
|
throw new PlatformException("批量删除题目日志失败", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void logicDeleteBySettingIds(String settingIds){
|
|
|
try {
|
|
|
teacherOpenCourseQuestionLogDao.logicDeleteBySettingIds(settingIds);
|
|
|
} catch (Exception e) {
|
|
|
throw new PlatformException("批量删除题目日志失败", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public String addAll(TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery){
|
|
|
String msg = "";
|
|
|
List<TeacherOpenCourseQuestionLog> teacherOpenCourseQuestionLogList = new ArrayList<>();
|
|
|
try {
|
|
|
teacherOpenCourseQuestionLogList = JSON.parseArray(teacherOpenCourseQuestionLogQuery.getTeacherOpenCourseQuestionLogJsonStr(), TeacherOpenCourseQuestionLog.class);
|
|
|
} catch (Exception e) {
|
|
|
try {
|
|
|
teacherOpenCourseQuestionLogList.add(JSONObject.parseObject(teacherOpenCourseQuestionLogQuery.getTeacherOpenCourseQuestionLogJsonStr(), TeacherOpenCourseQuestionLog.class));
|
|
|
} catch (Exception e1) {}
|
|
|
}
|
|
|
ToolUtils.deleteNullList(teacherOpenCourseQuestionLogList);
|
|
|
if(null != teacherOpenCourseQuestionLogList && teacherOpenCourseQuestionLogList.size()>0){
|
|
|
for(int i=0;i<teacherOpenCourseQuestionLogList.size();i++){
|
|
|
TeacherOpenCourseQuestionLog teacherOpenCourseQuestionLog = teacherOpenCourseQuestionLogList.get(i);
|
|
|
teacherOpenCourseQuestionLog.setUserId(teacherOpenCourseQuestionLogQuery.getUserId());
|
|
|
teacherOpenCourseQuestionLog.setOrgId(teacherOpenCourseQuestionLogQuery.getOrgId());
|
|
|
}
|
|
|
insertBatch(teacherOpenCourseQuestionLogList);
|
|
|
}
|
|
|
return msg;
|
|
|
}
|
|
|
|
|
|
public JsonResult add(TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery){
|
|
|
String msg = "";
|
|
|
TeacherOpenCourseQuestionLog teacherOpenCourseQuestionLog = teacherOpenCourseQuestionLogQuery.pojo();
|
|
|
teacherOpenCourseQuestionLogDao.insert(teacherOpenCourseQuestionLog);
|
|
|
teacherOpenCourseQuestionLogQuery.setTeacherOpenCourseQuestionLogId(teacherOpenCourseQuestionLog.getTeacherOpenCourseQuestionLogId());
|
|
|
JsonResult jsonResult = new JsonResult();
|
|
|
jsonResult.setData(teacherOpenCourseQuestionLog.getTeacherOpenCourseQuestionLogId());//自增的ID丢进去
|
|
|
jsonResult.setCode(JsonReturnCode.SUCCESS.getCode());
|
|
|
jsonResult.setMsg(msg);
|
|
|
return jsonResult;
|
|
|
}
|
|
|
|
|
|
public String edit(TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery){
|
|
|
String msg = "";
|
|
|
TeacherOpenCourseQuestionLog teacherOpenCourseQuestionLog = teacherOpenCourseQuestionLogQuery.pojo();
|
|
|
teacherOpenCourseQuestionLogDao.updateTemplateById(teacherOpenCourseQuestionLog);
|
|
|
return msg;
|
|
|
}
|
|
|
|
|
|
public String updateGivenByIds(TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery){
|
|
|
String msg = "";
|
|
|
if(StringUtils.isNotBlank(teacherOpenCourseQuestionLogQuery.get_given())){
|
|
|
boolean flag = teacherOpenCourseQuestionLogDao.updateGivenByIds(teacherOpenCourseQuestionLogQuery) > 0;
|
|
|
if(!flag){
|
|
|
msg = "更新指定参数失败";
|
|
|
}
|
|
|
}else{
|
|
|
msg = "指定参数为空";
|
|
|
}
|
|
|
return msg;
|
|
|
}
|
|
|
|
|
|
public List<TeacherOpenCourseQuestionLog> getValues (Object paras){
|
|
|
return sqlManager.select(SqlId.of("jlw.teacherOpenCourseQuestionLog.getTeacherOpenCourseQuestionLogValues"), TeacherOpenCourseQuestionLog.class, paras);
|
|
|
}
|
|
|
|
|
|
public List<TeacherOpenCourseQuestionLog> getValuesByQuery (TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery){
|
|
|
return teacherOpenCourseQuestionLogDao.getValuesByQuery(teacherOpenCourseQuestionLogQuery);
|
|
|
}
|
|
|
public List<TeacherOpenCourseQuestionLog> getValuesByQueryNotWithPermission (TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery){
|
|
|
return teacherOpenCourseQuestionLogDao.getValuesByQueryNotWithPermission(teacherOpenCourseQuestionLogQuery);
|
|
|
}
|
|
|
|
|
|
public List<TeacherOpenCourseQuestionLog> getValuesBySettingIds (String settingIds){
|
|
|
TeacherOpenCourseQuestionLogQuery logQuery = new TeacherOpenCourseQuestionLogQuery();
|
|
|
logQuery.setTeacherOpenCourseQuestionSettingIdPlural(settingIds);
|
|
|
logQuery.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
return teacherOpenCourseQuestionLogDao.getValuesByQueryNotWithPermission(logQuery);
|
|
|
}
|
|
|
|
|
|
public TeacherOpenCourseQuestionLog getInfo (Long teacherOpenCourseQuestionLogId){
|
|
|
TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery = new TeacherOpenCourseQuestionLogQuery();
|
|
|
teacherOpenCourseQuestionLogQuery.setTeacherOpenCourseQuestionLogId(teacherOpenCourseQuestionLogId);
|
|
|
teacherOpenCourseQuestionLogQuery.setTeacherOpenCourseQuestionLogStatusPlural("1,2");//需要根据实际情况来
|
|
|
List<TeacherOpenCourseQuestionLog> list = teacherOpenCourseQuestionLogDao.getValuesByQuery(teacherOpenCourseQuestionLogQuery);
|
|
|
if(null != list && list.size()>0){
|
|
|
return list.get(0);
|
|
|
}else{
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public TeacherOpenCourseQuestionLog getInfo (TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery){
|
|
|
List<TeacherOpenCourseQuestionLog> list = teacherOpenCourseQuestionLogDao.getValuesByQuery(teacherOpenCourseQuestionLogQuery);
|
|
|
if(null != list && list.size()>0){
|
|
|
return list.get(0);
|
|
|
}else{
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 和题目有关的做题提交方法
|
|
|
*
|
|
|
* @param questionLogMap <题目ID, [数组:前端传递过来不需要考虑排序和重复问题]> 例: {"10086": ["D", "A", "B", "A"]}
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
*/
|
|
|
@Deprecated
|
|
|
public void addQuestionLog(
|
|
|
@NotEmpty(message = "请上传题目快照ID和答案!") Map<Long, TreeSet<String>> questionLogMap,
|
|
|
@NotNull(message = "开课题目配置ID不能为空!") final Long questionSettingId) {
|
|
|
|
|
|
// 查询学生身份
|
|
|
final Student student = studentDao.getByUserId(getUserId());
|
|
|
|
|
|
List<TeacherOpenCourseQuestionLog> list = new ArrayList<>();
|
|
|
|
|
|
// 不需要判断空,控制类层已经用@NotEmpty判断过了。
|
|
|
questionLogMap.forEach((resourcesQuestionSnapshotId, answerSet) -> {
|
|
|
// 答案逗号隔开
|
|
|
String answersText = join(answerSet.toArray(), ",");
|
|
|
// 构建实体
|
|
|
TeacherOpenCourseQuestionLog questionLog = new TeacherOpenCourseQuestionLog();
|
|
|
questionLog.setTeacherOpenCourseQuestionSettingId(questionSettingId);
|
|
|
questionLog.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
questionLog.setOrgId(student.getOrgId());
|
|
|
questionLog.setUserId(student.getUserId());
|
|
|
// 包含/字符,代表是路径地址,是附件类型
|
|
|
if(answersText.contains("/")) {
|
|
|
questionLog.setTeacherOpenCourseQuestionLogUploadFile(answersText);
|
|
|
}
|
|
|
// 否则就是普通选择题的答案
|
|
|
else {
|
|
|
questionLog.setTeacherOpenCourseQuestionLogAnswer(answersText);
|
|
|
}
|
|
|
questionLog.setTeacherOpenCourseQuestionLogAddTime(new Date());
|
|
|
questionLog.setResourcesQuestionSnapshotId(resourcesQuestionSnapshotId);
|
|
|
questionLog.setStudentId(student.getStudentId());
|
|
|
// 用于数据权限
|
|
|
questionLog.setOrgId(student.getOrgId());
|
|
|
questionLog.setUserId(student.getUserId());
|
|
|
list.add(questionLog);
|
|
|
});
|
|
|
|
|
|
// 批量入库
|
|
|
insertBatch(list);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 教师端-手动批改一些题目的分数
|
|
|
* 教师端-给学生打分
|
|
|
*
|
|
|
* @param teacherOpenCourseQuestionLogIds 指定更新分数的题目日志IDs
|
|
|
* @param score 分数
|
|
|
* @param reply 评语
|
|
|
*/
|
|
|
public void manualModifyQuestionScores(@NotNull(message = "题目做题日志ID不能为空!") final String teacherOpenCourseQuestionLogIds,
|
|
|
@NotNull(message = "设置的分数不能为空!") final BigDecimal score, @Nullable String reply) {
|
|
|
TeacherOpenCourseQuestionLogQuery teacherOpenCourseQuestionLogQuery = new TeacherOpenCourseQuestionLogQuery();
|
|
|
teacherOpenCourseQuestionLogQuery.setTeacherOpenCourseQuestionLogIdPlural(teacherOpenCourseQuestionLogIds);
|
|
|
teacherOpenCourseQuestionLogQuery.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
List<TeacherOpenCourseQuestionLog> updateList = teacherOpenCourseQuestionLogDao.getValuesByQueryNotWithPermission(teacherOpenCourseQuestionLogQuery);
|
|
|
|
|
|
Assert.notEmpty(updateList, "未查询到做题记录!");
|
|
|
|
|
|
final Date now = new Date();
|
|
|
// 批量更新分数。支持重复设置分数和评语
|
|
|
updateList.forEach(questionLog -> {
|
|
|
BigDecimal questionScore = questionLog.getQuestionScore();
|
|
|
questionLog.setStudentScore(score);
|
|
|
questionLog.setTeacherOpenCourseQuestionLogReply(reply);
|
|
|
// 这里不是记录用时,只是记录教师批阅的时间
|
|
|
questionLog.setTeacherOpenCourseQuestionLogUpdateTime(now);
|
|
|
// 是否错题库,也可以用作标记答案是对的错的
|
|
|
questionLog.setIsErrorFavorite(NumberUtil.equals(questionScore, score));
|
|
|
});
|
|
|
updateBatchTemplate(updateList);
|
|
|
|
|
|
// 处理多个学生的数据
|
|
|
updateList.stream().collect(groupingBy(TeacherOpenCourseQuestionLog::getStudentId)).forEach((studentId, logList) -> {
|
|
|
TeacherOpenCourseQuestionLog questionLog = logList.get(0);
|
|
|
Student student = studentDao.getById(studentId);
|
|
|
|
|
|
if (ObjectUtil.isAllNotEmpty(student, questionLog)) {
|
|
|
addQuestionLogSummary(student, questionLog.getTeacherOpenCourseQuestionSettingId(), updateList);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 和题目有关的做题提交方法
|
|
|
* 直接计算出学生得分情况flock
|
|
|
* 不能做练习题
|
|
|
*
|
|
|
* @param questionLogAddDTO 题目信息
|
|
|
*/
|
|
|
public void addQuestionLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO) {
|
|
|
|
|
|
final Long questionSettingId = questionLogAddDTO.getQuestionSettingId();
|
|
|
Map<String, String> questionLogMap = questionLogAddDTO.getQuestionLogMap();
|
|
|
|
|
|
// 查询学生身份
|
|
|
final Student student = getStudent();
|
|
|
Assert.notNull(student, "非学生身份,无法提交!");
|
|
|
|
|
|
TeacherOpenCourseQuestionSetting questionSetting = teacherOpenCourseQuestionSettingService.getInfo(questionSettingId);
|
|
|
Assert.notNull(questionSetting, "未匹配到试卷信息!");
|
|
|
|
|
|
// 校验当前时间是否在考试的时间段
|
|
|
teacherOpenCourseQuestionSettingService.verifyQuestionStartAndEndTimeWithNowTimeThrow(questionSettingId);
|
|
|
|
|
|
switch(questionSetting.getTeacherOpenCourseQuestionSettingType()) {
|
|
|
case HOMEWORK_FILE: {
|
|
|
addFileRelatedLog(join(questionLogMap.values().toArray(), ","), questionSettingId, student);
|
|
|
} break;
|
|
|
|
|
|
// 这些都和题目相关,暂时先放在一个方法
|
|
|
case CHAPTER_EXERCISE:
|
|
|
case EXAM:
|
|
|
case HOMEWORK_QUESTION:{
|
|
|
addQuestionRelatedLog(questionLogAddDTO, questionSetting, student);
|
|
|
} break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 和题目有关的做题提交方法
|
|
|
* 直接计算出学生得分情况
|
|
|
* 只支持练习
|
|
|
*
|
|
|
* @param questionLogAddDTO 题目信息
|
|
|
*/
|
|
|
public void addQuestionLogTest(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO) {
|
|
|
|
|
|
final Long questionSettingId = questionLogAddDTO.getQuestionSettingId();
|
|
|
|
|
|
// 查询学生身份
|
|
|
final Student student = getStudent();
|
|
|
Assert.notNull(student, "非学生身份,无法提交!");
|
|
|
|
|
|
TeacherOpenCourseQuestionSetting questionSetting = teacherOpenCourseQuestionSettingService.getInfo(questionSettingId);
|
|
|
Assert.notNull(questionSetting, "未匹配到试卷信息!");
|
|
|
|
|
|
// 校验当前时间是否在考试的时间段
|
|
|
teacherOpenCourseQuestionSettingService.verifyQuestionStartAndEndTimeWithNowTimeThrow(questionSettingId);
|
|
|
|
|
|
switch(questionSetting.getTeacherOpenCourseQuestionSettingType()) {
|
|
|
// 这些都和题目相关,暂时先放在一个方法
|
|
|
case CHAPTER_EXERCISE: {
|
|
|
addQuestionRelatedLog(questionLogAddDTO, questionSetting, student);
|
|
|
} break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 跟题目相关的题目日志提交方法
|
|
|
* 附件类型的需要单独,拆分除去
|
|
|
*
|
|
|
* @param absFilePath
|
|
|
* @param questionSettingId
|
|
|
* @param student
|
|
|
*/
|
|
|
private void addFileRelatedLog(@NotBlank(message = "上传的附件不能为空!") String absFilePath, Long questionSettingId, Student student) {
|
|
|
Date now = new Date();
|
|
|
TeacherOpenCourseQuestionLogQuery query = new TeacherOpenCourseQuestionLogQuery();
|
|
|
query.setTeacherOpenCourseQuestionLogUploadFile(absFilePath);
|
|
|
query.setTeacherOpenCourseQuestionSettingId(questionSettingId);
|
|
|
query.setTeacherOpenCourseQuestionLogAddTime(now);
|
|
|
query.setTeacherOpenCourseQuestionLogFinishTime(RandomUtil.randomLong(100, 3600));
|
|
|
query.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
// 附件作业,只有一题,所有默认是全部提交的方式
|
|
|
query.setQuestionLogAddType(FINALLY_SUBMIT);
|
|
|
query.setOrgId(student.getOrgId());
|
|
|
query.setUserId(student.getUserId());
|
|
|
query.setStudentId(student.getStudentId());
|
|
|
add(query);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 跟题目相关的题目日志提交方法
|
|
|
* 附件类型的需要单独,拆分除去
|
|
|
* 提交试卷 试卷提交
|
|
|
*
|
|
|
* @param questionLogAddDTO 题目信息
|
|
|
* @param student 学生信息
|
|
|
*/
|
|
|
private void addQuestionRelatedLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO,
|
|
|
@NotNull(message = "开课信息不能为空") TeacherOpenCourseQuestionSetting questionSetting,
|
|
|
@NotNull(message = "学生信息不能为空") final Student student) {
|
|
|
|
|
|
// 参数获取
|
|
|
QuestionLogAddTypeEnum addType = questionLogAddDTO.getQuestionLogAddType();
|
|
|
Date addTime = questionLogAddDTO.getAddTime(); Long studentId = student.getStudentId();
|
|
|
Long questionSettingId = questionLogAddDTO.getQuestionSettingId();
|
|
|
Map<String, String> questionLogMap = questionLogAddDTO.getQuestionLogMap();
|
|
|
// 查询符合条件的日志表
|
|
|
String questionSnapshotIds = join(questionLogMap.keySet().toArray(), ",");
|
|
|
|
|
|
// 做题思路:先从开课QuestionSetting表中生成题目日志,到开课QuestionLog中,学生这个时候提交答案,再修改日志中的结果。
|
|
|
// 查询条件
|
|
|
TeacherOpenCourseQuestionLogQuery questionLogQuery = TeacherOpenCourseQuestionLogQuery.builder()
|
|
|
.resourcesQuestionSnapshotIdPlural(questionSnapshotIds)
|
|
|
.teacherOpenCourseQuestionLogStatus(1)
|
|
|
.studentId(student.getStudentId())
|
|
|
.teacherOpenCourseQuestionSettingId(questionSettingId)
|
|
|
.build();
|
|
|
List<TeacherOpenCourseQuestionLog> logList = teacherOpenCourseQuestionLogDao.getValuesByQueryNotWithPermission(questionLogQuery);
|
|
|
|
|
|
final List<QuestionLogAddTypeEnum> tempList = Arrays.asList(PRE_SUBMIT, null);
|
|
|
// 只是未提交的数据
|
|
|
logList = logList.stream().filter(item -> tempList.contains(item.getQuestionLogAddType())).collect(Collectors.toList());
|
|
|
Assert.notEmpty(logList, "未查询到题目信息!");
|
|
|
// 题目日志IDs汇总
|
|
|
String logsIds = logList.stream().map(item -> item.getTeacherOpenCourseQuestionLogId().toString()).collect(joining(","));
|
|
|
|
|
|
// 当前时间, 存储要更新的题目日志集合
|
|
|
Date now = new Date(); List<TeacherOpenCourseQuestionLog> updateList = new ArrayList<>();
|
|
|
// 代表填空题和分析题的类型
|
|
|
List<Integer> questionType = Arrays.asList(4, 5);
|
|
|
// 处理答案和分数
|
|
|
logList.forEach(questionLog -> {
|
|
|
// 验证最新的提交时间,防止网络延迟
|
|
|
if (!validateQuestionLogAddTimeLatest(questionSettingId, questionLog.getResourcesQuestionSnapshotId().toString(), studentId, addTime)) {
|
|
|
return;
|
|
|
}
|
|
|
// 验证提交类型
|
|
|
validateQuestionLogAddTypeThrow(questionLog.getQuestionLogAddType(), questionLogAddDTO.getQuestionLogAddType());
|
|
|
// 学生提交的答案
|
|
|
String answer = questionLogMap.get(questionLog.getResourcesQuestionSnapshotId().toString());
|
|
|
|
|
|
// 默认0分
|
|
|
questionLog.setStudentScore(BigDecimal.valueOf(0));
|
|
|
|
|
|
// 学生提交的答案处理
|
|
|
String answersText = null;
|
|
|
if (questionType.contains(questionLog.getQuestionType())) {
|
|
|
|
|
|
answersText = answer;
|
|
|
|
|
|
cn.hutool.json.JSONObject studentAnswerArr = new cn.hutool.json.JSONObject();
|
|
|
|
|
|
if (StrUtil.isNotBlank(answersText)) {
|
|
|
studentAnswerArr = JSONUtil.parseObj(answersText);
|
|
|
}
|
|
|
// 学生答案和正确答案集合
|
|
|
|
|
|
cn.hutool.json.JSONObject questionAnswerArr = JSONUtil.parseObj(questionLog.getQuestionAnswer());
|
|
|
// 每题分数
|
|
|
BigDecimal eachScore = NumberUtil.div(questionLog.getQuestionScore(), questionAnswerArr.size(), 1);
|
|
|
|
|
|
BigDecimal studentScore = new BigDecimal("0");
|
|
|
for (Map.Entry<String, Object> map : questionAnswerArr.entrySet()) {
|
|
|
Object questionAns = map.getValue();
|
|
|
String k = map.getKey();
|
|
|
String studentAns = studentAnswerArr.getStr(k, "");
|
|
|
|
|
|
// 比对答案
|
|
|
if ( StrUtil.isAllNotBlank(questionAns.toString(), studentAns) && questionAns.toString().equalsIgnoreCase(studentAns)) {
|
|
|
studentScore.add(eachScore);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 填空题和分析题计算分数
|
|
|
// 计算该题目学生的得分情况
|
|
|
questionLog.setStudentScore(studentScore);
|
|
|
// 是否是错题
|
|
|
questionLog.setIsErrorFavorite(NumberUtil.isLess(studentScore, questionLog.getQuestionScore()));
|
|
|
}else {
|
|
|
answersText = join(answer.split(","), ",");
|
|
|
// 是否是正确答案
|
|
|
Boolean isCorrectAnswer = questionLog.getQuestionAnswer().equalsIgnoreCase(answersText);
|
|
|
|
|
|
// 学生得分
|
|
|
questionLog.setStudentScore(isCorrectAnswer ? questionLog.getQuestionScore() : ZERO);
|
|
|
questionLog.setIsErrorFavorite(!isCorrectAnswer);
|
|
|
}
|
|
|
|
|
|
// 完成时间,从生成试卷时候,开始计算
|
|
|
long finishSecondTime = DateUtil.between(questionLog.getTeacherOpenCourseQuestionLogAddTime(), now, SECOND);
|
|
|
|
|
|
// 填充属性
|
|
|
questionLog.setTeacherOpenCourseQuestionLogAnswer(answersText);
|
|
|
questionLog.setTeacherOpenCourseQuestionLogUpdateTime(now);
|
|
|
questionLog.setTeacherOpenCourseQuestionLogFinishTime(finishSecondTime);
|
|
|
questionLog.setQuestionLogAddType(addType);
|
|
|
questionLog.setOrgId(student.getOrgId());
|
|
|
questionLog.setUserId(student.getUserId());
|
|
|
|
|
|
// 只添加可以更新的数据
|
|
|
updateList.add(questionLog);
|
|
|
});
|
|
|
// 学生做的题目的答案与日志关联
|
|
|
updateBatchTemplate(updateList);
|
|
|
|
|
|
// 最后提交试卷答案时候,才会记录错题
|
|
|
if (addType.equals(FINALLY_SUBMIT)) {
|
|
|
// 计算学生分数,并标记错题
|
|
|
calculateScoreOnSubmit(updateList);
|
|
|
updateBatchTemplate(updateList);
|
|
|
// 添加到题目日志汇总中
|
|
|
addQuestionLogSummary(student, questionSettingId, updateList);
|
|
|
// 批量插入错题集(错题库),方法内部自带分数判断。内部方法更新log中的错题标记。
|
|
|
teacherOpenCourseQuestionLogWrongService.insertBatchByQuestionLogList(updateList);
|
|
|
|
|
|
// 最终提交以后,将日志更改为2 删除状态,仅用于查看
|
|
|
deleteTeacherOpenCourseQuestionLog(listJoin(updateList, TeacherOpenCourseQuestionLog::getTeacherOpenCourseQuestionLogId));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 功能描述: <br>
|
|
|
* 计算学生分数
|
|
|
* 会判断题目是否打完,并抛出异常
|
|
|
*
|
|
|
* @param logList
|
|
|
* @Author: lx
|
|
|
* @Date: 2022/12/4 20:16
|
|
|
*/
|
|
|
public void calculateScoreOnSubmit(@NotEmpty(message = "学生题目日志不能为空!") List<TeacherOpenCourseQuestionLog> logList) {
|
|
|
logList.forEach(questionLog -> {
|
|
|
// 题目原本设置的分数
|
|
|
final BigDecimal questionScore = questionLog.getQuestionScore();
|
|
|
// 题目类型
|
|
|
final Integer questionType = questionLog.getQuestionType();
|
|
|
// 学生提交的结果
|
|
|
final String studentAnswer = defaultIfNull(questionLog.getTeacherOpenCourseQuestionLogAnswer(), "");
|
|
|
// 题目的答案
|
|
|
final String questionAnswer = defaultIfNull(questionLog.getQuestionAnswer(), "");
|
|
|
// 判断答案和学生提交的结果,都不能为空
|
|
|
final boolean allNotEmpty = isAllNotEmpty(questionAnswer, studentAnswer);
|
|
|
// 题目类型,答案属于一道题
|
|
|
final boolean oneQuestion = isOneQuestion(questionType);
|
|
|
|
|
|
// 一条日志记录,属于一道题
|
|
|
if (oneQuestion) {
|
|
|
final boolean isTrue = allNotEmpty && studentAnswer.equalsIgnoreCase(questionAnswer);
|
|
|
final BigDecimal mySimpleScore = isTrue ? questionScore: ZERO;
|
|
|
// 计算学生最后的得分
|
|
|
questionLog.setStudentScore(mySimpleScore);
|
|
|
|
|
|
if (isTrue) {
|
|
|
questionLog.setSuccessCount(1);
|
|
|
}
|
|
|
else {
|
|
|
questionLog.setErrorCount(1);
|
|
|
}
|
|
|
// 错题标记
|
|
|
questionLog.setIsErrorFavorite(!isTrue);
|
|
|
}
|
|
|
|
|
|
// 一条日志记录,属于多道题的逻辑处理
|
|
|
else {
|
|
|
// 分数,正确数量
|
|
|
BigDecimal score = ZERO; int successCount = 0;
|
|
|
// 学生提交的题目结果
|
|
|
final String[] studentAnswerArr = studentAnswer.split(",");
|
|
|
// 正确答案和学生答案对比
|
|
|
|
|
|
String[] successAnswerArr = questionAnswer.split(",");
|
|
|
final int studentAnswerLength = studentAnswerArr.length;
|
|
|
final int questionLength = successAnswerArr.length;
|
|
|
// 断言需要判断,题目答案的数量是否相等,不然会对不上答案,导致分数计算错误
|
|
|
// Assert.isTrue(studentAnswerLength == questionLength, "题干:" + questionLog.getQuestionStem()+ ",有选项未提交!");
|
|
|
|
|
|
// 一道题的分数
|
|
|
BigDecimal simpleQuestionScore = NumberUtil.mul(questionScore, questionLength);
|
|
|
// 循环比对这个题目,是否是复杂的题目:如选词填空或者分析题
|
|
|
for (int i = 0; i < questionLength; i++) {
|
|
|
String sqs = successAnswerArr[i];
|
|
|
boolean isTrue = allNotEmpty && sqs.equalsIgnoreCase(studentAnswerArr[i]);
|
|
|
BigDecimal oneScore = isTrue ? simpleQuestionScore : ZERO;
|
|
|
score = score.add(oneScore);
|
|
|
|
|
|
// 正确的数量计数
|
|
|
if (isTrue) {
|
|
|
successCount++;
|
|
|
}
|
|
|
}
|
|
|
// 全对的题目,才算做正确答案
|
|
|
final boolean isAllTrue = successCount == questionLength;
|
|
|
// 计算学生最后的得分
|
|
|
questionLog.setStudentScore(score);
|
|
|
questionLog.setSuccessCount(successCount);
|
|
|
questionLog.setErrorCount(abs(questionLength - successCount));
|
|
|
// 错题标记
|
|
|
questionLog.setIsErrorFavorite(!isAllTrue);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 根据题目实时计算,计算分数。并在做题统计表表中添加一条记录
|
|
|
*
|
|
|
* @param student
|
|
|
* @param questionSettingId
|
|
|
* @param updateList
|
|
|
*/
|
|
|
private void addQuestionLogSummary(Student student, Long questionSettingId, List<TeacherOpenCourseQuestionLog> updateList) {
|
|
|
if (student == null || questionSettingId == null) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
List<TeacherOpenCourseQuestionLog> logList = getValuesBySettingIds(questionSettingId.toString());
|
|
|
|
|
|
setErrorSuccessCountField(updateList, logList);
|
|
|
|
|
|
TeacherOpenCourseQuestionSetting questionSetting = teacherOpenCourseQuestionSettingService.getInfo(questionSettingId);
|
|
|
|
|
|
if (CollectionUtil.isNotEmpty(logList) && questionSetting != null) {
|
|
|
String logIds = logList.stream().map(TeacherOpenCourseQuestionLog::getTeacherOpenCourseQuestionLogId).map(Objects::toString).collect(joining(","));
|
|
|
addQuestionLogSummary(logIds, logList, student, questionSetting.getTeacherOpenCourseQuestionSettingName(), questionSetting.getTeacherOpenCourseQuestionSettingType());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 拷贝正确错误的字段
|
|
|
*
|
|
|
* @param updateList
|
|
|
* @param logList
|
|
|
*/
|
|
|
private void setErrorSuccessCountField(List<TeacherOpenCourseQuestionLog> updateList, List<TeacherOpenCourseQuestionLog> logList) {
|
|
|
Map<Long, List<TeacherOpenCourseQuestionLog>> listMap = updateList.stream()
|
|
|
.collect(groupingBy(TeacherOpenCourseQuestionLog::getTeacherOpenCourseQuestionLogId));
|
|
|
MultiValueMapAdapter<Long, TeacherOpenCourseQuestionLog> mapAdapter = new MultiValueMapAdapter<>(listMap);
|
|
|
|
|
|
logList.forEach(item -> {
|
|
|
TeacherOpenCourseQuestionLog first = mapAdapter.getFirst(item.getTeacherOpenCourseQuestionLogId());
|
|
|
if (first != null) {
|
|
|
item.setErrorCount(first.getErrorCount());
|
|
|
item.setSuccessCount(first.getSuccessCount());
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能描述: <br>
|
|
|
* 题目日志信息,分析汇总到某张表里
|
|
|
*
|
|
|
* @param logsIds 题目日志IDs保存
|
|
|
* @param logList 某个questionSettingId对应的题目日志列表
|
|
|
* @param student 学生
|
|
|
* @param questionSettingName 题目配置的名称(试卷名称)
|
|
|
* @param snapshotFromTypeEnum 题目来源
|
|
|
* @Author: lx
|
|
|
* @Date: 2022/12/4 20:16
|
|
|
*/
|
|
|
public void addQuestionLogSummary(
|
|
|
String logsIds, @NotEmpty(message = "学生题目日志不能为空!") List<TeacherOpenCourseQuestionLog> logList,
|
|
|
@NotNull Student student, @NotBlank String questionSettingName, @NotNull ResourcesQuestionSnapshotFromTypeEnum snapshotFromTypeEnum) {
|
|
|
// 构建实体
|
|
|
final QuestionLogSummary questionLogSummary = new QuestionLogSummary();
|
|
|
final Long questionSettingId = logList.get(0).getTeacherOpenCourseQuestionSettingId();
|
|
|
|
|
|
// 这里附件作业的数量,需要单独查询
|
|
|
final Integer questionTotalCount = snapshotFromTypeEnum.equals(HOMEWORK_FILE)
|
|
|
? teacherOpenCourseQuestionSettingDao.getHomeWorkFileTotalCountBySettingId(questionSettingId)
|
|
|
: resourcesQuestionSnapshotDao.getQuestionTotalCountBySettingId(questionSettingId);
|
|
|
|
|
|
// 做对数,做错数,总题数
|
|
|
Integer sumSuccessCount = 0, sumErrorCount = 0, sumDoCount = 0;
|
|
|
// 试卷总分数, 我的分数, 题目日志ID集
|
|
|
BigDecimal questionTotalScore = ZERO, myTotalScore = ZERO; final Set<String> idsSet = new HashSet<>();
|
|
|
|
|
|
// 获取最大的完成时间
|
|
|
TeacherOpenCourseQuestionLog maxFinish = logList.stream()
|
|
|
.max((o1, o2) -> toIntExact(defaultIfNull(o1.getTeacherOpenCourseQuestionLogFinishTime(), 0L) - defaultIfNull(o2.getTeacherOpenCourseQuestionLogFinishTime(), 0L)))
|
|
|
.get();
|
|
|
|
|
|
for (TeacherOpenCourseQuestionLog questionLog : logList) {
|
|
|
Integer successCount = questionLog.getSuccessCount();
|
|
|
Integer errorCount = questionLog.getErrorCount();
|
|
|
Assert.isTrue(ObjectUtil.isAllNotEmpty(successCount, errorCount), "题目正确数量和错误数量,计算出了点小问题!");
|
|
|
|
|
|
sumSuccessCount += successCount;
|
|
|
sumErrorCount += errorCount;
|
|
|
sumDoCount += ObjectUtil.isNotEmpty(questionLog.getTeacherOpenCourseQuestionLogAnswer()) ? 1 : 0;
|
|
|
questionTotalScore = questionTotalScore.add(defaultIfNull(questionLog.getQuestionScore(), questionLog.getStudentScore()));
|
|
|
myTotalScore = myTotalScore.add(defaultIfNull(questionLog.getStudentScore(), ZERO));
|
|
|
idsSet.add(questionLog.getTeacherOpenCourseQuestionLogId().toString());
|
|
|
}
|
|
|
// 及格分值比率
|
|
|
BigDecimal passRateSetting = new BigDecimal("0.6");
|
|
|
// 查询数据字典配置项
|
|
|
String passRateVal = coreDictService.getOrDefault("pass_rate", "0.6");
|
|
|
if (NumberUtil.isNumber(passRateVal)) {
|
|
|
passRateSetting = new BigDecimal(passRateVal);
|
|
|
}
|
|
|
boolean isPass = NumberUtil.isGreaterOrEqual(myTotalScore, NumberUtil.mul(questionTotalScore, passRateSetting));
|
|
|
questionLogSummary.setQuestionSettingId(questionSettingId);
|
|
|
questionLogSummary.setQuestionSettingName(questionSettingName);
|
|
|
questionLogSummary.setQuestionSettingType(snapshotFromTypeEnum);
|
|
|
questionLogSummary.setQuestionLogSummaryFromType(FROM_OPEN_COURSE);
|
|
|
questionLogSummary.setQuestionSettingTotalScore(questionTotalScore);
|
|
|
questionLogSummary.setQuestionLogSummaryStudentTotalScore(myTotalScore);
|
|
|
questionLogSummary.setPersonId(student.getStudentId());
|
|
|
questionLogSummary.setQuestionLogIds(logsIds);
|
|
|
questionLogSummary.setQuestionLogSummaryQuestionTotalCount(questionTotalCount);
|
|
|
questionLogSummary.setQuestionLogSummaryStudentDoCount(sumDoCount);
|
|
|
questionLogSummary.setQuestionLogSummarySuccessCount(sumSuccessCount);
|
|
|
questionLogSummary.setQuestionLogSummaryErrorCount(sumErrorCount);
|
|
|
BigDecimal successRate = NumberUtil.div(sumSuccessCount, questionTotalCount, 3).multiply(new BigDecimal(100));
|
|
|
questionLogSummary.setQuestionLogSummarySuccessRate(successRate);
|
|
|
questionLogSummary.setQuestionLogSummaryCurrentPassRate(passRateSetting);
|
|
|
questionLogSummary.setQuestionLogSummaryIsPass(BooleanUtil.toInteger(isPass));
|
|
|
questionLogSummary.setQuestionLogSummaryStatus(1);
|
|
|
questionLogSummary.setQuestionLogSummaryAddTime(new Date());
|
|
|
questionLogSummary.setOrgId(student.getOrgId());
|
|
|
questionLogSummary.setUserId(student.getUserId());
|
|
|
if (null != maxFinish) {
|
|
|
Long finishTime = maxFinish.getTeacherOpenCourseQuestionLogFinishTime();
|
|
|
questionLogSummary.setFinishTime(DateUtil.secondToTime(finishTime.intValue()));
|
|
|
questionLogSummary.setFinishSecondTime(finishTime);
|
|
|
}
|
|
|
|
|
|
QuestionLogSummaryQuery summaryQuery = new QuestionLogSummaryQuery();
|
|
|
summaryQuery.setQuestionLogSummaryStatus(1);
|
|
|
summaryQuery.setPersonId(student.getStudentId());
|
|
|
summaryQuery.setQuestionSettingId(questionSettingId);
|
|
|
QuestionLogSummary logSummary = questionLogSummaryService.getInfo(summaryQuery);
|
|
|
// 删除之前的题目统计
|
|
|
if (logSummary != null) {
|
|
|
// questionLogSummary.setQuestionLogSummaryId(logSummary.getQuestionLogSummaryId());
|
|
|
// 这个学生之前的其他试卷都设置成删除状态。
|
|
|
QuestionLogSummary updatePO = new QuestionLogSummary();
|
|
|
updatePO.setQuestionLogSummaryId(logSummary.getQuestionLogSummaryId());
|
|
|
updatePO.setQuestionLogSummaryStatus(GlobalStatusEnum.DELETED.getCode());
|
|
|
questionLogSummaryService.updateTemplate(updatePO);
|
|
|
}
|
|
|
|
|
|
// 更新或插入统计数据
|
|
|
// questionLogSummaryService.upsertByTemplate(questionLogSummary);
|
|
|
|
|
|
// 插入统计数据
|
|
|
questionLogSummaryService.insert(questionLogSummary);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 验证前端传递过来的添加时间是否是最新的
|
|
|
* @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);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 日志关联学生信息和配置ID信息
|
|
|
*
|
|
|
* @param studentId 学生ID
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
* @param judgeAddType 判断是否继续做题的,题目提交类型
|
|
|
* @param initAddType 预先提交到初始化提交类型
|
|
|
* @param resourcesQuestionSnapshots 题目快照结合
|
|
|
* @param isReSend 是否根据题目快照强制重新发题
|
|
|
*/
|
|
|
public void preSubmitStudentQuestionLog(@NotNull(message = "学生ID不能为空!") final Long studentId,
|
|
|
@NotNull(message = "题目配置ID不能为空!") final Long questionSettingId,
|
|
|
@NotNull(message = "判断是否继续做题的,题目提交类型不能为空!") final QuestionLogAddTypeEnum judgeAddType,
|
|
|
@Nullable final QuestionLogAddTypeEnum initAddType,
|
|
|
@NotEmpty(message = "题目快照列表不能为空!") List<ResourcesQuestionSnapshot> resourcesQuestionSnapshots,
|
|
|
final boolean isReSend
|
|
|
) {
|
|
|
|
|
|
if (CollectionUtil.isEmpty(resourcesQuestionSnapshots)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 不是强制发题,则不覆盖现有的题目日志,则继续做题
|
|
|
// 验证题目日志,是否已经存在试卷
|
|
|
if(!isReSend) {
|
|
|
|
|
|
long logCount = teacherOpenCourseQuestionLogDao.getNotSubmitQuestionLogsCount(studentId, questionSettingId);
|
|
|
|
|
|
// 如果题目日志里存在预先布置的题目,则直接返回
|
|
|
if (logCount > 0) {
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// teacherOpenCourseQuestionSettingId:类似试卷ID,题目日志里会出现重复的试卷ID
|
|
|
// 逻辑删除之前的题目日志,防止学生做题统计数据异常
|
|
|
// 保证试卷是最新的
|
|
|
// 考试逻辑删除记录,作业或者章节练习都是直接删除记录。
|
|
|
// 强制发题
|
|
|
if (isReSend) {
|
|
|
logicDeleteBySettingIds(questionSettingId.toString());
|
|
|
}
|
|
|
|
|
|
List<TeacherOpenCourseQuestionLog> teacherOpenCourseQuestionLogs = BeanUtil.copyToList(resourcesQuestionSnapshots, TeacherOpenCourseQuestionLog.class);
|
|
|
|
|
|
// 设计个单表,后面进行修改操作
|
|
|
teacherOpenCourseQuestionLogs.forEach(questionLog -> {
|
|
|
// 题目的答案,如果最后一位是逗号,则删除掉
|
|
|
String questionAnswer = ReUtil.replaceAll(questionLog.getQuestionAnswer(), "\\,$", "");
|
|
|
questionLog.setTeacherOpenCourseQuestionSettingId(questionSettingId);
|
|
|
questionLog.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
questionLog.setQuestionLogAddType(initAddType);
|
|
|
questionLog.setStudentId(studentId);
|
|
|
questionLog.setTeacherOpenCourseQuestionLogAddTime(new Date());
|
|
|
questionLog.setQuestionAnswer(questionAnswer);
|
|
|
});
|
|
|
|
|
|
insertBatch(teacherOpenCourseQuestionLogs);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据题目配置ID查询题目快照,并根据类型分组
|
|
|
*
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
* @return
|
|
|
*/
|
|
|
public Map<Integer, List<TeacherOpenCourseQuestionLog>> questionAnalysis(@NotNull(message = "题目配置ID不能为空!") Long questionSettingId, @NotNull(message = "学生ID不能为空!") Long studentId) {
|
|
|
TeacherOpenCourseQuestionLogQuery query = new TeacherOpenCourseQuestionLogQuery();
|
|
|
query.setTeacherOpenCourseQuestionSettingId(questionSettingId);
|
|
|
query.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
query.setStudentId(studentId);
|
|
|
|
|
|
List<TeacherOpenCourseQuestionLog> valuesByQuery = getValuesByQueryNotWithPermission(query);
|
|
|
return questionAnalysis(valuesByQuery);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 从题目日志中,获取题目快照信息,Bean Copy方式
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
* @param studentId 学生ID
|
|
|
* @return
|
|
|
*/
|
|
|
public List<ResourcesQuestionSnapshot> questionAnalysisBySettingId2(Long questionSettingId, Long studentId) {
|
|
|
Map<Integer, List<TeacherOpenCourseQuestionLog>> tempMap = questionAnalysis(questionSettingId, studentId);
|
|
|
List<TeacherOpenCourseQuestionLog> collect = tempMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
|
|
|
List<ResourcesQuestionSnapshot> result = new ArrayList<>();
|
|
|
|
|
|
// 循环遍历某些特殊的属性,单独映射
|
|
|
Optional.ofNullable(collect).ifPresent(item -> {
|
|
|
for (TeacherOpenCourseQuestionLog questionLog : item) {
|
|
|
ResourcesQuestionSnapshot resourcesQuestionSnapshot = BeanUtil.copyProperties(questionLog, ResourcesQuestionSnapshot.class);
|
|
|
// 学生提交过的答案
|
|
|
resourcesQuestionSnapshot.set("teacherOpenCourseQuestionLogAnswer", questionLog.getTeacherOpenCourseQuestionLogAnswer());
|
|
|
result.add(resourcesQuestionSnapshot);
|
|
|
}
|
|
|
});
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据题目快照ID查询题目快照,并根据类型分组
|
|
|
*
|
|
|
* @param questionSnapshotIds
|
|
|
* @return
|
|
|
*/
|
|
|
public Map<Integer, List<TeacherOpenCourseQuestionLog>> questionAnalysis(@NotNull(message = "题目配置ID不能为空!") TreeSet<Long> questionSnapshotIds, @NotNull(message = "学生ID不能为空!") Long studentId) {
|
|
|
TeacherOpenCourseQuestionLogQuery query = new TeacherOpenCourseQuestionLogQuery();
|
|
|
query.setResourcesQuestionSnapshotIdPlural(join(questionSnapshotIds.toArray(), ","));
|
|
|
query.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
query.setStudentId(studentId);
|
|
|
List<TeacherOpenCourseQuestionLog> valuesByQuery = getValuesByQueryNotWithPermission(query);
|
|
|
return questionAnalysis(valuesByQuery);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 开课相关-考试或者作业,答题提交后,获取问题解析。根据配置获取
|
|
|
* @param questionLogList
|
|
|
* @return
|
|
|
*/
|
|
|
public Map<Integer, List<TeacherOpenCourseQuestionLog>> questionAnalysis(@NotEmpty(message = "未查询到题目列表!") final List<TeacherOpenCourseQuestionLog> questionLogList) {
|
|
|
|
|
|
// 如果未查询到题目日志,返回空Map
|
|
|
if (ObjectUtil.isEmpty(questionLogList)) {
|
|
|
return MapUtil.empty();
|
|
|
}
|
|
|
|
|
|
TeacherOpenCourseQuestionSettingQuery settingQuery = new TeacherOpenCourseQuestionSettingQuery();
|
|
|
settingQuery.setTeacherOpenCourseQuestionSettingId(questionLogList.get(0).getTeacherOpenCourseQuestionSettingId());
|
|
|
final TeacherOpenCourseQuestionSetting hwSetting = teacherOpenCourseQuestionSettingService.getInfo(settingQuery);
|
|
|
|
|
|
// 答卷后显示答案解析
|
|
|
final Boolean isEndShowQa = BooleanUtil.toBoolean(String.valueOf(hwSetting.getTeacherOpenCourseQuestionSettingEndShowQa()));
|
|
|
// 答卷后显示答案对错
|
|
|
final Boolean isEndShowTrueFalse = BooleanUtil.toBoolean(String.valueOf(hwSetting.getTeacherOpenCourseQuestionSettingEndShowTrueFalse()));
|
|
|
|
|
|
return questionLogList.stream().map(item -> {
|
|
|
item.setQuestionAnswer(isEndShowQa ? item.getQuestionAnswer() : "");
|
|
|
item.setQuestionAnalysis(isEndShowTrueFalse ? item.getQuestionAnswer(): "");
|
|
|
return item;
|
|
|
}).collect(groupingBy(TeacherOpenCourseQuestionLog::getQuestionType));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查询学生的分数列表信息(分页)
|
|
|
* 不带权限
|
|
|
*
|
|
|
* @param query
|
|
|
*/
|
|
|
public PageQuery<TeacherOpenCourseQuestionLog> studentScoreList(PageQuery query) {
|
|
|
PageQuery ret = teacherOpenCourseQuestionLogDao.studentScoreList(query);
|
|
|
queryListAfter(ret.getList());
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void resetOperationByTeacherOpenCourseId(Long teacherOpenCourseId) {
|
|
|
log.info("可能需要实现重置操作!");
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void deleteTeacherOpenCourseAllRelatedByTeacherOpenCourseId(@NotNull(message = "开课ID不能为空!") Long teacherOpenCourseId) {
|
|
|
log.info("需要实现删除操作!");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 批量收藏题目
|
|
|
* @param teacherOpenCourseQuestionLogIds
|
|
|
* @param isTuck
|
|
|
*/
|
|
|
public void tuckByLogIds(@NotEmpty(message = "题目日志ID不能为空!") String teacherOpenCourseQuestionLogIds, final boolean isTuck) {
|
|
|
|
|
|
Function<String, TeacherOpenCourseQuestionLog> stringQuestionLogFunction = logId -> {
|
|
|
TeacherOpenCourseQuestionLog questionLog = new TeacherOpenCourseQuestionLog();
|
|
|
questionLog.setTeacherOpenCourseQuestionLogId(Long.valueOf(logId));
|
|
|
questionLog.setIsTuck(isTuck);
|
|
|
return questionLog;
|
|
|
};
|
|
|
List<TeacherOpenCourseQuestionLog> logSet = Arrays.asList(teacherOpenCourseQuestionLogIds.split(",")).stream()
|
|
|
.map(stringQuestionLogFunction).collect(Collectors.toList());
|
|
|
|
|
|
updateBatchTemplate(logSet);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 收藏题目,根据题目配置ID + 题目快照ID
|
|
|
*
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
* @param questionSnapIds 题目快照IDs
|
|
|
* @param isTuck 是否收藏 true收藏 false取消收藏
|
|
|
* @param student 学生信息
|
|
|
*/
|
|
|
public void tuck(@NotNull(message = "题目配置ID不能为空!") Long questionSettingId,
|
|
|
@NotBlank(message = "题目快照IDs不能为空!") String questionSnapIds, boolean isTuck,
|
|
|
@NotNull(message = "学生信息不能为空!") Student student) {
|
|
|
|
|
|
List<TeacherOpenCourseQuestionLog> list = getListBySettingIdAndSnapIds(questionSettingId, questionSnapIds, student);
|
|
|
if (ObjectUtil.isEmpty(list)) {
|
|
|
throw new PlatformException("收藏失败,未查询到要收藏的题目");
|
|
|
}
|
|
|
|
|
|
String logIds = list.stream()
|
|
|
.map(o -> o.getTeacherOpenCourseQuestionLogId().toString()).collect(joining(","));
|
|
|
|
|
|
// 根据做题的日志ID,收藏题目
|
|
|
tuckByLogIds(logIds, isTuck);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 收藏题目,根据题目配置ID + 题目快照ID
|
|
|
*
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
* @param questionSnapIds 题目快照IDs
|
|
|
* @param student 学生信息
|
|
|
*/
|
|
|
private List<TeacherOpenCourseQuestionLog> getListBySettingIdAndSnapIds(
|
|
|
@NotNull(message = "题目配置ID不能为空!") Long questionSettingId,
|
|
|
@NotBlank(message = "题目快照IDs不能为空!") String questionSnapIds, @NotNull Student student) {
|
|
|
TeacherOpenCourseQuestionLogQuery logQuery = new TeacherOpenCourseQuestionLogQuery();
|
|
|
logQuery.setTeacherOpenCourseQuestionSettingId(questionSettingId);
|
|
|
logQuery.setResourcesQuestionSnapshotIdPlural(questionSnapIds);
|
|
|
logQuery.setTeacherOpenCourseQuestionLogStatus(1);
|
|
|
logQuery.setStudentId(student.getStudentId());
|
|
|
return getValuesByQueryNotWithPermission(logQuery);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 批量收藏到错题库
|
|
|
* @param teacherOpenCourseQuestionLogIds
|
|
|
* @param isErrorFavorite
|
|
|
*/
|
|
|
public void errorFavorite(@NotEmpty(message = "题目日志ID不能为空!") String teacherOpenCourseQuestionLogIds, final boolean isErrorFavorite) {
|
|
|
|
|
|
Function<String, TeacherOpenCourseQuestionLog> stringTeacherOpenCourseQuestionLogFunction = logId -> {
|
|
|
TeacherOpenCourseQuestionLog questionLog = new TeacherOpenCourseQuestionLog();
|
|
|
questionLog.setTeacherOpenCourseQuestionLogId(Long.valueOf(logId));
|
|
|
questionLog.setIsErrorFavorite(isErrorFavorite);
|
|
|
return questionLog;
|
|
|
};
|
|
|
List<TeacherOpenCourseQuestionLog> logSet = Arrays.asList(teacherOpenCourseQuestionLogIds.split(",")).stream()
|
|
|
.map(stringTeacherOpenCourseQuestionLogFunction).collect(Collectors.toList());
|
|
|
|
|
|
updateBatchTemplate(logSet);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查询学生详细得分信息
|
|
|
* 查询分数详细信息
|
|
|
*
|
|
|
* @param query
|
|
|
* @return
|
|
|
*/
|
|
|
public PageQuery<TeacherOpenCourseQuestionLogScoreDetailsInfo> getQuestionLogScoreDetailsInfo(PageQuery query) {
|
|
|
return teacherOpenCourseQuestionLogDao.getQuestionLogScoreDetailsInfo(query);
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 验证是否需要重新发题,则返回重新发题的标记
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
* @param studentId 学生ID
|
|
|
* @param questionSettingType 题目配置类型
|
|
|
* @param questionLogAddType 题目日志添加类型(用于判断是否交卷)
|
|
|
* @return boolean
|
|
|
*/
|
|
|
public boolean verifyLogAddTypeIsReSend(Long questionSettingId,
|
|
|
Long studentId, ResourcesQuestionSnapshotFromTypeEnum questionSettingType,
|
|
|
QuestionLogAddTypeEnum questionLogAddType) {
|
|
|
// 只有章节练习,才能进行重发试题的操作
|
|
|
if (!questionSettingType.equals(CHAPTER_EXERCISE)) {
|
|
|
return false;
|
|
|
}
|
|
|
if (ObjectUtil.isAllNotEmpty(questionSettingId, studentId, questionSettingType, questionLogAddType)) {
|
|
|
return teacherOpenCourseQuestionLogDao.verifyLogAddTypeIsReSend(questionSettingId,studentId, questionSettingType, questionLogAddType);
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 功能描述: <br>
|
|
|
* 获取实体的答案和解析,并锁定题目日志的状态,无法导致不能去修改选项
|
|
|
*
|
|
|
* @param questionSettingId 题目配置ID
|
|
|
* @param questionSnapIds 题目快照IDs
|
|
|
* @param student 学生信息
|
|
|
* @return {@link List< TeacherOpenCourseQuestionLogAnswerLockVO >}
|
|
|
* @Author: lx
|
|
|
* @Date: 2022/12/1 0:55
|
|
|
*/
|
|
|
public List<TeacherOpenCourseQuestionLogAnswerLockVO> questionLogAnswerLock(
|
|
|
@NotNull(message = "题目配置ID不能为空!") Long questionSettingId,
|
|
|
@NotBlank(message = "题目快照IDs不能为空!") String questionSnapIds, @NotNull Student student) {
|
|
|
final List<TeacherOpenCourseQuestionLog> list = getListBySettingIdAndSnapIds(questionSettingId, questionSnapIds, student);
|
|
|
Assert.notEmpty(list, "未查询到试卷题目信息!");
|
|
|
|
|
|
// 只取非完成状态的数据
|
|
|
Predicate<TeacherOpenCourseQuestionLog> notFinishPredicate = questionLog -> !questionLog.getQuestionLogAddType().equals(FINALLY_SUBMIT);
|
|
|
// 构建数据
|
|
|
List<TeacherOpenCourseQuestionLog> questionLogs = list.stream().filter(notFinishPredicate).map(questionLog -> {
|
|
|
TeacherOpenCourseQuestionLog generalQuestionLog = new TeacherOpenCourseQuestionLog();
|
|
|
generalQuestionLog.setTeacherOpenCourseQuestionLogId(questionLog.getTeacherOpenCourseQuestionLogId());
|
|
|
generalQuestionLog.setQuestionLogAddType(LOCK); // 锁定状态
|
|
|
return generalQuestionLog;
|
|
|
}).collect(Collectors.toList());
|
|
|
|
|
|
// 批量修改锁定的状态
|
|
|
updateBatchTemplate(questionLogs);
|
|
|
|
|
|
// 只取部分属性给到前端
|
|
|
return BeanUtil.copyToList(list, TeacherOpenCourseQuestionLogAnswerLockVO.class);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查询附件作业-日志
|
|
|
* @param dto
|
|
|
* @return
|
|
|
*/
|
|
|
public List<TeacherOpenCourseHomeWorkLogExportVO> getHomeWorkLogExportList(TeacherOpenCourseHomeWorkLogDTO dto) {
|
|
|
return teacherOpenCourseQuestionLogDao.getHomeWorkLogExportList(dto);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查询附件作业-日志(分页)
|
|
|
* @param pageQuery
|
|
|
* @return
|
|
|
*/
|
|
|
public PageQuery<TeacherOpenCourseHomeWorkLogExportVO> getHomeWorkLogExportPageList(PageQuery pageQuery) {
|
|
|
return teacherOpenCourseQuestionLogDao.getHomeWorkLogExportPageList(pageQuery);
|
|
|
}
|
|
|
|
|
|
public FileQuestionLogListVO getFileQuestionLog(TeacherOpenCourseQuestionLogQuery query) {
|
|
|
TeacherOpenCourseQuestionLog log = getInfo(query);
|
|
|
if (null == log) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
Student student = studentDao.getById(log.getStudentId());
|
|
|
if (null != student) {
|
|
|
log.setStudentIdText(student.getStudentName());
|
|
|
log.setStudentSn(student.getStudentSn());
|
|
|
}
|
|
|
|
|
|
return BeanUtil.copyProperties(log, FileQuestionLogListVO.class);
|
|
|
}
|
|
|
}
|