LotteryCacheService.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. package com.webchat.service.lottery;
  2. import com.fasterxml.jackson.core.type.TypeReference;
  3. import com.webchat.common.constants.LotteryConstants;
  4. import com.webchat.common.enums.RedisKeyEnum;
  5. import com.webchat.common.util.JsonUtil;
  6. import com.webchat.domain.vo.response.lottery.LotteryActivityBaseVO;
  7. import com.webchat.domain.vo.response.lottery.LotteryActivityVO;
  8. import com.webchat.domain.vo.response.lottery.LotteryItemVO;
  9. import com.webchat.repository.dao.lottery.ILotteryActivityDAO;
  10. import com.webchat.repository.dao.lottery.ILotteryItemDAO;
  11. import com.webchat.repository.entity.lottery.LotteryActivityEntity;
  12. import com.webchat.repository.entity.lottery.LotteryItemEntity;
  13. import com.webchat.service.RedisService;
  14. import org.apache.commons.collections.CollectionUtils;
  15. import org.apache.commons.collections.MapUtils;
  16. import org.apache.commons.lang3.StringUtils;
  17. import org.springframework.beans.BeanUtils;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.data.redis.core.DefaultTypedTuple;
  20. import org.springframework.data.redis.core.ZSetOperations;
  21. import org.springframework.scheduling.annotation.Async;
  22. import org.springframework.stereotype.Service;
  23. import org.springframework.util.Assert;
  24. import java.util.Collections;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Set;
  28. import java.util.concurrent.ConcurrentHashMap;
  29. import java.util.stream.Collectors;
  30. /**
  31. * @Author 程序员七七
  32. * @webSite https://www.coderutil.com
  33. * @Date 2022/11/16 23:40
  34. * @description
  35. */
  36. @Service
  37. public class LotteryCacheService {
  38. @Autowired
  39. private ILotteryActivityDAO lotteryActivityDAO;
  40. @Autowired
  41. private ILotteryItemDAO lotteryItemDAO;
  42. @Autowired
  43. private RedisService redisService;
  44. /**
  45. * 刷新活动缓存,在活动保存后
  46. *
  47. * @param activityId
  48. */
  49. @Async
  50. public LotteryActivityVO refreshLotteryActivityCacheById(String activityId) {
  51. LotteryActivityEntity entity = lotteryActivityDAO.findByActivityId(activityId);
  52. return this.refreshLotteryActivityCacheByEntity(entity);
  53. }
  54. @Async
  55. public LotteryActivityVO refreshLotteryActivityCacheByEntity(LotteryActivityEntity entity) {
  56. if (entity == null) {
  57. return null;
  58. }
  59. String lotteryActivityId = entity.getActivityId();
  60. List<LotteryItemVO> lotteryItemVOList = this.getLotteryItemVoListFromDB(lotteryActivityId);
  61. /**
  62. * 刷新奖品库存缓存
  63. */
  64. this.refreshLotteryItemStockCache(lotteryActivityId, lotteryItemVOList);
  65. /**
  66. * 刷新抽奖奖品详情缓存
  67. */
  68. this.refreshLotteryItemDetailCache(lotteryActivityId, lotteryItemVOList, null);
  69. /**
  70. * 刷新活动列表缓存
  71. */
  72. this.refreshLotteryActivityListCache();
  73. /**
  74. * 刷新历史抽奖活动缓存
  75. */
  76. this.refreshLotteryActivityHistoryCache();
  77. /**
  78. * 刷新最新一期活动id缓存
  79. */
  80. this.refreshLastLotteryActivityIdCache();
  81. /**
  82. * 刷新活动详情缓存
  83. */
  84. return refreshLotteryActivityDetailCache(entity, lotteryItemVOList);
  85. }
  86. /**
  87. * 查询最新一期活动id
  88. *
  89. * @return
  90. */
  91. public String getLastLotteryActivityIdFromCache() {
  92. String key = RedisKeyEnum.LOTTERY_ACTIVITY_LAST_ID_CACHE.getKey();
  93. String activityId = redisService.get(key);
  94. if (StringUtils.isNotBlank(activityId)) {
  95. return activityId;
  96. }
  97. return this.refreshLastLotteryActivityIdCache();
  98. }
  99. /**
  100. * 刷新最新一期活动ID缓存
  101. *
  102. * @return
  103. */
  104. private String refreshLastLotteryActivityIdCache() {
  105. String key = RedisKeyEnum.LOTTERY_ACTIVITY_LAST_ID_CACHE.getKey();
  106. String activityId = lotteryActivityDAO.findByLastLotteryActivity();
  107. redisService.remove(key);
  108. if (StringUtils.isNotBlank(activityId)) {
  109. redisService.set(key, activityId, RedisKeyEnum.LOTTERY_ACTIVITY_LAST_ID_CACHE.getExpireTime());
  110. }
  111. return activityId;
  112. }
  113. /**
  114. * 刷新历史抽奖活动缓存
  115. */
  116. private void refreshLotteryActivityHistoryCache() {
  117. List<LotteryActivityEntity> lotteryActivityEntities =
  118. lotteryActivityDAO.findAllByStatusNotOrderByCreateDateDesc(
  119. LotteryConstants.LotteryActivityStatus.DELETED.getStatus());
  120. String key = RedisKeyEnum.LOTTERY_ACTIVITY_HISTORY_CACHE.getKey();
  121. if (CollectionUtils.isEmpty(lotteryActivityEntities)) {
  122. redisService.remove(key);
  123. return;
  124. }
  125. Set<ZSetOperations.TypedTuple<String>> tuples = lotteryActivityEntities.stream().map(
  126. activity -> {
  127. String value = String.valueOf(activity.getActivityId());
  128. Double score = Double.valueOf(activity.getId());
  129. return new DefaultTypedTuple<>(value, score);
  130. }).collect(Collectors.toSet());
  131. redisService.zadd(key, tuples, RedisKeyEnum.LOTTERY_ACTIVITY_HISTORY_CACHE.getExpireTime());
  132. }
  133. /**
  134. * 查询历史活动
  135. * @param lastId
  136. * @param size
  137. * @return
  138. */
  139. public List<LotteryActivityBaseVO> queryHistoryActivityFromCache(Long lastId, int size) {
  140. String key = RedisKeyEnum.LOTTERY_ACTIVITY_HISTORY_CACHE.getKey();
  141. Long maxScore = lastId == null ? Long.MAX_VALUE : lastId;
  142. Set<String> activityIdStrSet = redisService.zreverseRangeByScore(key, maxScore, 0, size);
  143. if (CollectionUtils.isEmpty(activityIdStrSet)) {
  144. return Collections.emptyList();
  145. }
  146. return activityIdStrSet.stream().map(id -> {
  147. return this.getLotteryActivityDetailFromCache(id);
  148. }).collect(Collectors.toList());
  149. }
  150. /**
  151. * 刷新抽奖活动详情缓存
  152. *
  153. * @param entity
  154. * @param lotteryItemVOList
  155. * @return
  156. */
  157. private LotteryActivityVO refreshLotteryActivityDetailCache(LotteryActivityEntity entity,
  158. List<LotteryItemVO> lotteryItemVOList) {
  159. String lotteryActivityId = entity.getActivityId();
  160. RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_DETAIL_CACHE;
  161. String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
  162. LotteryActivityVO lotteryActivityVO = this.covertLotteryActivityVO(entity);
  163. lotteryActivityVO.setItems(lotteryItemVOList);
  164. lotteryActivityVO.setStatusName(LotteryConstants.getLotteryActivityStatusName(entity.getStatus()));
  165. redisService.set(key, JsonUtil.toJsonString(lotteryActivityVO), lotteryActivityCacheKey.getExpireTime());
  166. return lotteryActivityVO;
  167. }
  168. /**
  169. * 刷新抽奖活动奖品库存余量缓存
  170. *
  171. * @param lotteryActivityId
  172. * @param lotteryItemVOList
  173. * @return MAP<itemId, stock>
  174. */
  175. public Map<Long, Integer> refreshLotteryItemStockCache(String lotteryActivityId,
  176. List<LotteryItemVO> lotteryItemVOList) {
  177. RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_STOCK_CACHE;
  178. String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
  179. if (CollectionUtils.isEmpty(lotteryItemVOList)) {
  180. redisService.remove(key);
  181. return Collections.emptyMap();
  182. }
  183. Map<Long, Integer> lotteryItemStockMap = new ConcurrentHashMap<>();
  184. lotteryItemVOList.stream().forEach(item -> {
  185. lotteryItemStockMap.put(item.getId(), item.getStock());
  186. });
  187. redisService.set(key, JsonUtil.toJsonString(lotteryItemStockMap), lotteryActivityCacheKey.getExpireTime());
  188. return lotteryItemStockMap;
  189. }
  190. /**
  191. * 刷新抽奖奖品详情缓存
  192. *
  193. * @param lotteryActivityId
  194. * @param lotteryItemVOList
  195. */
  196. public void refreshLotteryItemDetailCache(String lotteryActivityId, List<LotteryItemVO> lotteryItemVOList) {
  197. this.refreshLotteryItemDetailCache(lotteryActivityId, lotteryItemVOList, null);
  198. }
  199. public LotteryItemVO refreshLotteryItemDetailCache(String lotteryActivityId, List<LotteryItemVO> lotteryItemVOList,
  200. Long itemId) {
  201. RedisKeyEnum lotteryActivityItemDetailKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_DETAIL_CACHE;
  202. String key = lotteryActivityItemDetailKey.getKey(lotteryActivityId);
  203. if (CollectionUtils.isEmpty(lotteryItemVOList)) {
  204. redisService.remove(key);
  205. return null;
  206. }
  207. LotteryItemVO backItem = null;
  208. for (LotteryItemVO lotteryItem : lotteryItemVOList) {
  209. redisService.hset(key, lotteryItem.getId().toString(),
  210. JsonUtil.toJsonString(lotteryItem), lotteryActivityItemDetailKey.getExpireTime());
  211. if (itemId != null && itemId.equals(lotteryItem.getId())) {
  212. backItem = lotteryItem;
  213. }
  214. }
  215. return backItem;
  216. }
  217. /**
  218. * 查询抽奖商品信息
  219. *
  220. * @param lotteryActivityId
  221. * @param itemId
  222. * @return
  223. */
  224. public LotteryItemVO getLotteryItemVOFromCache(String lotteryActivityId, Long itemId) {
  225. RedisKeyEnum lotteryActivityItemDetailKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_DETAIL_CACHE;
  226. String key = lotteryActivityItemDetailKey.getKey(lotteryActivityId);
  227. String cache = redisService.hget(key, String.valueOf(itemId));
  228. if (StringUtils.isNotBlank(cache)) {
  229. return JsonUtil.fromJson(cache, LotteryItemVO.class);
  230. }
  231. List<LotteryItemVO> lotteryItemVOList = this.getLotteryItemVoListFromDB(lotteryActivityId);
  232. return refreshLotteryItemDetailCache(lotteryActivityId, lotteryItemVOList, itemId);
  233. }
  234. /**
  235. * 查询抽奖活动奖品库存数据
  236. *
  237. * @param lotteryActivityId
  238. * @return
  239. */
  240. public Map<Long, Integer> getLotteryItemStockFromCache(String lotteryActivityId) {
  241. RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_STOCK_CACHE;
  242. String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
  243. String stockCache = redisService.get(key);
  244. if (StringUtils.isNotBlank(stockCache)) {
  245. return JsonUtil.fromJson(stockCache, new TypeReference<ConcurrentHashMap<Long, Integer>>() {
  246. });
  247. }
  248. return Collections.emptyMap();
  249. }
  250. /**
  251. * 抽奖出结果后扣减库存
  252. *
  253. * @param lotteryActivityId
  254. * @param itemId
  255. */
  256. public void deductItemStock(String lotteryActivityId, Long itemId) {
  257. // 这里目前不是原子性操作,在高并发情况下可能会有问题
  258. RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_STOCK_CACHE;
  259. String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
  260. Map<Long, Integer> stockMap = this.getLotteryItemStockFromCache(lotteryActivityId);
  261. Assert.isTrue(MapUtils.isNotEmpty(stockMap), "库存为空");
  262. stockMap.put(itemId, stockMap.get(itemId) - 1);
  263. redisService.set(key, JsonUtil.toJsonString(stockMap), lotteryActivityCacheKey.getExpireTime());
  264. }
  265. /**
  266. * 查询活动下的奖品列表
  267. *
  268. * @param activityId
  269. * @return
  270. */
  271. private List<LotteryItemVO> getLotteryItemVoListFromDB(String activityId) {
  272. List<LotteryItemEntity> lotteryItemEntities = lotteryItemDAO.findAllByActivityIdOrderBySlotAsc(activityId);
  273. if (CollectionUtils.isEmpty(lotteryItemEntities)) {
  274. return Collections.emptyList();
  275. }
  276. return lotteryItemEntities.stream().map(item -> {
  277. LotteryItemVO lotteryItemVO = new LotteryItemVO();
  278. BeanUtils.copyProperties(item, lotteryItemVO);
  279. lotteryItemVO.setTypeName(LotteryConstants.getLotteryActivityItemTypeName(item.getType()));
  280. return lotteryItemVO;
  281. }).collect(Collectors.toList());
  282. }
  283. private LotteryActivityVO covertLotteryActivityVO(LotteryActivityEntity entity) {
  284. LotteryActivityVO lotteryActivityVO = new LotteryActivityVO();
  285. BeanUtils.copyProperties(entity, lotteryActivityVO);
  286. lotteryActivityVO.setCreateTime(entity.getCreateDate().getTime());
  287. return lotteryActivityVO;
  288. }
  289. /**
  290. * 查询最新一期活动
  291. *
  292. * @return
  293. */
  294. public LotteryActivityVO getLastLotteryActivityDetailFromCache() {
  295. String activityId = this.getLastLotteryActivityIdFromCache();
  296. if (StringUtils.isBlank(activityId)) {
  297. return null;
  298. }
  299. return this.getLotteryActivityDetailFromCache(activityId);
  300. }
  301. /**
  302. * 查询抽奖活动详情
  303. *
  304. * @param lotteryActivityId
  305. * @return
  306. */
  307. public LotteryActivityVO getLotteryActivityDetailFromCache(String lotteryActivityId) {
  308. RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_DETAIL_CACHE;
  309. String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
  310. String lotteryActivityCache = redisService.get(key);
  311. LotteryActivityVO lotteryActivity;
  312. if (StringUtils.isNotBlank(lotteryActivityCache)) {
  313. lotteryActivity = JsonUtil.fromJson(lotteryActivityCache, LotteryActivityVO.class);
  314. } else {
  315. lotteryActivity = this.refreshLotteryActivityCacheById(lotteryActivityId);
  316. }
  317. return lotteryActivity;
  318. }
  319. /**
  320. * 刷新抽奖活动缓存
  321. */
  322. private void refreshLotteryActivityListCache() {
  323. }
  324. }