幸运25-giftSendService策略入口
This commit is contained in:
@@ -597,6 +597,7 @@ public class Constant {
|
||||
* Bravo礼物
|
||||
*/
|
||||
public static final byte BRAVO_GIFT = GiftTypeEnum.BRAVO.getType();
|
||||
public static final byte LUCKY_25 = GiftTypeEnum.LUCKY_25.getType();
|
||||
}
|
||||
|
||||
public static class PayloadSkiptype {
|
||||
@@ -1431,6 +1432,10 @@ public class Constant {
|
||||
public static final String BRAVO_GIFT_CONFIG = "bravo_gift_config";
|
||||
|
||||
public static final String GUILD_USD_WITHDRAW_RATE = "guild_usd_withdraw_rate";
|
||||
|
||||
public static final String LUCKY_NUMBER_ACT_CONFIG = "lucky_number_act_config";
|
||||
|
||||
public static final String LUCKY_25_GIFT_CONFIG = "lucky_25_gift_config";
|
||||
}
|
||||
|
||||
public static class WithDrawStatus {
|
||||
|
@@ -23,10 +23,12 @@ public enum GiftTypeEnum {
|
||||
LUCKY_BAG_LINEAR((byte) 15, "线性福袋"),
|
||||
SUPER_LUCKY((byte) 16, "幸运礼物"),
|
||||
COUNTRY((byte) 17, "国家"),
|
||||
LUCKY_24((byte) 18, "幸运礼物"),
|
||||
LUCKY_24((byte) 18, "幸运24礼物"),
|
||||
CP((byte) 19, "cp"),
|
||||
CUSTOM((byte) 20, "大R定制"),
|
||||
BRAVO((byte) 21, "Bravo");
|
||||
BRAVO((byte) 21, "Bravo"),
|
||||
LUCKY_25((byte) 22, "幸运25礼物"),
|
||||
;
|
||||
|
||||
private byte type;
|
||||
private String desc;
|
||||
|
@@ -1441,6 +1441,17 @@ public enum RedisKey {
|
||||
gusd_withdraw_limit,//公会长提现次数限制
|
||||
|
||||
bravo_banner_queue, // bravo轮播队列
|
||||
|
||||
//幸运25
|
||||
lucky_25_stock,
|
||||
lucky_25_user_meta,
|
||||
lucky_25_user_pool,
|
||||
lucky_25_user_history,
|
||||
lucky_25_user_lock,
|
||||
lucky_25_robot_push_msg,
|
||||
lucky_25_status, // 礼物消息的状态
|
||||
lock_lucky_25_message, // 消费送礼物消息锁
|
||||
lucky_25_user_10w_stat, // 消费送礼物消息锁
|
||||
;
|
||||
|
||||
public String getKey() {
|
||||
|
@@ -233,8 +233,9 @@ public enum BillObjTypeEnum {
|
||||
LUCKY_NUM_JACKPOT_INPUT_GOLD(155, "幸运数字奖池金币投入", BillTypeEnum.OUT, CurrencyEnum.DIAMOND, BillDomainTypeEnum.ACTIVITY),
|
||||
LUCKY_NUM_JACKPOT_OUTPUT_GOLD(156, "幸运数字奖池金币瓜分", BillTypeEnum.IN, CurrencyEnum.DIAMOND, BillDomainTypeEnum.ACTIVITY),
|
||||
|
||||
LUCKY_24_GIFT_PAY( 157, "幸运礼物支出", BillTypeEnum.OUT, CurrencyEnum.DIAMOND, BillDomainTypeEnum.SEND_GIFT),
|
||||
LUCKY_24_GIFT_PAY( 157, "幸运24礼物支出", BillTypeEnum.OUT, CurrencyEnum.DIAMOND, BillDomainTypeEnum.SEND_GIFT),
|
||||
BRAVO_GIFT_PAY( 158, "Bravo礼物支出", BillTypeEnum.OUT, CurrencyEnum.DIAMOND, BillDomainTypeEnum.SEND_GIFT),
|
||||
LUCKY_25_GIFT_PAY( 159, "幸运25礼物支出", BillTypeEnum.OUT, CurrencyEnum.DIAMOND, BillDomainTypeEnum.SEND_GIFT),
|
||||
;
|
||||
|
||||
BillObjTypeEnum(int value, String desc, BillTypeEnum type, CurrencyEnum currency, BillDomainTypeEnum domain) {
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package com.accompany.sharding.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
@TableName(value = "lucky_25_record")
|
||||
public class Lucky25Record extends Lucky24Record {
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.accompany.sharding.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
|
||||
@ApiModel
|
||||
public class Lucky25PersonalStat extends Lucky24PersonalStat {
|
||||
|
||||
public Lucky25PersonalStat(String date, Integer partitionId, Long uid, Long erbanNo) {
|
||||
super(date, partitionId, uid, erbanNo);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.accompany.sharding.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
|
||||
@ApiModel
|
||||
public class Lucky25PlatformStat extends Lucky24PlatformStat {
|
||||
|
||||
public Lucky25PlatformStat(String date, Integer partitionId) {
|
||||
super(date, partitionId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.accompany.sharding.mapper;
|
||||
|
||||
import com.accompany.sharding.model.Lucky25Record;
|
||||
import com.accompany.sharding.vo.Lucky25PersonalStat;
|
||||
import com.accompany.sharding.vo.Lucky25PlatformStat;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface Lucky25RecordMapper extends BaseMapper<Lucky25Record> {
|
||||
|
||||
List<Lucky25PlatformStat> listPlatform(@Param("partitionId") Integer partitionId, @Param("startTime") Date startTime, @Param("endTime") Date endTime,
|
||||
@Param("zoneIdHour") long zoneIdHour);
|
||||
|
||||
List<Lucky25PersonalStat> listPersonal(@Param("partitionId") Integer partitionId,
|
||||
@Param("uid") Long uid,
|
||||
@Param("startTime") Date startTime, @Param("endTime") Date endTime,
|
||||
@Param("zoneIdHour") long zoneIdHour);
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
<?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.sharding.mapper.Lucky25RecordMapper">
|
||||
|
||||
<select id="listPlatform" resultType="com.accompany.sharding.vo.Lucky25PlatformStat">
|
||||
select `date`, partition_id,
|
||||
sum(`totalInput`) `totalInput`, sum(`totalOutput`) `totalOutput`,
|
||||
sum(`totalOutput`) / sum(`totalInput`) `productionRatio`,
|
||||
count(*) `count`, count((IF(`maxOutput` > 0, 1, null))) `winCount`,
|
||||
sum(`num`) `num`, sum(`winNum`) `winNum`, sum(`winNum`) / sum(`num`) `winRate`
|
||||
from (
|
||||
select date(date_add(r.create_time, INTERVAL #{zoneIdHour} HOUR)) `date`,
|
||||
partition_id,
|
||||
sum(gift_num * gift_gold_price) `totalInput`,
|
||||
sum(win_gold_num) `totalOutput`,
|
||||
count(*) `num`,
|
||||
count((case when win_gold_num > 0 then 1 end)) `winNum`,
|
||||
max(win_gold_num) `maxOutput`
|
||||
from lucky_24_record r
|
||||
where r.create_time >= #{startTime} and r.create_time <= #{endTime}
|
||||
and r.partition_id = #{partitionId}
|
||||
group by `date`, uid) l
|
||||
group by `date`
|
||||
</select>
|
||||
|
||||
<select id="listPersonal" resultType="com.accompany.sharding.vo.Lucky25PersonalStat">
|
||||
select date(date_add(r.create_time, INTERVAL #{zoneIdHour} HOUR)) `date`, partition_id,
|
||||
r.uid,
|
||||
sum(gift_num * gift_gold_price) `totalInput`,
|
||||
sum(win_gold_num) `totalOutput`,
|
||||
sum(gift_num * gift_gold_price) - sum(win_gold_num) `production`,
|
||||
sum(win_gold_num) / sum(gift_num * gift_gold_price) `productionRatio`,
|
||||
sum(gift_num * gift_gold_price) / count(*) `avgInput`,
|
||||
count(*) `num`,
|
||||
count((case when win_gold_num > 0 then r.uid else null end)) `winNum`,
|
||||
ifnull(count((case when win_gold_num > 0 then r.uid else null end)) / count(*),0) `winRate`
|
||||
from lucky_24_record r
|
||||
where r.create_time >= #{startTime} and r.create_time <= #{endTime}
|
||||
<if test="null != uid">
|
||||
and r.uid = #{uid}
|
||||
</if>
|
||||
and r.partition_id = #{partitionId}
|
||||
group by `date`, r.uid
|
||||
</select>
|
||||
|
||||
</mapper>
|
@@ -0,0 +1,43 @@
|
||||
package com.accompany.business.constant;
|
||||
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum Lucky25PoolTypeEnum {
|
||||
|
||||
/**
|
||||
* 普通奖品池
|
||||
*/
|
||||
NEW_USER_POOL(1, "新人奖池"),
|
||||
|
||||
/**
|
||||
* 偏差宝箱奖池
|
||||
*/
|
||||
NORMAL_POOL(2, "普通奖池"),
|
||||
BLACK_POOL(3, "黑名单奖池"),
|
||||
HIGH_POOL(4, "保底奖池"),
|
||||
LOW_POOL(5, "衰减奖池"),
|
||||
;
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private int type;
|
||||
private String name;
|
||||
|
||||
public static Lucky25PoolTypeEnum get(int type) {
|
||||
Optional<Lucky25PoolTypeEnum> result = Arrays.stream(Lucky25PoolTypeEnum.values()).filter(prizePoolTypeEnum ->
|
||||
prizePoolTypeEnum.type == type).findFirst();
|
||||
if (result.isPresent()) {
|
||||
return result.get();
|
||||
}
|
||||
throw new ServiceException(BusiStatus.PARAMETERILLEGAL);
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.accompany.business.dto.lucky;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class Lucky25GiftConfig {
|
||||
|
||||
private BigDecimal platformRatio;
|
||||
private BigDecimal receiverRatio;
|
||||
//n
|
||||
private BigDecimal productionRatio;
|
||||
|
||||
private Map<Integer, Lucky25GiftConfig> ratioPartitionMap;
|
||||
|
||||
private Long highInput_A;
|
||||
private BigDecimal highInputExpect_D;
|
||||
|
||||
private Long preJudgeValue_H;
|
||||
private BigDecimal totalInput_J;
|
||||
private BigDecimal totalInputOffset_K1;
|
||||
private BigDecimal totalInputOffset_K2;
|
||||
|
||||
private Integer poolSize = 500;
|
||||
private Integer newUserPoolCount = 0;
|
||||
|
||||
private BigDecimal todayProductionRatio;
|
||||
|
||||
private Integer specialTipMulti;
|
||||
|
||||
private Long allRoomChatToastValue;
|
||||
|
||||
private Long specialFloatMulti;
|
||||
private Long specialFloatValue;
|
||||
|
||||
private Long warnMulti;
|
||||
private List<Long> followUidList;
|
||||
|
||||
private Map<Long, BigDecimal> whiteUidProductionRatioMap;
|
||||
private List<Long> blackUidList;
|
||||
|
||||
private String diamondIcon;
|
||||
|
||||
public Lucky25GiftConfig getRatioByPartitionId(Integer partitionId){
|
||||
return ratioPartitionMap.getOrDefault(partitionId, this);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.accompany.business.dto.lucky;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
public class Lucky25Result extends Lucky24Result {
|
||||
|
||||
public Lucky25Result(Integer poolId, Long input, long output, Boolean isSupplement) {
|
||||
super(poolId, input, output, isSupplement);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package com.accompany.business.message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 礼物消息
|
||||
*/
|
||||
@Data
|
||||
public class Lucky25Message extends Lucky24Message {
|
||||
private Integer partitionId;
|
||||
private Long uid;
|
||||
private Long receiverUid;
|
||||
private Long roomUid;
|
||||
private Integer giftId;
|
||||
private Long giftGoldPrice;
|
||||
private Integer giftNum;
|
||||
private Integer poolId;
|
||||
private Boolean isSupplement;
|
||||
private Integer drawMultiple;
|
||||
private Integer afterMultiple;
|
||||
private Long winGoldNum;
|
||||
private Long createTime;
|
||||
private String messId;
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.accompany.business.model.lucky;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
|
||||
@ApiModel
|
||||
@TableName(value = "lucky_25_pool", autoResultMap = true)
|
||||
public class Lucky25Pool extends Lucky24Pool {
|
||||
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.accompany.business.mybatismapper.lucky;
|
||||
|
||||
import com.accompany.business.model.lucky.Lucky25Pool;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
public interface Lucky25PoolMapper extends BaseMapper<Lucky25Pool> {
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.accompany.business.mybatismapper.lucky;
|
||||
|
||||
import com.accompany.sharding.vo.Lucky25PersonalStat;
|
||||
import com.accompany.sharding.vo.Lucky25PlatformStat;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Lucky25StatMapper {
|
||||
|
||||
int savePlatform(@Param("item") Lucky25PlatformStat item);
|
||||
|
||||
int savePersonal(@Param("item") Lucky25PersonalStat item);
|
||||
|
||||
List<Lucky25PlatformStat> listPlatformStat(@Param("partitionId") Integer partitionId, @Param("startDate") String startDate, @Param("endDate") String endDate);
|
||||
|
||||
List<Lucky25PersonalStat> listPersonalStat(@Param("partitionId") Integer partitionId,
|
||||
@Param("uid") Long uid,
|
||||
@Param("startDate") String startDate, @Param("endDate") String endDate);
|
||||
|
||||
}
|
@@ -86,6 +86,8 @@ public class GiftMessageService extends BaseService {
|
||||
outEnum = BillObjTypeEnum.LUCKY_24_GIFT_PAY;
|
||||
} else if (giftMessage.getGiftType() == GiftTypeEnum.BRAVO.getType()) {
|
||||
outEnum = BillObjTypeEnum.BRAVO_GIFT_PAY;
|
||||
} else if (giftMessage.getGiftType() == GiftTypeEnum.LUCKY_25.getType()) {
|
||||
outEnum = BillObjTypeEnum.LUCKY_25_GIFT_PAY;
|
||||
}
|
||||
|
||||
Date giftSendTime = new Date(giftMessage.getMessTime());
|
||||
|
@@ -61,7 +61,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -106,32 +105,9 @@ public class GiftSendService extends BaseService {
|
||||
private Lucky24GiftSendService lucky24GiftSendService;
|
||||
@Autowired
|
||||
private BravoGiftSendService bravoGiftSendService;
|
||||
@Autowired
|
||||
private Lucky25GiftSendService lucky25GiftSendService;
|
||||
|
||||
private List<LuckyBagGiftPrizePoolFilter> filters;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// 注意filter的顺序
|
||||
List<LuckyBagGiftPrizePoolFilter> filters =
|
||||
Lists.newArrayList(
|
||||
applicationContext.getBean(LuckyBagGiftDeviatePrizePoolFilter.class),
|
||||
applicationContext.getBean(LuckyBagGiftNormalPrizePoolFilter.class)
|
||||
);
|
||||
setFilters(filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法隔离级别声明为public,只保证子类能访问
|
||||
*
|
||||
* @param filters
|
||||
*/
|
||||
public void setFilters(List<LuckyBagGiftPrizePoolFilter> filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public List<LuckyBagGiftPrizePoolFilter> getFilters() {
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据礼物id获取有效的礼物
|
||||
@@ -498,6 +474,8 @@ public class GiftSendService extends BaseService {
|
||||
lucky24GiftSendService.draw(sendUid, u.getPartitionId(), room, Arrays.asList(recvUids), gift, giftNum, sendGiftTime);
|
||||
} else if (gift.getGiftType() == Constant.GiftType.BRAVO_GIFT) {
|
||||
bravoGiftSendService.draw(sendUid, u.getPartitionId(), room, Arrays.asList(recvUids), gift, giftNum, sendGiftTime);
|
||||
} else if (gift.getGiftType() == Constant.GiftType.LUCKY_25) {
|
||||
lucky25GiftSendService.draw(sendUid, u.getPartitionId(), room, Arrays.asList(recvUids), gift, giftNum, sendGiftTime);
|
||||
}
|
||||
|
||||
double everyGiftValue = calGiftValueByGiftType(everyGoldNum, gift.getGiftType(), u.getPartitionId());
|
||||
@@ -517,6 +495,9 @@ public class GiftSendService extends BaseService {
|
||||
} else if (giftType == Constant.GiftType.BRAVO_GIFT) {
|
||||
return bravoGiftSendService.getConfig().getRatioByPartitionId(partitionId).getReceiverRatio()
|
||||
.multiply(everyGoldNumB).doubleValue();
|
||||
} else if (giftType == Constant.GiftType.LUCKY_25) {
|
||||
return lucky25GiftSendService.getConfig().getRatioByPartitionId(partitionId).getReceiverRatio()
|
||||
.multiply(everyGoldNumB).doubleValue();
|
||||
}
|
||||
return everyGoldNum.doubleValue();
|
||||
}
|
||||
@@ -529,6 +510,7 @@ public class GiftSendService extends BaseService {
|
||||
BillObjTypeEnum objTypeEnum =
|
||||
GiftTypeEnum.LUCKY_24.getType() == gift.getGiftType()? BillObjTypeEnum.LUCKY_24_GIFT_PAY:
|
||||
GiftTypeEnum.BRAVO.getType() == gift.getGiftType()? BillObjTypeEnum.BRAVO_GIFT_PAY:
|
||||
GiftTypeEnum.LUCKY_25.getType() == gift.getGiftType()? BillObjTypeEnum.LUCKY_25_GIFT_PAY:
|
||||
null != room? BillObjTypeEnum.GIFT_ROOM_PAY: BillObjTypeEnum.GIFT_PERSON_PAY;
|
||||
UserPurse after = reduceStockV5(sender.getUid(), sender.getPartitionId(), gift.getGiftId(),
|
||||
totalGoldNum, totalGiftNum, giftSource, objTypeEnum);
|
||||
@@ -937,7 +919,13 @@ public class GiftSendService extends BaseService {
|
||||
* @return
|
||||
*/
|
||||
private Map<Long, List<LuckyBagRecord>> luckyBagDrawV2(long sendUid, int giftNum, Gift luckyBag, Date giftSendTime, List<Long> receiveUids) {
|
||||
LuckyBagGiftPrizePoolFilterChain filterChain = new LuckyBagGiftPrizePoolFilterChain(getFilters());
|
||||
// 注意filter的顺序
|
||||
List<LuckyBagGiftPrizePoolFilter> filters =
|
||||
Lists.newArrayList(
|
||||
applicationContext.getBean(LuckyBagGiftDeviatePrizePoolFilter.class),
|
||||
applicationContext.getBean(LuckyBagGiftNormalPrizePoolFilter.class)
|
||||
);
|
||||
LuckyBagGiftPrizePoolFilterChain filterChain = new LuckyBagGiftPrizePoolFilterChain(filters);
|
||||
LuckyBagGiftPrizePoolRequest request =
|
||||
new LuckyBagGiftPrizePoolRequest(sendUid, giftNum * receiveUids.size(), giftSendTime, luckyBag.getGiftId(),
|
||||
luckyBag.getOpenDeviate(), luckyBag.getGiftType());
|
||||
|
@@ -0,0 +1,260 @@
|
||||
package com.accompany.business.service.gift;
|
||||
|
||||
import com.accompany.business.dto.lucky.*;
|
||||
import com.accompany.business.message.Lucky24Message;
|
||||
import com.accompany.business.message.Lucky25Message;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.service.lucky.*;
|
||||
import com.accompany.business.service.mq.RocketMQService;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.RandomUtil;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.core.model.Room;
|
||||
import com.accompany.core.service.SysConfService;
|
||||
import com.accompany.core.service.common.JedisService;
|
||||
import com.accompany.sharding.model.Lucky25Record;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RMap;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25GiftSendService {
|
||||
|
||||
@Autowired
|
||||
private SysConfService sysConfService;
|
||||
@Autowired
|
||||
private Lucky25StockService stockService;
|
||||
@Autowired
|
||||
private Lucky25UserMetaService userMetaService;
|
||||
@Autowired
|
||||
private Lucky25PoolService poolService;
|
||||
@Autowired
|
||||
private Lucky25RecordService recordService;
|
||||
@Autowired
|
||||
private Lucky25IncomeAllotService incomeAllotService;
|
||||
@Autowired
|
||||
private Lucky25SettlementService settlementService;
|
||||
@Autowired
|
||||
private Lucky25RobotMsgService robotMsgService;
|
||||
@Autowired
|
||||
private RocketMQService rocketMQService;
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
|
||||
public void draw(long senderUid, Integer partitionId, Room room, List<Long> receiverList,
|
||||
Gift gift, int everyGiftNum, Date sendGiftTime) {
|
||||
|
||||
long everyoneGoldNum = everyGiftNum * gift.getGoldPrice();
|
||||
|
||||
Lucky25GiftConfig config = getConfig();
|
||||
Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId);
|
||||
SuperLuckyGiftIncomeAllot incomeAllot = incomeAllotService.calculate(partitionConfig, gift, everyGiftNum, receiverList);
|
||||
|
||||
// 增加库存
|
||||
BigDecimal afterStock = stockService.addStock(partitionId, incomeAllot.getRemainValue());
|
||||
log.info("[lucky25] uid {}, partitionId {}, addStockGoldNum {}, afterStock {}",
|
||||
senderUid, partitionId, incomeAllot.getRemainValue(), afterStock);
|
||||
|
||||
Map<Long, Lucky25Record> recordMap = draw(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime);
|
||||
log.info("[lucky25] uid {}, totalWinGoldNum {}", senderUid, JSON.toJSONString(recordMap));
|
||||
|
||||
sendMq(recordMap);
|
||||
}
|
||||
|
||||
public Map<Long, Lucky25Record> draw(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long senderUid, int partitionId,
|
||||
Gift gift, int everyGiftNum, long everyoneGoldNum,
|
||||
List<Long> receiverList, Room room, Date sendGiftTime) {
|
||||
if (everyoneGoldNum < partitionConfig.getHighInput_A()
|
||||
|| (!CollectionUtils.isEmpty(config.getBlackUidList()) && config.getBlackUidList().contains(senderUid))){
|
||||
log.error("[lucky25] uid {} totalInput {} configHighInput {} 原", senderUid, everyoneGoldNum, partitionConfig.getHighInput_A());
|
||||
return receiverList.parallelStream()
|
||||
.collect(Collectors.toMap(receiverUid-> receiverUid,
|
||||
receiverUid-> drawMultiple(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null)));
|
||||
}
|
||||
|
||||
BigDecimal totalInput_A = BigDecimal.valueOf(everyoneGoldNum);
|
||||
BigDecimal historyInput = BigDecimal.ZERO;
|
||||
BigDecimal historyOutput = BigDecimal.ZERO;
|
||||
|
||||
RMap<String, Number> userStatCacheMap = userMetaService.getUser10wStat(senderUid);
|
||||
Map<String, Number> userStatMap = userStatCacheMap.readAllMap();
|
||||
if (userStatMap.isEmpty()){
|
||||
List<Lucky25Result> historyResult = userMetaService.getUserLast10wHistory(senderUid);
|
||||
historyInput = BigDecimal.valueOf(historyResult.stream().mapToLong(Lucky25Result::getInput).sum());
|
||||
historyOutput = BigDecimal.valueOf(historyResult.stream().mapToLong(Lucky25Result::getOutput).sum());
|
||||
|
||||
userStatCacheMap.fastPut(Lucky25UserMetaService.INPUT_KEY, historyInput);
|
||||
userStatCacheMap.fastPut(Lucky25UserMetaService.OUTPUT_KEY, historyOutput);
|
||||
userStatCacheMap.expire(Duration.of(10, ChronoUnit.SECONDS));
|
||||
} else {
|
||||
historyInput = BigDecimal.valueOf(userStatMap.getOrDefault(Lucky25UserMetaService.INPUT_KEY, 0L).longValue());
|
||||
historyOutput = BigDecimal.valueOf(userStatMap.getOrDefault(Lucky25UserMetaService.OUTPUT_KEY, 0L).longValue());
|
||||
}
|
||||
|
||||
AtomicBoolean space1000flag = new AtomicBoolean(false);
|
||||
AtomicBoolean space100flag = new AtomicBoolean(false);
|
||||
|
||||
BigDecimal productionRatio_B = historyOutput.compareTo(BigDecimal.ZERO) != 0 ?
|
||||
historyOutput.divide(historyInput,2, RoundingMode.HALF_UP): BigDecimal.ZERO;
|
||||
BigDecimal historyDiff_C = historyInput.subtract(historyOutput);
|
||||
BigDecimal expect_D = partitionConfig.getHighInputExpect_D();
|
||||
BigDecimal X = productionRatio_B.compareTo(BigDecimal.ONE) != 0 ?
|
||||
historyDiff_C.divide(BigDecimal.ONE.subtract(productionRatio_B), 2, RoundingMode.HALF_UP): BigDecimal.ZERO;
|
||||
BigDecimal water = X.multiply(BigDecimal.ONE.subtract(expect_D).add(BigDecimal.valueOf(0.01D)));
|
||||
BigDecimal space1000 = (historyDiff_C.subtract(water)).divide(BigDecimal.valueOf(1000L), 2, RoundingMode.HALF_UP);
|
||||
if (space1000.compareTo(totalInput_A) > 0){
|
||||
return receiverList.parallelStream()
|
||||
.collect(Collectors.toMap(receiverUid-> receiverUid,
|
||||
receiverUid-> {
|
||||
//left
|
||||
int randomIndex = RandomUtil.randomByRange(0, 10);
|
||||
long drawMultiple = randomIndex < 1? 1000L: randomIndex < 2? 500L: randomIndex < 5? 100L: 0L;
|
||||
log.info("[lucky25] uid {} space1000 {} totalInput {} left receiverUid {} randomIndex {} drawMultiple {}",
|
||||
senderUid, space1000, totalInput_A, receiverUid, randomIndex, drawMultiple);
|
||||
if (drawMultiple > 0L && !space1000flag.compareAndSet(false, true)){
|
||||
drawMultiple = 0L;
|
||||
log.info("[lucky25] uid {} space1000 cas false {} totalInput {} left receiverUid {} randomIndex {} drawMultiple {}",
|
||||
senderUid, space1000, totalInput_A, receiverUid, randomIndex, drawMultiple);
|
||||
}
|
||||
Lucky25Result result = new Lucky25Result(null, everyoneGoldNum, drawMultiple, false);
|
||||
return updateMeta(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null, result);
|
||||
}));
|
||||
}
|
||||
BigDecimal space100 = (historyDiff_C.subtract(water)).divide(BigDecimal.valueOf(100L), 2, RoundingMode.HALF_UP);
|
||||
if (space100.compareTo(totalInput_A) > 0){
|
||||
return receiverList.parallelStream()
|
||||
.collect(Collectors.toMap(receiverUid-> receiverUid,
|
||||
receiverUid-> {
|
||||
//middle
|
||||
int randomIndex = RandomUtil.randomByRange(0, 100);
|
||||
long drawMultiple = randomIndex < 25? 100L: randomIndex < 55? 5L: 0L;
|
||||
log.info("[lucky25] uid {} space100 {} totalInput {} middle receiver {} randomIndex {} drawMultiple {}",
|
||||
senderUid, space100, totalInput_A, receiverUid, randomIndex, drawMultiple);
|
||||
if (drawMultiple > 0L && !space100flag.compareAndSet(false, true)){
|
||||
drawMultiple = 0L;
|
||||
log.info("[lucky25] uid {} space100 cas false {} totalInput {} left receiverUid {} randomIndex {} drawMultiple {}",
|
||||
senderUid, space1000, totalInput_A, receiverUid, randomIndex, drawMultiple);
|
||||
}
|
||||
Lucky25Result result = new Lucky25Result(null, everyoneGoldNum, drawMultiple, false);
|
||||
return updateMeta(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null, result);
|
||||
}));
|
||||
}
|
||||
log.info("[lucky25] uid {} space100 {} totalInput {} 原来 10L", senderUid, space1000, totalInput_A);
|
||||
return receiverList.parallelStream()
|
||||
.collect(Collectors.toMap(receiverUid-> receiverUid,
|
||||
receiverUid-> drawMultiple(config, partitionConfig, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, null)));
|
||||
}
|
||||
|
||||
private void sendMq(Map<Long, Lucky25Record> recordMap) {
|
||||
Map<String, String> caches = new HashMap<>(recordMap.size());
|
||||
List<Lucky25Message> messageList = new ArrayList<>();
|
||||
|
||||
DefaultIdentifierGenerator idGenerator = DefaultIdentifierGenerator.getInstance();
|
||||
|
||||
for (Lucky25Record record: recordMap.values()){
|
||||
String id = idGenerator.nextUUID(null);
|
||||
|
||||
Lucky25Message message = new Lucky25Message();
|
||||
message.setMessId(id);
|
||||
message.setPartitionId(record.getPartitionId());
|
||||
message.setUid(record.getUid());
|
||||
message.setReceiverUid(record.getReceiverUid());
|
||||
message.setRoomUid(record.getRoomUid());
|
||||
message.setGiftId(record.getGiftId());
|
||||
message.setGiftGoldPrice(record.getGiftGoldPrice());
|
||||
message.setGiftNum(record.getGiftNum());
|
||||
message.setPoolId(record.getPoolId());
|
||||
message.setIsSupplement(record.getIsSupplement());
|
||||
message.setDrawMultiple(record.getDrawMultiple());
|
||||
message.setAfterMultiple(record.getAfterMultiple());
|
||||
message.setWinGoldNum(record.getWinGoldNum());
|
||||
message.setCreateTime(record.getCreateTime().getTime());
|
||||
|
||||
messageList.add(message);
|
||||
|
||||
caches.put(id, JSON.toJSONString(message));
|
||||
}
|
||||
|
||||
jedisService.hwrite(RedisKey.lucky_25_status.getKey(), caches);
|
||||
|
||||
rocketMQService.sendBatchLucky25Message(messageList);
|
||||
}
|
||||
|
||||
public Lucky25Record drawMultiple(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, long senderUid, int partitionId,
|
||||
Gift gift, int giftNum, long receiverUid, long everyoneGoldNum, Room room, Date sendGiftTime,
|
||||
Long maxMultiple) {
|
||||
Lucky25Result drawResult = poolService.drawMultipleFromPool(config, senderUid, partitionId);
|
||||
return updateMeta(config, partitionConfig, senderUid, partitionId, gift, giftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, maxMultiple, drawResult);
|
||||
}
|
||||
|
||||
private Lucky25Record updateMeta(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, long senderUid, int partitionId,
|
||||
Gift gift, int giftNum, long receiverUid, long everyoneGoldNum, Room room, Date sendGiftTime,
|
||||
Long maxMultiple, Lucky25Result drawResult){
|
||||
long drawMultiple = drawResult.getOutput();
|
||||
long afterMultiple = drawMultiple;
|
||||
if (null != maxMultiple) {
|
||||
afterMultiple = Math.min(afterMultiple, maxMultiple);
|
||||
}
|
||||
// 个人库存
|
||||
if (afterMultiple > 0L){
|
||||
long preWinGoldNum = afterMultiple * everyoneGoldNum;
|
||||
if (!userMetaService.judgePersonalStock(partitionConfig, senderUid, receiverUid, everyoneGoldNum, preWinGoldNum)){
|
||||
afterMultiple = 0L;
|
||||
}
|
||||
}
|
||||
// 平台库存
|
||||
if (afterMultiple > 0L){
|
||||
BigDecimal preWinGoldNum = BigDecimal.valueOf(afterMultiple * everyoneGoldNum);
|
||||
if (!judgeStock(partitionId, preWinGoldNum, senderUid, receiverUid)){
|
||||
afterMultiple = 0L;
|
||||
}
|
||||
}
|
||||
long winGoldNum = afterMultiple * everyoneGoldNum;
|
||||
userMetaService.updateUserMeta(senderUid, partitionId, everyoneGoldNum, winGoldNum);
|
||||
|
||||
if (winGoldNum > 0L){
|
||||
settlementService.syncSendReward(config, senderUid, room, gift, winGoldNum, afterMultiple);
|
||||
}
|
||||
|
||||
return recordService.buildRecord(senderUid, partitionId, gift, giftNum, null != room? room.getUid(): null,
|
||||
receiverUid, drawResult.getPoolId(), Boolean.TRUE.equals(drawResult.getIsSupplement()),
|
||||
drawMultiple, afterMultiple, sendGiftTime);
|
||||
}
|
||||
|
||||
private boolean judgeStock(Integer partitionId, BigDecimal winGoldNum, Long senderUid, Long receiverUid){
|
||||
BigDecimal afterStock = stockService.subStock(partitionId, winGoldNum);
|
||||
boolean enough = afterStock.compareTo(BigDecimal.ZERO) >= 0;
|
||||
if (!enough){
|
||||
log.info("[lucky25] drawMultiple sender {} receiver {} 产出大于库存 winGoldNum {} afterStock {}",
|
||||
senderUid, receiverUid, winGoldNum, afterStock);
|
||||
afterStock = stockService.addStock(partitionId, winGoldNum);
|
||||
robotMsgService.pushStockNotEnough(partitionId, afterStock);
|
||||
}
|
||||
return enough;
|
||||
}
|
||||
|
||||
public Lucky25GiftConfig getConfig() {
|
||||
String configStr = sysConfService.getSysConfValueById(Constant.SysConfId.LUCKY_25_GIFT_CONFIG);
|
||||
if (!StringUtils.hasText(configStr)) {
|
||||
throw new ServiceException(BusiStatus.ALREADY_NOTEXISTS_CONFIG);
|
||||
}
|
||||
return JSON.parseObject(configStr, Lucky25GiftConfig.class);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
package com.accompany.business.service.gift;
|
||||
|
||||
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.message.Lucky25Message;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.service.lucky.*;
|
||||
import com.accompany.business.service.lucky.rank.Lucky24SendWeekRankService;
|
||||
import com.accompany.business.service.room.RoomService;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.core.model.Room;
|
||||
import com.accompany.core.service.base.BaseService;
|
||||
import com.accompany.sharding.model.Lucky25Record;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25MessageService extends BaseService {
|
||||
|
||||
@Autowired
|
||||
private Lucky25RecordService recordService;
|
||||
@Autowired
|
||||
private Lucky25IncomeAllotService incomeAllotService;
|
||||
@Autowired
|
||||
private SuperLuckyGiftSendService superLuckyGiftSendService;
|
||||
@Autowired
|
||||
private Lucky25RobotMsgService robotMsgService;
|
||||
@Autowired
|
||||
private Lucky25GiftSendService sendService;
|
||||
@Autowired
|
||||
private RoomService roomService;
|
||||
@Autowired
|
||||
private GiftService giftService;
|
||||
@Autowired
|
||||
private Lucky24SendWeekRankService lucky24SendWeekRankService;
|
||||
|
||||
public void handleGiftMessage(Lucky25Message giftMessage) {
|
||||
// 防止消息被重复消费
|
||||
if (!jedisLockService.isExist(RedisKey.lock_lucky_25_message.getKey(giftMessage.getMessId()), 30)) {
|
||||
logger.warn("handleLucky25Message giftMessage had handle, mess: " + giftMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jedisService.hexists(RedisKey.lucky_25_status.getKey(), giftMessage.getMessId())){
|
||||
logger.warn("handleLucky25Message giftMessage had handle, mess: " + giftMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("【处理Lucky25 mq】 开始处理 giftMessage: {}", JSON.toJSONString(giftMessage));
|
||||
|
||||
Room room = null != giftMessage.getRoomUid()? roomService.getRoomByUid(giftMessage.getRoomUid()): null;
|
||||
Gift gift = giftService.getGiftById(giftMessage.getGiftId());
|
||||
Date createTime = new Date(giftMessage.getCreateTime());
|
||||
|
||||
Lucky25Record record = insertRecord(giftMessage);
|
||||
|
||||
log.info("【处理Lucky25 mq】 record 插入成功 messId:{} recordId:{} record:{}",
|
||||
giftMessage.getMessId(), record.getId(), JSON.toJSONString(record));
|
||||
|
||||
// 收礼者收益
|
||||
Lucky25GiftConfig config = sendService.getConfig();
|
||||
Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(giftMessage.getPartitionId());
|
||||
SuperLuckyGiftIncomeAllot receiverIncomeAllot = incomeAllotService.calculate(partitionConfig, gift, giftMessage.getGiftNum(), Collections.singletonList(record.getReceiverUid()));
|
||||
superLuckyGiftSendService.syncSettlement(giftMessage.getUid(), gift, giftMessage.getGiftNum(), giftMessage.getGiftNum(), room, receiverIncomeAllot, createTime);
|
||||
|
||||
logger.info("【处理Lucky25 mq】 收礼收益已发放 messId: {} incomeAllot: {}", giftMessage.getMessId(), JSON.toJSONString(receiverIncomeAllot));
|
||||
|
||||
// 后面都是异步发消息
|
||||
// if (record.getAfterMultiple() >= config.getWarnMulti()){
|
||||
// long totalGoldNum = giftMessage.getGiftNum() * giftMessage.getGiftGoldPrice();
|
||||
// robotMsgService.pushSuperMulti(record.getUid(), record.getReceiverUid(), record.getAfterMultiple(), totalGoldNum, record.getWinGoldNum(),
|
||||
// record.getRoomUid());
|
||||
// }
|
||||
|
||||
if (CollectionUtils.isEmpty(config.getFollowUidList()) && config.getFollowUidList().contains(record.getUid())){
|
||||
robotMsgService.pushFollowUser(record.getUid(), record.getReceiverUid(), record.getRoomUid());
|
||||
}
|
||||
|
||||
lucky24SendWeekRankService.updateRank(record);
|
||||
|
||||
// 删除该标识,表示消息已经消费过
|
||||
jedisService.hdel(RedisKey.lucky_25_status.getKey(), giftMessage.getMessId());
|
||||
}
|
||||
|
||||
private Lucky25Record insertRecord(Lucky25Message giftMessage) {
|
||||
Lucky25Record record = new Lucky25Record();
|
||||
record.setMessId(giftMessage.getMessId());
|
||||
record.setPartitionId(giftMessage.getPartitionId());
|
||||
record.setUid(giftMessage.getUid());
|
||||
record.setReceiverUid(giftMessage.getReceiverUid());
|
||||
record.setRoomUid(giftMessage.getRoomUid());
|
||||
record.setGiftId(giftMessage.getGiftId());
|
||||
record.setGiftGoldPrice(giftMessage.getGiftGoldPrice());
|
||||
record.setGiftNum(giftMessage.getGiftNum());
|
||||
record.setPoolId(giftMessage.getPoolId());
|
||||
record.setIsSupplement(giftMessage.getIsSupplement());
|
||||
record.setDrawMultiple(giftMessage.getDrawMultiple());
|
||||
record.setAfterMultiple(giftMessage.getAfterMultiple());
|
||||
record.setWinGoldNum(giftMessage.getWinGoldNum());
|
||||
record.setCreateTime(new Date(giftMessage.getCreateTime()));
|
||||
|
||||
recordService.insertRecord(record);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.service.purse.UserPurseService;
|
||||
import com.accompany.business.service.record.BillRecordService;
|
||||
import com.accompany.core.enumeration.BillObjTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25IncomeAllotService implements LuckyGiftIncomeAllotService {
|
||||
|
||||
@Autowired
|
||||
private UserPurseService userPurseService;
|
||||
@Autowired
|
||||
private BillRecordService billRecordService;
|
||||
|
||||
public SuperLuckyGiftIncomeAllot calculate(Lucky25GiftConfig config, Gift gift, int giftNum, List<Long> receiverUids) {
|
||||
BigDecimal giftNumB = BigDecimal.valueOf(giftNum);
|
||||
BigDecimal totalNum = BigDecimal.valueOf(receiverUids.size()).multiply(giftNumB);
|
||||
// 单价
|
||||
BigDecimal giftValue = BigDecimal.valueOf(gift.getGoldPrice());
|
||||
// 总价
|
||||
BigDecimal everyTotalValue = giftValue.multiply(giftNumB);
|
||||
BigDecimal totalValue = giftValue.multiply(totalNum);
|
||||
|
||||
// 收礼者收益单价
|
||||
BigDecimal receiverIncome = everyTotalValue.multiply(config.getReceiverRatio());
|
||||
|
||||
BigDecimal receiverTotalIncome = totalValue.multiply(config.getReceiverRatio());
|
||||
|
||||
Map<Long, BigDecimal> receiverIncomeMap = new HashMap<>();
|
||||
for (Long receiver: receiverUids){
|
||||
receiverIncomeMap.put(receiver, receiverIncome);
|
||||
}
|
||||
|
||||
// 平台抽成
|
||||
BigDecimal platformGoldNum = totalValue.multiply(config.getPlatformRatio());
|
||||
// 增加库存
|
||||
BigDecimal addStockGoldNum = totalValue.subtract(platformGoldNum);
|
||||
|
||||
return new SuperLuckyGiftIncomeAllot(totalValue, receiverTotalIncome, receiverIncomeMap, BigDecimal.ZERO, addStockGoldNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIncome(Long receiverUid, double income, BillObjTypeEnum billObjTypeEnum, Long senderUid, Long roomUid, Integer giftId, Integer everyGiftNum, Long giftTotalGoldNum, Date sendGiftTime) {
|
||||
//userPurseService.addGoldWithoutTx(receiverUid, income.doubleValue(), billObjTypeEnum,
|
||||
userPurseService.addGold(receiverUid, income, billObjTypeEnum,
|
||||
(userPurse)-> billRecordService.insertGiftSendBillRecord(receiverUid, senderUid, roomUid, null, billObjTypeEnum, income,
|
||||
giftId, everyGiftNum, giftTotalGoldNum, sendGiftTime, userPurse));
|
||||
}
|
||||
}
|
@@ -0,0 +1,301 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.constant.Lucky25PoolTypeEnum;
|
||||
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
|
||||
import com.accompany.business.dto.lucky.Lucky25Result;
|
||||
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
|
||||
import com.accompany.business.dto.lucky.Lucky25Result;
|
||||
import com.accompany.business.model.lucky.Lucky25Pool;
|
||||
import com.accompany.business.mybatismapper.lucky.Lucky25PoolMapper;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.common.utils.RandomUtil;
|
||||
import com.accompany.core.enumeration.PartitionEnum;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25PoolService {
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
@Autowired
|
||||
private Lucky25UserMetaService userMetaService;
|
||||
@Autowired
|
||||
private Lucky25PoolMapper poolMapper;
|
||||
|
||||
public Lucky25Result drawMultipleFromPool(Lucky25GiftConfig config, Long uid, int partitionId){
|
||||
RQueue<Lucky25Result> userPool = getUserPool(uid);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Lucky25Result result = userPool.poll();
|
||||
if (null != result) {
|
||||
userPool.expireAsync(Duration.of(14, ChronoUnit.DAYS));
|
||||
return result;
|
||||
}
|
||||
genPool(config, uid, partitionId, userPool);
|
||||
}
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY);
|
||||
}
|
||||
|
||||
private void genPool(Lucky25GiftConfig config, Long uid, int partitionId, RQueue<Lucky25Result> userPool) {
|
||||
boolean locked = false;
|
||||
RLock lock = redissonClient.getLock(RedisKey.lucky_25_user_lock.getKey(uid.toString()));
|
||||
try {
|
||||
locked = lock.tryLock(5,3, TimeUnit.SECONDS);
|
||||
|
||||
if (!userPool.isEmpty()){
|
||||
log.info("[lucky25] genPool uid {} 已生成pool,无需再生成", uid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!locked) {
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY);
|
||||
}
|
||||
|
||||
Lucky25PoolTypeEnum poolType = selectPoolType(config, uid);
|
||||
|
||||
List<Lucky25Pool> poolList = poolMapper.selectList(null);
|
||||
if (CollectionUtils.isEmpty(poolList)){
|
||||
throw new ServiceException(BusiStatus.SEIZE_TREASURE_POOL_CONFIG_ERROR);
|
||||
}
|
||||
|
||||
Lucky25GiftConfig partitionConfig = config.getRatioByPartitionId(partitionId);
|
||||
Lucky25Pool pool = selectPool(config, partitionConfig, uid, partitionId, poolType, poolList);
|
||||
|
||||
buildPool(config, uid, userPool, pool);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (locked) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPool(Lucky25GiftConfig config, Long uid, RQueue<Lucky25Result> userPool, Lucky25Pool pool) {
|
||||
List<Lucky25Pool.Lucky24PoolItem> poolItemList = pool.getItemList().stream().filter(item->item.getNum()>0).collect(Collectors.toList());
|
||||
List<Integer> winList = buildWinList(poolItemList);
|
||||
int[] poolArray = new int[config.getPoolSize()];
|
||||
for (int i = 0; i < config.getPoolSize(); i++) {
|
||||
poolArray[i] = 0;
|
||||
}
|
||||
|
||||
if (!winList.isEmpty()){
|
||||
List<Integer> winIndexList = new ArrayList<>();
|
||||
int winDistance = config.getPoolSize() / winList.size();
|
||||
for (int i = 0; i < winList.size(); i++) {
|
||||
int nextWinIndex = config.getPoolSize() - 1 - i * winDistance;
|
||||
// 确保索引不会超出超集范围
|
||||
if (nextWinIndex > 0) {
|
||||
poolArray[nextWinIndex] = winList.get(i);
|
||||
winIndexList.add(nextWinIndex);
|
||||
}
|
||||
}
|
||||
log.info("[lucky25] genPool buildPool uid {}, winIndexList {}", uid, JSON.toJSONString(winIndexList));
|
||||
}
|
||||
|
||||
List<Lucky25Result> poolList = Arrays.stream(poolArray).boxed().map(output->{
|
||||
Lucky25Result result = new Lucky25Result();
|
||||
result.setPoolId(pool.getId());
|
||||
result.setOutput(output);
|
||||
return result;
|
||||
}).collect(Collectors.toList());
|
||||
userPool.addAll(poolList);
|
||||
}
|
||||
|
||||
private List<Integer> buildWinList(List<Lucky25Pool.Lucky24PoolItem> poolItemList) {
|
||||
List<Integer> resultList = new ArrayList<>();
|
||||
|
||||
for (Lucky25Pool.Lucky24PoolItem item : poolItemList) {
|
||||
int multi = item.getMulti();
|
||||
int num = item.getNum();
|
||||
|
||||
// 使用循环生成 num 个 multi 值,并添加到 resultList 中
|
||||
for (int i = 0; i < num; i++) {
|
||||
resultList.add(multi);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.shuffle(resultList);
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private Lucky25Pool selectPool(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long uid, Integer partitionId, Lucky25PoolTypeEnum poolType, List<Lucky25Pool> poolList) {
|
||||
if (Lucky25PoolTypeEnum.NEW_USER_POOL.equals(poolType)){
|
||||
poolList = poolList.stream()
|
||||
.filter(pool->pool.getType() == Lucky25PoolTypeEnum.NEW_USER_POOL.getType())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int randomIndex = RandomUtil.randomByRange(0, poolList.size());
|
||||
Lucky25Pool randomPool = poolList.get(randomIndex);
|
||||
log.info("[lucky25] genPool selectPool type {}, pooList {}, randomIndex {}, expect {}",
|
||||
poolType, JSON.toJSONString(poolList.stream().map(Lucky25Pool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
|
||||
return randomPool;
|
||||
}
|
||||
|
||||
// 黑名单
|
||||
if (!CollectionUtils.isEmpty(config.getBlackUidList())
|
||||
&& config.getBlackUidList().contains(uid)){
|
||||
List<Lucky25Pool> balckPoolList = poolList.stream()
|
||||
.filter(pool->pool.getType() == Lucky25PoolTypeEnum.BLACK_POOL.getType())
|
||||
.sorted(Comparator.comparing(Lucky25Pool::getExpect))
|
||||
.limit(1L)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int randomIndex = RandomUtil.randomByRange(0, balckPoolList.size());
|
||||
Lucky25Pool randomPool = balckPoolList.get(randomIndex);
|
||||
log.info("[lucky25] genPool selectPool type {}, blackPooList {}, randomIndex {}, expect {}",
|
||||
poolType, JSON.toJSONString(balckPoolList.stream().map(Lucky25Pool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
|
||||
|
||||
return randomPool;
|
||||
}
|
||||
|
||||
PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId);
|
||||
long today = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId());
|
||||
String todayStartTimeStr = String.valueOf(today);
|
||||
|
||||
String todayInputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.INPUT_KEY);
|
||||
String todayOutputKey = String.join("_", todayStartTimeStr, Lucky25UserMetaService.OUTPUT_KEY);
|
||||
|
||||
RMap<String, Number> userMetaMap = userMetaService.getUserMeta(uid);
|
||||
BigDecimal input = BigDecimal.valueOf(userMetaMap.getOrDefault(todayInputKey, 0L).longValue());
|
||||
BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(todayOutputKey, 0L).longValue());
|
||||
BigDecimal todayProductionRatio = BigDecimal.ZERO.equals(output)? BigDecimal.ZERO: output.divide(input,2, RoundingMode.HALF_UP);
|
||||
if (input.compareTo(BigDecimal.valueOf(300000)) >= 0 && todayProductionRatio.compareTo(partitionConfig.getTodayProductionRatio()) >= 0){
|
||||
List<Lucky25Pool> excludePoolList = poolList.stream()
|
||||
.filter(pool->pool.getType() == Lucky25PoolTypeEnum.NORMAL_POOL.getType())
|
||||
.filter(pool-> pool.getItemList().stream().noneMatch(poolItem->poolItem.getMulti() == 1000 && poolItem.getNum() > 0))
|
||||
.filter(pool-> partitionConfig.getProductionRatio().compareTo(pool.getExpect()) > 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int randomIndex = RandomUtil.randomByRange(0, excludePoolList.size());
|
||||
Lucky25Pool randomPool = excludePoolList.get(randomIndex);
|
||||
log.info("[lucky25] genPool selectPool type {}, excludePooList {}, randomIndex {}, expect {}",
|
||||
poolType, JSON.toJSONString(excludePoolList.stream().map(Lucky25Pool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
|
||||
|
||||
//SpringContextHolder.getBean(Lucky25RobotMsgService.class).pushTodayProduction(uid, input, output, todayProductionRatio, config.getTodayProductionRatio());
|
||||
|
||||
return randomPool;
|
||||
}
|
||||
|
||||
BigDecimal thisExpect = calExpect(config, partitionConfig, uid);
|
||||
List<Lucky25Pool> expectPoolList = BigDecimal.ZERO.compareTo(thisExpect) < 0 ?
|
||||
poolList.stream().filter(pool->pool.getType() == Lucky25PoolTypeEnum.NORMAL_POOL.getType())
|
||||
.filter(pool->pool.getExpect().compareTo(thisExpect) > 0).collect(Collectors.toList()):
|
||||
poolList.stream().filter(pool->pool.getType() == Lucky25PoolTypeEnum.NORMAL_POOL.getType())
|
||||
.filter(pool->thisExpect.negate().compareTo(pool.getExpect()) >= 0).collect(Collectors.toList());
|
||||
|
||||
if (CollectionUtils.isEmpty(expectPoolList)){
|
||||
expectPoolList = poolList.stream().filter(pool->pool.getType() == Lucky25PoolTypeEnum.NEW_USER_POOL.getType())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
int randomIndex = RandomUtil.randomByRange(0, expectPoolList.size());
|
||||
Lucky25Pool randomPool = expectPoolList.get(randomIndex);
|
||||
log.info("[lucky25] genPool selectPool type {}, pooList {}, randomIndex {}, expect {}",
|
||||
poolType, JSON.toJSONString(expectPoolList.stream().map(Lucky25Pool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
|
||||
return randomPool;
|
||||
}
|
||||
|
||||
private BigDecimal calExpect(Lucky25GiftConfig config, Lucky25GiftConfig partitionConfig, Long uid) {
|
||||
BigDecimal n = !CollectionUtils.isEmpty(config.getWhiteUidProductionRatioMap()) && config.getWhiteUidProductionRatioMap().containsKey(uid) ?
|
||||
config.getWhiteUidProductionRatioMap().get(uid): partitionConfig.getProductionRatio();
|
||||
|
||||
BigDecimal userLast2000ProductionRatio = userMetaService.getUserLast2000ProductionRatio(uid);
|
||||
if (userLast2000ProductionRatio.compareTo(n) <= 0){
|
||||
BigDecimal thisExpect = n.add(n).subtract(userLast2000ProductionRatio);
|
||||
log.info("[lucky25] genPool calExpect uid {}, userLast2000ProductionRatio {} 小于等于配置基准 {},修正后期望值 {}",
|
||||
uid, userLast2000ProductionRatio, n, thisExpect);
|
||||
return thisExpect;
|
||||
}
|
||||
BigDecimal userProductionRatio = userMetaService.getUserProductionRatio(uid);
|
||||
if (userProductionRatio.compareTo(n) <= 0){
|
||||
BigDecimal thisExpect = n.add(n).subtract(userProductionRatio);
|
||||
log.info("[lucky25] genPool calExpect uid {}, userTotalProductionRatio {} 小于等于配置基准 {},修正后期望值 {}",
|
||||
uid, userProductionRatio, n, thisExpect);
|
||||
return thisExpect;
|
||||
}
|
||||
BigDecimal thisExpect = n;
|
||||
log.info("[lucky25] genPool calExpect uid {}, userLast2000ProductionRation {}, userTotalProductionRatio {} 都大于配置基准 {},修正后期望值 {}",
|
||||
uid, userLast2000ProductionRatio, userProductionRatio, n, thisExpect);
|
||||
return thisExpect.negate();
|
||||
}
|
||||
|
||||
//todo deviceId
|
||||
private Lucky25PoolTypeEnum selectPoolType(Lucky25GiftConfig config, long senderUid) {
|
||||
if (!CollectionUtils.isEmpty(config.getBlackUidList()) && config.getBlackUidList().contains(senderUid)){
|
||||
log.info("[lucky25] genPool selectPoolType uid {}, 黑名单", senderUid);
|
||||
return Lucky25PoolTypeEnum.BLACK_POOL;
|
||||
}
|
||||
if (config.getNewUserPoolCount() <= 0){
|
||||
log.info("[lucky25] genPool selectPoolType uid {}, 配置里的newUserPoolCount小于等于0", senderUid);
|
||||
return Lucky25PoolTypeEnum.NORMAL_POOL;
|
||||
}
|
||||
long userTimes = userMetaService.getTimes(senderUid);
|
||||
Lucky25PoolTypeEnum typeEnum = userTimes < (long) config.getPoolSize() * config.getNewUserPoolCount()?
|
||||
Lucky25PoolTypeEnum.NEW_USER_POOL:
|
||||
Lucky25PoolTypeEnum.NORMAL_POOL;
|
||||
log.info("[lucky25] genPool selectPoolType uid {}, userTimes {}, type {}",
|
||||
senderUid, userTimes, typeEnum);
|
||||
return typeEnum;
|
||||
}
|
||||
|
||||
private RDeque<Lucky25Result> getUserPool(Long uid) {
|
||||
return redissonClient.getDeque(RedisKey.lucky_25_user_pool.getKey(uid.toString()));
|
||||
}
|
||||
|
||||
public void updateUserMulti(Long uid) {
|
||||
RDeque<Lucky25Result> userPool = getUserPool(uid);
|
||||
if (userPool.isEmpty()){
|
||||
log.info("[lucky25] updateUserMulti uid {} 未生成pool", uid);
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY, "用户未生成奖池");
|
||||
}
|
||||
boolean locked = false;
|
||||
RLock lock = redissonClient.getLock(RedisKey.lucky_25_user_lock.getKey(uid.toString()));
|
||||
try {
|
||||
locked = lock.tryLock(5,3, TimeUnit.SECONDS);
|
||||
if (!locked) {
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY);
|
||||
}
|
||||
|
||||
if (userPool.isEmpty()){
|
||||
log.info("[lucky25] updateUserMulti uid {} 未生成pool", uid);
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY, "用户未生成奖池");
|
||||
}
|
||||
|
||||
List<Lucky25Result> list = userPool.poll(11);
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
Lucky25Result result = list.get(i);
|
||||
if (i >= list.size() - 1) {
|
||||
result.setIsSupplement(Boolean.TRUE);
|
||||
result.setOutput(1000);
|
||||
}
|
||||
userPool.addFirst(result);
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (locked) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.mybatismapper.lucky.Lucky25StatMapper;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.sharding.mapper.Lucky25RecordMapper;
|
||||
import com.accompany.sharding.model.Lucky25Record;
|
||||
import com.accompany.sharding.vo.Lucky24PersonalStat;
|
||||
import com.accompany.sharding.vo.Lucky24PlatformStat;
|
||||
import com.accompany.sharding.vo.Lucky25PersonalStat;
|
||||
import com.accompany.sharding.vo.Lucky25PlatformStat;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25RecordService extends ServiceImpl<Lucky25RecordMapper, Lucky25Record> {
|
||||
|
||||
@Autowired
|
||||
private Lucky25StatMapper statMapper;
|
||||
@Resource(name = "bizExecutor")
|
||||
private ThreadPoolExecutor bizExecutor;
|
||||
|
||||
public Lucky25Record buildRecord(long senderUid, int partitionId, Gift gift, int giftNum, Long roomUid, long receiverUid, Integer poolId,
|
||||
boolean isSupplement, long drawMultiple, long afterMultiple, Date sendGiftTime) {
|
||||
Lucky25Record record = new Lucky25Record();
|
||||
record.setUid(senderUid);
|
||||
record.setPartitionId(partitionId);
|
||||
record.setReceiverUid(receiverUid);
|
||||
record.setRoomUid(roomUid);
|
||||
record.setGiftId(gift.getGiftId());
|
||||
record.setGiftNum(giftNum);
|
||||
record.setGiftGoldPrice(gift.getGoldPrice());
|
||||
record.setPoolId(poolId);
|
||||
record.setIsSupplement(isSupplement);
|
||||
record.setDrawMultiple((int) drawMultiple);
|
||||
record.setAfterMultiple((int) afterMultiple);
|
||||
record.setWinGoldNum(afterMultiple * gift.getGoldPrice() * giftNum);
|
||||
record.setCreateTime(sendGiftTime);
|
||||
return record;
|
||||
}
|
||||
|
||||
public void insertRecord(Lucky25Record record) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
try {
|
||||
record.setId(null);
|
||||
this.baseMapper.insert(record);
|
||||
return;
|
||||
} catch (DuplicateKeyException ignore) {
|
||||
log.error("[insertLucky25Record] 插入送礼记录失败", ignore);
|
||||
}
|
||||
}
|
||||
log.error(String.format("[insertLucky25Record] 插入送礼记录3次都失败 %s", JSON.toJSONString(record)));
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY);
|
||||
}
|
||||
|
||||
public void statDate(Integer partitionId, Date startTime, Date endTime, long zoneIdHour) {
|
||||
List<Lucky25PlatformStat> platformList = this.baseMapper.listPlatform(partitionId, startTime, endTime, zoneIdHour);
|
||||
log.info("[Lucky25RecordStat] platform partitionId {} startTime {} endTime {} zoneIdHour {} platformList: {}",
|
||||
partitionId, DateTimeUtil.convertDate(startTime, DateTimeUtil.DEFAULT_DATETIME_PATTERN),
|
||||
DateTimeUtil.convertDate(endTime, DateTimeUtil.DEFAULT_DATETIME_PATTERN), zoneIdHour, JSON.toJSONString(platformList));
|
||||
if (!CollectionUtils.isEmpty(platformList)) {
|
||||
bizExecutor.execute(() -> {
|
||||
for (Lucky25PlatformStat platformStat : platformList) {
|
||||
statMapper.savePlatform(platformStat);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<Lucky25PersonalStat> personalList = this.baseMapper.listPersonal(partitionId, null, startTime, endTime, zoneIdHour);
|
||||
log.info("[Lucky25RecordStat] personal partitionId {} startTime {} endTime {} zoneIdHour {} personalList: {}",
|
||||
partitionId, DateTimeUtil.convertDate(startTime, DateTimeUtil.DEFAULT_DATETIME_PATTERN),
|
||||
DateTimeUtil.convertDate(endTime, DateTimeUtil.DEFAULT_DATETIME_PATTERN), zoneIdHour, JSON.toJSONString(personalList));
|
||||
if (!CollectionUtils.isEmpty(personalList)) {
|
||||
List<List<Lucky25PersonalStat>> list = Lists.partition(personalList, 20);
|
||||
for (List<Lucky25PersonalStat> personalStatList : list) {
|
||||
bizExecutor.execute(() -> {
|
||||
for (Lucky25PersonalStat personalStat : personalStatList) {
|
||||
statMapper.savePersonal(personalStat);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,133 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.service.user.UsersService;
|
||||
import com.accompany.common.config.WebSecurityConfig;
|
||||
import com.accompany.common.constant.AppEnum;
|
||||
import com.accompany.common.push.MarkdownMessage;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.core.enumeration.PartitionEnum;
|
||||
import com.accompany.core.model.PartitionInfo;
|
||||
import com.accompany.core.model.Users;
|
||||
import com.accompany.core.service.message.MessageRobotPushService;
|
||||
import com.accompany.core.service.partition.PartitionInfoService;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class Lucky25RobotMsgService {
|
||||
|
||||
@Autowired
|
||||
private WebSecurityConfig webSecurityConfig;
|
||||
@Autowired
|
||||
private MessageRobotPushService messageRobotPushService;
|
||||
@Autowired
|
||||
private UsersService usersService;
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
@Autowired
|
||||
private PartitionInfoService partitionInfoService;
|
||||
|
||||
@Async
|
||||
public void pushStockNotEnough(int partitionId, BigDecimal afterStock) {
|
||||
RBucket<MarkdownMessage> stockCache = redissonClient.getBucket(RedisKey.lucky_25_robot_push_msg.getKey("stock"));
|
||||
if (stockCache.isExists()){
|
||||
return;
|
||||
}
|
||||
|
||||
PartitionInfo partitionInfo = partitionInfoService.getById(partitionId);
|
||||
|
||||
MarkdownMessage markdownMessage = new MarkdownMessage();
|
||||
markdownMessage.addTitle(AppEnum.getCurApp().getValue() + "幸运24库存告急");
|
||||
markdownMessage.add("当前库存:" + afterStock.toPlainString());
|
||||
if (null != partitionInfo){
|
||||
markdownMessage.add("分区:" + partitionInfo.getDesc());
|
||||
}
|
||||
messageRobotPushService.pushMessageByKey(webSecurityConfig.getLucky24StockWarningPushKey(), markdownMessage, false);
|
||||
|
||||
stockCache.set(markdownMessage, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void pushSuperMulti(long senderUid, long receiverUid, long multi, long input, long output, Long roomUid) {
|
||||
RBucket<MarkdownMessage> multiCache = redissonClient.getBucket(RedisKey.lucky_25_robot_push_msg.getKey("multi", String.valueOf(senderUid)));
|
||||
if (multiCache.isExists()){
|
||||
return;
|
||||
}
|
||||
|
||||
Users sender = usersService.getNotNullUsersByUid(senderUid);
|
||||
PartitionInfo partitionInfo = partitionInfoService.getById(sender.getPartitionId());
|
||||
|
||||
Users receiver = usersService.getNotNullUsersByUid(receiverUid);
|
||||
Users room = null != roomUid? usersService.getNotNullUsersByUid(roomUid) : null;
|
||||
|
||||
MarkdownMessage markdownMessage = new MarkdownMessage();
|
||||
markdownMessage.addTitle(AppEnum.getCurApp().getValue() + "幸运1000通知");
|
||||
markdownMessage.add("送礼用户:" + String.format("%s(%d)%s", sender.getNick(), sender.getErbanNo(), partitionInfo.getDesc()));
|
||||
markdownMessage.add("收礼用户:" + String.format("%s(%d)", receiver.getNick(), receiver.getErbanNo()));
|
||||
markdownMessage.add("出发倍数:" + multi);
|
||||
markdownMessage.add("进入:" + input);
|
||||
markdownMessage.add("返回:" + output);
|
||||
if (null != room) {
|
||||
markdownMessage.add("房间id:" + room.getErbanNo());
|
||||
}
|
||||
if (null != partitionInfo) {
|
||||
markdownMessage.add("分区:" + partitionInfo.getDesc());
|
||||
}
|
||||
messageRobotPushService.pushMessageByKey(webSecurityConfig.getLucky24StockWarningPushKey(), markdownMessage, false);
|
||||
|
||||
multiCache.set(markdownMessage, 30, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void pushFollowUser(long senderUid, long receiverUid, Long roomUid) {
|
||||
RBucket<MarkdownMessage> followCache = redissonClient.getBucket(RedisKey.lucky_25_robot_push_msg.getKey("follow"));
|
||||
if (followCache.isExists()){
|
||||
return;
|
||||
}
|
||||
|
||||
Users sender = usersService.getNotNullUsersByUid(senderUid);
|
||||
PartitionInfo partitionInfo = partitionInfoService.getById(sender.getPartitionId());
|
||||
|
||||
Users receiver = usersService.getNotNullUsersByUid(receiverUid);
|
||||
Users room = null != roomUid? usersService.getNotNullUsersByUid(roomUid) : null;
|
||||
|
||||
MarkdownMessage markdownMessage = new MarkdownMessage();
|
||||
markdownMessage.addTitle(AppEnum.getCurApp().getValue() + "幸运25用户上线通知");
|
||||
markdownMessage.add("送礼用户:" + String.format("%s(%d)%s", sender.getNick(), sender.getErbanNo(), partitionInfo.getDesc()));
|
||||
markdownMessage.add("收礼用户:" + String.format("%s(%d)", receiver.getNick(), receiver.getErbanNo()));
|
||||
markdownMessage.add("上线时间:" + DateTimeUtil.convertDate(new Date(), DateTimeUtil.DEFAULT_DATETIME_PATTERN));
|
||||
if (null != room) {
|
||||
markdownMessage.add("房间id:" + room.getErbanNo());
|
||||
}
|
||||
markdownMessage.add("分区:" + partitionInfo.getDesc());
|
||||
messageRobotPushService.pushMessageByKey(webSecurityConfig.getLucky24StockWarningPushKey(), markdownMessage, false);
|
||||
|
||||
followCache.set(markdownMessage, 30, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void pushTodayProduction(long senderUid,
|
||||
BigDecimal input, BigDecimal output, BigDecimal productionRatio, BigDecimal todayProductionRatioConfig) {
|
||||
|
||||
Users sender = usersService.getNotNullUsersByUid(senderUid);
|
||||
PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(sender.getPartitionId());
|
||||
|
||||
MarkdownMessage markdownMessage = new MarkdownMessage();
|
||||
markdownMessage.addTitle(AppEnum.getCurApp().getValue() + "幸运25拦截1000数组");
|
||||
markdownMessage.add("送礼用户:" + String.format("%s(%d)%s", sender.getNick(), sender.getErbanNo(), partitionEnum.getDesc()));
|
||||
markdownMessage.add("今天总进入:" + input.toPlainString());
|
||||
markdownMessage.add("今天总退出:" + output.toPlainString());
|
||||
markdownMessage.add("今天退出率:" + productionRatio.toPlainString());
|
||||
markdownMessage.add("配置退出率:" + todayProductionRatioConfig.toPlainString());
|
||||
|
||||
messageRobotPushService.pushMessageByKey(webSecurityConfig.getLucky24StockWarningPushKey(), markdownMessage, false);
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.service.gift.SuperLuckyGiftSendService;
|
||||
import com.accompany.business.service.purse.UserPurseService;
|
||||
import com.accompany.core.enumeration.BillObjTypeEnum;
|
||||
import com.accompany.core.model.Room;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25SettlementService {
|
||||
|
||||
@Autowired
|
||||
private UserPurseService userPurseService;
|
||||
@Autowired
|
||||
private SuperLuckyGiftSendService superLuckyGiftSendService;
|
||||
|
||||
public void syncSendReward(Lucky25GiftConfig config, long senderUid, Room room, Gift gift, long winGoldNum, long afterMultiple){
|
||||
sendReward(config, senderUid, room, gift, winGoldNum, afterMultiple);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendReward(Lucky25GiftConfig config, long senderUid, Room room, Gift gift, long winGoldNum, long afterMultiple){
|
||||
// 道具奖励
|
||||
double winGoldNumD = (double) winGoldNum;
|
||||
userPurseService.addDiamond(senderUid, winGoldNumD, BillObjTypeEnum.SUPER_LUCKY_GIFT_DIAMOND);
|
||||
|
||||
//飘屏
|
||||
if (null != room){
|
||||
superLuckyGiftSendService.sendTip(senderUid, room, BigDecimal.valueOf(winGoldNum), BigDecimal.valueOf(afterMultiple),
|
||||
afterMultiple >= config.getSpecialTipMulti() ? 2 : 1);
|
||||
|
||||
if (winGoldNum >= config.getAllRoomChatToastValue()){
|
||||
superLuckyGiftSendService.sendAllRoomScreen(senderUid, room, gift, BigDecimal.valueOf(winGoldNum));
|
||||
}
|
||||
|
||||
//飘屏
|
||||
if (winGoldNum >= config.getSpecialFloatValue()
|
||||
|| afterMultiple >= config.getSpecialFloatMulti()) {
|
||||
superLuckyGiftSendService.sendFloating(room, senderUid, gift, BigDecimal.valueOf(afterMultiple), BigDecimal.valueOf(winGoldNum));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void settlement(Lucky25GiftConfig config, long senderUid, Room room, Gift gift, int everyGiftNum, int totalGiftNum,
|
||||
Map<Long, Long> winGoldNumMap, SuperLuckyGiftIncomeAllot incomeAllot, Date sendGiftTime) {
|
||||
superLuckyGiftSendService.settlement(senderUid, gift, everyGiftNum, totalGiftNum, room, incomeAllot, sendGiftTime);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25StockService implements StockService {
|
||||
|
||||
@Autowired
|
||||
protected RedissonClient redissonClient;
|
||||
|
||||
@Override
|
||||
public RedissonClient getRedissonClient() {
|
||||
return redissonClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisKey getRedisKey() {
|
||||
return RedisKey.lucky_25_stock;
|
||||
}
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.dto.lucky.Lucky25GiftConfig;
|
||||
import com.accompany.business.dto.lucky.Lucky25Result;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.core.enumeration.PartitionEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RList;
|
||||
import org.redisson.api.RMap;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky25UserMetaService {
|
||||
|
||||
private static final String TIMES_KEY = "times";
|
||||
public static final String INPUT_KEY = "input";
|
||||
public static final String OUTPUT_KEY = "output";
|
||||
|
||||
private static final String TODAY = "today";
|
||||
|
||||
private static final int HISTORY_QUEUE_SIZE = 40000;
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
public Long getTimes(Long uid) {
|
||||
return getUserMeta(uid).getOrDefault(TIMES_KEY, 0L).longValue();
|
||||
}
|
||||
|
||||
public BigDecimal getUserLast2000ProductionRatio(Long uid) {
|
||||
RList<Lucky25Result> historyQueue = getUserHistoryQueue(uid);
|
||||
int size = historyQueue.size();
|
||||
//保留20000条记录
|
||||
int num = Math.min(size, 2000);
|
||||
List<Lucky25Result> historyResult = historyQueue.range(num);
|
||||
long historyInput = historyResult.stream().limit(num).mapToLong(Lucky25Result::getInput).sum();
|
||||
long historyOutput = historyResult.stream().limit(num).mapToLong(Lucky25Result::getOutput).sum();
|
||||
BigDecimal productionRatio = num > 0 ? BigDecimal.valueOf(historyOutput).divide(BigDecimal.valueOf(historyInput),2, RoundingMode.HALF_UP): BigDecimal.ZERO;
|
||||
log.info("[lucky25] buildPool last2000 uid {}, historyInput {}, historyOutput {}, num {}, productionRatio {}",
|
||||
uid, historyInput, historyOutput, num, productionRatio);
|
||||
if (size > HISTORY_QUEUE_SIZE){
|
||||
historyQueue.trim(0, HISTORY_QUEUE_SIZE);
|
||||
}
|
||||
return productionRatio;
|
||||
}
|
||||
|
||||
public List<Lucky25Result> getUserLast10wHistory(Long uid) {
|
||||
RList<Lucky25Result> historyQueue = getUserHistoryQueue(uid);
|
||||
int size = historyQueue.size();
|
||||
//保留20000条记录
|
||||
int num = Math.min(size, HISTORY_QUEUE_SIZE);
|
||||
List<Lucky25Result> historyResult = historyQueue.range(num);
|
||||
if (size > HISTORY_QUEUE_SIZE){
|
||||
historyQueue.trim(0, HISTORY_QUEUE_SIZE);
|
||||
}
|
||||
return historyResult;
|
||||
}
|
||||
|
||||
public BigDecimal getUserProductionRatio(Long uid) {
|
||||
RMap<String, Number> userMetaMap = getUserMeta(uid);
|
||||
BigDecimal output = BigDecimal.valueOf(userMetaMap.getOrDefault(OUTPUT_KEY, 0L).longValue());
|
||||
BigDecimal input = BigDecimal.valueOf(userMetaMap.getOrDefault(INPUT_KEY, 0L).longValue());
|
||||
BigDecimal productionRatio = BigDecimal.ZERO.equals(output)? BigDecimal.ZERO: output.divide(input,2,RoundingMode.HALF_UP);
|
||||
log.info("[lucky25] buildPool total uid {}, totalOutput {}, totalInput {}, productionRatio {}",
|
||||
uid, output, input, productionRatio);
|
||||
return productionRatio;
|
||||
}
|
||||
|
||||
public boolean judgePersonalStock(Lucky25GiftConfig config, long senderUid, long receiverUid, long thisInput, long preWinGoldNum) {
|
||||
if (preWinGoldNum < config.getPreJudgeValue_H()){
|
||||
return true;
|
||||
}
|
||||
|
||||
BigDecimal preWinGoldNumB = BigDecimal.valueOf(preWinGoldNum);
|
||||
|
||||
RMap<String, Number> userMetaMap = getUserMeta(senderUid);
|
||||
BigDecimal input = BigDecimal.valueOf(userMetaMap.addAndGet(INPUT_KEY, thisInput).longValue());
|
||||
BigDecimal output = BigDecimal.valueOf(userMetaMap.addAndGet(OUTPUT_KEY, preWinGoldNum).longValue());
|
||||
BigDecimal inputOffset = input.compareTo(config.getTotalInput_J()) >= 0 ? config.getTotalInputOffset_K1() : config.getTotalInputOffset_K2();
|
||||
|
||||
BigDecimal beforeInput = input.subtract(BigDecimal.valueOf(thisInput));
|
||||
BigDecimal beforeOutput = output.subtract(preWinGoldNumB);
|
||||
BigDecimal personalStock = beforeInput.multiply(inputOffset).subtract(beforeOutput);
|
||||
if (personalStock.compareTo(preWinGoldNumB) < 0){
|
||||
userMetaMap.addAndGet(INPUT_KEY, -thisInput);
|
||||
userMetaMap.addAndGet(OUTPUT_KEY, -preWinGoldNum);
|
||||
log.info("[lucky25] personalStock false uid {} input {} J {} inputOffset {} output {} personalStock {} preWinGoldNum {} receiver {}",
|
||||
senderUid, beforeInput, config.getTotalInput_J(), inputOffset, beforeOutput, personalStock, preWinGoldNum, receiverUid);
|
||||
return false;
|
||||
}
|
||||
userMetaMap.addAndGet(INPUT_KEY, -thisInput);
|
||||
userMetaMap.addAndGet(OUTPUT_KEY, -preWinGoldNum);
|
||||
log.info("[lucky25] personalStock true uid {} input {} J {} inputOffset {} output {} personalStock {} preWinGoldNum {} receiver {}",
|
||||
senderUid, beforeInput, config.getTotalInput_J(), inputOffset, beforeOutput, personalStock, preWinGoldNum, receiverUid);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateUserMeta(long senderUid, int partitionId, long input, long output) {
|
||||
RList<Lucky25Result> historyQueue = getUserHistoryQueue(senderUid);
|
||||
historyQueue.add(0, new Lucky25Result(null, input, output, null));
|
||||
historyQueue.expire(Duration.of(14, ChronoUnit.DAYS));
|
||||
|
||||
if (output > 0L){
|
||||
getUser10wStat(senderUid).delete();
|
||||
}
|
||||
|
||||
RMap<String, Number> userMetaMap = getUserMeta(senderUid);
|
||||
long times = userMetaMap.addAndGet(TIMES_KEY, 1L).longValue();
|
||||
long totalInput = userMetaMap.addAndGet(INPUT_KEY, input).longValue();
|
||||
long totalOutput = userMetaMap.addAndGet(OUTPUT_KEY, output).longValue();
|
||||
|
||||
log.info("[lucky25] updateUserMeta uid {} times {} totalInput {} totalOutput {}",
|
||||
senderUid, times, totalInput, totalOutput);
|
||||
|
||||
PartitionEnum partitionEnum = PartitionEnum.getByPartitionId(partitionId);
|
||||
long todayStartTimeLong = DateTimeUtil.getZonedTodayTime(partitionEnum.getZoneId());
|
||||
|
||||
long userMetaToday = userMetaMap.computeIfAbsent(TODAY, k->todayStartTimeLong).longValue();
|
||||
if (userMetaToday < todayStartTimeLong
|
||||
&& userMetaMap.replace(TODAY, userMetaToday, todayStartTimeLong)){
|
||||
// 清理昨天
|
||||
String oldToday = String.valueOf(userMetaToday);
|
||||
String oldTodayInputKey = String.join("_", oldToday, INPUT_KEY);
|
||||
String oldTodayOutputKey = String.join("_", oldToday, OUTPUT_KEY);
|
||||
userMetaMap.fastRemoveAsync(oldTodayInputKey, oldTodayOutputKey);
|
||||
}
|
||||
|
||||
String today = String.valueOf(todayStartTimeLong);
|
||||
String todayInputKey = String.join("_", today, INPUT_KEY);
|
||||
long todayInput = userMetaMap.addAndGet(todayInputKey, input).longValue();
|
||||
String todayOutputKey = String.join("_", today, OUTPUT_KEY);
|
||||
long todayOutput = userMetaMap.addAndGet(todayOutputKey, output).longValue();
|
||||
|
||||
log.info("[lucky25] updateUserMeta uid {} times {} today {} todayInput {} todayOutput {}",
|
||||
senderUid, times, todayStartTimeLong, todayInput, todayOutput);
|
||||
}
|
||||
|
||||
public RMap<String, Number> getUserMeta(Long uid) {
|
||||
return redissonClient.getMap(RedisKey.lucky_25_user_meta.getKey(uid.toString()));
|
||||
}
|
||||
|
||||
public RList<Lucky25Result> getUserHistoryQueue(Long uid) {
|
||||
return redissonClient.getList(RedisKey.lucky_25_user_history.getKey(uid.toString()));
|
||||
}
|
||||
|
||||
public RMap<String, Number> getUser10wStat(Long uid) {
|
||||
return redissonClient.getMapCache(RedisKey.lucky_25_user_10w_stat.getKey(uid.toString()));
|
||||
}
|
||||
|
||||
}
|
@@ -80,9 +80,26 @@ public class RocketMQService {
|
||||
.collect(Collectors.toList());
|
||||
SendResult sendResult = rocketMQTemplate.syncSend(MqConstant.BRAVO_TOPIC, messageList);
|
||||
if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())){
|
||||
log.info("sendLucky24Message success result: {} message: {}", JSON.toJSONString(sendResult), messageList);
|
||||
log.info("sendBravoMessage success result: {} message: {}", JSON.toJSONString(sendResult), messageList);
|
||||
} else {
|
||||
log.error("sendLucky24Message fail result: {} message: {}", JSON.toJSONString(sendResult), messageList);
|
||||
log.error("sendBravoMessage fail result: {} message: {}", JSON.toJSONString(sendResult), messageList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 送礼物消息,发送到MQ
|
||||
*
|
||||
* @param lucky24Messages
|
||||
*/
|
||||
public void sendBatchLucky25Message(Collection<Lucky25Message> lucky24Messages) {
|
||||
List<Message<String>> messageList = lucky24Messages.stream()
|
||||
.map(giftMessage -> MessageBuilder.withPayload(JSON.toJSONString(giftMessage)).build())
|
||||
.collect(Collectors.toList());
|
||||
SendResult sendResult = rocketMQTemplate.syncSend(MqConstant.LUCKY_25_TOPIC, messageList);
|
||||
if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())){
|
||||
log.info("sendLucky25Message success result: {} message: {}", JSON.toJSONString(sendResult), messageList);
|
||||
} else {
|
||||
log.error("sendLucky25Message fail result: {} message: {}", JSON.toJSONString(sendResult), messageList);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,5 @@
|
||||
<?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.lucky.Lucky25PoolMapper">
|
||||
|
||||
</mapper>
|
@@ -0,0 +1,37 @@
|
||||
<?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.lucky.Lucky25StatMapper">
|
||||
<insert id="savePlatform">
|
||||
insert into lucky_25_record_platform_stat
|
||||
VALUES (#{item.date}, #{item.partitionId}, #{item.totalInput}, #{item.totalOutput}, #{item.productionRatio},
|
||||
#{item.num}, #{item.count}, #{item.winNum}, #{item.winCount}, #{item.winRate})
|
||||
ON DUPLICATE KEY UPDATE total_input = #{item.totalInput}, total_output = #{item.totalOutput}, production_ratio = #{item.productionRatio},
|
||||
num = #{item.num}, `count` = #{item.count}, win_num = #{item.winNum}, win_count = #{item.winCount}, win_rate = #{item.winRate};
|
||||
</insert>
|
||||
|
||||
<insert id="savePersonal">
|
||||
insert into lucky_25_record_personal_stat
|
||||
VALUES (#{item.date}, #{item.partitionId}, #{item.uid}, #{item.totalInput}, #{item.totalOutput},
|
||||
#{item.production}, #{item.productionRatio}, #{item.avgInput},
|
||||
#{item.num}, #{item.winNum}, #{item.winRate})
|
||||
ON DUPLICATE KEY UPDATE total_input = #{item.totalInput}, total_output = #{item.totalOutput},
|
||||
production = #{item.production}, production_ratio = #{item.productionRatio}, avg_input = #{item.avgInput},
|
||||
num = #{item.num}, win_num = #{item.winNum}, win_rate = #{item.winRate};
|
||||
</insert>
|
||||
|
||||
<select id="listPlatformStat" resultType="com.accompany.sharding.vo.Lucky25PlatformStat">
|
||||
select * from lucky_25_record_platform_stat s
|
||||
where s.date between #{startDate} and #{endDate}
|
||||
and s.partition_id = #{partitionId}
|
||||
</select>
|
||||
|
||||
<select id="listPersonalStat" resultType="com.accompany.sharding.vo.Lucky25PersonalStat">
|
||||
select * from lucky_25_record_personal_stat s
|
||||
where s.date between #{startDate} and #{endDate}
|
||||
and s.partition_id = #{partitionId}
|
||||
<if test="null != uid">
|
||||
and s.uid = #{uid}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
@@ -44,6 +44,7 @@ public interface MqConstant {
|
||||
String GAME_MSG_PUSH_TOPIC = "game_msg_push_topic";
|
||||
String GAME_MSG_PUSH_CONSUME_GROUP = "game_msg_push_consume_group";
|
||||
|
||||
|
||||
String LUCKY_25_TOPIC = "lucky_25_topic";
|
||||
String LUCKY_25_CONSUME_GROUP = "lucky_25_consume_group";
|
||||
|
||||
}
|
||||
|
@@ -1,9 +1,7 @@
|
||||
package com.accompany.mq.consumer;
|
||||
|
||||
import com.accompany.business.message.BravoMessage;
|
||||
import com.accompany.business.message.Lucky24Message;
|
||||
import com.accompany.business.service.gift.BravoMessageService;
|
||||
import com.accompany.business.service.gift.Lucky24MessageService;
|
||||
import com.accompany.mq.constant.MqConstant;
|
||||
import com.accompany.mq.listener.AbstractMessageListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@@ -5,7 +5,6 @@ import com.accompany.business.service.gift.Lucky24MessageService;
|
||||
import com.accompany.mq.constant.MqConstant;
|
||||
import com.accompany.mq.listener.AbstractMessageListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.spring.annotation.ConsumeMode;
|
||||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
@@ -0,0 +1,28 @@
|
||||
package com.accompany.mq.consumer;
|
||||
|
||||
import com.accompany.business.message.Lucky25Message;
|
||||
import com.accompany.business.service.gift.Lucky25MessageService;
|
||||
import com.accompany.mq.constant.MqConstant;
|
||||
import com.accompany.mq.listener.AbstractMessageListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "spring.application.name", havingValue = "web")
|
||||
@RocketMQMessageListener(topic = MqConstant.LUCKY_25_TOPIC, consumerGroup = MqConstant.LUCKY_25_CONSUME_GROUP)
|
||||
public class Lucky25MessageConsumer extends AbstractMessageListener<Lucky25Message> {
|
||||
|
||||
@Autowired
|
||||
private Lucky25MessageService messageService;
|
||||
|
||||
@Override
|
||||
public void onMessage(Lucky25Message giftMessage) {
|
||||
log.info("onMessage lucky25Message: {}", giftMessage.toString());
|
||||
messageService.handleGiftMessage(giftMessage);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
package com.accompany.scheduler.task.luckyBag;
|
||||
|
||||
import com.accompany.business.message.Lucky25Message;
|
||||
import com.accompany.business.service.gift.Lucky25MessageService;
|
||||
import com.accompany.business.service.lucky.Lucky25RecordService;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.core.model.PartitionInfo;
|
||||
import com.accompany.core.service.common.JedisService;
|
||||
import com.accompany.core.service.partition.PartitionInfoService;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class Lucky25Task {
|
||||
|
||||
@Autowired
|
||||
private PartitionInfoService partitionInfoService;
|
||||
@Autowired
|
||||
private Lucky25RecordService service;
|
||||
@Resource(name = "bizExecutor")
|
||||
private ThreadPoolExecutor bizExecutor;
|
||||
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
@Autowired
|
||||
private Lucky25MessageService messageService;
|
||||
|
||||
/**
|
||||
* 重新消费队列的消息
|
||||
*/
|
||||
@Scheduled(cron = "0 */5 * * * ?")
|
||||
public void retryLucky25Queue() {
|
||||
log.info("retryLucky25Queue start ...");
|
||||
Map<String, String> map = jedisService.hgetAll(RedisKey.lucky_25_status.getKey());
|
||||
if (map == null || map.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
long curTime = System.currentTimeMillis();
|
||||
long gapTime = 1000 * 60 * 10; // 十分钟内没被消费
|
||||
|
||||
map.entrySet().parallelStream().forEach(entry -> {
|
||||
try {
|
||||
String messId = entry.getKey();
|
||||
String val = entry.getValue();
|
||||
Lucky25Message giftMessage = JSON.parseObject(val, Lucky25Message.class);
|
||||
if (curTime - giftMessage.getCreateTime() > gapTime) {
|
||||
messageService.handleGiftMessage(giftMessage);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("retryLucky25Queue error", e);
|
||||
}
|
||||
});
|
||||
log.info("retryLucky25Queue end ...");
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 2 * * * ? ")
|
||||
public void lucky25RecordStat() {
|
||||
Date now = new Date();
|
||||
List<PartitionInfo> partitionInfoList = partitionInfoService.listAll();
|
||||
for (PartitionInfo partitionInfo : partitionInfoList) {
|
||||
ZonedDateTime zdt = DateTimeUtil.convertWithZoneId(now, partitionInfo.getZoneId());
|
||||
ZonedDateTime hourAgo = zdt.minusHours(1L);
|
||||
log.info("[lucky24RecordStat] zdt {} hourAgo {}, zdtDay {} hourAgoDay {}",
|
||||
zdt, hourAgo, zdt.getDayOfYear(), hourAgo.getDayOfWeek());
|
||||
if (zdt.getDayOfYear() == hourAgo.getDayOfYear()){
|
||||
continue;
|
||||
}
|
||||
bizExecutor.execute(() -> {
|
||||
// 获取当天的第一秒
|
||||
ZonedDateTime startOfDay = hourAgo.withHour(0)
|
||||
.withMinute(0)
|
||||
.withSecond(0)
|
||||
.withNano(0);
|
||||
Date systemStartTime = DateTimeUtil.converLocalDateTimeToDate(startOfDay.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
|
||||
Date startTime = DateTimeUtil.converLocalDateTimeToDate(startOfDay.toLocalDateTime());
|
||||
|
||||
long zoneIdHour = Duration.between(systemStartTime.toInstant(), startTime.toInstant()).toHours();
|
||||
|
||||
// 获取当天的最后一秒
|
||||
ZonedDateTime endOfDay = hourAgo.withHour(23)
|
||||
.withMinute(59)
|
||||
.withSecond(59)
|
||||
.withNano(999999999);
|
||||
Date systemEndTime = DateTimeUtil.converLocalDateTimeToDate(endOfDay.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
|
||||
|
||||
service.statDate(partitionInfo.getId(), systemStartTime, systemEndTime, zoneIdHour);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Date abc = DateTimeUtil.convertStrToDate("2024-12-18 05:00:00", DateTimeUtil.DEFAULT_DATETIME_PATTERN);
|
||||
ZonedDateTime now = abc.toInstant().atZone(ZoneId.of("Asia/Riyadh"));
|
||||
long zoneIdHour = Duration.between(abc.toInstant(), DateTimeUtil.converLocalDateTimeToDate(now.toLocalDateTime()).toInstant()).toHours();
|
||||
System.out.println(now + "_" + DateTimeUtil.convertDate(abc, DateTimeUtil.DEFAULT_DATETIME_PATTERN) + "_" + zoneIdHour);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user