Ver Fonte

update locks

deason há 1 ano atrás
pai
commit
b50aa194ca

+ 2 - 0
src/main/java/com/qmth/exam/reserve/bean/Constants.java

@@ -5,6 +5,8 @@ package com.qmth.exam.reserve.bean;
  */
 public interface Constants {
 
+    String SYSTEM_BUSY = "当前请求繁忙,请稍后再试!";
+
     /**
      * 分类层级
      */

+ 12 - 5
src/main/java/com/qmth/exam/reserve/cache/CacheConstants.java

@@ -13,7 +13,7 @@ public interface CacheConstants {
     /**
      * 用户登录会话缓存前缀
      */
-    String CACHE_SESSION_PREFIX = "SESSION:";
+    String CACHE_SESSION_PREFIX = "$SESSION:";
 
     /**
      * 用户登录标识_{userId}
@@ -26,13 +26,20 @@ public interface CacheConstants {
     String CACHE_STUDENT_LOGIN = "S_";
 
     /**
-     * 自动排考前缀:{yyyyMMdd}
+     * 某考生预约操作锁
+     * $lock:student_apply_{studentId}
      */
-    String LOCK_ARRANGE_EXAM = "LOCK:ARRANGE_EXAM:";
+    String LOCK_STUDENT_APPLY = "student_apply_%s";
 
     /**
-     * 自动安排时间锁
+     * 系统自动为考生预约操作锁
+     * $lock:auto_apply
      */
-    String LOCK_AUTO_APPLY = "LOCK:AUTO_APPLY";
+    String LOCK_AUTO_APPLY = "auto_apply";
+
+    /**
+     * 自动排考前缀:{yyyyMMdd}
+     */
+    String LOCK_ARRANGE_EXAM = "LOCK:ARRANGE_EXAM:";
 
 }

+ 85 - 47
src/main/java/com/qmth/exam/reserve/service/impl/ExamReserveServiceImpl.java

@@ -2,7 +2,9 @@ package com.qmth.exam.reserve.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
 import com.qmth.boot.core.exception.StatusException;
+import com.qmth.exam.reserve.bean.Constants;
 import com.qmth.exam.reserve.bean.RichTextBean;
 import com.qmth.exam.reserve.bean.apply.ApplyTimePeriodResult;
 import com.qmth.exam.reserve.bean.apply.ApplyVO;
@@ -10,6 +12,7 @@ import com.qmth.exam.reserve.bean.apply.TicketInfo;
 import com.qmth.exam.reserve.bean.apply.TimePeriodInfo;
 import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
 import com.qmth.exam.reserve.bean.login.LoginUser;
+import com.qmth.exam.reserve.cache.CacheConstants;
 import com.qmth.exam.reserve.dao.StudentApplyDao;
 import com.qmth.exam.reserve.entity.*;
 import com.qmth.exam.reserve.enums.ApplyStatus;
@@ -26,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.text.ParseException;
 import java.util.*;
+import java.util.concurrent.locks.Lock;
 
 @Service
 public class ExamReserveServiceImpl implements ExamReserveService {
@@ -47,6 +51,9 @@ public class ExamReserveServiceImpl implements ExamReserveService {
     @Autowired
     private ExamSiteService examSiteService;
 
+    @Autowired
+    private ConcurrentService concurrentService;
+
     @Override
     @Transactional
     public void saveStudentApply(LoginUser student, Long examSiteId, Long timePeriodId) {
@@ -68,18 +75,6 @@ public class ExamReserveServiceImpl implements ExamReserveService {
             throw new StatusException("当前预约任务与学生的不匹配");
         }
 
-        StudentEntity stu = studentService.findLessInfoByStudentId(student.getId());
-        if (stu == null) {
-            log.warn("预约失败,学生信息不存在!studentId:{}", student.getId());
-            throw new StatusException("学生信息不存在");
-        }
-
-        if (stu.getApplyFinished()) {
-            String msg = "当前学生无剩余可约时段,已完成预约" + stu.getApplyNumber() + "次";
-            log.warn(msg);
-            // throw new StatusException(msg);
-        }
-
         TimePeriodEntity timePeriod = timePeriodService.getById(timePeriodId);
         if (timePeriod == null || !curApplyTask.getTaskId().equals(timePeriod.getApplyTaskId())) {
             log.warn("预约失败,当前预约时段信息有误!timePeriodId:{}", timePeriodId);
@@ -96,41 +91,68 @@ public class ExamReserveServiceImpl implements ExamReserveService {
             throw new StatusException("当前预约时段已禁止预约");
         }
 
-        // 当前预约时段剩余可约数量
-        int availableCount = 0;//todo
-        // if (availableCount == 0) {
-        //     throw new StatusException("当前预约时段已约满");
-        // }
-
         // 考生预约操作锁
-        // 更新预约时段、机房容量等操作
-        // 系统自动预约“任务执行期间”不允许预约
-
-        LambdaQueryWrapper<StudentApplyEntity> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(StudentApplyEntity::getStudentId, student.getId());
-        wrapper.eq(StudentApplyEntity::getExamSiteId, examSiteId);
-        wrapper.eq(StudentApplyEntity::getTimePeriodId, timePeriodId);
-        StudentApplyEntity existEntity = studentApplyService.getOne(wrapper);
-        if (existEntity != null) {
-            if (!existEntity.getCancel()) {
-                log.warn("预约失败,当前时段已预约,请勿重复预约!applyId:{}", existEntity.getId());
-                throw new StatusException("当前时段已预约,请勿重复预约");
+        String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, student.getId());
+        Lock studentApplyLock = concurrentService.getLock(studentApplyLockKey);
+        try {
+            if (!studentApplyLock.tryLock()) {
+                log.warn("获取锁失败,不允许同一个考生同时操作预约!lockKey:{}", studentApplyLockKey);
+                throw new StatusException(Constants.SYSTEM_BUSY);
             }
 
-            // 存在“已取消”预约记录,则恢复预约
-            this.updateStudentApplyForCancel(existEntity.getId(), false, student.getId());
-        } else {
-            // 不存在则新增预约记录
-            StudentApplyEntity entity = new StudentApplyEntity();
-            entity.setStudentId(student.getId());
-            entity.setExamSiteId(examSiteId);
-            entity.setTimePeriodId(timePeriodId);
-            entity.setOperateId(student.getId());
-            entity.setCancel(false);
-            studentApplyService.save(entity);
-            log.warn("新增考生预约记录!applyId:{} examSiteId:{} timePeriodId:{}", entity.getId(), examSiteId, timePeriodId);
-        }
+            if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
+                // 系统自动预约“任务执行期间”不允许预约
+                log.warn("系统自动预约中,不允许考生操作预约!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
+                throw new StatusException(Constants.SYSTEM_BUSY);
+            }
+
+            StudentEntity stu = studentService.findLessInfoByStudentId(student.getId());
+            if (stu == null) {
+                log.warn("预约失败,学生信息不存在!studentId:{}", student.getId());
+                throw new StatusException("学生信息不存在");
+            }
 
+            if (stu.getApplyFinished()) {
+                String msg = "当前学生无剩余可约时段,已完成预约" + stu.getApplyNumber() + "次";
+                log.warn(msg);
+                // throw new StatusException(msg);
+            }
+
+            // 当前预约时段剩余可约数量
+            int availableCount = 0;//todo
+            // if (availableCount == 0) {
+            //     throw new StatusException("当前预约时段已约满");
+            // }
+
+            // 更新预约时段、机房容量等操作
+
+            LambdaQueryWrapper<StudentApplyEntity> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(StudentApplyEntity::getStudentId, student.getId());
+            wrapper.eq(StudentApplyEntity::getExamSiteId, examSiteId);
+            wrapper.eq(StudentApplyEntity::getTimePeriodId, timePeriodId);
+            StudentApplyEntity existEntity = studentApplyService.getOne(wrapper);
+            if (existEntity != null) {
+                if (!existEntity.getCancel()) {
+                    log.warn("预约失败,当前时段已预约,请勿重复预约!applyId:{}", existEntity.getId());
+                    throw new StatusException("当前时段已预约,请勿重复预约");
+                }
+
+                // 存在“已取消”预约记录,则恢复预约
+                this.updateStudentApplyForCancel(existEntity.getId(), false, student.getId());
+            } else {
+                // 不存在则新增预约记录
+                StudentApplyEntity entity = new StudentApplyEntity();
+                entity.setStudentId(student.getId());
+                entity.setExamSiteId(examSiteId);
+                entity.setTimePeriodId(timePeriodId);
+                entity.setOperateId(student.getId());
+                entity.setCancel(false);
+                studentApplyService.save(entity);
+                log.warn("新增考生预约记录!applyId:{} examSiteId:{} timePeriodId:{}", entity.getId(), examSiteId, timePeriodId);
+            }
+        } finally {
+            studentApplyLock.unlock();
+        }
     }
 
     @Override
@@ -181,12 +203,28 @@ public class ExamReserveServiceImpl implements ExamReserveService {
             throw new StatusException("当前预约记录已禁止取消预约");
         }
 
-        // todo
         // 考生预约操作锁
-        // 更新预约时段、机房容量等操作
-        // 系统自动预约“任务执行期间”不允许取消预约
+        String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, student.getId());
+        Lock studentApplyLock = concurrentService.getLock(studentApplyLockKey);
+        try {
+            if (!studentApplyLock.tryLock()) {
+                log.warn("获取锁失败,不允许同一个考生同时操作预约!lockKey:{}", studentApplyLockKey);
+                throw new StatusException(Constants.SYSTEM_BUSY);
+            }
+
+            if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
+                // 系统自动预约“任务执行期间”不允许取消预约
+                log.warn("系统自动预约中,不允许考生操作预约!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
+                throw new StatusException(Constants.SYSTEM_BUSY);
+            }
 
-        this.updateStudentApplyForCancel(applyId, true, student.getId());
+            this.updateStudentApplyForCancel(applyId, true, student.getId());
+
+            // todo
+            // 更新预约时段、机房容量等操作
+        } finally {
+            studentApplyLock.unlock();
+        }
     }
 
     private void updateStudentApplyForCancel(Long applyId, boolean cancel, Long operateId) {