|
|
package com.yoho.activity.service.impl;
|
|
|
|
|
|
import com.google.common.collect.Lists;
|
|
|
import com.yoho.activity.common.redis.CacheKeyEnum;
|
|
|
import com.yoho.activity.common.redis.RedisValueCacheLoader;
|
|
|
import com.yoho.activity.common.utils.DateUtils;
|
|
|
import com.yoho.activity.dal.CouponExchangeActivityMapper;
|
|
|
import com.yoho.activity.dal.CouponExchangeDetailMapper;
|
|
|
import com.yoho.activity.dal.model.CouponExchangeActivity;
|
|
|
import com.yoho.activity.dal.model.CouponExchangeDetail;
|
|
|
import com.yoho.activity.service.transaction.CompensableCouponExchangeService;
|
|
|
import com.yoho.core.redis.cluster.lock.SimpleDistributedLock;
|
|
|
import com.yoho.core.redis.cluster.lock.SimpleDistributedLockFactory;
|
|
|
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
|
|
|
import com.yoho.error.ServiceError;
|
|
|
import com.yoho.error.exception.ServiceException;
|
|
|
import com.yoho.service.model.activity.CouponExchangeActivityVO;
|
|
|
import org.apache.commons.collections.CollectionUtils;
|
|
|
import org.apache.commons.collections.MapUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
* Created by wujiexiang on 20/5/29.
|
|
|
*/
|
|
|
@Service
|
|
|
public class CouponExchangeService {
|
|
|
|
|
|
static Logger logger = LoggerFactory.getLogger(CouponExchangeService.class);
|
|
|
|
|
|
@Autowired
|
|
|
private CouponExchangeActivityMapper couponExchangeActivityMapper;
|
|
|
|
|
|
@Autowired
|
|
|
private CouponExchangeDetailMapper couponExchangeDetailMapper;
|
|
|
|
|
|
@Autowired
|
|
|
private RedisValueCacheLoader redisValueCacheLoader;
|
|
|
|
|
|
@Autowired
|
|
|
private CompensableCouponExchangeService compensableCouponExchangeService;
|
|
|
|
|
|
@Autowired
|
|
|
private SimpleDistributedLockFactory simpleDistributedLockFactory;
|
|
|
|
|
|
@Autowired
|
|
|
private YohoCoinService yohoCoinService;
|
|
|
|
|
|
/**
|
|
|
* 查询
|
|
|
*
|
|
|
* @param tokens
|
|
|
* @return
|
|
|
*/
|
|
|
public List<CouponExchangeActivityVO> queryValidActivityList(int uid, List<String> tokens) {
|
|
|
if (CollectionUtils.isEmpty(tokens)) {
|
|
|
return Lists.newArrayList();
|
|
|
}
|
|
|
|
|
|
Map<String, CouponExchangeActivity> cacheMap = loadCouponExchangeActivityMap(tokens);
|
|
|
|
|
|
if (MapUtils.isEmpty(cacheMap)) {
|
|
|
logger.warn("not find any activity by {}", tokens);
|
|
|
return Lists.newArrayList();
|
|
|
}
|
|
|
|
|
|
//查询用户有货币数量
|
|
|
int userYohoCoinNum = yohoCoinService.getUserYohoCoinNum(uid);
|
|
|
|
|
|
return cacheMap.values().stream().map(activity -> {
|
|
|
//有货币数量是否足够
|
|
|
boolean supportExchange = activity.getExchangeYohoCoinNum() >= userYohoCoinNum;
|
|
|
return CouponExchangeActivityVO.builder().name(activity.getName())
|
|
|
.subName(activity.getSubName()).token(activity.getToken()).exchangeYohoCoinNum(activity.getExchangeYohoCoinNum())
|
|
|
.isSupportExchange(supportExchange ? "Y" : "N")
|
|
|
.description(supportExchange ? null : "有货币不足")
|
|
|
.build();
|
|
|
}).collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 兑换
|
|
|
*
|
|
|
* @param uid
|
|
|
* @param token
|
|
|
* @return
|
|
|
*/
|
|
|
public String exchange(int uid, String token) {
|
|
|
|
|
|
logger.info("[{} - {}] in exchange", uid, token);
|
|
|
|
|
|
CouponExchangeActivity activity = loadCouponExchangeActivity(token);
|
|
|
|
|
|
if (Objects.isNull(activity)) {
|
|
|
logger.warn("not find activity by {}", token);
|
|
|
throw new ServiceException(500, "兑换活动不存在");
|
|
|
}
|
|
|
|
|
|
if (shouldExchangeAgain(uid, token)) {
|
|
|
logger.warn("[{} - {}] can't exchange again", uid, token);
|
|
|
throw new ServiceException(500, "您已兑换过该券");
|
|
|
}
|
|
|
|
|
|
CacheKeyEnum cacheKey = CacheKeyEnum.USER_EXCHANGE_COUPON_LOCK;
|
|
|
|
|
|
SimpleDistributedLock simpleLock = createLock(uid, token, cacheKey);
|
|
|
// 加防并发领取锁
|
|
|
simpleLock.lockOrElseThrow(cacheKey.getExpire(), TimeUnit.SECONDS,
|
|
|
() -> new ServiceException(ServiceError.ACTIVITY_REQUEST_TOO_FREQUENT));
|
|
|
String couponCode;
|
|
|
try {
|
|
|
//补偿兑换
|
|
|
couponCode = compensableCouponExchangeService.exchange(uid, activity);
|
|
|
recordCouponExchangeDetail(uid, couponCode, activity);
|
|
|
} finally {
|
|
|
simpleLock.releaseLock();
|
|
|
}
|
|
|
return couponCode;
|
|
|
}
|
|
|
|
|
|
|
|
|
private Map<String, CouponExchangeActivity> loadCouponExchangeActivityMap(List<String> tokens) {
|
|
|
Map<String, CouponExchangeActivity> cacheMap = redisValueCacheLoader.load(CacheKeyEnum.COUPON_EXCHANGE_ACTIVITY, tokens, CouponExchangeActivity.class, createOriginCallBack());
|
|
|
return cacheMap;
|
|
|
}
|
|
|
|
|
|
private CouponExchangeActivity loadCouponExchangeActivity(String token) {
|
|
|
return loadCouponExchangeActivityMap(Lists.newArrayList(token)).get(token);
|
|
|
}
|
|
|
|
|
|
private RedisValueCacheLoader.OriginCallBack createOriginCallBack() {
|
|
|
return (keys) -> {
|
|
|
//只关注有效的活动
|
|
|
List<CouponExchangeActivity> couponExchangeActivities = couponExchangeActivityMapper.selectByTokens(keys);
|
|
|
return couponExchangeActivities.stream().collect(Collectors.toMap(activity -> activity.getToken(), activity -> activity));
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 能否再次兑换å
|
|
|
*
|
|
|
* @param uid
|
|
|
* @param token
|
|
|
* @return
|
|
|
*/
|
|
|
private boolean shouldExchangeAgain(int uid, String token) {
|
|
|
return couponExchangeDetailMapper.select(uid, token) != null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 创建锁ß
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
private SimpleDistributedLock createLock(int uid, String token, CacheKeyEnum cacheKey) {
|
|
|
RedisKeyBuilder lockKey = RedisKeyBuilder.newInstance().appendFixed(cacheKey.getCacheKey()).appendVarWithMH(uid, token);
|
|
|
|
|
|
SimpleDistributedLock simpleLock = simpleDistributedLockFactory.create(lockKey);
|
|
|
|
|
|
return simpleLock;
|
|
|
}
|
|
|
|
|
|
private void recordCouponExchangeDetail(int uid, String couponCode, CouponExchangeActivity activity) {
|
|
|
//记录兑换明细
|
|
|
try {
|
|
|
CouponExchangeDetail detail = new CouponExchangeDetail();
|
|
|
detail.setUid(uid);
|
|
|
detail.setToken(activity.getToken());
|
|
|
detail.setSendCouponId(activity.getSendCouponId());
|
|
|
detail.setExchangeYohoCoinNum(activity.getExchangeYohoCoinNum());
|
|
|
detail.setCouponCode(couponCode);
|
|
|
detail.setCreateTime(DateUtils.getCurrentTimeSecond());
|
|
|
couponExchangeDetailMapper.insert(detail);
|
|
|
} catch (Exception ex) {
|
|
|
logger.warn("[{} - {}] exception happened when record exchange detail for couponCode:{}", uid, activity.getToken(), couponCode);
|
|
|
}
|
|
|
}
|
|
|
} |
...
|
...
|
|