haogh 1 개월 전
부모
커밋
48076256d7

+ 87 - 32
src/main/java/com/qmth/exam/reserve/service/impl/ExamRoomServiceImpl.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 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.tools.excel.ExcelReader;
 import com.qmth.boot.tools.excel.enums.ExcelType;
@@ -14,6 +15,7 @@ import com.qmth.exam.reserve.bean.login.LoginUser;
 import com.qmth.exam.reserve.bean.room.ExamRoomReq;
 import com.qmth.exam.reserve.bean.room.ExamRoomSaveReq;
 import com.qmth.exam.reserve.bean.room.ExamRoomVO;
+import com.qmth.exam.reserve.cache.CacheConstants;
 import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService;
 import com.qmth.exam.reserve.dao.ExamRoomDao;
 import com.qmth.exam.reserve.entity.ExamRoomEntity;
@@ -24,6 +26,7 @@ import com.qmth.exam.reserve.service.*;
 import com.qmth.exam.reserve.util.PageUtil;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.redisson.api.RLock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
@@ -33,7 +36,10 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
 import java.io.InputStream;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -58,6 +64,9 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
     @Autowired
     private TimePeriodExamRoomService timePeriodExamRoomService;
 
+    @Autowired
+    private ConcurrentService concurrentService;
+
 
     @Override
     public PageResult<ExamRoomVO> pageExamRoom(ExamRoomReq req) {
@@ -69,20 +78,26 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
     @Transactional
     @Override
     public void saveExamRoom(LoginUser user, ExamRoomSaveReq req) {
+        if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
+            log.warn("[考场保存]系统自动预约中,不允许修改考场!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
+            throw new StatusException("系统正在自动预约中,不允许修改考场");
+        }
+
         checkExamRoom(req);
         ExamRoomEntity examRoomEntity = new ExamRoomEntity();
         BeanUtils.copyProperties(req, examRoomEntity);
         ExamRoomEntity beforeUpdateRoom = new ExamRoomEntity();
+        //考点容量
+        ExamSiteEntity examSite = examSiteService.getById(req.getExamSiteId());
+        int oldCapacity = examSite.getCapacity();
+        int newCapacity = 0;
         if (req.getId() == null) {
             examRoomEntity.setEnable(Boolean.TRUE);
             save(examRoomEntity);
-            //刷新缓存
-            refreshApplyCountCache(examRoomEntity.getId(), examRoomEntity.getExamSiteId(), 0, examRoomEntity.getCapacity());
         } else {
             beforeUpdateRoom = getById(req.getId());
             updateById(examRoomEntity);
-            //刷新缓存
-            refreshApplyCountCache(examRoomEntity.getId(), examRoomEntity.getExamSiteId(), beforeUpdateRoom.getCapacity(), examRoomEntity.getCapacity());
+//            refreshApplyCountCache(examRoomEntity.getId(), examRoomEntity.getExamSiteId(), beforeUpdateRoom.getCapacity(), examRoomEntity.getCapacity());
         }
         //更新考点容量和教学点容量
         updateExamSiteAndTeachingCapacity(req.getExamSiteId());
@@ -90,12 +105,29 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
         if(req.getId() != null && !req.getExamSiteId().equals(beforeUpdateRoom.getExamSiteId())) {
             updateExamSiteAndTeachingCapacity(beforeUpdateRoom.getExamSiteId());
         }
+
+        examSite = examSiteService.getById(req.getExamSiteId());
+        newCapacity = examSite.getCapacity();
+
+        //刷新缓存
+        refreshApplyCountCache(examRoomEntity.getId(), examRoomEntity.getExamSiteId(), oldCapacity, newCapacity);
     }
 
+
+
     @Transactional
     @Override
     public void enable(Long id, Boolean enable) {
+        if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
+            log.warn("[考场禁用/启用]系统自动预约中,不允许修改考场!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
+            throw new StatusException("系统正在自动预约中,不允许启用/禁用考场");
+        }
+
         ExamRoomEntity examRoom = getById(id);
+        //考点容量
+        ExamSiteEntity examSite = examSiteService.getById(examRoom.getExamSiteId());
+        int oldCapacity = examSite.getCapacity();
+
         LambdaUpdateWrapper<ExamRoomEntity> wrapper = new LambdaUpdateWrapper<>();
         wrapper.set(ExamRoomEntity::getEnable, enable);
         wrapper.eq(ExamRoomEntity::getId, id);
@@ -103,12 +135,21 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
 
         updateExamSiteAndTeachingCapacity(examRoom.getExamSiteId());
 
-        refreshApplyCountCache(id, examRoom.getExamSiteId(), enable ? 0 : examRoom.getCapacity(), enable ? examRoom.getCapacity() : 0);
+        //更新之后考点的容量
+        examSite = examSiteService.getById(examRoom.getExamSiteId());
+
+        //刷新缓存
+        refreshApplyCountCache(id, examRoom.getExamSiteId(), oldCapacity, examSite.getCapacity());
     }
 
     @Override
     public List<Map<String, Object>> importExamRoom(LoginUser user, InputStream inputStream) {
-        List<DataMap> lineList = null;
+        if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
+            log.warn("[考场导入]系统自动预约中,不允许修改考场!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
+            throw new StatusException("系统正在自动预约中,不允许导入考场");
+        }
+
+        List<DataMap> lineList;
         ExcelReader reader = ExcelReader.create(ExcelType.XLSX, inputStream, 0);
         try {
             lineList = reader.getDataMapList();
@@ -224,9 +265,14 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
 
     private void saveRoom(ExamRoomEntity room) {
         ExamRoomEntity examRoom = getExamRoom(room.getExamSiteId(), room.getCode());
-        int capacity;
+        int oldCapacity = 0;
+        ExamSiteEntity examSite;
         if (examRoom != null) {
-            capacity = examRoom.getCapacity();
+            examSite = examSiteService.getById(examRoom.getExamSiteId());
+            if(examSite == null) {
+                throw new StatusException("找不到考点");
+            }
+            oldCapacity = examSite.getCapacity();
             examRoom.setCapacity(room.getCapacity());
             LambdaUpdateWrapper<ExamRoomEntity> wrapper = new LambdaUpdateWrapper<>();
             wrapper.set(ExamRoomEntity::getName, room.getName());
@@ -235,15 +281,15 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
             wrapper.set(ExamRoomEntity::getEnable, Boolean.TRUE);
             wrapper.eq(ExamRoomEntity::getId, examRoom.getId());
             update(null, wrapper);
-            if(capacity != room.getCapacity()) {
-                refreshApplyCountCache(room.getId(), room.getExamSiteId(), capacity, room.getCapacity());
-            }
         } else {
             save(room);
-            refreshApplyCountCache(room.getId(), room.getExamSiteId(), 0, room.getCapacity());
-
         }
+        //更新考点、教学点容量
         updateExamSiteAndTeachingCapacity(room.getExamSiteId());
+        //更新之后的容量
+        examSite = examSiteService.getById(room.getExamSiteId());
+        //刷新缓存
+        refreshApplyCountCache(room.getId(), room.getExamSiteId(), oldCapacity, examSite.getCapacity());
     }
 
     private List<ExamSiteEntity> listExamSite(String examSiteCode, String examSiteName, Long categoryId) {
@@ -255,24 +301,11 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
     }
 
     private void updateExamSiteAndTeachingCapacity(Long examSiteId) {
-//        ExamSiteEntity oldExamSite = examSiteService.getById(examSiteId);
         // 更新考点容量
         examSiteService.updateExamSiteCapacity(examSiteId);
-
         ExamSiteEntity newExamSite = examSiteService.getById(examSiteId);
         //更新教学点容量
         categoryService.updateTeachingCapacity(newExamSite.getCategoryId());
-
- /*       // 考点下没有启用的考场,容量设置为0
-        if (oldExamSite.getCapacity() == null) {
-            oldExamSite.setCapacity(0);
-        }
-        if (newExamSite.getCapacity() == null) {
-            newExamSite.setCapacity(0);
-        }*/
-
-        //刷新考点容量缓存(以前的逻辑)
-        //cacheService.refreshApplyAvailableCountCache(examSiteId, oldExamSite.getCapacity(), newExamSite.getCapacity());
     }
 
     private void refreshApplyCountCache(Long examRoomId,Long examSiteId,  int oldCapacity, int newCapacity) {
@@ -283,12 +316,34 @@ public class ExamRoomServiceImpl extends ServiceImpl<ExamRoomDao, ExamRoomEntity
         Map<Long, TimePeriodExamRoomEntity> timePeriodExamRoomMap = timePeriodExamRooms.stream().collect(
                 Collectors.toMap(TimePeriodExamRoomEntity::getTimePeriodId, Function.identity()));
 
-        for(TimePeriodEntity timePeriod : timePeriods) {
-            //刷新缓存
-            TimePeriodExamRoomEntity timePeriodExamRoomEntity = timePeriodExamRoomMap.get(timePeriod.getId());
-            if(timePeriodExamRoomEntity == null || timePeriodExamRoomEntity.getEnable()) {
-                cacheService.refreshApplyAvailableCountCache(examSiteId, timePeriod.getId(), oldCapacity, newCapacity);
+        for (TimePeriodEntity timePeriod : timePeriods) {
+            String examSiteTimePeriodLockKey = String.format(CacheConstants.LOCK_EXAM_SITE_TIME_PERIOD, examSiteId, timePeriod.getId());
+            RLock examSiteTimePeriodLock = (RLock) concurrentService.getLock(examSiteTimePeriodLockKey);
+            try {
+                if (!examSiteTimePeriodLock.tryLock()) {
+                    log.warn("[考场容量更新] 获取锁失败,不能同时修改考点考场容量, lockKey:{}", examSiteTimePeriodLock);
+                    throw new StatusException("考场保存失败,请稍后再试");
+                } else {
+                    //刷新缓存
+                    TimePeriodExamRoomEntity timePeriodExamRoomEntity = timePeriodExamRoomMap.get(timePeriod.getId());
+                    if (timePeriodExamRoomEntity == null || timePeriodExamRoomEntity.getEnable()) {
+                        cacheService.refreshApplyAvailableCountCache(examSiteId, timePeriod.getId(), oldCapacity, newCapacity);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("[考场容量更新] 考场保存失败, msg:{}", e.getMessage());
+                throw new StatusException("考场保存失败,请稍后再试");
+            } finally {
+                try {
+                    if (examSiteTimePeriodLock.isLocked() && examSiteTimePeriodLock.isHeldByCurrentThread()) {
+                        examSiteTimePeriodLock.unlock();
+                        log.info("[考场容量更新] 解锁成功,lockKey:{}", examSiteTimePeriodLock);
+                    }
+                } catch (Exception e) {
+                    log.warn(e.getMessage());
+                }
             }
+
         }
     }
 

+ 85 - 29
src/main/java/com/qmth/exam/reserve/service/impl/TimePeriodExamRoomServiceImpl.java

@@ -2,6 +2,7 @@ package com.qmth.exam.reserve.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
 import com.qmth.boot.core.exception.StatusException;
 import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
 import com.qmth.exam.reserve.bean.examsite.ExamSiteCacheBean;
@@ -11,6 +12,7 @@ import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteBean;
 import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteInfo;
 import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteReq;
 import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteVo;
+import com.qmth.exam.reserve.cache.CacheConstants;
 import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService;
 import com.qmth.exam.reserve.cache.impl.ExamSiteCacheService;
 import com.qmth.exam.reserve.cache.impl.OrgCacheService;
@@ -26,6 +28,7 @@ import com.qmth.exam.reserve.util.DateUtil;
 import com.qmth.exam.reserve.util.UnionUtil;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.redisson.api.RLock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
@@ -58,6 +61,9 @@ public class TimePeriodExamRoomServiceImpl extends ServiceImpl<TimePeriodExamRoo
     @Autowired
     private ExamRoomService examRoomService;
 
+    @Autowired
+    private ConcurrentService concurrentService;
+
 
     @Override
     public List<TimePeriodExamSiteVo> ListDetail(LoginUser loginUser, Long examRoomId) {
@@ -186,6 +192,11 @@ public class TimePeriodExamRoomServiceImpl extends ServiceImpl<TimePeriodExamRoo
     @Transactional(isolation = Isolation.READ_COMMITTED)
     @Override
     public void save(Long userId, Long examRoomId, List<TimePeriodExamSiteReq> timePeriodExamRoomList) {
+        if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) {
+            log.warn("[考场排班保存]系统自动预约中,不允许操作考场排班!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY);
+            throw new StatusException("系统正在自动预约中,不允许修改");
+        }
+
         if (CollectionUtils.isEmpty(timePeriodExamRoomList)) {
             log.warn("[考场排班设置]时间段列表为空");
             throw new StatusException("保存失败,时间段列表为空");
@@ -351,43 +362,88 @@ public class TimePeriodExamRoomServiceImpl extends ServiceImpl<TimePeriodExamRoo
         disableList.stream()
                 .filter(item -> !item.getEnable())
                 .forEach(item -> {
-                    // 更新容量缓存,禁用需要减考场容量
-                    applyTaskCacheService.refreshApplyAvailableCountCache(
-                            examRoom.getExamSiteId(),
-                            item.getTimePeriodId(),
-                            examRoom.getCapacity(),
-                            0
-                    );
+                    String examSiteTimePeriodLockKey = String.format(CacheConstants.LOCK_EXAM_SITE_TIME_PERIOD, examRoom.getExamSiteId(),
+                            item.getTimePeriodId());
+                    RLock examSiteTimePeriodLock = (RLock) concurrentService.getLock(examSiteTimePeriodLockKey);
+                    try {
+                        if (!examSiteTimePeriodLock.tryLock()) {
+                            log.warn("[考场排班设置] 获取锁失败,不能同时修改考场排班设置, lockKey:{}", examSiteTimePeriodLock);
+                            throw new StatusException("考场排班保存失败,请稍后再试");
+                        } else {
+                            // 更新容量缓存,禁用需要减考场容量
+                            applyTaskCacheService.refreshApplyAvailableCountCache(
+                                    examRoom.getExamSiteId(),
+                                    item.getTimePeriodId(),
+                                    examRoom.getCapacity(),
+                                    0
+                            );
+                        }
+
+                    } catch (Exception e) {
+                        log.error("[考场排班设置] 考场排班失败, msg:{}", e.getMessage());
+                        throw new StatusException("考场排班保存失败,请稍后再试");
+                    } finally {
+                        try {
+                            if (examSiteTimePeriodLock.isLocked() && examSiteTimePeriodLock.isHeldByCurrentThread()) {
+                                examSiteTimePeriodLock.unlock();
+                                log.info("[考场排班设置] 解锁成功,lockKey:{}", examSiteTimePeriodLock);
+                            }
+                        } catch (Exception e) {
+                            log.warn(e.getMessage());
+                        }
+                    }
                 });
     }
 
     private void refreshUpdatedTimePeriodCache(List<TimePeriodExamRoomEntity> updateList,
-            Map<Long, TimePeriodExamSiteBean> timePeriodMap,
-            ExamRoomEntity examRoom) {
+            Map<Long, TimePeriodExamSiteBean> timePeriodMap, ExamRoomEntity examRoom) {
         updateList.forEach(item -> {
             TimePeriodExamSiteBean timePeriod = timePeriodMap.get(item.getId());
-            //由启用变为禁用,缓存容量=-考场的容量
-            if (timePeriod != null && Boolean.TRUE.equals(timePeriod.getEnable()) && !item.getEnable()) {
-                // 更新容量缓存
-                applyTaskCacheService.refreshApplyAvailableCountCache(
-                        examRoom.getExamSiteId(),
-                        timePeriod.getTimePeriodId(),
-                        examRoom.getCapacity(),
-                        0
-                );
-            }
+            String examSiteTimePeriodLockKey = String.format(CacheConstants.LOCK_EXAM_SITE_TIME_PERIOD, examRoom.getExamSiteId(),
+                    item.getTimePeriodId());
+            RLock examSiteTimePeriodLock = (RLock) concurrentService.getLock(examSiteTimePeriodLockKey);
+
+            try {
+                if (!examSiteTimePeriodLock.tryLock()) {
+                    log.warn("[考场排班设置] 获取锁失败,不能同时修改考场排班设置, lockKey:{}", examSiteTimePeriodLock);
+                    throw new StatusException("考场排班保存失败,请稍后再试");
+                } else {
+                    //由启用变为禁用,缓存容量=-考场的容量
+                    if (timePeriod != null && Boolean.TRUE.equals(timePeriod.getEnable()) && !item.getEnable()) {
+                        // 更新容量缓存
+                        applyTaskCacheService.refreshApplyAvailableCountCache(
+                                examRoom.getExamSiteId(),
+                                timePeriod.getTimePeriodId(),
+                                examRoom.getCapacity(),
+                                0
+                        );
+                    }
+
+                    //由禁用变为启用,缓存容量=+考场的容量
+                    if (timePeriod != null && Boolean.FALSE.equals(timePeriod.getEnable()) && item.getEnable()) {
+                        //更新容量缓存
+                        applyTaskCacheService.refreshApplyAvailableCountCache(
+                                examRoom.getExamSiteId(),
+                                timePeriod.getTimePeriodId(),
+                                0,
+                                examRoom.getCapacity()
+                        );
+                    }
+                }
 
-            //由禁用变为启用,缓存容量=+考场的容量
-            if (timePeriod != null && Boolean.FALSE.equals(timePeriod.getEnable()) && item.getEnable()) {
-                //更新容量缓存
-                applyTaskCacheService.refreshApplyAvailableCountCache(
-                        examRoom.getExamSiteId(),
-                        timePeriod.getTimePeriodId(),
-                        0,
-                        examRoom.getCapacity()
-                );
+            } catch (Exception e) {
+                log.error("[考场排班设置] 考场排班失败, msg:{}", e.getMessage());
+                throw new StatusException("考场排班保存失败,请稍后再试");
+            } finally {
+                try {
+                    if (examSiteTimePeriodLock.isLocked() && examSiteTimePeriodLock.isHeldByCurrentThread()) {
+                        examSiteTimePeriodLock.unlock();
+                        log.info("[考场排班设置] 解锁成功,lockKey:{}", examSiteTimePeriodLock);
+                    }
+                } catch (Exception e) {
+                    log.warn(e.getMessage());
+                }
             }
-
         });
     }