|
@@ -12,7 +12,7 @@ import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
-import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
+import java.util.concurrent.locks.Lock;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
@@ -30,12 +30,12 @@ import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.qmth.boot.core.collection.PageResult;
|
|
import com.qmth.boot.core.collection.PageResult;
|
|
|
|
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
|
|
import com.qmth.boot.core.exception.StatusException;
|
|
import com.qmth.boot.core.exception.StatusException;
|
|
import com.qmth.boot.tools.excel.ExcelReader;
|
|
import com.qmth.boot.tools.excel.ExcelReader;
|
|
import com.qmth.boot.tools.excel.enums.ExcelType;
|
|
import com.qmth.boot.tools.excel.enums.ExcelType;
|
|
import com.qmth.boot.tools.excel.model.DataMap;
|
|
import com.qmth.boot.tools.excel.model.DataMap;
|
|
import com.qmth.boot.tools.io.ZipWriter;
|
|
import com.qmth.boot.tools.io.ZipWriter;
|
|
-import com.qmth.boot.tools.uuid.FastUUID;
|
|
|
|
import com.qmth.exam.reserve.bean.login.LoginUser;
|
|
import com.qmth.exam.reserve.bean.login.LoginUser;
|
|
import com.qmth.exam.reserve.bean.stdapply.AgentAndTimeVO;
|
|
import com.qmth.exam.reserve.bean.stdapply.AgentAndTimeVO;
|
|
import com.qmth.exam.reserve.bean.stdapply.MaterialTitleInfo;
|
|
import com.qmth.exam.reserve.bean.stdapply.MaterialTitleInfo;
|
|
@@ -105,6 +105,9 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
|
|
@Autowired
|
|
@Autowired
|
|
private MaterialGenerateService materialService;
|
|
private MaterialGenerateService materialService;
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
+ private ConcurrentService concurrentService;
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
public PageResult<StudentApplyVO> page(StudentApplyReq req) {
|
|
public PageResult<StudentApplyVO> page(StudentApplyReq req) {
|
|
IPage<StudentApplyVO> iPage = this.baseMapper
|
|
IPage<StudentApplyVO> iPage = this.baseMapper
|
|
@@ -454,16 +457,19 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
|
|
@Override
|
|
@Override
|
|
public void autoAssign(Long taskId) {
|
|
public void autoAssign(Long taskId) {
|
|
checkAfterOpenTime();
|
|
checkAfterOpenTime();
|
|
|
|
+ Lock lock = concurrentService.getLock(CacheConstants.LOCK_AUTO_APPLY);
|
|
try {
|
|
try {
|
|
- boolean isSuccess = redisClient.tryLock(CacheConstants.LOCK_AUTO_APPLY, FastUUID.get(), TIMEOUT,
|
|
|
|
- TimeUnit.MINUTES);
|
|
|
|
- if (!isSuccess) {
|
|
|
|
|
|
+ if (!lock.tryLock()) {
|
|
|
|
+ log.warn("获取锁失败,不允许同时执行自动分配!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
|
|
throw new StatusException("其他老师正在执行自动分配,请不要重复执行!");
|
|
throw new StatusException("其他老师正在执行自动分配,请不要重复执行!");
|
|
}
|
|
}
|
|
// 1、未完成预约的考生
|
|
// 1、未完成预约的考生
|
|
- LambdaQueryWrapper<StudentEntity> lm = new LambdaQueryWrapper<>();
|
|
|
|
- lm.eq(StudentEntity::getApplyFinished, Boolean.FALSE);
|
|
|
|
- List<StudentEntity> studentList = studentService.list(lm);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * LambdaQueryWrapper<StudentEntity> lm = new LambdaQueryWrapper<>();
|
|
|
|
+ * lm.eq(StudentEntity::getApplyFinished, Boolean.FALSE); List<StudentEntity>
|
|
|
|
+ * studentList = studentService.list(lm);
|
|
|
|
+ */
|
|
|
|
+ List<StudentEntity> studentList = studentService.listNoFinishStudent(taskId, Boolean.FALSE);
|
|
Map<Long, List<StudentEntity>> map = studentList.stream()
|
|
Map<Long, List<StudentEntity>> map = studentList.stream()
|
|
.collect(Collectors.groupingBy(StudentEntity::getCategoryId));
|
|
.collect(Collectors.groupingBy(StudentEntity::getCategoryId));
|
|
// 2、考位是否充足
|
|
// 2、考位是否充足
|
|
@@ -490,7 +496,7 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
|
|
log.error(e.getMessage());
|
|
log.error(e.getMessage());
|
|
throw new StatusException(e.getMessage());
|
|
throw new StatusException(e.getMessage());
|
|
} finally {
|
|
} finally {
|
|
- redisClient.delete(CacheConstants.LOCK_AUTO_APPLY);
|
|
|
|
|
|
+ lock.unlock();
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
@@ -500,6 +506,8 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
|
|
int num = 0;
|
|
int num = 0;
|
|
for (Iterator<StudentEntity> iterator = teachingStudentList.iterator(); iterator.hasNext();) {
|
|
for (Iterator<StudentEntity> iterator = teachingStudentList.iterator(); iterator.hasNext();) {
|
|
StudentEntity student = iterator.next();
|
|
StudentEntity student = iterator.next();
|
|
|
|
+ String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, student.getId());
|
|
|
|
+ Lock studentApplyLock = concurrentService.getLock(studentApplyLockKey);
|
|
if (num >= remainNum)
|
|
if (num >= remainNum)
|
|
break;
|
|
break;
|
|
List<StudentApplyEntity> studentApplyList = listStudentApply(student.getId(), Boolean.FALSE);
|
|
List<StudentApplyEntity> studentApplyList = listStudentApply(student.getId(), Boolean.FALSE);
|
|
@@ -510,7 +518,11 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
|
|
studentApply.setExamSiteId(siteId);
|
|
studentApply.setExamSiteId(siteId);
|
|
studentApply.setCancel(Boolean.FALSE);
|
|
studentApply.setCancel(Boolean.FALSE);
|
|
studentApply.setTimePeriodId(timeId);
|
|
studentApply.setTimePeriodId(timeId);
|
|
- baseMapper.insert(studentApply);
|
|
|
|
|
|
+ if (studentApplyLock.tryLock()) {
|
|
|
|
+ baseMapper.insert(studentApply);
|
|
|
|
+ } else {
|
|
|
|
+ log.warn("获取锁失败,不允许同一个考生同时操作预约!lockKey:{}", studentApplyLockKey);
|
|
|
|
+ }
|
|
num++;
|
|
num++;
|
|
if (toApplyNum - (studentApplyList.size() + 1) == 0) {
|
|
if (toApplyNum - (studentApplyList.size() + 1) == 0) {
|
|
iterator.remove();
|
|
iterator.remove();
|
|
@@ -616,42 +628,50 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
|
|
log.info("没有开启预约任务");
|
|
log.info("没有开启预约任务");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- boolean isSuccess = redisClient.tryLock(
|
|
|
|
- CacheConstants.LOCK_ARRANGE_EXAM + DateUtil.formatShortDateString(new Date()), FastUUID.get(), TIMEOUT,
|
|
|
|
- TimeUnit.MINUTES);
|
|
|
|
|
|
+ // boolean isSuccess = redisClient.tryLock(
|
|
|
|
+ // CacheConstants.LOCK_ARRANGE_EXAM + DateUtil.formatShortDateString(new
|
|
|
|
+ // Date()), FastUUID.get(), TIMEOUT,
|
|
|
|
+ // TimeUnit.MINUTES);
|
|
|
|
+ String autoLayoutLockKey = String.format(CacheConstants.LOCK_ARRANGE_EXAM,
|
|
|
|
+ DateUtil.formatShortDateString(new Date()));
|
|
|
|
+ Lock autoLayoutLock = concurrentService.getLock(autoLayoutLockKey);
|
|
try {
|
|
try {
|
|
- if (isSuccess) {
|
|
|
|
- // 1.根据当前日期,查询不能取消的时段
|
|
|
|
- List<TimePeriodEntity> timePeriodList = listTimePeroid(applyTask.getId());
|
|
|
|
- List<TimePeriodEntity> noCancelTimePeroidList = listNoCancelApplyTimePeroid(timePeriodList,
|
|
|
|
- applyTask.getAllowApplyCancelDays());
|
|
|
|
- if (noCancelTimePeroidList.isEmpty()) {
|
|
|
|
- log.info("当前时间不在取消预约范围内");
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- // 2.查询考试日期的待排考的考生
|
|
|
|
- List<StudentApplyEntity> toBeLayoutStudentList = this.baseMapper.listTimePeriod(
|
|
|
|
- noCancelTimePeroidList.stream().map(item -> item.getId()).collect(Collectors.toList()),
|
|
|
|
- Boolean.FALSE);
|
|
|
|
- // 3.开始排考
|
|
|
|
- Map<Long, List<StudentApplyEntity>> toBeLayoutStudentMap = toBeLayoutStudentList.stream()
|
|
|
|
- .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId));
|
|
|
|
- for (Long examSiteId : toBeLayoutStudentMap.keySet()) {
|
|
|
|
- Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap = toBeLayoutStudentMap.get(examSiteId)
|
|
|
|
- .stream().collect(Collectors.groupingBy(StudentApplyEntity::getTimePeriodId));
|
|
|
|
- List<ExamRoomEntity> roomList = listExamRoom(examSiteId);
|
|
|
|
- if (roomList.isEmpty()) {
|
|
|
|
- throw new StatusException(examSiteId + ":未设置考场");
|
|
|
|
- }
|
|
|
|
- ExamSiteEntity examSite = examSiteService.getById(examSiteId);
|
|
|
|
- layoutStudentByTimePeriod(applyTask.getId(), examSite, roomList, timeLayoutStudentMap);
|
|
|
|
|
|
+ if (!autoLayoutLock.tryLock()) {
|
|
|
|
+ log.warn("获取锁失败,已有线程在执行排考!lockKey:{}", autoLayoutLock);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // 1.根据当前日期,查询不能取消的时段
|
|
|
|
+ List<TimePeriodEntity> timePeriodList = listTimePeroid(applyTask.getId());
|
|
|
|
+ List<TimePeriodEntity> noCancelTimePeroidList = listNoCancelApplyTimePeroid(timePeriodList,
|
|
|
|
+ applyTask.getAllowApplyCancelDays());
|
|
|
|
+ if (noCancelTimePeroidList.isEmpty()) {
|
|
|
|
+ log.info("当前时间不在取消预约范围内");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // 2.查询考试日期的待排考的考生
|
|
|
|
+ List<StudentApplyEntity> toBeLayoutStudentList = this.baseMapper.listTimePeriod(
|
|
|
|
+ noCancelTimePeroidList.stream().map(item -> item.getId()).collect(Collectors.toList()),
|
|
|
|
+ Boolean.FALSE);
|
|
|
|
+ // 3.开始排考
|
|
|
|
+ Map<Long, List<StudentApplyEntity>> toBeLayoutStudentMap = toBeLayoutStudentList.stream()
|
|
|
|
+ .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId));
|
|
|
|
+ for (Long examSiteId : toBeLayoutStudentMap.keySet()) {
|
|
|
|
+ Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap = toBeLayoutStudentMap.get(examSiteId).stream()
|
|
|
|
+ .collect(Collectors.groupingBy(StudentApplyEntity::getTimePeriodId));
|
|
|
|
+ List<ExamRoomEntity> roomList = listExamRoom(examSiteId);
|
|
|
|
+ if (roomList.isEmpty()) {
|
|
|
|
+ throw new StatusException(examSiteId + ":未设置考场");
|
|
}
|
|
}
|
|
|
|
+ ExamSiteEntity examSite = examSiteService.getById(examSiteId);
|
|
|
|
+ layoutStudentByTimePeriod(applyTask.getId(), examSite, roomList, timeLayoutStudentMap);
|
|
}
|
|
}
|
|
} catch (StatusException e) {
|
|
} catch (StatusException e) {
|
|
log.error(e.getMessage());
|
|
log.error(e.getMessage());
|
|
e.printStackTrace();
|
|
e.printStackTrace();
|
|
} finally {
|
|
} finally {
|
|
- redisClient.delete(CacheConstants.LOCK_ARRANGE_EXAM + DateUtil.formatShortDateString(new Date()));
|
|
|
|
|
|
+ // redisClient.delete(CacheConstants.LOCK_ARRANGE_EXAM +
|
|
|
|
+ // DateUtil.formatShortDateString(new Date()));
|
|
|
|
+ autoLayoutLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|