package com.qmth.exam.reserve.cache.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.qmth.exam.reserve.bean.apply.ApplyRecordCacheBean; import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO; import com.qmth.exam.reserve.bean.examsite.ExamSiteCapacityInfo; import com.qmth.exam.reserve.cache.CacheConstants; import com.qmth.exam.reserve.cache.RedisClient; import com.qmth.exam.reserve.entity.StudentApplyEntity; import com.qmth.exam.reserve.entity.TimePeriodEntity; import com.qmth.exam.reserve.service.*; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.redisson.api.RAtomicLong; import org.redisson.api.RQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @Component public class ApplyTaskCacheService implements CacheConstants { private static final Logger log = LoggerFactory.getLogger(ApplyTaskCacheService.class); @Autowired private RedisClient redisClient; @Autowired private ApplyTaskService applyTaskService; @Autowired private ExamSiteService examSiteService; @Autowired private TimePeriodService timePeriodService; @Autowired private StudentService studentService; @Autowired private StudentApplyService studentApplyService; /** * 获取当前启用的预约任务缓存 */ public CurrentApplyTaskVO currentApplyTask(Long orgId) { String cacheKey = String.format(CACHE_CURRENT_APPLY_TASK, orgId); CurrentApplyTaskVO value = redisClient.get(cacheKey, CurrentApplyTaskVO.class); if (value != null) { return value; } value = applyTaskService.currentApplyTask(orgId); if (value == null) { return null; } redisClient.set(cacheKey, value, CACHE_TIME_OUT_10, TimeUnit.MINUTES); log.info("SET cacheKey:{} curApplyTaskId:{}", cacheKey, value.getTaskId()); return value; } /** * 清除当前启用的预约任务缓存 */ public void clearCurrentApplyTaskCache(Long orgId) { String cacheKey = String.format(CACHE_CURRENT_APPLY_TASK, orgId); redisClient.delete(cacheKey); log.warn("DELETE cacheKey:{}", cacheKey); } /** * 获取某考生的“允许预约时段次数”缓存 */ public int getStudentApplyNumber(Long studentId) { String cacheKey = String.format(CACHE_STUDENT_APPLY_NUMBER, studentId); Integer value = redisClient.get(cacheKey, Integer.class); if (value != null) { return value; } value = studentService.findStudentApplyNumberById(studentId); redisClient.set(cacheKey, value, CACHE_TIME_OUT_10, TimeUnit.MINUTES); log.info("SET cacheKey:{} value:{}", cacheKey, value); return value; } /** * 清除某考生的“允许预约时段次数”缓存 */ public void clearStudentApplyNumberCache(Long studentId) { String cacheKey = String.format(CACHE_STUDENT_APPLY_NUMBER, studentId); redisClient.delete(cacheKey); log.warn("DELETE cacheKey:{}", cacheKey); } /** * 获取某考点的“可预约总量”缓存 */ public int getApplyTotalCount(Long examSiteId) { String cacheKey = String.format(CACHE_APPLY_TOTAL, examSiteId); Integer value = redisClient.get(cacheKey, Integer.class); if (value != null) { return value; } value = examSiteService.countExamSiteCapacityById(examSiteId); redisClient.set(cacheKey, value, CACHE_TIME_OUT_10, TimeUnit.MINUTES); log.info("SET cacheKey:{} value:{}", cacheKey, value); return value; } /** * 清除某考点的“可预约总量”缓存 */ public void clearApplyTotalCountCache(Long examSiteId) { String cacheKey = String.format(CACHE_APPLY_TOTAL, examSiteId); redisClient.delete(cacheKey); log.warn("DELETE cacheKey:{}", cacheKey); } /** * 获取某考点某时段的“已预约数量”(查数据库) */ public int getApplyFinishCountFormDB(Long examSiteId, Long timePeriodId) { return studentApplyService.countApplyFinishForExamSiteAndTimePeriod(examSiteId, timePeriodId); } /** * 获取某考点某时段的“已预约数量”缓存 */ public int getApplyFinishCount(Long examSiteId, Long timePeriodId) { String cacheKey = String.format(CACHE_APPLY_FINISH, examSiteId, timePeriodId); if (redisClient.exist(cacheKey)) { return (int) redisClient.getRedissonClient().getAtomicLong(cacheKey).get(); } RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); int value = this.getApplyFinishCountFormDB(examSiteId, timePeriodId); atomic.set(value); atomic.expire(CACHE_TIME_OUT_30, TimeUnit.DAYS); log.info("SET cacheKey:{} value:{}", cacheKey, value); return value; } /** * 获取当前预约时段剩余可约数量缓存 */ public int getApplyAvailableCount(Long examSiteId, Long timePeriodId) { int totalCount = this.getApplyTotalCount(examSiteId); int finishCount = this.getApplyFinishCount(examSiteId, timePeriodId); int availableCount = totalCount - finishCount; return Math.max(availableCount, 0); } /** * 获取某考点某时段的“剩余可约数量”缓存 */ public int getApplyAvailableCount2(Long examSiteId, Long timePeriodId) { String cacheKey = String.format(CACHE_APPLY_AVAILABLE_COUNT, examSiteId, timePeriodId); RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); return (int) atomic.get(); } /** * 累加 某考点某时段的“剩余可约数量”缓存 */ public void increaseApplyAvailableCount(Long examSiteId, Long timePeriodId) { String cacheKey = String.format(CACHE_APPLY_AVAILABLE_COUNT, examSiteId, timePeriodId); RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); atomic.incrementAndGet(); } /** * 累减 某考点某时段的“剩余可约数量”缓存 */ public void decreaseApplyAvailableCount(Long examSiteId, Long timePeriodId) { String cacheKey = String.format(CACHE_APPLY_AVAILABLE_COUNT, examSiteId, timePeriodId); RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); if (atomic.get() > 0) { atomic.decrementAndGet(); } } /** * 初始化 某考点某时段的“剩余可约数量”缓存 */ public void initApplyAvailableCountCache(Long examSiteId, int oldCapacity, int newCapacity) { // 获取所有时段ID集合 List timePeriods = timePeriodService.list(new LambdaQueryWrapper().select(TimePeriodEntity::getId)); if (CollectionUtils.isEmpty(timePeriods)) { return; } // 新、旧考点容量差额 int diffCapacity = newCapacity - oldCapacity; for (TimePeriodEntity timePeriod : timePeriods) { String cacheKey = String.format(CACHE_APPLY_AVAILABLE_COUNT, examSiteId, timePeriod.getId()); if (redisClient.exist(cacheKey)) { if (diffCapacity == 0) { // 考点总容量未变化,不用更新缓存 continue; } // 总容量变化时,则更新考点剩余容量缓存 RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); long availableCount = Math.max(atomic.get() + diffCapacity, 0); atomic.set(availableCount); log.warn("SET cacheKey:{} value:{}", cacheKey, availableCount); } else { // 初始考点剩余容量缓存 RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); atomic.set(newCapacity); log.debug("SET cacheKey:{} value:{}", cacheKey, newCapacity); } } } /** * 初始化 所有考点所有时段的“剩余可约数量”缓存 */ public void initApplyAvailableCountCacheForAllExamSites() { List examSites = examSiteService.findAllExamSiteCapacityList(); for (ExamSiteCapacityInfo examSite : examSites) { this.initApplyAvailableCountCache(examSite.getExamSiteId(), examSite.getCapacity(), examSite.getCapacity()); } } /** * 累加 某考点某时段的“已预约数量”缓存 */ public void increaseApplyFinishCount(Long examSiteId, Long timePeriodId) { String cacheKey = String.format(CACHE_APPLY_FINISH, examSiteId, timePeriodId); RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); atomic.incrementAndGet(); } /** * 累减 某考点某时段的“已预约数量”缓存 */ public void decreaseApplyFinishCount(Long examSiteId, Long timePeriodId) { String cacheKey = String.format(CACHE_APPLY_FINISH, examSiteId, timePeriodId); RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey); if (atomic.get() > 0) { atomic.decrementAndGet(); } } /** * 获取某考生的 已完成预约次数缓存(查数据库) */ public int getStudentApplyFinishCountFromDB(Long studentId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentApplyEntity::getStudentId, studentId); wrapper.eq(StudentApplyEntity::getCancel, Boolean.FALSE); return studentApplyService.count(wrapper); } /** * 获取某考生的 已完成预约次数缓存 */ public int getStudentApplyFinishCount(Long studentId) { Map maps = this.getStudentApplyRecords(studentId); int studentApplyFinishCount = 0; for (ApplyRecordCacheBean bean : maps.values()) { if (!bean.getCancel()) { studentApplyFinishCount++; } } return studentApplyFinishCount; } /** * 获取某考生的 所有的“预约记录”缓存 */ public Map getStudentApplyRecords(Long studentId) { String cacheKey = String.format(CACHE_STUDENT_APPLY_RECORD, studentId); if (!redisClient.exist(cacheKey)) { Map maps = this.getStudentApplyRecordsFromDB(studentId); if (MapUtils.isEmpty(maps)) { return new HashMap<>(); } for (ApplyRecordCacheBean bean : maps.values()) { this.saveStudentApplyRecord(bean); } redisClient.expire(cacheKey, CACHE_TIME_OUT_30, TimeUnit.DAYS); return maps; } return redisClient.getEntriesForHash(cacheKey, ApplyRecordCacheBean.class); } /** * 获取某考生的 所有的“预约记录”(查数据库) */ public Map getStudentApplyRecordsFromDB(Long studentId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentApplyEntity::getStudentId, studentId); List list = studentApplyService.list(wrapper); if (CollectionUtils.isEmpty(list)) { return new HashMap<>(); } Map maps = new HashMap<>(); for (StudentApplyEntity entity : list) { ApplyRecordCacheBean bean = new ApplyRecordCacheBean(); bean.setStudentId(entity.getStudentId()); bean.setExamSiteId(entity.getExamSiteId()); bean.setTimePeriodId(entity.getTimePeriodId()); bean.setCancel(entity.getCancel()); bean.setOperateId(entity.getOperateId()); bean.setOperateTime(entity.getUpdateTime()); String hashKey = String.format("%s_%s", entity.getExamSiteId(), entity.getTimePeriodId()); maps.put(hashKey, bean); } return maps; } /** * 获取某考生的 某考点某时段的“预约记录”缓存 */ public ApplyRecordCacheBean getStudentApplyRecord(Long studentId, Long examSiteId, Long timePeriodId) { String cacheKey = String.format(CACHE_STUDENT_APPLY_RECORD, studentId); String hashKey = String.format("%s_%s", examSiteId, timePeriodId); return redisClient.getForHash(cacheKey, hashKey, ApplyRecordCacheBean.class); } /** * 获取某考生的 某考点某时段的“预约记录”(查数据库) */ public StudentApplyEntity getStudentApplyRecordFormDB(Long studentId, Long examSiteId, Long timePeriodId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentApplyEntity::getExamSiteId, examSiteId); wrapper.eq(StudentApplyEntity::getTimePeriodId, timePeriodId); wrapper.eq(StudentApplyEntity::getStudentId, studentId); return studentApplyService.getOne(wrapper); } /** * 保存某考生的 某考点某时段的“预约记录”缓存 */ public void saveStudentApplyRecord(ApplyRecordCacheBean value) { String cacheKey = String.format(CACHE_STUDENT_APPLY_RECORD, value.getStudentId()); String hashKey = String.format("%s_%s", value.getExamSiteId(), value.getTimePeriodId()); redisClient.setForHash(cacheKey, hashKey, value); log.info("SET cacheKey:{} hashKey:{} cancel:{}", cacheKey, hashKey, value.getCancel()); } /** * 推送至考生预约记录队列 */ public void pushStudentApplyRecordQueue(ApplyRecordCacheBean value) { if (value == null) { return; } RQueue queue = redisClient.getRedissonClient() .getQueue(QUEUE_STUDENT_APPLY_RECORD); boolean success = queue.offer(value); log.info("{}_{}_{}_{} offerQueue:{}", value.getStudentId(), value.getExamSiteId(), value.getTimePeriodId(), value.getCancel(), success); if (!success) { throw new RuntimeException("推送至考生预约记录队列失败"); } } /** * 获取考生预约记录队列数量 */ public int getStudentApplyRecordQueueSize() { RQueue queue = redisClient.getRedissonClient() .getQueue(QUEUE_STUDENT_APPLY_RECORD); return queue.size(); } }