You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tianze-pro/web/src/main/java/com/ibeetl/jlw/service/GeneralQuestionLogService.java

1361 lines
67 KiB
Java

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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.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.entity.CoreUser;
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.util.TimeTool;
import com.ibeetl.admin.core.web.JsonResult;
import com.ibeetl.admin.core.web.JsonReturnCode;
import com.ibeetl.jlw.dao.GeneralQuestionLogDao;
import com.ibeetl.jlw.dao.GeneralResourcesQuestionSnapshotDao;
import com.ibeetl.jlw.dao.StudentDao;
import com.ibeetl.jlw.entity.*;
import com.ibeetl.jlw.entity.dto.QuestionLogAddDTO;
import com.ibeetl.jlw.entity.vo.QuestionLogAnswerLockVO;
import com.ibeetl.jlw.enums.GlobalStatusEnum;
import com.ibeetl.jlw.enums.QuestionBusinessTypeEnum;
import com.ibeetl.jlw.enums.QuestionLogAddTypeEnum;
import com.ibeetl.jlw.enums.ResourcesQuestionSnapshotFromTypeEnum;
import com.ibeetl.jlw.web.query.GeneralQuestionLogQuery;
import com.ibeetl.jlw.web.query.GeneralQuestionSettingQuery;
import com.ibeetl.jlw.web.query.QuestionLogSummaryQuery;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
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.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.*;
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.ExcelUtil.getCellFormatValue;
import static com.ibeetl.admin.core.util.StreamUtils.listJoin;
import static com.ibeetl.jlw.enums.QuestionLogAddTypeEnum.*;
import static com.ibeetl.jlw.enums.ResourcesQuestionSnapshotFromTypeEnum.CHAPTER_EXERCISE;
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
public class GeneralQuestionLogService extends CoreBaseService<GeneralQuestionLog>{
@Autowired private GeneralQuestionLogDao generalQuestionLogDao;
@Autowired private GeneralResourcesQuestionSnapshotDao generalResourcesQuestionSnapshotDao;
@Autowired private StudentDao studentDao;
@Autowired private QuestionLogSummaryService questionLogSummaryService;
@Autowired private CoreDictService coreDictService;
@Autowired @Lazy
private GeneralQuestionSettingService generalQuestionSettingService;
public PageQuery<GeneralQuestionLog>queryByCondition(PageQuery query){
PageQuery ret = generalQuestionLogDao.queryByCondition(query);
queryListAfter(ret.getList());
return ret;
}
public PageQuery<GeneralQuestionLog>queryByConditionQuery(PageQuery query){
PageQuery ret = generalQuestionLogDao.queryByConditionQuery(query);
queryListAfter(ret.getList());
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)){
generalQuestionLogDao.deleteByIds(ids);
}
}
public void deleteGeneralQuestionLog(String ids){
try {
generalQuestionLogDao.deleteGeneralQuestionLogByIds(ids);
} catch (Exception e) {
throw new PlatformException("批量删除通用做题日志失败", e);
}
}
public void logicDeleteBySettingIds(String settingIds){
try {
generalQuestionLogDao.logicDeleteBySettingIds(settingIds);
} catch (Exception e) {
throw new PlatformException("批量删除通用做题日志失败", e);
}
}
public void deleteBySettingIds(String settingIds){
try {
generalQuestionLogDao.deleteBySettingIds(settingIds);
} catch (Exception e) {
throw new PlatformException("批量删除通用做题日志失败", e);
}
}
public String addAll(GeneralQuestionLogQuery generalQuestionLogQuery){
String msg = "";
List<GeneralQuestionLog> generalQuestionLogList = new ArrayList<>();
try {
generalQuestionLogList = JSON.parseArray(generalQuestionLogQuery.getGeneralQuestionLogJsonStr(), GeneralQuestionLog.class);
} catch (Exception e) {
try {
generalQuestionLogList.add(JSONObject.parseObject(generalQuestionLogQuery.getGeneralQuestionLogJsonStr(), GeneralQuestionLog.class));
} catch (Exception e1) {}
}
ToolUtils.deleteNullList(generalQuestionLogList);
if(null != generalQuestionLogList && generalQuestionLogList.size()>0){
for(int i=0;i<generalQuestionLogList.size();i++){
GeneralQuestionLog generalQuestionLog = generalQuestionLogList.get(i);
generalQuestionLog.setUserId(generalQuestionLogQuery.getUserId());
generalQuestionLog.setOrgId(generalQuestionLogQuery.getOrgId());
}
insertBatch(generalQuestionLogList);
}
return msg;
}
public JsonResult add(GeneralQuestionLogQuery generalQuestionLogQuery){
String msg = "";
GeneralQuestionLog generalQuestionLog = generalQuestionLogQuery.pojo();
generalQuestionLogDao.insert(generalQuestionLog);
generalQuestionLogQuery.setGeneralQuestionLogId(generalQuestionLog.getGeneralQuestionLogId());
JsonResult jsonResult = new JsonResult();
jsonResult.setData(generalQuestionLog.getGeneralQuestionLogId());//自增的ID丢进去
jsonResult.setCode(JsonReturnCode.SUCCESS.getCode());
jsonResult.setMsg(msg);
return jsonResult;
}
public String edit(GeneralQuestionLogQuery generalQuestionLogQuery){
String msg = "";
GeneralQuestionLog generalQuestionLog = generalQuestionLogQuery.pojo();
generalQuestionLogDao.updateTemplateById(generalQuestionLog);
return msg;
}
public String updateGivenByIds(GeneralQuestionLogQuery generalQuestionLogQuery){
String msg = "";
if(StringUtils.isNotBlank(generalQuestionLogQuery.get_given())){
boolean flag = generalQuestionLogDao.updateGivenByIds(generalQuestionLogQuery) > 0;
if(!flag){
msg = "更新指定参数失败";
}
}else{
msg = "指定参数为空";
}
return msg;
}
public List<GeneralQuestionLog> getValues (Object paras){
return sqlManager.select(SqlId.of("jlw.generalQuestionLog.getGeneralQuestionLogValues"), GeneralQuestionLog.class, paras);
}
public List<GeneralQuestionLog> getValuesByQuery (GeneralQuestionLogQuery generalQuestionLogQuery){
return generalQuestionLogDao.getValuesByQuery(generalQuestionLogQuery);
}
public List<GeneralQuestionLog> getValuesByQueryNotWithPermission (GeneralQuestionLogQuery generalQuestionLogQuery){
return generalQuestionLogDao.getValuesByQueryNotWithPermission(generalQuestionLogQuery);
}
public GeneralQuestionLog getInfo (Long generalQuestionLogId){
GeneralQuestionLogQuery generalQuestionLogQuery = new GeneralQuestionLogQuery();
generalQuestionLogQuery.setGeneralQuestionLogId(generalQuestionLogId);
generalQuestionLogQuery.setGeneralQuestionLogStatusPlural("1,2");//需要根据实际情况来
List<GeneralQuestionLog> list = generalQuestionLogDao.getValuesByQuery(generalQuestionLogQuery);
if(null != list && list.size()>0){
return list.get(0);
}else{
return null;
}
}
public GeneralQuestionLog getInfo (GeneralQuestionLogQuery generalQuestionLogQuery){
List<GeneralQuestionLog> list = generalQuestionLogDao.getValuesByQuery(generalQuestionLogQuery);
if(null != list && list.size()>0){
return list.get(0);
}else{
return null;
}
}
public JsonResult importTemplate(List<FileEntity> fileEntityList,List<Long>list,CoreUser coreUser){
List<String[]>errMsg = new ArrayList<>();
String msg ="";
int count = 0;
Date date = new Date();
for(int item=0;null != fileEntityList && item<fileEntityList.size();item++){
FileEntity fileEntity = fileEntityList.get(item);
if(null != fileEntity){
File file = new File(fileEntity.getAbsoluteUrl());
if(file.exists() && file.isFile() && file.canRead() && ToolUtils.findInSet("xls,xlsx",fileEntity.getFormat())){
Workbook wb = null;
InputStream is = null;
try {
is = new FileInputStream(fileEntity.getAbsoluteUrl());
if("xls".equals(fileEntity.getFormat())){
wb = new HSSFWorkbook(is);
}else if("xlsx".equals(fileEntity.getFormat())){
wb = new XSSFWorkbook(is);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != is){
is.close();
}
}catch (Exception e){
e.printStackTrace();
}
if(wb != null){
//获取Sheet1
Sheet sheet = wb.getSheet("Sheet1");
//获取最大行数
int rowNum = sheet.getPhysicalNumberOfRows();
//获取第一行
Row firstRow = sheet.getRow(0);
//获取最大列数
int colNum = firstRow.getPhysicalNumberOfCells();
String columns[] = {
"通用题目配置",
"题目快照",
"学生提交的答案",
"附件,学生端上传附件",
"创建时间",
"状态",
"学生",
"学生得分",
"题型",
"分值",
"题干",
"选项A",
"选项B",
"选项C",
"选项D",
"选项E",
"答案",
"解析",
"是否收藏夹",
"是否错题库",
};
Map<String,Integer> map = new HashMap<>();//获取需要的表头的列
//从第一列找到需要的表头
for (int i=0; i<colNum; i++){
String cellData = getCellFormatValue(firstRow.getCell(i));
for(int j=0;j<columns.length;j++){
if(columns[j].equals(cellData)){
map.put(columns[j],i);
}
}
}
//验证所需要的表头是否全
Integer flag = 0;
for(int i=0;i<columns.length;i++){
if(null != map.get(columns[i])){
flag ++;
}
}
if(flag != columns.length){
String str = " ";
for(int i=0;i<columns.length;i++){
str += "\""+columns[i]+"\""+(i == columns.length-1?"":", ");
}
return JsonResult.failMessage("导入失败,表格表头应包含"+str);
}
for (int i = 1; i<rowNum; i++) {
Row row = sheet.getRow(i);
if(null == row){
errMsg.add(new String[]{"第"+(i+1)+"数据为空"});
continue;
}
String generalQuestionSettingId = getCellFormatValue(row.getCell(map.get(columns[0])));
String generalResourcesQuestionSnapshotId = getCellFormatValue(row.getCell(map.get(columns[1])));
String generalQuestionLogAnswer = getCellFormatValue(row.getCell(map.get(columns[2])));
String generalQuestionLogUploadFile = getCellFormatValue(row.getCell(map.get(columns[3])));
String generalQuestionLogAddTime = getCellFormatValue(row.getCell(map.get(columns[4])));
String generalQuestionLogStatus = getCellFormatValue(row.getCell(map.get(columns[5])));
String studentId = getCellFormatValue(row.getCell(map.get(columns[6])));
String studentScore = getCellFormatValue(row.getCell(map.get(columns[7])));
String questionType = getCellFormatValue(row.getCell(map.get(columns[8])));
String questionScore = getCellFormatValue(row.getCell(map.get(columns[9])));
String questionStem = getCellFormatValue(row.getCell(map.get(columns[10])));
String questionOptionA = getCellFormatValue(row.getCell(map.get(columns[11])));
String questionOptionB = getCellFormatValue(row.getCell(map.get(columns[12])));
String questionOptionC = getCellFormatValue(row.getCell(map.get(columns[13])));
String questionOptionD = getCellFormatValue(row.getCell(map.get(columns[14])));
String questionOptionE = getCellFormatValue(row.getCell(map.get(columns[15])));
String questionAnswer = getCellFormatValue(row.getCell(map.get(columns[16])));
String questionAnalysis = getCellFormatValue(row.getCell(map.get(columns[17])));
String isTuck = getCellFormatValue(row.getCell(map.get(columns[18])));
String isErrorFavorite = getCellFormatValue(row.getCell(map.get(columns[19])));
//TODO 判断(如重复等复杂判断要额外写)
if(StringUtils.isBlank(generalQuestionSettingId)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[0])+1)+"列,第"+(i+1)+"行通用题目配置为空"});
continue;
}else
if(StringUtils.isBlank(generalResourcesQuestionSnapshotId)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[1])+1)+"列,第"+(i+1)+"行题目快照为空"});
continue;
}else
if(StringUtils.isBlank(generalQuestionLogAnswer)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[2])+1)+"列,第"+(i+1)+"行学生提交的答案为空"});
continue;
}else
if(StringUtils.isBlank(generalQuestionLogUploadFile)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[3])+1)+"列,第"+(i+1)+"行附件,学生端上传附件为空"});
continue;
}else
if(StringUtils.isBlank(generalQuestionLogAddTime)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[4])+1)+"列,第"+(i+1)+"行创建时间为空"});
continue;
}else
if(StringUtils.isBlank(generalQuestionLogStatus)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[5])+1)+"列,第"+(i+1)+"行状态为空"});
continue;
}else
if(StringUtils.isBlank(studentId)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[6])+1)+"列,第"+(i+1)+"行学生为空"});
continue;
}else
if(StringUtils.isBlank(studentScore)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[7])+1)+"列,第"+(i+1)+"行学生得分为空"});
continue;
}else
if(StringUtils.isBlank(questionType)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[8])+1)+"列,第"+(i+1)+"行题型为空"});
continue;
}else
if(StringUtils.isBlank(questionScore)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[9])+1)+"列,第"+(i+1)+"行分值为空"});
continue;
}else
if(StringUtils.isBlank(questionStem)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[10])+1)+"列,第"+(i+1)+"行题干为空"});
continue;
}else
if(StringUtils.isBlank(questionOptionA)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[11])+1)+"列,第"+(i+1)+"行选项A为空"});
continue;
}else
if(StringUtils.isBlank(questionOptionB)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[12])+1)+"列,第"+(i+1)+"行选项B为空"});
continue;
}else
if(StringUtils.isBlank(questionOptionC)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[13])+1)+"列,第"+(i+1)+"行选项C为空"});
continue;
}else
if(StringUtils.isBlank(questionOptionD)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[14])+1)+"列,第"+(i+1)+"行选项D为空"});
continue;
}else
if(StringUtils.isBlank(questionOptionE)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[15])+1)+"列,第"+(i+1)+"行选项E为空"});
continue;
}else
if(StringUtils.isBlank(questionAnswer)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[16])+1)+"列,第"+(i+1)+"行答案为空"});
continue;
}else
if(StringUtils.isBlank(questionAnalysis)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[17])+1)+"列,第"+(i+1)+"行解析为空"});
continue;
}else
if(StringUtils.isBlank(isTuck)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[18])+1)+"列,第"+(i+1)+"行是否收藏夹为空"});
continue;
}else
if(StringUtils.isBlank(isErrorFavorite)){
errMsg.add(new String[]{"第"+ToolUtils.numberToLetter(map.get(columns[19])+1)+"列,第"+(i+1)+"行是否错题库为空"});
continue;
}else
{
//TODO 保存
GeneralQuestionLog generalQuestionLog = new GeneralQuestionLog();
generalQuestionLog.setGeneralQuestionSettingId(Long.parseLong(generalQuestionSettingId));
generalQuestionLog.setGeneralResourcesQuestionSnapshotId(Long.parseLong(generalResourcesQuestionSnapshotId));
generalQuestionLog.setGeneralQuestionLogAnswer(generalQuestionLogAnswer);
generalQuestionLog.setGeneralQuestionLogUploadFile(generalQuestionLogUploadFile);
generalQuestionLog.setGeneralQuestionLogAddTime(TimeTool.getTime(generalQuestionLogAddTime));
generalQuestionLog.setGeneralQuestionLogStatus(Integer.parseInt(generalQuestionLogStatus));
generalQuestionLog.setStudentId(Long.parseLong(studentId));
generalQuestionLog.setStudentScore(new BigDecimal(studentScore));
generalQuestionLog.setQuestionType(Integer.parseInt(questionType));
generalQuestionLog.setQuestionScore(new BigDecimal(questionScore));
generalQuestionLog.setQuestionStem(questionStem);
generalQuestionLog.setQuestionOptionA(questionOptionA);
generalQuestionLog.setQuestionOptionB(questionOptionB);
generalQuestionLog.setQuestionOptionC(questionOptionC);
generalQuestionLog.setQuestionOptionD(questionOptionD);
generalQuestionLog.setQuestionOptionE(questionOptionE);
generalQuestionLog.setQuestionAnswer(questionAnswer);
generalQuestionLog.setQuestionAnalysis(questionAnalysis);
generalQuestionLog.setIsTuck(Boolean.valueOf(isTuck));
generalQuestionLog.setIsErrorFavorite(Boolean.valueOf(isErrorFavorite));
generalQuestionLog.setOrgId(coreUser.getOrgId());
generalQuestionLog.setUserId(coreUser.getId());
count++;
}
}
}
}
}
}
JsonResult jsonResult = new JsonResult();
jsonResult.setCode(count>0?JsonReturnCode.SUCCESS.getCode():JsonReturnCode.FAIL.getCode());
jsonResult.setData(errMsg);
jsonResult.setMsg((count>0?"导入成功,共导入"+count+"条":"导入失败")+(StringUtils.isNotBlank(msg)?"<br>"+msg:""));
return jsonResult;
}
public List<Map<String,Object>> getExcelValues (GeneralQuestionLogQuery generalQuestionLogQuery){
return generalQuestionLogDao.getExcelValues(generalQuestionLogQuery);
}
/**
* 教师端-手动批改一些题目的分数
* 教师端-给学生打分
*
* @param generalQuestionLogIds 指定更新分数的题目日志IDs
* @param score 分数
* @param reply 评语
*/
public void manualModifyQuestionScores(@NotNull(message = "题目做题日志ID不能为空") final String generalQuestionLogIds,
@NotNull(message = "设置的分数不能为空!") final BigDecimal score, @Nullable final String reply) {
GeneralQuestionLogQuery generalQuestionLogQuery = new GeneralQuestionLogQuery();
generalQuestionLogQuery.setGeneralQuestionLogIdPlural(generalQuestionLogIds);
generalQuestionLogQuery.setGeneralQuestionLogStatus(1);
List<GeneralQuestionLog> updateList = generalQuestionLogDao.getValuesByQueryNotWithPermission(generalQuestionLogQuery);
final Date now = new Date();
// 批量更新分数。支持重复设置分数和评语
updateList.forEach(questionLog -> {
BigDecimal questionScore = questionLog.getQuestionScore();
questionLog.setStudentScore(score);
questionLog.setGeneralQuestionLogReply(reply);
// 这里不是记录用时,只是记录教师批阅的时间
questionLog.setGeneralQuestionLogUpdateTime(now);
// 是否错题库,也可以用作标记答案是对的错的
questionLog.setIsErrorFavorite(NumberUtil.equals(questionScore, score));
});
updateBatchTemplate(updateList);
}
/**
* 和题目有关的做题提交方法
* 直接计算出学生得分情况
*
* @param questionLogAddDTO 题目信息
*/
public void addQuestionLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO) {
Long questionSettingId = questionLogAddDTO.getQuestionSettingId();
Map<String, String> questionLogMap = questionLogAddDTO.getQuestionLogMap();
// 查询学生身份
final Student student = getStudent();
Assert.notNull(student, "非学生身份,无法提交!");
GeneralQuestionSetting questionSettingInfo = generalQuestionSettingService.getInfo(questionSettingId);
Assert.notNull(questionSettingInfo, "未匹配到试卷信息!");
// 校验当前时间是否在考试的时间段
generalQuestionSettingService.verifyQuestionStartAndEndTimeWithNowTimeThrow(questionSettingId);
switch(questionSettingInfo.getGeneralQuestionSettingType()) {
case HOMEWORK_FILE: {
addFileRelatedLog(join(questionLogMap.values().toArray(), ","), questionSettingId, student);
} break;
// 这些都和题目相关,暂时先放在一个方法
case CHAPTER_EXERCISE:
case EXAM:
case HOMEWORK_QUESTION: {
addQuestionRelatedLog(questionLogAddDTO, questionSettingInfo, student);
} break;
}
}
/**
* 和题目有关的做题提交方法
* 直接计算出学生得分情况
* 只支持练习
*
* @param questionLogAddDTO 题目信息
*/
public void addQuestionLogTest(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO) {
final Long questionSettingId = questionLogAddDTO.getQuestionSettingId();
// 查询学生身份
final Student student = getStudent();
Assert.notNull(student, "非学生身份,无法提交!");
GeneralQuestionSetting questionSetting = generalQuestionSettingService.getInfo(questionSettingId);
Assert.notNull(questionSetting, "未匹配到试卷信息!");
// 校验当前时间是否在考试的时间段
generalQuestionSettingService.verifyQuestionStartAndEndTimeWithNowTimeThrow(questionSettingId);
switch(questionSetting.getGeneralQuestionSettingType()) {
// 这些都和题目相关,暂时先放在一个方法
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();
GeneralQuestionLogQuery query = new GeneralQuestionLogQuery();
query.setGeneralQuestionLogUploadFile(absFilePath);
query.setGeneralQuestionSettingId(questionSettingId);
query.setGeneralQuestionLogAddTime(now);
query.setGeneralQuestionLogFinishTime(RandomUtil.randomLong(100, 3600));
query.setGeneralQuestionLogStatus(1);
// 附件作业,只有一题,所有默认是全部提交的方式
query.setQuestionLogAddType(FINALLY_SUBMIT);
query.setOrgId(student.getOrgId());
query.setUserId(student.getUserId());
query.setStudentId(student.getStudentId());
add(query);
}
/**
* 跟题目相关的题目日志提交方法
* 附件类型的需要单独,拆分除去
* 提交试卷 试卷提交
*
* @param questionLogAddDTO 题目信息
* @param questionSetting 题目配置信息
* @param student 学生信息
*/
private void addQuestionRelatedLog(@NotNull(message = "提交题目信息不能为空") QuestionLogAddDTO questionLogAddDTO, GeneralQuestionSetting questionSetting, 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(), ",");
// 做题思路先从通用GeneralQuestionSetting表中生成题目日志到GeneralQuestionLog中学生这个时候提交答案再修改日志中的结果。
// 查询条件
GeneralQuestionLogQuery questionLogQuery = GeneralQuestionLogQuery.builder()
.generalResourcesQuestionSnapshotIdPlural(questionSnapshotIds)
.generalQuestionLogStatus(1)
.studentId(student.getStudentId())
.generalQuestionSettingId(questionSettingId)
.build();
List<GeneralQuestionLog> logList = generalQuestionLogDao.getValuesByQuery(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.getGeneralQuestionLogId().toString()).collect(joining(","));
// 当前时间, 存储要更新的题目日志集合
Date now = new Date(); List<GeneralQuestionLog> updateList = new ArrayList<>();
// 代表填空题和分析题的类型
List<Integer> questionType = Arrays.asList(4, 5);
// 处理答案和分数
logList.forEach(questionLog -> {
// 验证最新的提交时间,防止网络延迟
if (!validateQuestionLogAddTimeLatest(questionSettingId, questionLog.getGeneralResourcesQuestionSnapshotId().toString(), studentId, addTime)) {
return;
}
// 验证提交类型
validateQuestionLogAddTypeThrow(questionLog.getQuestionLogAddType(), questionLogAddDTO.getQuestionLogAddType());
// 学生提交的答案
String answer = questionLogMap.get(questionLog.getGeneralQuestionLogId().toString());
// 默认0分
questionLog.setStudentScore(BigDecimal.valueOf(0));
// 学生提交的答案处理
String answersText = null;
if (questionType.contains(questionLog.getQuestionType())) {
answersText = answer;
// 学生答案和正确答案集合
cn.hutool.json.JSONObject 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 (studentAns.equalsIgnoreCase(questionAns.toString()) && StrUtil.isAllNotBlank(questionAns.toString(), 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.getGeneralQuestionLogAddTime(), now, SECOND);
// 填充属性
questionLog.setGeneralQuestionLogUpdateTime(now);
questionLog.setGeneralQuestionLogFinishTime(finishSecondTime);
questionLog.setGeneralQuestionLogAnswer(answersText);
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, questionSetting.getGeneralQuestionSettingId(), updateList);
// 最终提交以后将日志更改为2 删除状态,仅用于查看
deleteGeneralQuestionLog(listJoin(updateList, GeneralQuestionLog::getGeneralQuestionLogId));
}
}
/**
* 功能描述: <br>
* 计算学生分数
* 会判断题目是否打完,并抛出异常
*
* @param logList
* @Author: lx
* @Date: 2022/12/4 20:16
*/
public void calculateScoreOnSubmit(@NotEmpty(message = "学生题目日志不能为空!") List<GeneralQuestionLog> logList) {
logList.forEach(questionLog -> {
// 题目原本设置的分数
final BigDecimal questionScore = questionLog.getQuestionScore();
// 学生提交的结果
final String studentAnswer = defaultIfNull(questionLog.getGeneralQuestionLogAnswer(), "");
// 题目的答案
final String questionAnswer = defaultIfNull(questionLog.getQuestionAnswer(), "");
// 判断答案和学生提交的结果,都不能为空
final boolean allNotEmpty = isAllNotEmpty(questionAnswer, studentAnswer);
// 题目类型,答案属于一道题
final boolean oneQuestion = isOneQuestion(questionLog.getQuestionType());
// 一条日志记录,属于一道题
if (oneQuestion) {
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
*/
private void addQuestionLogSummary(Student student, Long questionSettingId, List<GeneralQuestionLog> updateList) {
if (student == null || questionSettingId == null) {
return;
}
List<GeneralQuestionLog> logList = getValuesBySettingIds(questionSettingId.toString());
GeneralQuestionSetting questionSetting = generalQuestionSettingService.getInfo(questionSettingId);
setErrorSuccessCountField(updateList, logList);
if (CollectionUtil.isNotEmpty(logList) && questionSetting != null) {
String logIds = logList.stream().map(GeneralQuestionLog::getGeneralQuestionLogId).map(Objects::toString).collect(joining(","));
addQuestionLogSummary(logIds, logList, student, questionSetting.getGeneralQuestionSettingName(), questionSetting.getBusinessType(), questionSetting.getGeneralQuestionSettingType());
}
}
/**
* 拷贝正确错误的字段
*
* @param updateList
* @param logList
*/
private void setErrorSuccessCountField(List<GeneralQuestionLog> updateList, List<GeneralQuestionLog> logList) {
Map<Long, List<GeneralQuestionLog>> listMap = updateList.stream()
.collect(groupingBy(GeneralQuestionLog::getGeneralQuestionLogId));
MultiValueMapAdapter<Long, GeneralQuestionLog> mapAdapter = new MultiValueMapAdapter<>(listMap);
logList.forEach(item -> {
GeneralQuestionLog first = mapAdapter.getFirst(item.getGeneralQuestionLogId());
if (first != null) {
item.setErrorCount(first.getErrorCount());
item.setSuccessCount(first.getSuccessCount());
}
});
}
private List<GeneralQuestionLog> getValuesBySettingIds(String settingIds) {
GeneralQuestionLogQuery logQuery = new GeneralQuestionLogQuery();
logQuery.setGeneralQuestionSettingIdPlural(settingIds);
logQuery.setGeneralQuestionLogStatus(1);
return generalQuestionLogDao.getValuesByQueryNotWithPermission(logQuery);
}
/**
* 功能描述: <br>
* 题目日志信息,分析汇总到某张表里
*
* @param logsIds 题目日志IDs保存
* @param logList 某个questionSettingId对应的题目日志列表
* @param student 学生
* @param questionSettingName 题目配置的名称(试卷名称)
* @param fromType 题目配置来源类型
* @param snapshotFromTypeEnum 题目来源
* @Author: lx
* @Date: 2022/12/4 20:16
*/
public void addQuestionLogSummary(
String logsIds, @NotEmpty(message = "学生题目日志不能为空!") List<GeneralQuestionLog> logList,
@NotNull Student student, @NotBlank String questionSettingName, @NotNull QuestionBusinessTypeEnum fromType,
@NotNull ResourcesQuestionSnapshotFromTypeEnum snapshotFromTypeEnum) {
// 构建实体
final QuestionLogSummary questionLogSummary = new QuestionLogSummary();
final Long questionSettingId = logList.get(0).getGeneralQuestionSettingId();
final Integer questionTotalCount = generalResourcesQuestionSnapshotDao.getQuestionTotalCountBySettingId(questionSettingId);
// 做对数,做错数,总题数
Integer sumSuccessCount = 0, sumErrorCount = 0, sumDoCount = 0;
// 试卷总分数, 我的分数, 题目日志ID集
BigDecimal questionTotalScore = ZERO, myTotalScore = ZERO; final Set<String> idsSet = new HashSet<>();
// 获取最大的完成时间
GeneralQuestionLog maxFinish = logList.stream()
.max((o1, o2) -> toIntExact(defaultIfNull(o1.getGeneralQuestionLogFinishTime(), 0L) - defaultIfNull(o2.getGeneralQuestionLogFinishTime(), 0L)))
.get();
for (GeneralQuestionLog questionLog : logList) {
Integer successCount = questionLog.getSuccessCount();
Integer errorCount = questionLog.getErrorCount();
Assert.isTrue(ObjectUtil.isAllNotEmpty(successCount, errorCount), "题目正确数量和错误数量,计算出了点小问题!");
sumSuccessCount += successCount;
sumErrorCount += errorCount;
sumDoCount += ObjectUtil.isNotEmpty(questionLog.getGeneralQuestionLogAnswer()) ? 1 : 0;
questionTotalScore = questionTotalScore.add(defaultIfNull(questionLog.getQuestionScore(), questionLog.getStudentScore()));
myTotalScore = myTotalScore.add(defaultIfNull(questionLog.getStudentScore(), ZERO));
idsSet.add(questionLog.getGeneralQuestionLogId().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(fromType);
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.getGeneralQuestionLogFinishTime();
questionLogSummary.setFinishTime(DateUtil.secondToTime(finishTime.intValue()));
questionLogSummary.setFinishSecondTime(finishTime);
}
QuestionLogSummaryQuery summaryQuery = new QuestionLogSummaryQuery();
summaryQuery.setQuestionLogSummaryStatus(1);
summaryQuery.setPersonId(student.getStudentId());
summaryQuery.setQuestionSettingId(questionSettingId);
summaryQuery.setQuestionLogSummaryFromType(fromType);
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 generalQuestionLogDao.validateQuestionLogAddTimeLatest(questionSettingId, questionSnapshotIds, studentId, addTime);
}
/**
* 日志关联学生信息和配置ID信息
*
* @param studentId 学生ID
* @param questionSettingId 题目配置ID
* @param preAddType 初始化的提交类型
* @param resourcesQuestionSnapshots 题目快照结合
* @param isReSend 是否根据题目快照强制重新发题
*/
public void preSubmitStudentQuestionLog(@NotNull(message = "学生ID不能为空") final Long studentId,
@NotNull(message = "题目配置ID不能为空") final Long questionSettingId,
@Nullable QuestionLogAddTypeEnum preAddType,
@NotEmpty(message = "题目快照列表不能为空!") List<GeneralResourcesQuestionSnapshot> resourcesQuestionSnapshots,
boolean isReSend
) {
if (CollectionUtil.isEmpty(resourcesQuestionSnapshots)) {
return;
}
// 不是强制发题,则不覆盖现有的题目日志,则继续做题
// 验证题目日志,是否已经存在试卷
if(!isReSend) {
long logCount = generalQuestionLogDao.getNotSubmitQuestionLogsCount(studentId, questionSettingId);
// 如果题目日志里存在预先布置的题目,则直接返回
if (logCount > 0) {
return;
}
}
// generalQuestionSettingId类似试卷ID题目日志里会出现重复的试卷ID
// 逻辑删除之前的题目日志,防止学生做题统计数据异常
// 保证试卷是最新的
// 考试逻辑删除记录,作业或者章节练习都是直接删除记录。
// 强制发题
if (isReSend) {
logicDeleteBySettingIds(questionSettingId.toString());
}
// 断言
Assert.notBlank(resourcesQuestionSnapshots.get(0).getQuestionAnswer(), "题目快照选项不能为空!");
List<GeneralQuestionLog> generalQuestionLogs = BeanUtil.copyToList(resourcesQuestionSnapshots, GeneralQuestionLog.class);
// 设计个单表,后面进行修改操作
generalQuestionLogs.forEach(questionLog -> {
// 题目的答案,如果最后一位是逗号,则删除掉
String questionAnswer = ReUtil.replaceAll(questionLog.getQuestionAnswer(), "\\,$", "");
questionLog.setGeneralQuestionSettingId(questionSettingId);
questionLog.setGeneralQuestionLogStatus(1);
questionLog.setQuestionLogAddType(preAddType);
questionLog.setStudentId(studentId);
questionLog.setGeneralQuestionLogAddTime(new Date());
questionLog.setQuestionAnswer(questionAnswer);
});
insertBatch(generalQuestionLogs);
}
/**
* 根据题目配置ID查询题目快照并根据类型分组
*
* @param questionSettingId
* @return
*/
public Map<Integer, List<GeneralQuestionLog>> questionAnalysis(@NotNull(message = "题目配置ID不能为空") Long questionSettingId, @NotNull(message = "学生ID不能为空") Long studentId) {
GeneralQuestionLogQuery query = new GeneralQuestionLogQuery();
query.setGeneralQuestionSettingId(questionSettingId);
query.setGeneralQuestionLogStatus(1);
query.setStudentId(studentId);
List<GeneralQuestionLog> valuesByQuery = getValuesByQueryNotWithPermission(query);
return questionAnalysis(valuesByQuery);
}
/**
* 根据题目快照ID查询题目快照并根据类型分组
*
* @param questionSnapshotIds
* @return
*/
public Map<Integer, List<GeneralQuestionLog>> questionAnalysis(@NotNull(message = "题目配置ID不能为空") TreeSet<Long> questionSnapshotIds, @NotNull(message = "学生ID不能为空") Long studentId) {
GeneralQuestionLogQuery query = new GeneralQuestionLogQuery();
query.setGeneralQuestionLogIdPlural(join(questionSnapshotIds.toArray(), ","));
query.setGeneralQuestionLogStatus(1);
query.setStudentId(studentId);
List<GeneralQuestionLog> valuesByQuery = getValuesByQueryNotWithPermission(query);
return questionAnalysis(valuesByQuery);
}
/**
* 题目相关-考试或者作业,答题提交后,获取问题解析。根据配置获取
* @param questionLogList
* @return
*/
public Map<Integer, List<GeneralQuestionLog>> questionAnalysis(@NotEmpty(message = "未查询到题目列表!") final List<GeneralQuestionLog> questionLogList) {
GeneralQuestionSettingQuery settingQuery = new GeneralQuestionSettingQuery();
settingQuery.setGeneralQuestionSettingId(questionLogList.get(0).getGeneralQuestionSettingId());
final GeneralQuestionSetting hwSetting = generalQuestionSettingService.getInfo(settingQuery);
// 答卷后显示答案解析
final Boolean isEndShowQa = BooleanUtil.toBoolean(String.valueOf(hwSetting.getGeneralQuestionSettingEndShowQa()));
// 答卷后显示答案对错
final Boolean isEndShowTrueFalse = BooleanUtil.toBoolean(String.valueOf(hwSetting.getGeneralQuestionSettingEndShowTrueFalse()));
return questionLogList.stream().map(item -> {
GeneralQuestionLog questionLog = new GeneralQuestionLog();
questionLog.setGeneralResourcesQuestionSnapshotId(item.getGeneralResourcesQuestionSnapshotId());
questionLog.setQuestionAnswer(isEndShowQa ? item.getQuestionAnswer() : "");
questionLog.setQuestionAnalysis(isEndShowTrueFalse ? item.getQuestionAnswer(): "");
return questionLog;
}).collect(groupingBy(GeneralQuestionLog::getQuestionType));
}
/**
* 查询学生的分数列表信息(分页)
* 不带权限
*
* @param query
*/
public PageQuery<GeneralQuestionLog> studentScoreList(PageQuery query) {
PageQuery ret = generalQuestionLogDao.studentScoreList(query);
queryListAfter(ret.getList());
return ret;
}
/**
* 收藏题目根据题目配置ID + 题目快照ID
*
* @param questionSettingId 题目配置ID
* @param questionSnapIds 题目快照IDs
* @param student 学生信息
*/
public List<GeneralQuestionLog> getListBySettingIdAndSnapIds(
@NotNull(message = "题目配置ID不能为空") Long questionSettingId,
@NotBlank(message = "题目快照IDs不能为空") String questionSnapIds, @NotNull Student student) {
GeneralQuestionLogQuery logQuery = new GeneralQuestionLogQuery();
logQuery.setGeneralQuestionSettingId(questionSettingId);
logQuery.setGeneralResourcesQuestionSnapshotIdPlural(questionSnapIds);
logQuery.setGeneralQuestionLogStatus(1);
logQuery.setStudentId(student.getStudentId());
return getValuesByQueryNotWithPermission(logQuery);
}
/**
* 收藏题目根据题目配置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<GeneralQuestionLog> list = getListBySettingIdAndSnapIds(questionSettingId, questionSnapIds, student);
Assert.notEmpty(list, "收藏失败,未查询到要收藏的题目");
String logIds = list.stream()
.map(o -> o.getGeneralQuestionLogId().toString()).collect(joining(","));
// 根据做题的日志ID收藏题目
tuckByLogIds(logIds, isTuck);
}
/**
* 批量收藏题目
* @param generalQuestionLogIds
* @param isTuck
*/
public void tuckByLogIds(@NotEmpty(message = "题目日志ID不能为空") String generalQuestionLogIds, final boolean isTuck) {
Function<String, GeneralQuestionLog> stringQuestionLogFunction = logId -> {
GeneralQuestionLog questionLog = new GeneralQuestionLog();
questionLog.setGeneralQuestionLogId(Long.valueOf(logId));
questionLog.setIsTuck(isTuck);
return questionLog;
};
List<GeneralQuestionLog> logSet = Arrays.asList(generalQuestionLogIds.split(",")).stream()
.map(stringQuestionLogFunction).collect(Collectors.toList());
updateBatchTemplate(logSet);
}
/**
* 批量收藏到错题库
* @param generalQuestionLogIds
* @param isErrorFavorite
*/
public void errorFavorite(@NotEmpty(message = "题目日志ID不能为空") String generalQuestionLogIds, final boolean isErrorFavorite) {
Function<String, GeneralQuestionLog> stringgeneralQuestionLogFunction = logId -> {
GeneralQuestionLog questionLog = new GeneralQuestionLog();
questionLog.setGeneralQuestionLogId(Long.valueOf(logId));
questionLog.setIsErrorFavorite(isErrorFavorite);
return questionLog;
};
List<GeneralQuestionLog> logSet = Arrays.asList(generalQuestionLogIds.split(",")).stream()
.map(stringgeneralQuestionLogFunction).collect(Collectors.toList());
updateBatchTemplate(logSet);
}
/**
* 查询分数详细信息(系统,非教师开课)
* @param query 题目分数查询实体
* @return
*/
public PageQuery<GeneralQuestionLogScoreDetailsInfo> getQuestionLogScoreDetailsInfo(PageQuery query) {
return generalQuestionLogDao.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 generalQuestionLogDao.verifyLogAddTypeIsReSend(questionSettingId, studentId, questionSettingType, questionLogAddType);
}
return false;
}
/**
* 导出
* @param request
* @param response
* @param generalQuestionLogQuery
* @param coreUser
*/
public void exportErrorFavorite(HttpServletRequest request, HttpServletResponse response, GeneralQuestionLogQuery generalQuestionLogQuery, CoreUser coreUser) {
if(null == coreUser){
return;
}
HSSFWorkbook workbook = null;
try {
//表头数据
String[] header = {
"通用题目配置", "题目快照", "学生提交的答案", "附件,学生端上传附件", "创建时间", "状态", "学生",
"学生得分", "题型", "分值", "题干", "选项A", "选项B", "选项C", "选项D", "选项E", "答案", "解析",
"是否收藏夹", "是否错题库",
};
String[] headerCode = {
"generalQuestionSettingId", "generalResourcesQuestionSnapshotId", "generalQuestionLogAnswer",
"generalQuestionLogUploadFile", "generalQuestionLogAddTime", "generalQuestionLogStatus",
"studentId", "studentScore", "questionType", "questionScore", "questionStem", "questionOptionA",
"questionOptionB", "questionOptionC", "questionOptionD", "questionOptionE", "questionAnswer",
"questionAnalysis", "isTuck", "isErrorFavorite",
};
//数据内容
List<Map<String, Object>> mapList = getExcelValues(generalQuestionLogQuery);
//内容宽度
Map<String, Object> widthMap = mapList.get(0);
mapList.remove(0);
//声明一个工作簿
workbook = new HSSFWorkbook();
//生成一个表格,设置表格名称为"Sheet1"
HSSFSheet sheet = workbook.createSheet("Sheet1");
//冻结表头
sheet.createFreezePane(0, 1, 0, 1);
//设置默认列宽度为5个字节
sheet.setDefaultColumnWidth(5);
//创建第一行表头
HSSFRow headRow = sheet.createRow(0);
//头部样式
HSSFCellStyle headerStyle = workbook.createCellStyle();
//垂直居中
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//水平居中
headerStyle.setAlignment(HorizontalAlignment.CENTER);
//单元格样式
HSSFCellStyle cellStyle = workbook.createCellStyle();
//垂直居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//水平居左
cellStyle.setAlignment(HorizontalAlignment.CENTER);
//自动换行
cellStyle.setWrapText(true);
//遍历添加表头
for (int i = 0; i < header.length; i++) {
//设置表格特定的列宽度
if (null != widthMap.get(headerCode[i])) {
String width = widthMap.get(headerCode[i]).toString().split("\\.")[0];
Integer w = Integer.parseInt(width) > header[i].length()*3?Integer.parseInt(width):header[i].length()*3;
sheet.setColumnWidth(i, w * 190);
}
//创建一个单元格
HSSFCell cell = headRow.createCell(i);
//创建一个内容对象
HSSFRichTextString text = new HSSFRichTextString(header[i]);
//将内容对象的文字内容写入到单元格中
cell.setCellValue(text);
//设置样式
cell.setCellStyle(headerStyle);
}
//遍历结果集,把内容加入表格
for (int i = 0; i < mapList.size(); i++) {
HSSFRow row = sheet.createRow(i + 1);
row.setHeight((short) (50*10));
Map<String, Object> map = mapList.get(i);
for (int j = 0; j < headerCode.length; j++) {
HSSFCell cell = row.createCell(j);
cell.setCellStyle(cellStyle);
HSSFRichTextString text = new HSSFRichTextString(null != map.get(headerCode[j]) ? map.get(headerCode[j]).toString() : " ");
cell.setCellValue(text);
}
}
//准备将Excel的输出流通过response输出到页面下载
//八进制输出流
response.setContentType("application/octet-stream");
//这后面可以设置导出Excel的名称此例中名为student.xls
String fileName = ToolUtils.web2fileName(request,"generalQuestionLog(" + TimeTool.getNowTime("YMD") + ").xls");
response.setHeader("Content-disposition", "attachment;filename="+fileName);
//刷新缓冲
response.flushBuffer();
//workbook将Excel写入到response的输出流中供页面下载
workbook.write(response.getOutputStream());
}catch (Exception e){
e.printStackTrace();
} finally {
try {
if (null != workbook) {
workbook.close();
}
if (null != response && null != response.getOutputStream()) {
response.getOutputStream().close();
}
} catch (Exception e) { }
}
}
/**
* 功能描述: <br>
* 获取实体的答案和解析,并锁定题目日志的状态,无法导致不能去修改选项
*
* @param questionSettingId 题目配置ID
* @param questionSnapIds 题目快照IDs
* @param student 学生信息
* @return {@link List< QuestionLogAnswerLockVO>}
* @Author: lx
* @Date: 2022/12/1 0:55
*/
public List<QuestionLogAnswerLockVO> questionLogAnswerLock(
@NotNull(message = "题目配置ID不能为空") Long questionSettingId,
@NotBlank(message = "题目快照IDs不能为空") String questionSnapIds, @NotNull Student student) {
final List<GeneralQuestionLog> list = getListBySettingIdAndSnapIds(questionSettingId, questionSnapIds, student);
Assert.notEmpty(list, "未查询到试卷题目信息!");
// 只取非完成状态的数据
Predicate<GeneralQuestionLog> notFinishPredicate = questionLog -> !questionLog.getQuestionLogAddType().equals(FINALLY_SUBMIT);
// 构建数据
List<GeneralQuestionLog> questionLogs = list.stream().filter(notFinishPredicate).map(questionLog -> {
GeneralQuestionLog generalQuestionLog = new GeneralQuestionLog();
generalQuestionLog.setGeneralQuestionLogId(questionLog.getGeneralQuestionLogId());
generalQuestionLog.setQuestionLogAddType(LOCK); // 锁定状态
return generalQuestionLog;
}).collect(Collectors.toList());
// 批量修改锁定的状态
updateBatchTemplate(questionLogs);
// 只取部分属性给到前端
return BeanUtil.copyToList(list, QuestionLogAnswerLockVO.class);
}
}