...
|
...
|
@@ -2,6 +2,7 @@ package com.yohoufo.promotion.service.impl; |
|
|
|
|
|
import com.google.common.collect.Lists;
|
|
|
import com.google.common.collect.Range;
|
|
|
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
|
|
|
import com.yoho.error.ServiceError;
|
|
|
import com.yoho.error.exception.ServiceException;
|
|
|
import com.yohobuy.ufo.model.promotion.UserCouponsBo;
|
...
|
...
|
@@ -14,6 +15,9 @@ import com.yohobuy.ufo.model.promotion.request.UserCouponListReq; |
|
|
import com.yohobuy.ufo.model.promotion.response.CouponInfo;
|
|
|
import com.yohobuy.ufo.model.promotion.response.CouponInfoListBo;
|
|
|
import com.yohobuy.ufo.model.promotion.response.CouponSendBo;
|
|
|
import com.yohoufo.common.exception.UfoServiceException;
|
|
|
import com.yohoufo.common.lock.RedisLock;
|
|
|
import com.yohoufo.common.lock.RedisLockFactory;
|
|
|
import com.yohoufo.common.utils.DateUtil;
|
|
|
import com.yohoufo.dal.promotion.CouponMapper;
|
|
|
import com.yohoufo.dal.promotion.UserCouponMapper;
|
...
|
...
|
@@ -32,6 +36,7 @@ import java.time.LocalDateTime; |
|
|
import java.time.LocalTime;
|
|
|
import java.time.ZoneOffset;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
...
|
...
|
@@ -53,6 +58,9 @@ public class CouponServiceImpl implements ICouponService { |
|
|
@Autowired
|
|
|
SingleCentCouponService singleCentSyncCoupNumService;
|
|
|
|
|
|
@Autowired
|
|
|
private RedisLockFactory redisLockFactory;
|
|
|
|
|
|
|
|
|
private final Logger logger = LoggerFactory.getLogger(getClass());
|
|
|
|
...
|
...
|
@@ -109,7 +117,7 @@ public class CouponServiceImpl implements ICouponService { |
|
|
validityRange = Range.open(now, end);
|
|
|
}
|
|
|
|
|
|
return sendCoupons(uid, couponTokens.stream().collect(Collectors.joining(",")), repeatable, validityRange);
|
|
|
return sendCoupons(uid, couponTokens.stream().collect(Collectors.joining(",")), repeatable, validityRange, true);
|
|
|
}
|
|
|
|
|
|
|
...
|
...
|
@@ -118,6 +126,8 @@ public class CouponServiceImpl implements ICouponService { |
|
|
|
|
|
// 允许重复发放
|
|
|
boolean repeatable;
|
|
|
//是否需要校验可领取时间
|
|
|
boolean receiveStartTimeChkNecessary = false;
|
|
|
// 有效期
|
|
|
Range<Integer> validityRange;
|
|
|
|
...
|
...
|
@@ -129,15 +139,20 @@ public class CouponServiceImpl implements ICouponService { |
|
|
}else if(CouponSendType.PLATFORM_SEND_COUPON.equals(sendType)){
|
|
|
repeatable = true;
|
|
|
validityRange = null;
|
|
|
} else {
|
|
|
} else {//用户领取
|
|
|
repeatable = false;
|
|
|
validityRange = null;
|
|
|
receiveStartTimeChkNecessary = true;
|
|
|
}
|
|
|
|
|
|
return sendCoupons(uid, couponTokens, repeatable, validityRange);
|
|
|
return sendCoupons(uid, couponTokens, repeatable, validityRange, receiveStartTimeChkNecessary);
|
|
|
}
|
|
|
|
|
|
private List<CouponSendBo> sendCoupons(Integer uid, String couponTokens, boolean repeatable, Range<Integer> validityRange) {
|
|
|
private List<CouponSendBo> sendCoupons(Integer uid,
|
|
|
String couponTokens,
|
|
|
boolean repeatable,
|
|
|
Range<Integer> validityRange,
|
|
|
boolean receiveStartTimeChkNecessary) {
|
|
|
|
|
|
if (uid == null || uid.intValue() <=0
|
|
|
|| StringUtils.isEmpty(couponTokens)){
|
...
|
...
|
@@ -154,12 +169,16 @@ public class CouponServiceImpl implements ICouponService { |
|
|
CouponSendBo couponSendBo = new CouponSendBo();
|
|
|
couponSendBo.setCouponToken(couponToken);
|
|
|
try {
|
|
|
couponSendBo.setCouponCode(sendPerCoupon(uid, couponToken, repeatable,validityRange));
|
|
|
couponSendBo.setCouponCode(sendPerCoupon(uid, couponToken, repeatable,validityRange, receiveStartTimeChkNecessary));
|
|
|
couponSendBo.setSuccess(true);
|
|
|
} catch (ServiceException ex) {
|
|
|
logger.info("happened serviceException,uid:{},couponTokens:{}", uid, couponTokens, ex);
|
|
|
couponSendBo.setErrCode(ex.getCode());
|
|
|
couponSendBo.setErrMsg(ex.getServiceError().getMappingGatewayError().getRight());
|
|
|
} catch (UfoServiceException e) {
|
|
|
logger.warn("happened ufoServiceException,uid:{},couponTokens:{}", uid, couponTokens, e);
|
|
|
couponSendBo.setErrCode(e.getCode());
|
|
|
couponSendBo.setErrMsg(e.getErrorMessage());
|
|
|
} catch (Exception e) {
|
|
|
logger.warn("happened exception,uid:{},couponTokens:{}", uid, couponTokens, e);
|
|
|
couponSendBo.setErrCode(ServiceError.PROMOTION_COUPON_SEND_FAIL.getCode());
|
...
|
...
|
@@ -171,23 +190,45 @@ public class CouponServiceImpl implements ICouponService { |
|
|
return couponSendBoList;
|
|
|
}
|
|
|
|
|
|
private String sendPerCoupon(Integer uid, String couponToken, boolean repeatable, Range<Integer> validityRange) {
|
|
|
private void checkReceiveStartTime(int uid, Coupon coupon){
|
|
|
Integer receiveStartTime;
|
|
|
Integer currentDT = DateUtil.getCurrentTimeSecond();
|
|
|
if (Objects.nonNull(receiveStartTime = coupon.getReceiveStartTime())
|
|
|
&& currentDT < receiveStartTime){
|
|
|
logger.warn("sendCoupon find current time not reach receiveStartTime,uid {} couponToken {} [{} {}]", uid, coupon.getCouponToken(), currentDT, receiveStartTime);
|
|
|
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_ARRIVE_GET_START_TIME);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private String sendPerCoupon(Integer uid,
|
|
|
String couponToken,
|
|
|
boolean repeatable,
|
|
|
Range<Integer> validityRange,
|
|
|
boolean receiveStartTimeChkNecessary) {
|
|
|
|
|
|
logger.info("senCoupon enter,{},{},{}", uid, couponToken, repeatable);
|
|
|
logger.info("sendCoupon enter,{},{},{},{},{}", uid, couponToken, repeatable, validityRange, receiveStartTimeChkNecessary);
|
|
|
|
|
|
// 根据 couponToken 获取coupon&couponType信息
|
|
|
CouponAndType couponAndType = couponCacheService.getCouponAndType(couponToken);
|
|
|
if (couponAndType == null){
|
|
|
logger.warn("query coupon info,coupontype is null:{}", couponToken);
|
|
|
logger.warn("sendCoupon query coupon info,coupontype is null:{}", couponToken);
|
|
|
throw new ServiceException(ServiceError.PROMOTION_COUPON_HAS_NOT_EXISTS);
|
|
|
}
|
|
|
|
|
|
Coupon coupon = couponAndType.getCoupon();
|
|
|
//check receive start time
|
|
|
if(receiveStartTimeChkNecessary){
|
|
|
//
|
|
|
checkReceiveStartTime(uid, coupon);
|
|
|
}
|
|
|
|
|
|
RedisLock redisLock = null;
|
|
|
if(!repeatable){
|
|
|
// 校验是否可以领取(重复领取,时间是否合法,状态)
|
|
|
checkCanAcquire(uid, couponToken, couponAndType);
|
|
|
redisLock = checkCanAcquire(uid, couponToken, couponAndType);
|
|
|
}
|
|
|
|
|
|
if (couponAndType.getCoupon().getStatus() != CouponsStatusEnum.VALID.getCode()){
|
|
|
if (coupon.getStatus() != CouponsStatusEnum.VALID.getCode()){
|
|
|
logger.info("couponrulebo status is not valid:{},{}",uid, couponToken);
|
|
|
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
|
|
|
}
|
...
|
...
|
@@ -208,19 +249,16 @@ public class CouponServiceImpl implements ICouponService { |
|
|
}
|
|
|
|
|
|
int count = 0;
|
|
|
// 不可重复
|
|
|
if (!repeatable){
|
|
|
count = userCouponMapper.insertWhere(userCoupon);
|
|
|
}else{
|
|
|
count = userCouponMapper.insert(userCoupon);
|
|
|
}
|
|
|
|
|
|
count = userCouponMapper.insert(userCoupon);
|
|
|
if (count == 0){
|
|
|
logger.info("can not repeatable acquire coupon. uid is {}, coupon is {}", uid, couponToken);
|
|
|
throw new ServiceException(ServiceError.PROMOTION_COUPON_HAS_RECEIVED);
|
|
|
}
|
|
|
|
|
|
logger.info("senCoupon success,{},{},{},{}", uid, couponToken, couponCode, count);
|
|
|
if(Objects.nonNull(redisLock)){
|
|
|
redisLock.unlock();
|
|
|
}
|
|
|
return couponCode;
|
|
|
}
|
|
|
|
...
|
...
|
@@ -317,13 +355,22 @@ public class CouponServiceImpl implements ICouponService { |
|
|
}
|
|
|
|
|
|
|
|
|
private void checkCanAcquire(Integer uid, String couponToken, CouponAndType couponAndType) {
|
|
|
private RedisLock checkCanAcquire(Integer uid, String couponToken, CouponAndType couponAndType) {
|
|
|
// 验证该用户是否重复领取
|
|
|
UserCoupon userCoupon = userCouponMapper.selectByUidAndToken(uid, couponToken);
|
|
|
|
|
|
RedisLock redisLock = redisLockFactory.newLock(RedisKeyBuilder.newInstance()
|
|
|
.appendFixed("ufo:promotion:coupons:checkCanAcquire:")
|
|
|
.appendVar(uid + "-" + couponToken), 3, TimeUnit.SECONDS);
|
|
|
if (!redisLock.tryLock()) {
|
|
|
logger.info("{} checkCanAcquire fail, it already in the process", uid);
|
|
|
throw new UfoServiceException(400, "优惠券领取中");
|
|
|
}
|
|
|
if (userCoupon != null){
|
|
|
logger.warn("user has acquried this coupon before:{},{}", uid, couponToken);
|
|
|
throw new ServiceException(ServiceError.PROMOTION_COUPON_HAS_RECEIVED);
|
|
|
}
|
|
|
return redisLock;
|
|
|
|
|
|
}
|
|
|
|
...
|
...
|
|