From 6c3c5f3fb28cc65684e065b60a09768cf8a77429 Mon Sep 17 00:00:00 2001 From: peng <peng.com> Date: 星期一, 29 九月 2025 23:45:13 +0800 Subject: [PATCH] 添加抽奖次数加锁 --- framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeServiceImpl.java | 356 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 306 insertions(+), 50 deletions(-) diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeServiceImpl.java b/framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeServiceImpl.java index 60f23e6..b550cc6 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/lmk/service/impl/PrizeServiceImpl.java @@ -5,6 +5,7 @@ import cn.lili.common.properties.RocketmqCustomProperties; import cn.lili.common.security.AuthUser; import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.BeanUtil; import cn.lili.common.utils.StringUtils; import cn.lili.modules.lmk.domain.entity.*; import cn.lili.modules.lmk.domain.form.AddPrizeNumForm; @@ -12,20 +13,28 @@ import cn.lili.modules.lmk.domain.vo.PrizeDetailVO; import cn.lili.modules.lmk.domain.vo.PrizeProbabilityVO; import cn.lili.modules.lmk.domain.vo.PrizeRecordTimeVO; +import cn.lili.modules.lmk.domain.vo.PrizeResultVO; import cn.lili.modules.lmk.enums.general.*; import cn.lili.modules.lmk.service.*; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.service.CouponService; import cn.lili.mybatis.BaseEntity; import cn.lili.rocketmq.RocketmqSendCallbackBuilder; import cn.lili.utils.COSUtil; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; +import org.springframework.beans.BeanUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -42,6 +51,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +@Slf4j @Service @RequiredArgsConstructor public class PrizeServiceImpl implements PrizeService { @@ -52,11 +62,18 @@ private final PrizeRecordService prizeRecordService; private static final String PRIZE_PREFIX = "prize_draw:"; private static final String PRIZE_NUMBER = "prize_number:"; + private static final String PRIZE_ADD_NUM = "prize_add_num:"; private final RedisTemplate<String, String> redisTemplate; private final RedissonClient redissonClient; private final RocketmqCustomProperties rocketmqCustomProperties; private final RocketMQTemplate rocketMQTemplate; private final COSUtil cosUtil; + private final CouponService couponService; + private final AddPrizeRuleService addPrizeRuleService; + private final ActionRecordService actionRecordService; + private final ShareActionService shareActionService; + private final AddNumCheekService addNumCheekService; + private final OrderService orderService; @Override @Transactional(rollbackFor = Exception.class) @@ -216,6 +233,12 @@ } BigDecimal max = currentProbability.multiply(BigDecimal.valueOf(100)); BigDecimal bigDecimal = generateRandom(BigDecimal.ONE, max); + try { + log.info("鎶藉鍙风爜涓�---------------------------->{}", bigDecimal); + log.info("鎶藉姒傜巼闆嗗悎涓�------------------------->{}", JSONObject.toJSONString(prizeProbabilityList)); + } catch (Exception e) { + log.error("鎵撳嵃鎶藉淇℃伅鎶ラ敊----------------------->", e); + } for (PrizeProbabilityVO prizeProbabilityVO : prizeProbabilityList) { BigDecimal minP = prizeProbabilityVO.getProbability()[0][0]; BigDecimal maxP = prizeProbabilityVO.getProbability()[0][1]; @@ -223,6 +246,31 @@ prizeWon = prizeProbabilityVO.getActivityPrizeRefId(); break; } + } + Coupon coupon = null; + //鏍¢獙浼樻儬鍗锋槸鍚﹀厑璁哥敤鎴峰棰嗕笉鍏佽褰撴湭涓澶勭悊 + if (prizeWon != null) { + ActivityRefPrize activityRefPrize = canPrizeMap.get(prizeWon); + String prizeDrawId = activityRefPrize.getPrizeId(); + PrizeDraw prizeDraw = prizeDrawService.getById(prizeDrawId); + String couponId = prizeDraw.getCouponId(); + coupon = couponService.getById(couponId); + if (coupon == null) { + prizeWon = null; + } else { + Integer couponLimitNum = coupon.getCouponLimitNum(); + //鑾峰彇鐢ㄦ埛杩欎釜浼樻儬鍗风殑涓鎯呭喌 + LambdaQueryWrapper<PrizeRecord> eq = Wrappers.<PrizeRecord>lambdaQuery() + .eq(PrizeRecord::getUserId, userId) + .eq(PrizeRecord::getPrizeActivityId, prizeId) + .eq(PrizeRecord::getPrizeId, prizeDrawId); + int size = prizeRecordService.list(eq).size(); + if ( couponLimitNum>0 && size >= couponLimitNum) { + log.info("瑙﹀彂浼樻儬鍗烽鍙栭檺鍒�---------->{}",prizeWon); + prizeWon = null; + } + } + } //鏈腑濂栫殑鎯呭喌 if (prizeWon == null) { @@ -290,6 +338,7 @@ // if (!lock.equals(nowLock)) { // throw new RuntimeException("褰撳墠娲诲姩澶伀鐖嗕簡璇风◢鍚庡啀璇�"); // } + String couponId = coupon.getId(); // 璧癿q寮傛澶勭悊 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override @@ -298,7 +347,10 @@ rocketMQTemplate.asyncSend(destination, JSON.toJSONString(prizeRecord), RocketmqSendCallbackBuilder.commonCallback()); } }); - return Result.ok().data(activityRefPrize); + PrizeResultVO prizeResultVO = new PrizeResultVO(); + BeanUtils.copyProperties(activityRefPrize, prizeResultVO); + prizeResultVO.setCouponId(couponId); + return Result.ok().data(prizeResultVO); } public static BigDecimal generateRandom(BigDecimal min, BigDecimal max) { @@ -346,13 +398,26 @@ try { lock.lock(); prizeNumberList = getPrizeNumberList(prizeId, userId); + //榛樿鐢熸垚 + boolean needGenerate = true; + for (PrizeNumber prizeNumber : prizeNumberList) { + String userAction = prizeNumber.getUserAction(); + if (PrizeUserActionEnum.SYSTEM.name().equals(userAction)) { + needGenerate = false; + break; + } + } // 褰撳墠鐢ㄦ埛娌℃湁鍒濆鍖栨娊濂栨暟鎹渶瑕佸垵濮嬪寲褰撳ぉ鎶藉鏁版嵁 - if (prizeNumberList == null || prizeNumberList.isEmpty()) { - //娌℃湁榛樿鎶藉娆℃暟鐩存帴杩斿洖0娆� + if (prizeNumberList.isEmpty() || needGenerate) { + //娌℃湁榛樿鎶藉娆℃暟涓嶇敓鎴愭娊濂栨鏁� + int size = prizeNumberList.size(); if (prizeNum == null || prizeNum <= 0) { - return Result.ok().data(0); + return Result.ok().data(size); } prizeNumberList = new ArrayList<>(); + if (size+prizeNum>activity.getMaxPrize()){ + prizeNum = activity.getMaxPrize()-size; + } //璁剧疆榛樿鎶藉娆℃暟骞惰繑鍥為粯璁ゆ娊濂栨鏁� for (int i = 0; i < prizeNum; i++) { PrizeNumber prizeNumber = new PrizeNumber(); @@ -364,13 +429,9 @@ } //娣诲姞鎶藉娆℃暟骞惰繑鍥� prizeNumberService.saveBatch(prizeNumberList); - return Result.ok().data(prizeNumberList.size()); + return Result.ok().data(prizeNumberList.size()+size); } - } finally { - if (lock.isHeldByCurrentThread()) { - lock.unlock(); - } - } + Integer maxPrize = activity.getMaxPrize(); //鍏朵粬鎯呭喌 int useNum = 0; @@ -387,6 +448,24 @@ return Result.ok().data(0); } else { return Result.ok().data(userPrizeNum > maxPrize ? maxPrize - useNum : notUseNum); + } + } finally { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + + @Override + public void afterCompletion(int status) { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + }); + } } @@ -445,49 +524,226 @@ } @Override + @Transactional(rollbackFor = Exception.class) public Result addPrizeNum(AddPrizeNumForm addPrizeNumForm) { - PrizeActivity activity = prizeActivityService.getById(addPrizeNumForm.getPrizeActivityId()); - if (activity == null) { - throw new ServiceException("褰撳墠娲诲姩涓嶅瓨鍦�"); + //鎸囧畾娲诲姩缁欐寚瀹氭椿鍔ㄦ坊鍔犳病鎸囧畾娲诲姩缁欓粯璁ゆ椿鍔ㄦ坊鍔� + String prizeActivityId = addPrizeNumForm.getPrizeActivityId(); + if (StringUtils.isBlank(prizeActivityId)) { + LambdaQueryWrapper<PrizeActivity> pop = Wrappers.<PrizeActivity>lambdaQuery() + .eq(PrizeActivity::getEnableStatus, PrizeActivityStatusEnum.ON.name()) + .eq(PrizeActivity::getPopup, true); + PrizeActivity one = prizeActivityService.getOne(pop); + if (one == null) { + return Result.ok().data(0); + } + addPrizeNumForm.setPrizeActivityId(one.getId()); } - if (!PrizeActivityStatusEnum.ON.name().equals(activity.getEnableStatus())) { - throw new ServiceException("褰撳墠娲诲姩娌″紑鍚�"); - } - Date date = new Date(); - //娲诲姩缁撴潫涓嶉渶瑕佹坊鍔� - if (date.after(activity.getEndTime())) { - throw new ServiceException("褰撳墠娲诲姩宸茬粨鏉�"); - } - Integer maxPrize = activity.getMaxPrize(); - String userId = addPrizeNumForm.getUserId(); - List<PrizeNumber> prizeNumberList = getPrizeNumberList(activity.getId(), userId); - if (prizeNumberList.size() >= maxPrize) { - throw new ServiceException("鎶藉娆℃暟宸茶揪鍒颁笂闄愪簡"); - } - PrizeUserActionEnum actionEnum = PrizeUserActionEnum.select(addPrizeNumForm.getAddType()); - if (actionEnum == null) { - throw new ServiceException("褰撳墠绫诲瀷涓嶅瓨鍦�"); - } - Boolean check ; - //todo 杩涜鏁版嵁鏍¢獙 - switch (actionEnum) { - case BUY: - System.err.println("BUY"); - break; - case SHARE: - //鐩存帴鑾峰緱鎶藉娆℃暟 - System.err.println("SHARE"); - break; - case SEE_SHOP: - System.err.println("SEE_SHOP"); - break; - case SEE_VIDEO: - System.err.println("SEE_VIDEO"); - break; - default: - break; + AuthUser currentUser = UserContext.getCurrentUser(); + if (currentUser == null) { + return Result.ok().data(0); } - return null; + //鎸囧畾鐢ㄦ埛缁欐寚瀹氱敤鎴锋坊鍔犳病鎸囧畾娲诲姩缁欏綋鍓嶇櫥褰曠敤鎴锋坊鍔� + if (StringUtils.isBlank(addPrizeNumForm.getUserId())) { + addPrizeNumForm.setUserId(currentUser.getId()); + + } + String userId = addPrizeNumForm.getUserId(); + RLock lock = redissonClient.getLock(PRIZE_ADD_NUM + userId); + try { + + lock.lock(); + PrizeActivity activity = prizeActivityService.getById(addPrizeNumForm.getPrizeActivityId()); + if (activity == null) { + throw new ServiceException("褰撳墠娲诲姩涓嶅瓨鍦�"); + } + if (!PrizeActivityStatusEnum.ON.name().equals(activity.getEnableStatus())) { + throw new ServiceException("褰撳墠娲诲姩娌″紑鍚�"); + } + Date date = new Date(); + //娲诲姩缁撴潫涓嶉渶瑕佹坊鍔� + if (date.after(activity.getEndTime())) { + throw new ServiceException("褰撳墠娲诲姩宸茬粨鏉�"); + } + Integer maxPrize = activity.getMaxPrize(); + List<PrizeNumber> prizeNumberList = getPrizeNumberList(activity.getId(), userId); + if (prizeNumberList.size() >= maxPrize) { + throw new ServiceException("鎶藉娆℃暟宸茶揪鍒颁笂闄愪簡"); + } + PrizeUserActionEnum actionEnum = PrizeUserActionEnum.select(addPrizeNumForm.getAddType()); + if (actionEnum == null) { + throw new ServiceException("褰撳墠绫诲瀷涓嶅瓨鍦�"); + } + AddPrizeRule addPrizeRule = getAddPrizeRule(actionEnum.name()); + if (addPrizeRule == null) { + return Result.ok().data(0); + } + List<PrizeNumber> needAdd = new ArrayList<>(); + Integer addNum = addPrizeRule.getAddNum(); + if (addNum == null) { + return Result.ok().data(0); + } + //鍒よ娣诲姞鍚庢槸鍚﹀ぇ浜庢渶澶ф鏁� + if (prizeNumberList.size() + addNum >= maxPrize) { + addNum = maxPrize - prizeNumberList.size(); + } + boolean addFlag = false; + //鍒ゆ柇璇ョ被鍨嬪綋澶╂槸鍚︽坊鍔犺繃浜� + for (PrizeNumber prizeNumber : prizeNumberList) { + if (actionEnum.name().equals(prizeNumber.getUserAction())) { + addFlag = true; + break; + } + } + String extend = addPrizeNumForm.getExtend(); + JSONObject jsonObject = JSONObject.parseObject(extend); + LambdaQueryWrapper<AddNumCheek> query = Wrappers.lambdaQuery(); + AddNumCheek addNumCheek = null; + String orderSn = null; + Order order = null; + String ruleValue = null; + switch (actionEnum) { + case SHARE_GOODS_VIDEO: + String shareId = jsonObject.getString("shareId"); + ShareAction shareAction = shareActionService.getById(shareId); + if (shareAction == null) { + return Result.ok().data(0); + } + if (addFlag) { + log.info("褰撳墠鐢ㄦ埛褰撳ぉ宸茬粡娣诲姞杩囦簡鍒嗕韩娣诲姞娆℃暟浜�-------->{}", userId); + return Result.ok().data(0); + } + break; + case SHARE_USER_REGISTRY: + String memberId = jsonObject.getString("memberId"); + query.eq(AddNumCheek::getType, PrizeUserActionEnum.SHARE_USER_REGISTRY.name()) + .eq(AddNumCheek::getCheckNo, memberId); + if (addNumCheekService.getOne(query) != null) { + log.info("褰撳墠鐢ㄦ埛宸茬粡琚個璇疯繃浜�----------------->{}", memberId); + return Result.ok().data(0); + } + addNumCheek = new AddNumCheek(); + addNumCheek.setType(PrizeUserActionEnum.SHARE_USER_REGISTRY.name()); + addNumCheek.setUserId(userId); + addNumCheek.setCheckNo(memberId); + addNumCheekService.save(addNumCheek); + break; + case SHARE_USER_SHOPPING: + orderSn = jsonObject.getString("orderSn"); + //鏍¢獙璁㈠崟鏄惁瀛樺湪 + order = orderService.getBySn(orderSn); + if (order == null) { + log.info("鍒嗕韩鐢ㄦ埛璐墿璁㈠崟涓嶅瓨鍦▄}", orderSn); + return Result.ok().data(0); + } + query.eq(AddNumCheek::getType, PrizeUserActionEnum.SHARE_USER_SHOPPING.name()) + .eq(AddNumCheek::getCheckNo, orderSn); + if (addNumCheekService.getOne(query) != null) { + log.info("褰撳墠閭�璇疯鍗曞凡缁忚棰嗗彇杩囦簡----------------->{}", orderSn); + return Result.ok().data(0); + } + addNumCheek = new AddNumCheek(); + addNumCheek.setType(PrizeUserActionEnum.SHARE_USER_SHOPPING.name()); + addNumCheek.setUserId(userId); + addNumCheek.setCheckNo(orderSn); + addNumCheekService.save(addNumCheek); + break; + case USER_STAY_TIME: + //todo 閫氳繃鐢ㄦ埛琛屼负鍒嗘瀽鑾峰彇鏁版嵁杩涜鍒ゆ柇 + ruleValue = addPrizeRule.getRuleValue(); + + if (addFlag) { + log.info("褰撳墠鐢ㄦ埛褰撳ぉ宸茬粡娣诲姞杩囦簡鍋滅暀鏃堕棿娣诲姞娆℃暟浜�-------->{}", userId); + return Result.ok().data(0); + } + break; + case USER_BUY_SUM_PRICE: + orderSn = jsonObject.getString("orderSn"); + //鏍¢獙璁㈠崟鏄惁瀛樺湪 + order = orderService.getBySn(orderSn); + if (order == null) { + log.info("鐢ㄦ埛璐墿璁㈠崟涓嶅瓨鍦▄}", orderSn); + return Result.ok().data(0); + } + ruleValue = addPrizeRule.getRuleValue(); + double price = Double.parseDouble(ruleValue); + Double flowPrice = order.getFlowPrice(); + if (flowPrice < price) { + log.info("褰撳墠璐墿璁㈠崟閲戦灏忎簬娣诲姞瑕佹眰----------------->{}", orderSn); + return Result.ok().data(0); + } + query.eq(AddNumCheek::getType, PrizeUserActionEnum.USER_BUY_SUM_PRICE.name()) + .eq(AddNumCheek::getCheckNo, orderSn); + if (addNumCheekService.getOne(query) != null) { + log.info("褰撳墠璐墿璁㈠崟宸茬粡琚鍙栬繃浜�----------------->{}", orderSn); + return Result.ok().data(0); + } + addNumCheek = new AddNumCheek(); + addNumCheek.setType(PrizeUserActionEnum.USER_BUY_SUM_PRICE.name()); + addNumCheek.setUserId(userId); + addNumCheek.setCheckNo(orderSn); + addNumCheekService.save(addNumCheek); + break; + case USER_BUY_ORDER_NUM: + orderSn = jsonObject.getString("orderSn"); + //鏍¢獙璁㈠崟鏄惁瀛樺湪 + order = orderService.getBySn(orderSn); + if (order == null) { + log.info("鐢ㄦ埛璐墿鏁伴噺璁㈠崟涓嶅瓨鍦▄}", orderSn); + return Result.ok().data(0); + } + query.eq(AddNumCheek::getType, PrizeUserActionEnum.USER_BUY_ORDER_NUM.name()) + .eq(AddNumCheek::getCheckNo, orderSn); + if (addNumCheekService.getOne(query) != null) { + log.info("褰撳墠璐墿璁㈠崟鏁伴噺宸茬粡琚鍙栬繃浜�----------------->{}", orderSn); + return Result.ok().data(0); + } + addNumCheek = new AddNumCheek(); + addNumCheek.setType(PrizeUserActionEnum.USER_BUY_ORDER_NUM.name()); + addNumCheek.setUserId(userId); + addNumCheek.setCheckNo(orderSn); + addNumCheekService.save(addNumCheek); + break; + case USER_SCAN_STORE: + break; + case USER_PUBLISH_EXAMINE: + + break; + default: + return Result.ok().data(0); + } + //璁剧疆榛樿鎶藉娆℃暟骞惰繑鍥為粯璁ゆ娊濂栨鏁� + for (int i = 0; i < addNum; i++) { + PrizeNumber prizeNumber = new PrizeNumber(); + prizeNumber.setActivityPrizeId(Long.parseLong(addPrizeNumForm.getPrizeActivityId())); + prizeNumber.setUserId(Long.parseLong(userId)); + prizeNumber.setUserAction(actionEnum.name()); + prizeNumber.setUseStatus(PrizeNumberUseEnum.WAIT.name()); + needAdd.add(prizeNumber); + } + prizeNumberService.saveBatch(needAdd); + return Result.ok().data(0); + } finally { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + + @Override + public void afterCompletion(int status) { + if (lock.isHeldByCurrentThread()) { + lock.unlock(); + } + } + }); + } + } + + public AddPrizeRule getAddPrizeRule(String ruleCode) { + LambdaQueryWrapper<AddPrizeRule> one = Wrappers.<AddPrizeRule>lambdaQuery().eq(AddPrizeRule::getRuleCode, ruleCode); + return addPrizeRuleService.getOne(one); } } -- Gitblit v1.8.0