import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* LotteryActivityRedisService 描述
* 抽獎活動
* @author tomas
* @create 2018/10/24
**/
@Slf4j
@Service("lotteryActivityRedisServiceImpl")
public class LotteryActivityRedisServiceImpl extends ActivityRedisService implements LotteryActivityRedisService {
@Autowired
private RedissonClient redissonClient;
@Autowired
private ActivityInfoMapper mapper;
/**
* 初始化 活動信息
* @param activityCode
* @return
*/
@Override
public ResultModel init(String activityCode){
try {
ActivityModel activityInfo = mapper.selectByCode(activityCode);
if(null!=activityInfo ){
RBucket<ActivityModel> activityBucket = redissonClient.getBucket(String.format(ACTIVITY_KEY,activityCode));
activityBucket.set(activityInfo);
if(new Date().after(activityInfo.getBeginTime())){
return ResultModel.getError("非可初始化活動時間");
}
//1.初始化獎品
String awardPoolMapKey = String.format(ACTIVITY_PRIZE_POOL_MAP_KEY,activityCode);
Prize p1=Prize.builder().id(6).prizeName("30天免單券").prizeType("一等獎").prizeTotalNum(6).remainingPrize(6).prizeRate(0.002).build();
Prize p2=Prize.builder().id(7).prizeName("7天免單券").prizeType("二等獎").prizeTotalNum(30).remainingPrize(30).prizeRate(0.01).build();
Prize p3=Prize.builder().id(8).prizeName("7折優惠券").prizeType("三等獎").prizeTotalNum(0).remainingPrize(0).prizeRate(0.9988).def(true).build();
//Prize p4=Prize.builder().id(0L).prizeName("謝謝參與").prizeType("一等獎").prizeTotalNum(100000000L).remainingPrize(100000000L).prizeRate(0.001).build();
List<Prize> list=new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
RMapCache<Integer, Prize> mapCache = redissonClient.getMapCache(awardPoolMapKey);
for (int i = 0; i < list.size(); i++) {
//將獎項數據初始化到redis中
Prize p=list.get(i);
mapCache.put(p.getId(),p,10, TimeUnit.DAYS);
}
//todo 多線程操做/異步操做
System.out.println("數據加載完成");
}
return ResultModel.getSuccess("初始化活動成功");
}catch (Exception e){
log.error("init activity Error activityCode ={} msg={} ",activityCode,e.getMessage());
}
return ResultModel.getError("初始化活動失敗");
}
/**
* 根據 code 和 userId 檢查用戶參與活動狀況
* @param model
* @return
*/
public ResultModel check(ActivityParamModel model){
return ResultModel.getSuccess();
}
/**
* 參與抽獎
* @param model
* @return
*/
public ResultModel lottery(ActivityParamModel model){
/* 獲取活動獎品列表 */
List<Prize> selectPrizeList = new ArrayList<>();
//獲取可抽取的獎品及其的機率集合
Map<String, Object> optionalAwardMap = this.getAllAwardProbability(model.getCode());
//獲取可選的獎品集合
selectPrizeList = (List<Prize>) optionalAwardMap.get("selectPrizeList");
//獲取可選獎品集合的總機率
double probabilityTotal = (double) optionalAwardMap.get("probabilityTotal");
if (probabilityTotal < 1) {
//當中獎機率不等於100%時,補充默認獎品
selectPrizeList = this.supplementDefaultAward( selectPrizeList, probabilityTotal, model.getCode());
}
//生成抽獎用的機率集合
List<Double> probabilityResult = this.generatorAwardProbability(selectPrizeList);
if(probabilityResult.size()<=0){
return ResultModel.getError("沒有可抽獎獎品");
}
/* 實例化抽獎算法,並抽獎 */
AliasMethod aliasMethod = new AliasMethod(probabilityResult);
/* 開始抽獎 */
int index = aliasMethod.next();
// 返回抽中獎品
Prize selectPrize = selectPrizeList.get(index);
if (selectPrize != null) {
//判斷是不是默認的獎品,不是默認獎品 須要 redis 減Remaining 數量
if (!selectPrize.isDef()) {
String awardPoolMapKey = String.format(ACTIVITY_PRIZE_POOL_MAP_KEY,model.getCode());
//默認返回數量爲0
if (selectPrize.getRemainingPrize()!= null && selectPrize.getRemainingPrize() >= 1 ) {
RLock lock = redissonClient.getLock(awardPoolMapKey.concat(String.valueOf(selectPrize.getId())));
lock.lock(200, TimeUnit.MILLISECONDS);
selectPrize.setRemainingPrize(new AtomicInteger(selectPrize.getRemainingPrize()).decrementAndGet());
redissonClient.getMapCache(awardPoolMapKey).put(selectPrize.getId(),selectPrize);
lock.unlock();
}else {
return lottery(model);
}
}
//判斷是否 是安慰獎
if(!selectPrize.isComfort()){
}
}
return ResultModel.getSuccess(selectPrize);
}
/**
* 分享功能
* @param model
* @return
*/
public ResultModel share(ActivityParamModel model){
return ResultModel.getSuccess();
}
/**
* 結束 活動開關
* @param activityCode
* @return
*/
public ResultModel stop(String activityCode){
return ResultModel.getSuccess();
}
/**
* @param selectAwardList 獎品選項
* @return
* @Describe 生成獎品機率
*/
private List<Double> generatorAwardProbability(List<Prize> selectAwardList) {
List<Double> prob = new ArrayList<>();
try {
/* 遍歷全部獎品,取得獎品機率生成隨機算法計算規則 */
for (Prize prize : selectAwardList) {
double rate =prize.getPrizeRate().doubleValue();
prob.add(rate);
}
return prob;
} catch (Exception e) {
log.error(e.getMessage());
return new ArrayList<>();
}
}
/**
* @param selectAwardList
* @param probabilityTotal
* @return
* @Describe 補充默認的獎品
*/
private List<Prize> supplementDefaultAward(List<Prize> selectAwardList, double probabilityTotal,String activityCode) {
for (Prize prize : selectAwardList) {
if(prize.isDef()){
String awardPoolMapKey = String.format(ACTIVITY_PRIZE_POOL_MAP_KEY,activityCode);
BigDecimal bigDecimal = new BigDecimal(1 - probabilityTotal);
prize.setPrizeRate(prize.getPrizeRate()+bigDecimal.doubleValue());
RLock lock = redissonClient.getLock(awardPoolMapKey.concat(String.valueOf(prize.getId())).concat("supply"));
lock.lock(200, TimeUnit.MILLISECONDS);
redissonClient.getMapCache(awardPoolMapKey).put(prize.getId(),prize);
lock.unlock();
}
}
return selectAwardList;
}
/**
* @param activityCode 活動id
* @return
* @Describe 獲取全部的獎品機率
*/
private Map<String, Object> getAllAwardProbability(String activityCode) {
//返回的結果集
Map<String, Object> result = new HashMap<String, Object>();
/* 中獎率累計 */
double probabilityTotal = 0.0;
/* 獲取活動對應的獎品列表 */
List<Prize> prizeList = this.getPrizeList(activityCode);
List<Prize> selectPrizeList = new ArrayList<>();
/* 獎品結果集 */
for (Prize prize : prizeList) {
//查詢獎品剩餘的數量
//只有數量大於零 的獎品才能放到 selectPrizeList 中
if (null!=prize && prize.getRemainingPrize() > 0) {
// 取出當前機率
double probability = prize.getPrizeRate();
probabilityTotal += probability;
selectPrizeList.add(prize);
} else {
continue;
}
}
result.put("probabilityTotal", probabilityTotal);
result.put("selectPrizeList", selectPrizeList);
return result;
}
/**
* @Describe 獲取活動對應的獎品列表
* @param activityCode
* @return List<Prize>
*/
private List<Prize> getPrizeList(String activityCode) {
String awardPoolMapKey = String.format(ACTIVITY_PRIZE_POOL_MAP_KEY,activityCode);
RMapCache<Integer, Prize> awardPoolMap = redissonClient.getMapCache(awardPoolMapKey);
return awardPoolMap.readAllValues().stream().collect(Collectors.toList()); }}