v1.0 : 深海活动

This commit is contained in:
fbc
2022-10-11 18:31:12 +08:00
parent 97768c0b8b
commit 61fb414535
15 changed files with 605 additions and 197 deletions

View File

@@ -35,6 +35,8 @@ public class WebSecurityConfig {
private String seekElfinEnterpriseWechatPushKey;
// 线性奖池提醒企业微信机器人key
private String linearylyPoolEnterpriseWechatPushKey;
// 深海活动预警机器人key
private String luckySeaPreWarningRobotKey;
public void setJwtWebKey(String jwtWebKey) {
WebSecurityConfig.jwtWebKey = jwtWebKey;
@@ -47,4 +49,8 @@ public class WebSecurityConfig {
public void setRsaWebPrivateKey(String rsaWebPrivateKey) {
WebSecurityConfig.rsaWebPrivateKey = rsaWebPrivateKey;
}
public void setLuckySeaPreWarningRobotKey(String luckySeaPreWarningRobotKey) {
this.luckySeaPreWarningRobotKey = luckySeaPreWarningRobotKey;
}
}

View File

@@ -1745,6 +1745,11 @@ public class Constant {
public static final String ACT_MAGIC_SCHOOL_AWARD_CONFIG = "act_magic_school_award_config";
public static final String GOOGLE_PAY_LIMIT_CONFIG = "google_pay_limit_config";
/**
* 深海活动预警配置
*/
public static final String LUCKY_SEA_PREWARNING_CONFIG = "lucky_sea_prewarning_config";
}
public static class ActiveMq {
@@ -2502,6 +2507,8 @@ public class Constant {
public static final String HEADWEAR_GOLD = "buss_headwear_gold"; // 购买头饰扣减金币
public static final String LUCKY_SEA_DRAW = "lucky_sea_draw";// 春日游园抽奖
}
public static class MonsterStatus {

View File

@@ -1106,6 +1106,7 @@ public enum RedisKey {
LOCK_LUCKY_SEA_USER_DRAW,
/** 深海奇缘活动当前轮正在进行抽奖的用户数 **/
lucky_sea_curr_write_data_user_count,
lucky_sea_run_task,
/** 许愿集福气 **/
activity_fortune,
/** 许愿集福气保存数据锁 **/

View File

@@ -138,6 +138,9 @@ public enum BillObjTypeEnum {
SHIP_ANTICS_PACK((byte) 82, "航海冒险礼包支出", BillTypeEnum.OUT, CurrencyEnum.DIAMOND),
ACTIVITY_LUCKY_SEA_DRAW((byte) 83, "深海活动抽奖", BillTypeEnum.OUT, CurrencyEnum.DIAMOND),
ACTIVITY_LUCKY_SEA_DIAMOND_IN((byte) 84, "深海活动奖励", BillTypeEnum.IN, CurrencyEnum.DIAMOND),
;
BillObjTypeEnum(byte value, String desc, BillTypeEnum type, CurrencyEnum currency) {
this.value = value;

View File

@@ -0,0 +1,87 @@
/*
* 文 件 名: LuckySeaPreWarningConfig
* 版 权:
* 描 述: <描述>
* 创建人: H1
* 创建时间: 2021/9/22
* 修改人:
* 修改内容:
* 修改时间:
*/
package com.accompany.business.config;
import lombok.Data;
import java.util.List;
/**
* <br>类描述: 深海奇缘类活动预警配置
* <br>功能详细描述:
*
* @author H1
* @date [2021/9/22]
*/
@Data
public class LuckySeaPreWarningConfig {
/**
* 应用名称
*/
private String appName;
/**
* 活动名称
*/
private String actName;
/**
* 金币预警
*/
private GoldPreWarning goldPreWarning;
/**
* 定时轮播
*/
private Broadcast broadcast;
/**
* 金币预警
*/
@Data
public static class GoldPreWarning {
/**
* 开关
*/
private Boolean open;
/**
* 监测列表
*/
private List<GoldPreWarningItem> itemList;
}
/**
* 监测内容
*/
@Data
public static class GoldPreWarningItem {
/**
* 监测轮数
*/
private Integer roundNum;
/**
* 预警比例 = out/in
*/
private Double warnRate;
}
/**
* 定时轮播
*/
@Data
public static class Broadcast {
/**
* 开关
*/
private Boolean open;
/**
* 广播开启时间段
*/
private String openTime;
}
}

View File

@@ -0,0 +1,28 @@
/*
* 文 件 名: LuckySeaBroadDTO
* 版 权:
* 描 述: <描述>
* 创建人: H1
* 创建时间: 2021/9/23
* 修改人:
* 修改内容:
* 修改时间:
*/
package com.accompany.business.dto.activity;
import lombok.Data;
/**
* <br>类描述:
* <br>功能详细描述:
*
* @author H1
* @date [2021/9/23]
*/
@Data
public class LuckySeaBroadCastDTO {
private Integer goldIn;
private Integer goldOut;
private Integer roundNum;
private Integer peopleNum;
}

View File

@@ -0,0 +1,26 @@
/*
* 文 件 名: LuckySeaGoldPreWarningDTO
* 版 权:
* 描 述: <描述>
* 创建人: H1
* 创建时间: 2021/9/22
* 修改人:
* 修改内容:
* 修改时间:
*/
package com.accompany.business.dto.activity;
import lombok.Data;
/**
* <br>类描述:
* <br>功能详细描述:
*
* @author H1
* @date [2021/9/22]
*/
@Data
public class LuckySeaGoldPreWarningDTO {
private Integer goldIn;
private Integer goldOut;
}

View File

@@ -36,15 +36,19 @@ public interface LuckySeaActMapper {
* @param startTime
* @param endTime
*/
List<LuckySeaRankListVo> listDiamondRank(@Param("start") Integer start, @Param("pageSize") Integer pageSize,
@Param("startTime") Date startTime, @Param("endTime") Date endTime);
List<LuckySeaRankListVo> listDiamondRank(@Param("start") Integer start,
@Param("pageSize") Integer pageSize,
@Param("startTime") Date startTime,
@Param("endTime") Date endTime);
/**
* 获取深海奇缘活动欧皇榜
* @return
*/
List<LuckySeaRankListVo> listLuckyManRank(@Param("start") Integer start, @Param("pageSize")Integer pageSize,
@Param("startTime") Date startTime, @Param("endTime") Date endTime);
List<LuckySeaRankListVo> listLuckyManRank(@Param("start") Integer start,
@Param("pageSize")Integer pageSize,
@Param("startTime") Date startTime,
@Param("endTime") Date endTime);
/**
@@ -99,10 +103,9 @@ public interface LuckySeaActMapper {
* 获取用户指定伦的投注情况
* @param roundId
* @param uid
* @param itemType
* @return
*/
List<LuckySeaActUserDrawItemVO> listUserDrawItemInfo(@Param("roundId") String roundId, @Param("uid") Long uid, @Param("itemType") Integer itemType);
List<LuckySeaActUserDrawItemVO> listUserDrawItemInfo(@Param("roundId") String roundId, @Param("uid") Long uid );
/**
* 获取用户指定轮的投注情况

View File

@@ -0,0 +1,39 @@
/*
* 文 件 名: LuckySeaPreWarningMapper
* 版 权:
* 描 述: <描述>
* 创建人: H1
* 创建时间: 2021/9/22
* 修改人:
* 修改内容:
* 修改时间:
*/
package com.accompany.business.mybatismapper.activity;
import com.accompany.business.dto.activity.LuckySeaBroadCastDTO;
import com.accompany.business.dto.activity.LuckySeaGoldPreWarningDTO;
import org.apache.ibatis.annotations.Param;
/**
* <br>接口描述:
* <br>功能详细描述:
*
* @author H1
* @date [2021/9/22]
*/
public interface LuckySeaPreWarningMapper {
/**
* 统计前N轮的活动in和out
* @param roundNum 统计活动轮数
* @return
*/
LuckySeaGoldPreWarningDTO getPreRoundStat(Integer roundNum);
/**
* 获取轮播广播统计信息
* @return
* @param startTime
* @param endTime
*/
LuckySeaBroadCastDTO getBroadCastStat(@Param("startTime") String startTime, @Param("endTime") String endTime);
}

View File

@@ -0,0 +1,230 @@
/*
* 文 件 名: LuckySeaPreWarningService
* 版 权:
* 描 述: <描述>
* 创建人: H1
* 创建时间: 2021/9/22
* 修改人:
* 修改内容:
* 修改时间:
*/
package com.accompany.business.service;
import com.accompany.business.config.LuckySeaPreWarningConfig;
import com.accompany.business.dto.activity.LuckySeaBroadCastDTO;
import com.accompany.business.dto.activity.LuckySeaGoldPreWarningDTO;
import com.accompany.business.mybatismapper.activity.LuckySeaPreWarningMapper;
import com.accompany.business.service.activities.ActivitiesLuckySeaService;
import com.accompany.business.service.activities.impl.ActivitiesLuckySeaServiceImpl;
import com.accompany.business.service.push.EnterpriseWechatPushService;
import com.accompany.common.config.SystemConfig;
import com.accompany.common.config.WebSecurityConfig;
import com.accompany.common.constant.Constant;
import com.accompany.common.push.MarkdownMessage;
import com.accompany.common.utils.DateTimeUtil;
import com.accompany.common.utils.StringUtils;
import com.accompany.core.service.SysConfService;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* <br>类描述:
* <br>功能详细描述:
*
* @author H1
* @date [2021/9/22]
*/
@Service
@Slf4j
public class LuckySeaPreWarningService {
@Autowired
private SysConfService sysConfService;
@Autowired
private LuckySeaPreWarningMapper luckySeaPreWarningMapper;
@Autowired
private WebSecurityConfig webSecurityConfig;
@Autowired
private ActivitiesLuckySeaService activitiesLuckySeaService;
@Autowired
private EnterpriseWechatPushService enterpriseWechatPushService;
private static final String START_TIME_KEY = "startTime";
private static final String END_TIME_KEY = "endTime";
/**
* 金币预警
*/
@Async
public void handleGoldPreWarning() {
log.info("[handleGoldPreWarning] =========== start ===========");
LuckySeaPreWarningConfig config = getConfig();
if (null == config) {
log.info("[handleGoldPreWarning] config is null");
return;
}
try {
handleGoldPreWarning(config);
log.info("[handleGoldPreWarning] =========== end ===========");
} catch (Exception e) {
log.error("[handleGoldPreWarning] error", e);
}
}
/**
* 获取预警配置
* @return
*/
private LuckySeaPreWarningConfig getConfig() {
String configVal = sysConfService.getSysConfValueById(Constant.SysConfId.LUCKY_SEA_PREWARNING_CONFIG);
return StringUtils.isBlank(configVal) ? null : JSON.parseObject(configVal, LuckySeaPreWarningConfig.class);
}
/**
* 处理金币预警
* @param config
*/
private void handleGoldPreWarning(LuckySeaPreWarningConfig config) {
LuckySeaPreWarningConfig.GoldPreWarning goldPreWarning = config.getGoldPreWarning();
if (!goldPreWarning.getOpen()) {
log.info("[handleGoldPreWarning] 开关未开启");
return;
}
List<LuckySeaPreWarningConfig.GoldPreWarningItem> itemList = goldPreWarning.getItemList();
if (CollectionUtils.isEmpty(itemList)) {
log.info("[handleGoldPreWarning] 未配置监测项");
return;
}
for (LuckySeaPreWarningConfig.GoldPreWarningItem item : itemList) {
LuckySeaGoldPreWarningDTO stat = luckySeaPreWarningMapper.getPreRoundStat(item.getRoundNum());
if (stat.getGoldIn() != 0) {
double rate = new BigDecimal(stat.getGoldOut()).divide(BigDecimal.valueOf(stat.getGoldIn()), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
log.info("[handleGoldPreWarning] 前{}轮 out{} in{} rate{}", item.getRoundNum(), stat.getGoldOut(), stat.getGoldIn(), rate);
if (rate >= item.getWarnRate()) {
sendGoldPreWarningEnterpriseWechatMsg(config, stat, item.getRoundNum(), rate);
}
}
}
}
/**
* 发送金币预警企业微信消息
* @param config
* @param stat
* @param roundNum
* @param rate
*/
private void sendGoldPreWarningEnterpriseWechatMsg(LuckySeaPreWarningConfig config, LuckySeaGoldPreWarningDTO stat, Integer roundNum, Double rate) {
MarkdownMessage msg = new MarkdownMessage();
String appName = config.getAppName();
String actName = config.getActName();
msg.add(MarkdownMessage.getHeaderText(3, actName + "产出金币预警"));
msg.add(MarkdownMessage.getReferenceText("["+ appName + "]" + "[" + actName + "]在前" + roundNum + "轮的产出/投入=" + "[" + rate + "],具体:"));
msg.add(MarkdownMessage.getReferenceText(""+ roundNum + "轮投入:" + stat.getGoldIn()));
msg.add(MarkdownMessage.getReferenceText(""+ roundNum + "轮产出:" + stat.getGoldOut()));
msg.add(MarkdownMessage.getReferenceText("请判断是否需要前往后台查看"));
String key = webSecurityConfig.getLuckySeaPreWarningRobotKey();
enterpriseWechatPushService.pushMessageByKey(key,msg);
}
/**
* 处理信息轮播
*/
public void handleBroadcast() {
log.info("[handleBroadcast] =========== start ===========");
LuckySeaPreWarningConfig config = getConfig();
if (null == config) {
log.info("[handleBroadcast] config is null");
return;
}
LuckySeaPreWarningConfig.Broadcast broadcast = config.getBroadcast();
if (!broadcast.getOpen()) {
log.info("[handleBroadcast] 开关未开启");
return;
}
// 校验是否处在开启时间
if (!checkIsOpeningTime(broadcast)) {
log.info("[handleBroadcast] 不处于播报时间段");
return;
}
Date now = new Date();
String endTime = DateTimeUtil.convertDate(now, DateTimeUtil.DEFAULT_DATETIME_PATTERN);
String startTime = DateTimeUtil.convertDate(DateTimeUtil.addHours(now, -1), DateTimeUtil.DEFAULT_DATETIME_PATTERN);
LuckySeaBroadCastDTO broadCastStat = luckySeaPreWarningMapper.getBroadCastStat(startTime, endTime);
sendBroadcastEnterpriseWechatMsg(config, broadCastStat, startTime, endTime);
log.info("[handleBroadcast] =========== end ===========");
}
/**
* 检查是否处于轮播时段
* @param broadcast
* @return
*/
private boolean checkIsOpeningTime(LuckySeaPreWarningConfig.Broadcast broadcast) {
String timesConfig = broadcast.getOpenTime();
if (StringUtils.isBlank(timesConfig)) {
return false;
}
String[] times = timesConfig.split("-");
Map<String, String> openTimeMap = ImmutableMap.of(START_TIME_KEY, times[0], END_TIME_KEY, times[1]);
String startTime = openTimeMap.get(START_TIME_KEY);
String endTime = openTimeMap.get(END_TIME_KEY);
String[] startHourAndMinutes = startTime.split(":");
String[] endHourAndMinutes = endTime.split(":");
DateTime now = DateTime.now();
DateTime startDateTime =
now.withTime(
Integer.valueOf(startHourAndMinutes[0]), Integer.valueOf(startHourAndMinutes[1]), 0, 0);
DateTime endDateTime =
now.withTime(
Integer.valueOf(endHourAndMinutes[0]), Integer.valueOf(endHourAndMinutes[1]), 0, 0);
// 如果结束时间小于开始时间,则结束日期往后延一天。因为存在跨天的情况
if (endDateTime.compareTo(startDateTime) < 0) {
if (now.getHourOfDay() > endDateTime.getHourOfDay()) {
endDateTime = endDateTime.plusDays(1);
} else {
startDateTime = startDateTime.minusDays(1);
}
}
return now.compareTo(startDateTime) >= 0 && now.compareTo(endDateTime) <= 0;
}
/**
* 发送定时轮播企业微信
* @param config
* @param broadCastStat
* @param startTime
* @param endTime
*/
private void sendBroadcastEnterpriseWechatMsg(LuckySeaPreWarningConfig config, LuckySeaBroadCastDTO broadCastStat, String startTime, String endTime) {
MarkdownMessage msg = new MarkdownMessage();
String appName = config.getAppName();
String actName = config.getActName();
msg.add(MarkdownMessage.getHeaderText(3, actName + "定时播报"));
msg.add(MarkdownMessage.getReferenceText("["+ appName + "]" + "[" + actName + "]在" + startTime + "-" + endTime + "期间内情况如下:"));
msg.add(MarkdownMessage.getReferenceText("参与用户数:" + broadCastStat.getPeopleNum()));
msg.add(MarkdownMessage.getReferenceText("用户参与总值:" + broadCastStat.getGoldIn()));
msg.add(MarkdownMessage.getReferenceText("用户产出总值:" + broadCastStat.getGoldOut()));
msg.add(MarkdownMessage.getReferenceText("游戏执行轮数:" + broadCastStat.getRoundNum()));
Long stock = activitiesLuckySeaService.getStock();
String stockStr = new BigDecimal(stock).divide(new BigDecimal(ActivitiesLuckySeaServiceImpl.HANDLE_STOCK_MULTIPLE), 2, RoundingMode.HALF_DOWN).toString();
msg.add(MarkdownMessage.getReferenceText("当前库存数:" + stockStr));
String key = webSecurityConfig.getLuckySeaPreWarningRobotKey();
enterpriseWechatPushService.pushMessageByKey(key,msg);
}
}

View File

@@ -243,7 +243,7 @@ public class YidunCallbackService extends BaseService {
msg.setType(0);
msg.setFrom(SystemConfig.secretaryUid);
msg.setTo(uid);
msg.setBody(String.format("头像审核通知\t\t%s\n你上传的头像,\n核不通过,请重新上", DateTimeUtil.converDateToLocalDate(new Date())));
msg.setBody(String.format("頭像審核通知\t\t%s\n你上傳的頭像,\n核不通過,請重新上", DateTimeUtil.converDateToLocalDate(new Date())));
sendSysMsgService.sendMsg(msg);
}
@@ -255,7 +255,7 @@ public class YidunCallbackService extends BaseService {
msg.setType(0);
msg.setFrom(SystemConfig.secretaryUid);
msg.setTo(uid);
msg.setBody(String.format("册审核通知\t\t%s\n你上的相册图片,\n核不通过,请重新上", DateTimeUtil.converDateToLocalDate(new Date())));
msg.setBody(String.format("冊審核通知\t\t%s\n你上的相冊圖片,\n核不通過,請重新上", DateTimeUtil.converDateToLocalDate(new Date())));
sendSysMsgService.sendMsg(msg);
}

View File

@@ -10,6 +10,8 @@
*/
package com.accompany.business.service.activities;
import com.accompany.business.config.LuckySeaActConfig;
import com.accompany.business.model.activity.LuckySeaActInfo;
import com.accompany.business.model.activity.LuckySeaItem;
@@ -49,9 +51,8 @@ public interface ActivitiesLuckySeaService {
/**
* 获取深海奇缘活动配置奖项列表
* @return
* @param itemType
*/
List<LuckySeaItem> listLuckySeaItem(Integer itemType);
List<LuckySeaItem> listLuckySeaItem();
/**
* 获取深海奇缘活动信息列表
@@ -109,9 +110,8 @@ public interface ActivitiesLuckySeaService {
/**
* 异步更新用户抽奖记录信息
* @param roundId
* @param modelType
*/
void updateUserDrawRecordAsync(String roundId, List<LuckySeaItem> itemList, Integer modelType);
void updateUserDrawRecordAsync(String roundId, List<LuckySeaItem> itemList);
/**
* 创建新的一轮游戏

View File

@@ -16,7 +16,6 @@ import com.accompany.business.dto.LuckySeaActUserRoundStatDTO;
import com.accompany.business.dto.LuckySeaUserDrawResultDto;
import com.accompany.business.enums.UserActPropertyObjType;
import com.accompany.business.model.ActivityPack;
import com.accompany.business.model.Gift;
import com.accompany.business.model.UserPurse;
import com.accompany.business.model.activity.LuckySeaActInfo;
import com.accompany.business.model.activity.LuckySeaItem;
@@ -25,7 +24,6 @@ import com.accompany.business.model.activity.LuckySeaUserDrawResult;
import com.accompany.business.model.user.UserActPropertyInfoV2;
import com.accompany.business.mybatismapper.activity.LuckySeaActMapper;
import com.accompany.business.mybatismapper.activity.LuckySeaUserDrawResultMapper;
import com.accompany.business.param.UserBackpackParam;
import com.accompany.business.param.activity.LuckySeaActDrawParams;
import com.accompany.business.service.activities.*;
import com.accompany.business.service.activities.vo.LuckySeaActUserDrawRecordVo;
@@ -33,12 +31,10 @@ import com.accompany.business.service.activities.vo.LuckySeaActUserInfo;
import com.accompany.business.service.activities.vo.LuckySeaRankListVo;
import com.accompany.business.service.activities.vo.LuckySeaRankVo;
import com.accompany.business.service.activity.ActivityPackService;
import com.accompany.business.service.gift.GiftService;
import com.accompany.business.service.purse.UserPurseService;
import com.accompany.business.service.record.BillRecordService;
import com.accompany.business.service.user.UserActPropertyInfoService;
import com.accompany.business.service.user.UserActPropertyInfoV2Service;
import com.accompany.business.service.user.UserBackpackService;
import com.accompany.business.service.user.UsersService;
import com.accompany.business.vo.activities.*;
import com.accompany.common.constant.Constant;
@@ -47,6 +43,7 @@ import com.accompany.common.redis.RedisKey;
import com.accompany.common.status.BusiStatus;
import com.accompany.common.utils.BlankUtil;
import com.accompany.common.utils.DateTimeUtil;
import com.accompany.common.utils.RandomUtil;
import com.accompany.common.utils.StringUtils;
import com.accompany.core.enumeration.BillObjTypeEnum;
import com.accompany.core.exception.ServiceException;
@@ -68,8 +65,8 @@ import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import static com.accompany.core.enumeration.BillObjTypeEnum.ACTIVITY_DIAMOND;
/**
* <br>类描述:
@@ -112,10 +109,6 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Autowired
private BillRecordService billRecordService;
@Autowired
private GiftService giftService;
@Autowired
private UserBackpackService userBackpackService;
@Autowired
private UserActPropertyInfoV2Service userActPropertyInfoV2Service;
/**
@@ -144,13 +137,13 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Override
public void updateUserLuckySeaActPiece(Long uid, Integer packId, Integer packNum) {
if (null == uid) {
throw new ServiceException("uid不能");
throw new ServiceException("uid不能");
}
if (null == packId) {
throw new ServiceException("packId不能");
throw new ServiceException("packId不能");
}
if (null == packNum || packNum <= 0) {
throw new ServiceException("購買禮包數必須大於0");
throw new ServiceException("购买礼包数必须大于0");
}
log.info("updateUserLuckySeaActPiece, uid = {}, packId = {}, packNum = {}", uid, packId, packNum);
String lock = null;
@@ -161,10 +154,11 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
}
ActivityPack pack = activityPackService.getPackById(packId);
if (null == pack) {
throw new ServiceException("購買的產品不存在");
throw new ServiceException("购买的产品不存在");
}
userActPropertyInfoV2Service.updateUserPiece(uid, (long) (pack.getTicketNum() * packNum), ActivitesPackTypeEnum.LUCKY_SEA.getValue());
log.info("updateTicketNum, num = {}",pack.getTicketNum() * packNum);
int pieceNum = pack.getTicketNum() * packNum;
userActPropertyInfoService.updatePiece(uid, (long)pieceNum);
log.info("updateTicketNum, num = {}",pieceNum);
}finally {
jedisLockService.unlock(RedisKey.LOCK_LUCKY_SEA_UPDATE_PIECE.getKey(uid.toString()), lock);
}
@@ -173,7 +167,7 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Override
public LuckySeaActUserInfo getUserLuckySeaActInfo(Long uid) {
if (null == uid) {
throw new ServiceException("uid不能");
throw new ServiceException("uid不能");
}
Users user = usersService.getUsersByUid(uid);
if (user == null) {
@@ -182,10 +176,6 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
LuckySeaActUserInfo info = new LuckySeaActUserInfo();
info.setAvatar(user.getAvatar());
info.setNick(user.getNick());
// 获取用户碎片数
UserActPropertyInfoV2 propertyInfo = userActPropertyInfoV2Service.getUserActPropertyInfo(uid, ActivitesPackTypeEnum.LUCKY_SEA.getValue());
info.setPieceNum(propertyInfo != null ? propertyInfo.getPieceNum() : 0);
// 获取用户钱包钻石数
UserPurse userPurse = userPurseService.queryUserPurse(uid);
info.setDiamonds(userPurse != null ? userPurse.getDiamonds() : 0);
@@ -199,10 +189,9 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
}
@Override
public List<LuckySeaItem> listLuckySeaItem(Integer itemType) {
public List<LuckySeaItem> listLuckySeaItem() {
QueryWrapper<LuckySeaItem> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(LuckySeaItem::getStatus, Constant.status.valid)
.eq(LuckySeaItem::getItemType, itemType.byteValue())
.orderByAsc(LuckySeaItem::getMultiple );
return luckySeaItemService.list(wrapper);
}
@@ -253,11 +242,6 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
if (actStatus.getDrawStatus().equals(Constant.LuckySeaUserStatus.WIN)) {
result.setDrawStatus(Constant.LuckySeaUserStatus.WIN);
result.setPrizeDiamonds(actStatus.getPrizeDiamonds());
result.setPrizeName(actStatus.getPrizeName());
result.setPrizePrice(actStatus.getPrizePrice());
if (result.getPrizePrice() != null && result.getPrizePrice() != 0) {
result.setPrizeCount(result.getPrizeDiamonds()/ result.getPrizePrice());
}
break;
} else {
result.setDrawStatus(Constant.LuckySeaUserStatus.NOT_WIN);
@@ -276,7 +260,7 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Override
public LuckySeaRankVo listRank(Integer type, Integer page, Integer pageSize, Long uid) {
if (null == type) {
throw new ServiceException("type不能");
throw new ServiceException("type不能");
}
if (null == page) {
page = Constant.DEFAULT_PAGE;
@@ -291,18 +275,7 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
Date beginTimeOfDay = DateTimeUtil.getBeginTimeOfDay(new Date());
Date endTimeOfDay = DateTimeUtil.getEndTimeOfDay(new Date());
if (type.equals(Constant.LuckySeaRankType.DIAMOND_RANK)) {
Integer num = 0;
List<LuckySeaRankListVo> luckySeaList = luckySeaActMapper.listDiamondRank( start, pageSize, beginTimeOfDay, endTimeOfDay);
for(LuckySeaRankListVo luckySeaVo : luckySeaList){
Integer giftDistanceNum = - new Double( - luckySeaVo.getNum() + num ).intValue() ;
num = new Double(luckySeaVo.getNum()).intValue();
if(giftDistanceNum >= 0){
luckySeaVo.setNum(giftDistanceNum.doubleValue());
}else{
luckySeaVo.setNum( - giftDistanceNum.doubleValue());
}
rankList.add(luckySeaVo);
}
rankList = luckySeaActMapper.listDiamondRank(start, pageSize, beginTimeOfDay, endTimeOfDay);
userRankInfo = luckySeaActMapper.listUserDiamondRank(uid, beginTimeOfDay, endTimeOfDay);
} else if (type.equals(Constant.LuckySeaRankType.LUCKY_MAN_RANK)) {
rankList = luckySeaActMapper.listLuckyManRank(start, pageSize, beginTimeOfDay, endTimeOfDay);
@@ -320,16 +293,16 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Override
public void draw(Long uid, List<LuckySeaActDrawParams> params) {
if (null == uid) {
throw new ServiceException("uid不能");
throw new ServiceException("uid不能");
}
String lockVal = jedisLockService.lock(RedisKey.LOCK_LUCKY_SEA_USER_DRAW.getKey(uid.toString()));
if (BlankUtil.isBlank(lockVal)) {
log.error("深海奇缘获取分布式锁失败uid:{}", uid);
log.error("春日游园获取分布式锁失败uid:{}", uid);
throw new ServiceException(BusiStatus.JOIN_GAME_USER_TOO_MORE);
}
String roundId = null;
try {
Long costPieceTotal = checkDrawParam(params, uid);
checkDrawParam(params, uid);
LuckySeaActInfo actInfo = luckySeaActInfoService.getByCurrTime();
if (actInfo == null) {
log.error("选择失败当前时段不存在活动。actInfo:" + JSON.toJSONString(actInfo));
@@ -339,13 +312,16 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
jedisService.incrBy(RedisKey.lucky_sea_curr_write_data_user_count.getKey(roundId), 1);
if (!actInfo.getStatus().equals(Constant.LuckySeaActStatus.CHOOSE_STAGE)) {
log.error("选择失败当前不处于选择阶段。actInfo:" + JSON.toJSONString(actInfo));
throw new ServiceException("選擇失敗,當前不處於選擇階段");
throw new ServiceException("Selection failed. It is not in the selection at present");
}
// TODO: 2021/1/14 待优化,这里可以优化为批量写入
Long costGoldTotal = 0L;
for (LuckySeaActDrawParams param : params) {
luckySeaUserDrawRecordService.updateUserDrawRecord(uid, actInfo.getRoundId(), param.getItemId(), param.getNum());
costGoldTotal += param.getNum();
}
userActPropertyInfoV2Service.updateUserPiece(uid, -costPieceTotal, ActivitesPackTypeEnum.LUCKY_SEA.getValue());
billRecordService.insertGeneralBillRecord(uid, null, null, BillObjTypeEnum.ACTIVITY_LUCKY_SEA_DRAW,
-Double.valueOf(costGoldTotal));
} finally {
jedisLockService.unlock(RedisKey.LOCK_LUCKY_SEA_USER_DRAW.getKey(uid.toString()), lockVal);
jedisService.decrBy(RedisKey.lucky_sea_curr_write_data_user_count.getKey(roundId), 1);
@@ -355,16 +331,14 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Override
public LuckySeaActConfig getLuckySeaTimeConfig() {
String luckySeaConfigStr = sysConfService.getSysConfValueById(Constant.SysConfId.LUCKY_SEA_TIME_CONFIG);
String modelType = sysConfService.getDefaultSysConfValueById(Constant.SysConfId.LUCKY_SEA_MODEL, Constant.luckySeaItemType.NORMAL.toString());
LuckySeaActConfig config = new LuckySeaActConfig();
if (StringUtils.isBlank(luckySeaConfigStr)) {
LuckySeaActConfig config = new LuckySeaActConfig();
config.setChooseStageTime(DEFAULT_CHOOSE_STAGE_TIME);
config.setWaitingDrawStageTime(DEFAULT_WAITING_DRAW_STAGE_TIME);
config.setDrawOverStageTime(DEFAULT_DRAW_OVER_STAGE_TIME);
return config;
}
config = JSON.parseObject(luckySeaConfigStr, LuckySeaActConfig.class);
config.setModelType(Integer.valueOf(modelType));
return config;
return JSON.parseObject(luckySeaConfigStr, LuckySeaActConfig.class);
}
@Override
@@ -384,26 +358,21 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
for (LuckySeaUserDrawResult result : list) {
LuckySeaActUserDrawRecordVo vo = new LuckySeaActUserDrawRecordVo();
Long reward = 0L;
Long price = 0L;
LuckySeaActInfo actInfo = luckySeaActInfoService.getById(result.getRoundId());
if (actInfo != null) {
vo.setRoundId(actInfo.getRoundId());
vo.setDrawTime(actInfo.getEndTime());
vo.setDrawId(actInfo.getDrawId());
vo.setItemType(result.getItemType().intValue());
}
List<LuckySeaUserDrawResultVo> resultVos = handleDrawResult(result.getResult());
for (LuckySeaUserDrawResultVo resultVo : resultVos) {
if (null != result.getDrawId() && result.getDrawId().equals(resultVo.getItemId())) {
reward = resultVo.getCostPiece() * resultVo.getMultiple() * 10;
price = Long.valueOf(resultVo.getMultiple() * 10);
reward = resultVo.getCostPiece() * resultVo.getMultiple();
}
}
vo.setResults(resultVos);
vo.setDrawStatus(result.getDrawStatus());
vo.setReward(reward);
vo.setPrice(price);
vo.setItemName(result.getItemName());
results.add(vo);
}
}
@@ -413,21 +382,21 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Override
public Long luckySeaActDraw(List<LuckySeaItem> itemList, String roundId, LuckySeaActConfig timeConfig, Long stockInCache) throws InterruptedException {
long beforeDraw = System.currentTimeMillis();
log.info("深海奇缘活动, 开始开奖, currTime:{}", beforeDraw);
log.info("春日游园活动, 开始开奖, currTime:{}", beforeDraw);
// 开奖
LuckySeaActInfo actInfo = luckySeaActInfoService.getById(roundId);
log.info("深海奇缘活动, 开奖时使用的配置, itemList:{}", JSON.toJSONString(itemList));
log.info("深海奇缘活动, 当前开奖的活动信息, actInfo:{}", JSON.toJSONString(actInfo));
log.info("春日游园活动, 开奖时使用的配置, itemList:{}", JSON.toJSONString(itemList));
log.info("春日游园活动, 当前开奖的活动信息, actInfo:{}", JSON.toJSONString(actInfo));
if (actInfo == null) {
throw new ServiceException("當前沒有進行中的活");
throw new ServiceException("当前没有进行中的活");
}
if (!actInfo.getStatus().equals(Constant.LuckySeaActStatus.DRAWING)) {
throw new ServiceException("動開獎狀態異");
throw new ServiceException("动开奖状态异");
}
// 查询当前是否有用户正在进行写入抽奖数据
String count = jedisService.get(RedisKey.lucky_sea_curr_write_data_user_count.getKey(roundId));
while (StringUtils.isNotBlank(count) && Integer.valueOf(count) > 0) {
log.info("深海奇缘活动, 当前还有{}用户正在写入抽奖数据, 轮数:{}", count, roundId);
log.info("春日游园活动, 当前还有{}用户正在写入抽奖数据, 轮数:{}", count, roundId);
Thread.sleep(100);
count = jedisService.get(RedisKey.lucky_sea_curr_write_data_user_count.getKey(roundId));
}
@@ -438,21 +407,19 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
// 计算此轮应该抽取的门票
BigDecimal ticketPiece = new BigDecimal(userInput).multiply(new BigDecimal(ticket))
.setScale(5, BigDecimal.ROUND_DOWN);
log.info("深海奇缘活动,轮次号:{}, 库存:{}, 用户扣除前投入总和:{}, 需要扣除门票:{}", actInfo.getRoundId(), stock, userInput, ticketPiece);
log.info("春日游园活动,轮次号:{}, 库存:{}, 用户扣除前投入总和:{}, 需要扣除门票:{}", actInfo.getRoundId(), stock, userInput, ticketPiece);
// 扣除门票之后的用户投入
BigDecimal userInputAfterDeduct = new BigDecimal(userInput).subtract(ticketPiece).setScale(5, BigDecimal.ROUND_DOWN);
log.info("深海奇缘活动,轮次号:{}, 用户扣除后投入总和:{}", actInfo.getRoundId(), userInputAfterDeduct);
log.info("春日游园活动,轮次号:{}, 用户扣除后投入总和:{}", actInfo.getRoundId(), userInputAfterDeduct);
// 扣除门票之后加入库存
if (!userInputAfterDeduct.equals(BigDecimal.ZERO.setScale(5, BigDecimal.ROUND_DOWN))) {
Long userInputAfterDeductInt = Long.valueOf(userInputAfterDeduct.multiply(new BigDecimal(HANDLE_STOCK_MULTIPLE)).setScale(0,BigDecimal.ROUND_DOWN).toString());
jedisService.incrBy(RedisKey.LUCKY_SEA_ACT_STOCK.getKey(), userInputAfterDeductInt);
}
double stockWithUserInput = stock.add(userInputAfterDeduct)
Long userInputAfterDeductInt = Long.valueOf(userInputAfterDeduct.multiply(new BigDecimal(HANDLE_STOCK_MULTIPLE)).setScale(0,BigDecimal.ROUND_DOWN).toString());
stockInCache = jedisService.incrBy(RedisKey.LUCKY_SEA_ACT_STOCK.getKey(), userInputAfterDeductInt);
double stockWithUserInput = new BigDecimal(stockInCache).divide(new BigDecimal(HANDLE_STOCK_MULTIPLE))
.setScale(5, BigDecimal.ROUND_DOWN).doubleValue();
log.info("深海奇缘活动,轮次号:{}, 加入用户投入之后的库存:{}", actInfo.getRoundId(), stockWithUserInput);
log.info("春日游园活动,轮次号:{}, 加入用户投入之后的库存:{}", actInfo.getRoundId(), stockWithUserInput);
List<LuckySeaActRoundStatVo> statList = luckySeaActMapper.listUserRoundStat(actInfo.getRoundId());
Long drawId = handleDrawPrize(statList, itemList, stockWithUserInput);
log.info("深海奇缘活动,轮次号:{}, 开奖结束itemId :{}", actInfo.getRoundId(), drawId);
log.info("春日游园活动,轮次号:{}, 开奖结束itemId :{}", actInfo.getRoundId(), drawId);
LuckySeaItem item = null;
for (LuckySeaItem items : itemList) {
if (drawId.equals(items.getId())) {
@@ -462,14 +429,14 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
long endDraw = System.currentTimeMillis();
log.info("深海奇缘活动, 结束开奖, currTime:{}", endDraw);
// 获取处理此次开奖的时间
Long drawSeconds = (endDraw - beforeDraw)/ 1000;
log.info("深海奇缘活动, 结束开奖,处理开奖花费{}秒", drawSeconds);
Long drawMills = endDraw - beforeDraw;
log.info("深海奇缘活动, 结束开奖,处理开奖花费{}", drawMills);
// 更新活动状态
int drawTime = Math.max(timeConfig.getWaitingDrawStageTime(), drawSeconds.intValue());
Date showResultStageStartTime = DateTimeUtil.addSeconds(actInfo.getDrawStageStartTime(), drawTime);
int drawTime = Math.max(timeConfig.getWaitingDrawStageTime() * 1000, drawMills.intValue());
Date showResultStageStartTime = DateTimeUtil.addMilliSecond(actInfo.getDrawStageStartTime(), drawTime);
updateLuckyActDrawResult(roundId, drawId, item, showResultStageStartTime,ticketPiece.doubleValue());
jedisService.del(RedisKey.lucky_sea_curr_write_data_user_count.getKey(roundId));
return drawSeconds;
return drawMills;
}
/**
@@ -478,48 +445,21 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
*/
@Async
@Transactional(rollbackFor = Exception.class)
private void distributeDiamondAsync(String roundId) {
log.info("深海奇缘活动, 开始分发钻石奖励, roundId:{}", roundId);
void distributeDiamondAsync(String roundId) {
log.info("春日游园活动, 开始分发钻石奖励, roundId:{}", roundId);
// 获取轮次下中奖的用户列表
QueryWrapper<LuckySeaUserDrawRecord> recordQueryWrapper = new QueryWrapper<>();
recordQueryWrapper.lambda().eq(LuckySeaUserDrawRecord::getRoundId, roundId)
.eq(LuckySeaUserDrawRecord::getDrawStatus, Constant.status.valid);
List<LuckySeaUserDrawRecord> drawUserRecordList = luckySeaUserDrawRecordService.list(recordQueryWrapper);
log.info("深海奇缘活动, 开始分发钻石奖励, 分发用户:{}", JSON.toJSONString(drawUserRecordList));
log.info("春日游园活动, 开始分发钻石奖励, 分发用户:{}", JSON.toJSONString(drawUserRecordList));
// 奖励用户对应钻石
for (LuckySeaUserDrawRecord record : drawUserRecordList) {
long diamondsNum = record.getCostPriceNum() * record.getItemMultiple() * 10;
log.info("深海奇缘活动, 开始分发钻石奖励。 uid{} diamondsNum:{}", record.getUid(), diamondsNum);
userPurseService.addDiamond(record.getUid(), Double.valueOf(diamondsNum), null, BillObjTypeEnum.ACTIVITY_DIAMOND.getDesc());
billRecordService.insertGeneralBillRecord(record.getUid(), 0L, record.getRoundId(), ACTIVITY_DIAMOND, Double.valueOf(diamondsNum));
log.info("深海奇缘活动, 结束分发钻石奖励。 uid{} diamondsNum:{}", record.getUid(), diamondsNum);
}
}
@Async
@Transactional(rollbackFor = Exception.class)
private void distributeGiftAsync(String roundId, Integer giftId) {
log.info("深海奇缘活动, 开始分发礼物奖励, roundId:{}", roundId);
// 获取轮次下中奖的用户列表
QueryWrapper<LuckySeaUserDrawRecord> recordQueryWrapper = new QueryWrapper<>();
recordQueryWrapper.lambda().eq(LuckySeaUserDrawRecord::getRoundId, roundId)
.eq(LuckySeaUserDrawRecord::getDrawStatus, Constant.status.valid);
List<LuckySeaUserDrawRecord> drawUserRecordList = luckySeaUserDrawRecordService.list(recordQueryWrapper);
log.info("深海奇缘活动, 开始分发礼物奖励, 分发用户:{}", JSON.toJSONString(drawUserRecordList));
// 奖励用户对应背包礼物
for (LuckySeaUserDrawRecord record : drawUserRecordList) {
Long giftNum = record.getCostPriceNum();
log.info("深海奇缘活动, 开始分发礼物奖励。 uid{}giftId:{}, giftNum:{}", record.getUid(), giftId, giftNum);
Gift gift = giftService.getGiftByIdFromDb(giftId);
UserBackpackParam userBackpackParam = new UserBackpackParam();
userBackpackParam.setGiftId(giftId);
userBackpackParam.setUid(record.getUid());
userBackpackParam.setGiftType(gift.getGiftType());
userBackpackParam.setGiftSeq(gift.getSeqNo());
userBackpackParam.setCount(giftNum.intValue());
userBackpackService.saveOrUpdateUserBackpack(userBackpackParam);
// billRecordService.insertGeneralBillRecord(record.getUid(), 0L, record.getRoundId(), ACTIVITY_DIAMOND, Double.valueOf(giftNum));
log.info("深海奇缘活动, 结束分发礼物奖励。 uid{}giftId:{}, giftNum:{}", record.getUid(), giftId, giftNum);
long diamondsNum = record.getCostPriceNum() * record.getItemMultiple();
log.info("春日游园活动, 开始分发钻石奖励。 uid{} diamondsNum:{}", record.getUid(), diamondsNum);
userPurseService.addDiamond(record.getUid(), Double.valueOf(diamondsNum), null, BillObjTypeEnum.ACTIVITY_LUCKY_SEA_DIAMOND_IN.getDesc());
billRecordService.insertGeneralBillRecord(record.getUid(), 0L, record.getRoundId(), BillObjTypeEnum.ACTIVITY_LUCKY_SEA_DIAMOND_IN, (double) diamondsNum);
log.info("春日游园活动, 结束分发钻石奖励。 uid{} diamondsNum:{}", record.getUid(), diamondsNum);
}
}
@@ -527,12 +467,10 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
* 异步写入用户的游戏记录
* @param roundId
* @param itemList
* @param itemType
*/
@Transactional(rollbackFor = Exception.class)
public void addUserDrawResult(String roundId, List<LuckySeaItem> itemList, Byte itemType) {
public void addUserDrawResult(String roundId, List<LuckySeaItem> itemList) {
LuckySeaActInfo actInfo = luckySeaActInfoService.getById(roundId);
LuckySeaItem item = luckySeaItemService.getById(actInfo.getDrawId());
QueryWrapper<LuckySeaUserDrawRecord> recordQueryWrapper = new QueryWrapper<>();
recordQueryWrapper.lambda().eq(LuckySeaUserDrawRecord::getRoundId, roundId).orderByAsc(LuckySeaUserDrawRecord::getUid).orderByAsc(LuckySeaUserDrawRecord::getDrawStatus);
List<LuckySeaUserDrawRecord> drawUserRecordList = luckySeaUserDrawRecordService.list(recordQueryWrapper);
@@ -560,8 +498,6 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
LuckySeaUserDrawResult result = new LuckySeaUserDrawResult();
result.setUid(uid);
result.setRoundId(actInfo.getRoundId());
result.setItemType(itemType);
result.setItemName(item.getName());
result.setDrawId(actInfo.getDrawId());
result.setResult(JSON.toJSONString(resultDtoList));
result.setDrawStatus(userDrawStatusMap.get(uid));
@@ -611,30 +547,22 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
/**
* 异步更新用户抽奖记录信息
* @param roundId
* @param modelType
*/
@Async
@Transactional(rollbackFor = Exception.class)
@Override
public void updateUserDrawRecordAsync(String roundId, List<LuckySeaItem> itemList, Integer modelType) {
public void updateUserDrawRecordAsync(String roundId, List<LuckySeaItem> itemList) {
try {
LuckySeaActInfo actInfo = luckySeaActInfoService.getById(roundId);
if (null != actInfo) {
luckySeaActMapper.batchUpdateUserDrawRecord(roundId, actInfo.getDrawId());
}
// 分发中奖用户的奖励
if (Constant.luckySeaItemType.NORMAL.equals(modelType.byteValue())) {
distributeDiamondAsync(roundId);
} else if (Constant.luckySeaItemType.GIFT.equals(modelType.byteValue())) {
LuckySeaItem item = luckySeaItemService.getById(actInfo.getDrawId());
if (item != null) {
distributeGiftAsync(roundId, item.getReferenceId());
}
}
addUserDrawResult(roundId, itemList, modelType.byteValue());
distributeDiamondAsync(roundId);
addUserDrawResult(roundId, itemList);
} catch (Exception e) {
log.error("深海奇缘活动,异步更新用户参与信息时出现异常, roundId:" + roundId, e);
throw new ServiceException("深海奇緣活動,異步更新用戶參与信息時出現異常", e);
log.error("春日游园活动,异步更新用户参与信息时出现异常, roundId:" + roundId, e);
throw new ServiceException("春日游园活动,异步更新用户参与信息时出现异常");
}
}
@@ -646,7 +574,7 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
* @param ticket
*/
@Transactional(rollbackFor = Exception.class)
private void updateLuckyActDrawResult(String roundId, Long drawId, LuckySeaItem item, Date showResultStageStartTime, Double ticket) {
void updateLuckyActDrawResult(String roundId, Long drawId, LuckySeaItem item, Date showResultStageStartTime, Double ticket) {
LuckySeaActInfo act = new LuckySeaActInfo();
act.setRoundId(roundId);
act.setDrawId(drawId);
@@ -696,14 +624,13 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
@Override
public List<LuckySeaActUserDrawItemVO> listUserDrawItemInfo(Long uid, String roundId) {
String modelType = sysConfService.getDefaultSysConfValueById(Constant.SysConfId.LUCKY_SEA_MODEL, Constant.luckySeaItemType.NORMAL.toString());
List<LuckySeaActUserDrawItemVO> itemVOS = luckySeaActMapper.listUserDrawItemInfo(roundId, uid, Integer.valueOf(modelType));
List<LuckySeaActUserDrawItemVO> itemVOS = luckySeaActMapper.listUserDrawItemInfo(roundId, uid);
return itemVOS;
}
@Override
public void compensateUserPieceWhenGameAbnormal(String roundId, Long beforeStoke) {
log.info("深海奇缘活动,开始处理发生异常的活动, roundId:{}, 还原库存:{}", roundId, beforeStoke/HANDLE_STOCK_MULTIPLE);
log.info("春日游园活动,开始处理发生异常的活动, roundId:{}, 还原库存:{}", roundId, beforeStoke/HANDLE_STOCK_MULTIPLE);
// 将库存还原为发生异常之前的库存
jedisService.set(RedisKey.LUCKY_SEA_ACT_STOCK.getKey(), String.valueOf(beforeStoke));
// 获取此轮参加游戏的用户投入碎片情况
@@ -713,7 +640,7 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
userActPropertyInfoV2Service.updateUserPiece(stat.getUid(), stat.getCostPieceNum(), ActivitesPackTypeEnum.LUCKY_SEA.getValue(), UserActPropertyObjType.ACT_ABNORMAL_COMPENSATE);
}
}
log.info("深海奇缘活动,结束处理发生异常的活动, roundId:{}, 处理的投入列表statList:{}", roundId, JSON.toJSONString(statList));
log.info("春日游园活动,结束处理发生异常的活动, roundId:{}, 处理的投入列表statList:{}", roundId, JSON.toJSONString(statList));
}
/**
@@ -737,7 +664,7 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
act.setCreateTime(new Date());
act.setUpdateTime(new Date());
luckySeaActInfoService.save(act);
log.info("深海奇缘活动, 创建新的一轮游戏 roundId:{}, startTime:{}", roundId, startTime);
log.info("春日游园活动, 创建新的一轮游戏 roundId:{}, startTime:{}", roundId, startTime);
return roundId;
}
@@ -764,19 +691,22 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
}
Long drawId = null;
if (!CollectionUtils.isEmpty(lessThanStockItemId)) {
log.info("深海奇缘活动, 存在小于库存的条件组, lessThanStockItemId: {}", JSON.toJSONString(lessThanStockItemId));
Collections.shuffle(lessThanStockItemId);
drawId = lessThanStockItemId.get(0);
log.info("春日游园活动, 存在小于库存的条件组, lessThanStockItemId: {}", JSON.toJSONString(lessThanStockItemId));
int randomIndex = RandomUtil.randomByRange(0, lessThanStockItemId.size());
drawId = lessThanStockItemId.get(randomIndex);
} else {
// 将map进行排序最小的放在map首位
itemPieceMap.entrySet().stream().sorted((p1, p2) -> p2.getValue().compareTo(p1.getValue()));
log.info("深海奇缘活动, 没有小于库存的条件组, itemPieceMap: {}", JSON.toJSONString(itemPieceMap));
drawId = itemPieceMap.keySet().iterator().next();
ArrayList<Map.Entry<Long, Long>> entries = new ArrayList<>(itemPieceMap.entrySet());
entries.sort(Map.Entry.comparingByValue());
Long minValue = entries.get(0).getValue();
List<Long> keys = itemPieceMap.entrySet().stream().filter(o -> Objects.equals(o.getValue(), minValue)).map(Map.Entry::getKey).collect(Collectors.toList());
log.info("春日游园活动, 没有小于库存的条件组, 满足最小用户投入的itemIdList: {}", JSON.toJSONString(keys));
int index = RandomUtil.randomByRange(0, keys.size());
drawId = keys.get(index);
}
// 更新库存
Long prizePiece = itemPieceMap.get(drawId);
log.info("深海奇缘活动, drawId: {}, prizePiece: {}", drawId, prizePiece);
log.info("春日游园活动, drawId: {}, prizePiece: {}", drawId, prizePiece);
// 因为redis中的库存最多保留5位小数需要转成整数之后在计算所以prizePiece需要乘上100000
jedisService.decrBy(RedisKey.LUCKY_SEA_ACT_STOCK.getKey(), prizePiece * HANDLE_STOCK_MULTIPLE);
return drawId;
@@ -798,17 +728,17 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
}
/**
* 获取深海奇缘活动配置的门票
* 获取春日游园活动配置的门票
* @return
*/
private Double getLuckySeaActTicket() {
String ticketStr = sysConfService.getSysConfValueById(Constant.SysConfId.LUCKY_SEA_TICKET);
if (StringUtils.isBlank(ticketStr)) {
throw new ServiceException("配置的門票為");
throw new ServiceException("配置的门票为");
}
Double ticket = Double.valueOf(ticketStr);
if (ticket >= 1) {
throw new ServiceException("票配置不能大或等1");
throw new ServiceException("票配置不能大或等1");
}
return ticket;
}
@@ -843,29 +773,26 @@ public class ActivitiesLuckySeaServiceImpl implements ActivitiesLuckySeaService
*/
private Long checkDrawParam(List<LuckySeaActDrawParams> params, Long uid) {
if (CollectionUtils.isEmpty(params)) {
throw new ServiceException("至少需要中一");
throw new ServiceException("至少需要中一");
}
Long costPieceTotal = 0L;
for (LuckySeaActDrawParams param : params) {
if (param.getItemId() == null) {
throw new ServiceException("itemId不能");
throw new ServiceException("itemId不能");
}
if (param.getNum() <= 0) {
throw new ServiceException("選擇數量需要大0");
throw new ServiceException("选择数量需要大0");
}
LuckySeaItem item = luckySeaItemService.getById(param.getItemId());
if (null == item || !item.getStatus().equals(Constant.status.valid)) {
log.error("选择的配置不存在, itemId" + param.getItemId());
throw new ServiceException("選擇的配置不存在");
throw new ServiceException("选择的配置不存在");
}
costPieceTotal += param.getNum();
}
// 检查用户碎片是否充足
UserActPropertyInfoV2 propertyInfo = userActPropertyInfoV2Service.getUserActPropertyInfo(uid, ActivitesPackTypeEnum.LUCKY_SEA.getValue());
if (null == propertyInfo || propertyInfo.getPieceNum() < costPieceTotal) {
throw new ServiceException(BusiStatus.PIECE_NOT_ENOUGH);
}
// 扣减用户钻石
log.info("【春日游园活动】 uid {} 消耗钻石总数 {}", uid, costPieceTotal);
userPurseService.subDiamond(uid, (double)costPieceTotal, Constant.BusinessType.LUCKY_SEA_DRAW);
return costPieceTotal;
}

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.accompany.business.mybatismapper.activity.LuckySeaPreWarningMapper">
<select id="getPreRoundStat" resultType="com.accompany.business.dto.activity.LuckySeaGoldPreWarningDTO">
select ifnull(sum(r.cost_piece_num),0) as goldIn,
ifnull(sum(if(r.draw_status = 1, r.cost_piece_num * r.item_multiple, 0 )), 0) as goldOut
from
(select round_id from lucky_sea_act_info where status = 3 order by round_id desc limit #{roundNum}) a, lucky_sea_user_draw_record r
where a.round_id = r.round_id;
</select>
<select id="getBroadCastStat" resultType="com.accompany.business.dto.activity.LuckySeaBroadCastDTO">
select
ifnull(sum(r.cost_piece_num),0) as goldIn,
ifnull(sum(if(r.draw_status = 1, r.cost_piece_num * r.item_multiple, 0 )), 0) as goldOut,
count(distinct a.round_id) roundNum,
count(distinct r.uid) peopleNum
from
(select round_id from lucky_sea_act_info where status = 3
and start_time between #{startTime} and #{endTime}) a
left join
lucky_sea_user_draw_record r
on a.round_id = r.round_id
;
</select>
</mapper>

View File

@@ -12,11 +12,14 @@ package com.accompany.scheduler.task.activity;
import com.accompany.business.config.LuckySeaActConfig;
import com.accompany.business.model.activity.LuckySeaItem;
import com.accompany.business.service.LuckySeaPreWarningService;
import com.accompany.business.service.activities.ActivitiesLuckySeaService;
import com.accompany.business.vo.activities.LuckySeaActInfoVo;
import com.accompany.common.constant.Constant;
import com.accompany.common.redis.RedisKey;
import com.accompany.common.utils.StringUtils;
import com.accompany.core.service.SysConfService;
import com.accompany.core.util.StringUtils;
import com.accompany.core.service.common.JedisLockService;
import com.accompany.scheduler.base.BaseTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -44,6 +47,10 @@ public class LuckySeaActTask extends BaseTask {
private ActivitiesLuckySeaService activitiesLuckySeaService;
@Autowired
private SysConfService sysConfService;
@Autowired
private JedisLockService jedisLockService;
@Autowired
private LuckySeaPreWarningService luckySeaPreWarningService;
@PostConstruct
public void handleLuckySeaAct() {
@@ -59,22 +66,28 @@ public class LuckySeaActTask extends BaseTask {
public void runTask() {
while (true) {
// 活动开关是否开着
Boolean actSwitch = Optional.ofNullable(
Boolean actSwitch = Optional.of(
Boolean.parseBoolean(sysConfService.getSysConfValueById(Constant.SysConfId.LUCKY_SEA_SWITCH))).orElse(false);
log.info("深海奇缘活动, 开关状态: {}", actSwitch);
log.info("春日游园活动, 开关状态: {}", actSwitch);
if (actSwitch) {
String modelType = sysConfService.getDefaultSysConfValueById(Constant.SysConfId.LUCKY_SEA_MODEL, Constant.luckySeaItemType.NORMAL.toString());
log.info("深海奇缘活动, 当前模式: {}", modelType);
String roundId = null;
Date nextRoundStartTime = null;
Long stock = activitiesLuckySeaService.getStock();
// 获取游戏的配置
List<LuckySeaItem> itemList = activitiesLuckySeaService.listLuckySeaItem();
LuckySeaActConfig timeConfig = activitiesLuckySeaService.getLuckySeaTimeConfig();
int totalTime = timeConfig.getWaitingDrawStageTime() + timeConfig.getChooseStageTime() + timeConfig.getDrawOverStageTime();
// 加锁,确保创建与开奖是单线程执行
String lockVal = jedisLockService.lock(RedisKey.lucky_sea_run_task.getKey(), 10 * 1000, totalTime * 1000);
if (StringUtils.isBlank(lockVal)) {
log.info("春日游园活动, 获取锁失败");
break;
}
long drawMills = 0L;
try {
boolean needCreateNewAct = true;
boolean needWaitUserDraw = true;
LuckySeaActConfig timeConfig = activitiesLuckySeaService.getLuckySeaTimeConfig();
Integer waitUserDrawTime = timeConfig.getChooseStageTime();
// 获取游戏的配置
List<LuckySeaItem> itemList = activitiesLuckySeaService.listLuckySeaItem(Integer.valueOf(modelType));
// 处理异常活动
LuckySeaActInfoVo lastLuckySeaActInfo = activitiesLuckySeaService.getNewestLuckySeaActInfo(null);
if (null != lastLuckySeaActInfo && StringUtils.isNotBlank(lastLuckySeaActInfo.getRoundId())) {
@@ -82,19 +95,19 @@ public class LuckySeaActTask extends BaseTask {
needCreateNewAct = false;
roundId = lastLuckySeaActInfo.getRoundId();
Long intervalSecond = (System.currentTimeMillis() - lastLuckySeaActInfo.getStartTime().getTime()) / 1000;
log.info("深海奇缘活动, 上一轮投注时间发生异常的活动到现在的间隔时间{}秒, roundId:{}", intervalSecond, roundId);
log.info("春日游园活动, 上一轮投注时间发生异常的活动到现在的间隔时间{}秒, roundId:{}", intervalSecond, roundId);
if (intervalSecond <= timeConfig.getChooseStageTime()) {
waitUserDrawTime = timeConfig.getChooseStageTime() - intervalSecond.intValue();
log.info("深海奇缘活动, 上一轮投注时间发生异常的活动留给用户的下注时间{}秒", waitUserDrawTime);
log.info("春日游园活动, 上一轮投注时间发生异常的活动留给用户的下注时间{}秒", waitUserDrawTime);
} else {
log.info("深海奇缘活动, 上一轮投注时间发生异常的活动直接进行开奖, roundId:{}", roundId);
log.info("春日游园活动, 上一轮投注时间发生异常的活动直接进行开奖, roundId:{}", roundId);
needWaitUserDraw = false;
}
} else if (lastLuckySeaActInfo.getStatus().equals(Constant.LuckySeaActStatus.DRAWING)) {
needCreateNewAct = false;
needWaitUserDraw = false;
roundId = lastLuckySeaActInfo.getRoundId();
log.info("深海奇缘活动, 上一轮投注时间发生异常的活动直接进行开奖, roundId:{}", roundId);
log.info("春日游园活动, 上一轮投注时间发生异常的活动直接进行开奖, roundId:{}", roundId);
}
}
// 创建新的一轮游戏
@@ -103,38 +116,50 @@ public class LuckySeaActTask extends BaseTask {
}
// 等待用户投碎片
if (needWaitUserDraw) {
log.info("深海奇缘活动, 开始等待用户投入碎片, currTime: {}", System.currentTimeMillis());
log.info("春日游园活动, 开始等待用户投入碎片, currTime: {}", System.currentTimeMillis());
Thread.sleep(waitUserDrawTime * 1000);
log.info("深海奇缘活动, 结束等待用户投入碎片, currTime: {}", System.currentTimeMillis());
log.info("春日游园活动, 结束等待用户投入碎片, currTime: {}", System.currentTimeMillis());
}
activitiesLuckySeaService.updateLuckyActStatus(roundId, Constant.LuckySeaActStatus.DRAWING);
// 开奖
Long drawSeconds = activitiesLuckySeaService.luckySeaActDraw(itemList, roundId, timeConfig, stock);
// 异步更新用户抽奖记录
activitiesLuckySeaService.updateUserDrawRecordAsync(roundId, itemList, Integer.valueOf(modelType));
// 等待开奖动画与页面渲染开奖结果时间
long sleepSeconds = timeConfig.getDrawOverStageTime() + timeConfig.getWaitingDrawStageTime() - drawSeconds;
log.info("深海奇缘活动, 等待开奖动画与页面渲染开奖结果时间{}秒", sleepSeconds);
Thread.sleep(sleepSeconds * 1000);
log.info("深海奇缘活动此轮结束, roundId:{}", roundId);
// 异步更新活动endTime
nextRoundStartTime = new Date();
activitiesLuckySeaService.endLuckyAct(roundId, nextRoundStartTime);
drawMills = activitiesLuckySeaService.luckySeaActDraw(itemList, roundId, timeConfig, stock);
} catch (Exception e) {
// 更新活动状态, 此轮游戏标识为异常,不开始下一轮游戏
log.error("深海奇缘活动出现异常, roundId: " + roundId, e);
log.error("春日游园活动开奖时出现异常, roundId: " + roundId, e);
activitiesLuckySeaService.updateLuckyActStatus(roundId, Constant.LuckySeaActStatus.GAME_ABNORMAL);
activitiesLuckySeaService.compensateUserPieceWhenGameAbnormal(roundId, stock);
break;
} finally {
jedisLockService.unlock(RedisKey.lucky_sea_run_task.getKey(), lockVal);
}
try {
// 异步更新用户抽奖记录
activitiesLuckySeaService.updateUserDrawRecordAsync(roundId, itemList);
// 等待开奖动画与页面渲染开奖结果时间
long sleepMills = (timeConfig.getDrawOverStageTime() + timeConfig.getWaitingDrawStageTime()) * 1000
- drawMills;
if (sleepMills > 0) {
log.info("春日游园活动, 等待开奖动画与页面渲染开奖结果时间{}毫秒", sleepMills);
Thread.sleep(sleepMills);
}
log.info("春日游园活动此轮结束, roundId:{}", roundId);
// 异步更新活动endTime
nextRoundStartTime = new Date();
activitiesLuckySeaService.endLuckyAct(roundId, nextRoundStartTime);
// 进行预警数据统计
luckySeaPreWarningService.handleGoldPreWarning();
} catch (Exception e) {
// 更新活动状态
log.error("春日游园活动更新用户抽奖记录与分发奖励时出现异常, roundId: " + roundId, e);
activitiesLuckySeaService.updateLuckyActStatus(roundId, Constant.LuckySeaActStatus.SUCCESS_DRAW_UPDATE_DATA_FAIL);
}
} else {
try {
log.info("深海奇缘活动, 开关未开启,线程进入休眠: {}", actSwitch);
log.info("春日游园活动, 开关未开启,线程进入休眠: {}", actSwitch);
Thread.sleep(60 * 1000);
continue;
} catch (Exception e){
log.error("深海奇缘活动线程休眠时出现异常", e);
break;
log.error("春日游园活动线程休眠时出现异常", e);
}
}
}