Browse Source

定时任务,处理异常考试数据

master
luogw 6 days ago
parent
commit
f640a14424
  1. 133
      src/main/java/com/project/exam/domain/job/examHandleJob.java

133
src/main/java/com/project/exam/domain/job/examHandleJob.java

@ -0,0 +1,133 @@
package com.project.exam.domain.job;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.project.exam.domain.entity.ExamRecordEntity;
import com.project.exam.mapper.ExamRecordMapper;
import com.project.task.domain.entity.TaskEntity;
import com.project.task.domain.entity.TaskUserEntity;
import com.project.task.mapper.TaskMapper;
import com.project.task.mapper.TaskUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* 考试异常处理定时任务
* 处理考试任务截止或考试时间结束时未提交的考试记录
*/
@Service
@Slf4j
public class examHandleJob {
@Autowired
private ExamRecordMapper examRecordMapper;
@Autowired
private TaskMapper taskMapper;
@Autowired
private TaskUserMapper taskUserMapper;
/**
* 考试超时缓冲时间只处理超过这个时间的超时考试
*/
private static final long EXAM_TIMEOUT_BUFFER_SECONDS = 10;
/**
* 每小时执行一次处理考试时间结束但未提交的考试记录
*/
@Scheduled(cron = "0 0 * * * ?")
public void handleUnsubmittedExams() {
log.info(">>> [考试异常处理任务] 启动执行...");
try {
Date now = new Date();
// 查询所有未提交的考试记录
LambdaQueryWrapper<ExamRecordEntity> examWrapper = new LambdaQueryWrapper<>();
examWrapper.isNull(ExamRecordEntity::getSubmitTime);
List<ExamRecordEntity> unsubmittedRecords = examRecordMapper.selectList(examWrapper);
if (unsubmittedRecords.isEmpty()) {
log.info(">>> [考试异常处理任务] 没有未提交的考试记录");
return;
}
log.info(">>> [考试异常处理任务] 发现 {} 条未提交的考试记录,开始检查考试时间", unsubmittedRecords.size());
// 处理每条未提交的考试记录
for (ExamRecordEntity examRecord : unsubmittedRecords) {
handleUnsubmittedExamRecord(examRecord, now);
}
log.info(">>> [考试异常处理任务] 执行完毕");
} catch (Exception e) {
log.error(">>> [考试异常处理任务] 执行异常", e);
}
}
/**
* 处理单条未提交的考试记录
* 如果考试时间已结束超过缓冲时间则设置为未通过提交时间为考试结束时间
*/
private void handleUnsubmittedExamRecord(ExamRecordEntity examRecord, Date now) {
try {
// 获取关联的 TaskUserEntity,从中获取考试开始时间
TaskUserEntity taskUser = taskUserMapper.selectById(examRecord.getTaskUserId());
if (taskUser == null) {
log.warn(">>> [考试异常处理任务] 找不到 TaskUserEntity,id: {}", examRecord.getTaskUserId());
return;
}
// 获取考试任务的时长配置
TaskEntity task = taskMapper.selectById(taskUser.getTaskId());
if (task == null) {
log.warn(">>> [考试异常处理任务] 找不到 TaskEntity,id: {}", taskUser.getTaskId());
return;
}
// 计算考试结束时间 = 开始时间 + 考试时长(分钟)
Date examEndTime = new Date(examRecord.getStartTime().getTime() + (long) task.getDuration() * 60 * 1000);
// 计算考试结束时间 + 缓冲时间
Date examTimeoutThreshold = new Date(examEndTime.getTime() + EXAM_TIMEOUT_BUFFER_SECONDS * 1000);
// 判断考试是否已经超时(超过缓冲时间)
if (examTimeoutThreshold.after(now)) {
log.debug(">>> [考试异常处理任务] 考试记录 {} 的考试时间未超过缓冲时间,跳过。考试结束时间: {},缓冲阈值: {},当前时间: {}",
examRecord.getId(), examEndTime, examTimeoutThreshold, now);
return;
}
log.info(">>> [考试异常处理任务] 考试记录 {} 已超时 {} 秒以上,将其设置为未通过。考试开始: {},时长: {} 分钟,结束: {},当前: {}",
examRecord.getId(), EXAM_TIMEOUT_BUFFER_SECONDS, examRecord.getStartTime(), task.getDuration(), examEndTime, now);
// 更新未提交的考试记录 - 使用条件更新防止并发问题
// 只更新提交时间仍为null的记录,防止用户刚提交时的竞态条件
LambdaUpdateWrapper<ExamRecordEntity> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(ExamRecordEntity::getId, examRecord.getId())
.isNull(ExamRecordEntity::getSubmitTime) // 再次检查提交时间是否为null
.set(ExamRecordEntity::getPass, false) // 设置为未通过
.set(ExamRecordEntity::getSubmitTime, examEndTime); // 提交时间设置为考试结束时间
int updateCount = examRecordMapper.update(null, updateWrapper);
if (updateCount > 0) {
log.info(">>> [考试异常处理任务] 考试记录 {} 更新成功", examRecord.getId());
} else {
log.warn(">>> [考试异常处理任务] 考试记录 {} 更新失败,可能已被其他操作修改(如用户已提交)", examRecord.getId());
}
} catch (Exception e) {
log.error(">>> [考试异常处理任务] 处理考试记录 {} 异常", examRecord.getId(), e);
}
}
}
Loading…
Cancel
Save