bravo-送礼
This commit is contained in:
@@ -585,7 +585,10 @@ public class Constant {
|
||||
* 大R定制礼物
|
||||
*/
|
||||
public static final byte CUSTOM_GIFT = GiftTypeEnum.CUSTOM.getType();
|
||||
|
||||
/**
|
||||
* Bravo礼物
|
||||
*/
|
||||
public static final byte BRAVO_GIFT = GiftTypeEnum.BRAVO.getType();
|
||||
}
|
||||
|
||||
public static class PayloadSkiptype {
|
||||
@@ -1367,7 +1370,6 @@ public class Constant {
|
||||
public static final String ROOM_RIGHT_BOTTOM_ICON_CONFIG = "room_right_bottom_icon_config";
|
||||
|
||||
public static final String GIFT_EARN_ALLOT_GUILD_CONFIG = "gift_earn_allot_guild_config";
|
||||
public static final String APP_CAPTCHA_SWITCH = "app_captcha_switch";
|
||||
/**
|
||||
* 每月充值活动屏蔽财富等级
|
||||
*/
|
||||
@@ -1420,6 +1422,10 @@ public class Constant {
|
||||
public static final String MINI_GAME_WEEK_JACKPOT_CONFIG = "mini_game_week_jackpot_config";
|
||||
|
||||
public static final String SHARE_LINK = "share_link";
|
||||
|
||||
public static final String APP_CAPTCHA_SWITCH = "app_captcha_switch";
|
||||
|
||||
public static final String BRAVO_GIFT_CONFIG = "bravo_gift_config";
|
||||
}
|
||||
|
||||
public static class WithDrawStatus {
|
||||
|
@@ -25,7 +25,8 @@ public enum GiftTypeEnum {
|
||||
COUNTRY((byte) 17, "国家"),
|
||||
LUCKY_24((byte) 18, "幸运礼物"),
|
||||
CP((byte) 19, "cp"),
|
||||
CUSTOM((byte) 20, "大R定制");
|
||||
CUSTOM((byte) 20, "大R定制"),
|
||||
BRAVO((byte) 21, "Bravo");
|
||||
|
||||
private byte type;
|
||||
private String desc;
|
||||
|
@@ -1437,6 +1437,16 @@ public enum RedisKey {
|
||||
|
||||
email_code,
|
||||
email_send_interval,
|
||||
|
||||
//幸运24
|
||||
bravo_stock,
|
||||
bravo_user_meta,
|
||||
bravo_user_pool,
|
||||
bravo_user_history,
|
||||
bravo_user_lock,
|
||||
bravo_robot_push_msg,
|
||||
bravo_status, // 礼物消息的状态
|
||||
lock_bravo_message, // 消费送礼物消息锁
|
||||
;
|
||||
|
||||
public String getKey() {
|
||||
|
@@ -0,0 +1,31 @@
|
||||
package com.accompany.sharding.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@TableName(value = "bravo_record")
|
||||
@Data
|
||||
public class BravoRecord {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
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 BigDecimal drawMultiple;
|
||||
private BigDecimal afterMultiple;
|
||||
private BigDecimal winGoldNum;
|
||||
private Date createTime;
|
||||
private String messId;
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package com.accompany.sharding.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@ApiModel
|
||||
public class BravoPersonalStat {
|
||||
|
||||
@ApiModelProperty("日期")
|
||||
private String date;
|
||||
@ApiModelProperty("分区id")
|
||||
private Integer partitionId;
|
||||
@ApiModelProperty("uid")
|
||||
private Long uid;
|
||||
@ApiModelProperty("erbanNO")
|
||||
private Long erbanNo;
|
||||
|
||||
@ApiModelProperty("送礼金币总额")
|
||||
private Long totalInput;
|
||||
@ApiModelProperty("送礼返币总额")
|
||||
private Long totalOutput;
|
||||
@ApiModelProperty("相差")
|
||||
private Long production;
|
||||
@ApiModelProperty("投产比")
|
||||
private BigDecimal productionRatio;
|
||||
@ApiModelProperty("平均投入金额")
|
||||
private BigDecimal avgInput;
|
||||
@ApiModelProperty("参与次数")
|
||||
private Long num;
|
||||
@ApiModelProperty("中奖人数")
|
||||
private Long winNum;
|
||||
@ApiModelProperty("中奖率")
|
||||
private BigDecimal winRate;
|
||||
|
||||
public BravoPersonalStat(String date, Integer partitionId, Long uid, Long erbanNo) {
|
||||
this.date = date;
|
||||
this.partitionId = partitionId;
|
||||
this.uid = uid;
|
||||
this.erbanNo = erbanNo;
|
||||
this.totalInput = 0L;
|
||||
this.totalOutput = 0L;
|
||||
this.production = 0L;
|
||||
this.productionRatio = BigDecimal.ZERO;
|
||||
this.avgInput = BigDecimal.ZERO;
|
||||
this.num = 0L;
|
||||
this.winNum = 0L;
|
||||
this.winRate = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.accompany.sharding.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@ApiModel
|
||||
public class BravoPlatformStat {
|
||||
|
||||
@ApiModelProperty("日期")
|
||||
private String date;
|
||||
@ApiModelProperty("分区id")
|
||||
private Integer partitionId;
|
||||
@ApiModelProperty("送礼金币总额")
|
||||
private Long totalInput;
|
||||
@ApiModelProperty("送礼返币总额")
|
||||
private Long totalOutput;
|
||||
@ApiModelProperty("投产比")
|
||||
private BigDecimal productionRatio;
|
||||
@ApiModelProperty("参与次数")
|
||||
private Long num;
|
||||
@ApiModelProperty("参与人数")
|
||||
private Long count;
|
||||
@ApiModelProperty("中奖次数")
|
||||
private Long winNum;
|
||||
@ApiModelProperty("中奖人数")
|
||||
private Long winCount;
|
||||
@ApiModelProperty("中奖率")
|
||||
private BigDecimal winRate;
|
||||
|
||||
public BravoPlatformStat(String date, Integer partitionId) {
|
||||
this.date = date;
|
||||
this.partitionId = partitionId;
|
||||
this.totalInput = 0L;
|
||||
this.totalOutput = 0L;
|
||||
this.productionRatio = BigDecimal.ZERO;
|
||||
this.num = 0L;
|
||||
this.count = 0L;
|
||||
this.winNum = 0L;
|
||||
this.winCount = 0L;
|
||||
this.winRate = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
@@ -145,6 +145,9 @@ public class ShardingSphereConfig {
|
||||
tableConfigs.add(getShardingTableRuleConfiguration("lucky_24_record",
|
||||
"ds.lucky_24_record_$->{20240914..20250531}",
|
||||
"create_time", "lucky24RecordStrategy"));
|
||||
tableConfigs.add(getShardingTableRuleConfiguration("bravo_record",
|
||||
"ds.bravo_record_$->{20250320..20250531}",
|
||||
"create_time", "bravoRecordStrategy"));
|
||||
|
||||
|
||||
Map<String, AlgorithmConfiguration> algorithmsConfigs = new HashMap<>();
|
||||
@@ -178,6 +181,9 @@ public class ShardingSphereConfig {
|
||||
//
|
||||
AlgorithmConfiguration lucky24RecordShardingAlgorithm = getLucky24RecordShardingAlgorithmConfiguration();
|
||||
algorithmsConfigs.put("lucky24RecordStrategy", lucky24RecordShardingAlgorithm);
|
||||
//
|
||||
AlgorithmConfiguration bravoRecordShardingAlgorithm = getBravoRecordShardingAlgorithmConfiguration();
|
||||
algorithmsConfigs.put("bravoRecordStrategy", bravoRecordShardingAlgorithm);
|
||||
|
||||
ShardingRuleConfiguration config = new ShardingRuleConfiguration();
|
||||
config.setTables(tableConfigs);
|
||||
@@ -186,6 +192,10 @@ public class ShardingSphereConfig {
|
||||
return config;
|
||||
}
|
||||
|
||||
private AlgorithmConfiguration getBravoRecordShardingAlgorithmConfiguration() {
|
||||
return getGiftSendRecordShardingAlgorithmConfiguration();
|
||||
}
|
||||
|
||||
private AlgorithmConfiguration getBillRecordShardingAlgorithmConfiguration() {
|
||||
return getGiftSendRecordShardingAlgorithmConfiguration();
|
||||
}
|
||||
|
@@ -0,0 +1,22 @@
|
||||
package com.accompany.sharding.mapper;
|
||||
|
||||
import com.accompany.sharding.model.BravoRecord;
|
||||
import com.accompany.sharding.vo.BravoPersonalStat;
|
||||
import com.accompany.sharding.vo.BravoPlatformStat;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface BravoRecordMapper extends BaseMapper<BravoRecord> {
|
||||
|
||||
List<BravoPlatformStat> listPlatform(@Param("partitionId") Integer partitionId, @Param("startTime") Date startTime, @Param("endTime") Date endTime,
|
||||
@Param("zoneIdHour") long zoneIdHour);
|
||||
|
||||
List<BravoPersonalStat> listPersonal(@Param("partitionId") Integer partitionId,
|
||||
@Param("uid") Long uid,
|
||||
@Param("startTime") Date startTime, @Param("endTime") Date endTime,
|
||||
@Param("zoneIdHour") long zoneIdHour);
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
|
||||
<?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.BravoRecordMapper">
|
||||
|
||||
<select id="listPlatform" resultType="com.accompany.sharding.vo.BravoPlatformStat">
|
||||
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 bravo_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.BravoPersonalStat">
|
||||
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 bravo_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,35 @@
|
||||
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 BravoPoolTypeEnum {
|
||||
|
||||
NEW_USER_POOL(1, "新人奖池"),
|
||||
NORMAL_POOL(2, "普通奖池"),
|
||||
BLACK_POOL(3, "黑名单奖池"),
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private int type;
|
||||
private String name;
|
||||
|
||||
public static BravoPoolTypeEnum get(int type) {
|
||||
Optional<BravoPoolTypeEnum> result = Arrays.stream(BravoPoolTypeEnum.values()).filter(prizePoolTypeEnum ->
|
||||
prizePoolTypeEnum.type == type).findFirst();
|
||||
if (result.isPresent()) {
|
||||
return result.get();
|
||||
}
|
||||
throw new ServiceException(BusiStatus.PARAMETERILLEGAL);
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.accompany.business.dto.lucky;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class BravoGiftConfig {
|
||||
|
||||
//平台抽成比
|
||||
private BigDecimal platformRatio;
|
||||
//收礼者收益比
|
||||
private BigDecimal receiverRatio;
|
||||
//对比值n
|
||||
private BigDecimal productionRatio_N;
|
||||
|
||||
private Map<Integer, BravoGiftConfig> ratioPartitionMap;
|
||||
|
||||
private Integer poolSize = 500;
|
||||
private Integer newUserPoolCount = 0;
|
||||
|
||||
private BigDecimal specialTipMulti;
|
||||
|
||||
private BigDecimal allRoomChatToastValue;
|
||||
|
||||
private BigDecimal specialFloatMulti;
|
||||
private BigDecimal specialFloatValue;
|
||||
|
||||
private Long warnMulti;
|
||||
private List<Long> followUidList;
|
||||
|
||||
private Map<Long, BigDecimal> whiteUidProductionRatioMap;
|
||||
private List<Long> blackUidList;
|
||||
|
||||
private String diamondIcon;
|
||||
|
||||
public BravoGiftConfig getRatioByPartitionId(Integer partitionId){
|
||||
return ratioPartitionMap.getOrDefault(partitionId, this);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.accompany.business.dto.lucky;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class BravoResult {
|
||||
private Integer poolId;
|
||||
private BigDecimal input;
|
||||
private BigDecimal output;
|
||||
private Boolean isSupplement;
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package com.accompany.business.message;
|
||||
|
||||
import com.accompany.mq.model.BaseMqMessage;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 礼物消息
|
||||
*/
|
||||
@Data
|
||||
public class BravoMessage extends BaseMqMessage {
|
||||
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 BigDecimal drawMultiple;
|
||||
private BigDecimal afterMultiple;
|
||||
private BigDecimal winGoldNum;
|
||||
private Long createTime;
|
||||
private String messId;
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package com.accompany.business.model.lucky;
|
||||
|
||||
import com.accompany.business.mybatis.typehandler.Lucky24PoolItemListTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel
|
||||
@TableName(value = "bravo_pool", autoResultMap = true)
|
||||
@Data
|
||||
public class BravoPool {
|
||||
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Integer id;
|
||||
@ApiModelProperty("类型,1=新手,2=普通")
|
||||
private Integer type;
|
||||
@ApiModelProperty("期望值")
|
||||
private BigDecimal expect;
|
||||
@ApiModelProperty("奖率")
|
||||
private BigDecimal winRate;
|
||||
@ApiModelProperty("更新时间")
|
||||
private Date updateTime;
|
||||
@ApiModelProperty("池子列表")
|
||||
@TableField(typeHandler = Lucky24PoolItemListTypeHandler.class)
|
||||
private List<BravoPoolItem> itemList;
|
||||
|
||||
@ApiModel
|
||||
@Data
|
||||
public static class BravoPoolItem {
|
||||
@ApiModelProperty("倍率")
|
||||
private Integer multi;
|
||||
@ApiModelProperty("数量")
|
||||
private Integer num;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.accompany.business.mybatis.typehandler;
|
||||
|
||||
import com.accompany.business.model.lucky.BravoPool;
|
||||
import com.accompany.business.model.lucky.Lucky24Pool;
|
||||
import com.accompany.core.mybatis.typehandler.ListTypeHandler;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BravoPoolItemListTypeHandler extends ListTypeHandler<BravoPool.BravoPoolItem> {
|
||||
// 将ListTypeHandler<T>(T为任意对象),具体为特定的对象String
|
||||
@Override
|
||||
protected TypeReference<List<BravoPool.BravoPoolItem>> specificType() {
|
||||
return new TypeReference<List<BravoPool.BravoPoolItem>>() {};
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.accompany.business.mybatismapper.lucky;
|
||||
|
||||
import com.accompany.business.model.lucky.BravoPool;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
public interface BravoPoolMapper extends BaseMapper<BravoPool> {
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.accompany.business.mybatismapper.lucky;
|
||||
|
||||
import com.accompany.sharding.vo.BravoPersonalStat;
|
||||
import com.accompany.sharding.vo.BravoPlatformStat;
|
||||
import com.accompany.sharding.vo.Lucky24PersonalStat;
|
||||
import com.accompany.sharding.vo.Lucky24PlatformStat;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface BravoStatMapper {
|
||||
|
||||
int savePlatform(@Param("item") BravoPlatformStat item);
|
||||
|
||||
int savePersonal(@Param("item") BravoPersonalStat item);
|
||||
|
||||
List<BravoPlatformStat> listPlatformStat(@Param("partitionId") Integer partitionId, @Param("startDate") String startDate, @Param("endDate") String endDate);
|
||||
|
||||
List<BravoPersonalStat> listPersonalStat(@Param("partitionId") Integer partitionId,
|
||||
@Param("uid") Long uid,
|
||||
@Param("startDate") String startDate, @Param("endDate") String endDate);
|
||||
|
||||
}
|
@@ -0,0 +1,169 @@
|
||||
package com.accompany.business.service.gift;
|
||||
|
||||
import com.accompany.business.dto.lucky.BravoGiftConfig;
|
||||
import com.accompany.business.dto.lucky.BravoResult;
|
||||
import com.accompany.business.dto.lucky.Lucky24Result;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.message.BravoMessage;
|
||||
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.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.BravoRecord;
|
||||
import com.accompany.sharding.model.BravoRecord;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class BravoGiftSendService {
|
||||
|
||||
@Autowired
|
||||
private SysConfService sysConfService;
|
||||
@Autowired
|
||||
private BravoStockService stockService;
|
||||
@Autowired
|
||||
private BravoUserMetaService userMetaService;
|
||||
@Autowired
|
||||
private BravoPoolService poolService;
|
||||
@Autowired
|
||||
private BravoRecordService recordService;
|
||||
@Autowired
|
||||
private BravoIncomeAllotService incomeAllotService;
|
||||
@Autowired
|
||||
private BravoSettlementService settlementService;
|
||||
@Autowired
|
||||
private Lucky24RobotMsgService 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) {
|
||||
|
||||
BigDecimal everyoneGoldNum = BigDecimal.valueOf(everyGiftNum).multiply(BigDecimal.valueOf(gift.getGoldPrice()));
|
||||
|
||||
BravoGiftConfig config = getConfig();
|
||||
BravoGiftConfig partitionConfig = config.getRatioByPartitionId(partitionId);
|
||||
SuperLuckyGiftIncomeAllot incomeAllot = incomeAllotService.calculate(partitionConfig, gift, everyGiftNum, receiverList);
|
||||
|
||||
// 增加库存
|
||||
BigDecimal afterStock = stockService.addStock(partitionId, incomeAllot.getRemainValue());
|
||||
log.info("[lucky24] uid {}, partitionId {}, addStockGoldNum {}, afterStock {}",
|
||||
senderUid, partitionId, incomeAllot.getRemainValue(), afterStock);
|
||||
|
||||
Map<Long, BravoRecord> recordMap = draw(config, senderUid, partitionId, gift, everyGiftNum, everyoneGoldNum, receiverList, room, sendGiftTime);
|
||||
log.info("[lucky24] uid {}, totalWinGoldNum {}", senderUid, JSON.toJSONString(recordMap));
|
||||
|
||||
sendMq(recordMap);
|
||||
}
|
||||
|
||||
public Map<Long, BravoRecord> draw(BravoGiftConfig config, Long senderUid, int partitionId,
|
||||
Gift gift, int everyGiftNum, BigDecimal everyoneGoldNum,
|
||||
List<Long> receiverList, Room room, Date sendGiftTime) {
|
||||
return receiverList.parallelStream()
|
||||
.collect(Collectors.toMap(receiverUid-> receiverUid,
|
||||
receiverUid-> drawMultiple(config, senderUid, partitionId, gift, everyGiftNum, receiverUid, everyoneGoldNum, room, sendGiftTime)));
|
||||
}
|
||||
|
||||
private void sendMq(Map<Long, BravoRecord> recordMap) {
|
||||
Map<String, String> caches = new HashMap<>(recordMap.size());
|
||||
List<BravoMessage> messageList = new ArrayList<>();
|
||||
|
||||
DefaultIdentifierGenerator idGenerator = DefaultIdentifierGenerator.getInstance();
|
||||
|
||||
for (BravoRecord record: recordMap.values()){
|
||||
String id = idGenerator.nextUUID(null);
|
||||
|
||||
BravoMessage message = new BravoMessage();
|
||||
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.bravo_status.getKey(), caches);
|
||||
|
||||
rocketMQService.sendBatchBravoMessage(messageList);
|
||||
}
|
||||
|
||||
public BravoRecord drawMultiple(BravoGiftConfig config, long senderUid, int partitionId,
|
||||
Gift gift, int giftNum, long receiverUid, BigDecimal everyoneGoldNum, Room room, Date sendGiftTime) {
|
||||
BravoResult drawResult = poolService.drawMultipleFromPool(config, senderUid, partitionId);
|
||||
return updateMeta(config, senderUid, partitionId, gift, giftNum, receiverUid, everyoneGoldNum, room, sendGiftTime, drawResult);
|
||||
}
|
||||
|
||||
private BravoRecord updateMeta(BravoGiftConfig config, long senderUid, int partitionId,
|
||||
Gift gift, int giftNum, long receiverUid, BigDecimal everyoneGoldNum, Room room, Date sendGiftTime,
|
||||
BravoResult drawResult){
|
||||
BigDecimal drawMultiple = drawResult.getOutput();
|
||||
BigDecimal afterMultiple = drawMultiple;
|
||||
// 平台库存
|
||||
if (afterMultiple.compareTo(BigDecimal.ZERO) > 0L){
|
||||
BigDecimal preWinGoldNum = afterMultiple.multiply(everyoneGoldNum);
|
||||
if (!judgeStock(partitionId, preWinGoldNum, senderUid, receiverUid)){
|
||||
afterMultiple = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
BigDecimal winGoldNum = afterMultiple.multiply(everyoneGoldNum);
|
||||
userMetaService.updateUserMeta(senderUid, everyoneGoldNum, winGoldNum);
|
||||
|
||||
if (winGoldNum.compareTo(BigDecimal.ZERO) > 0){
|
||||
settlementService.syncSendReward(config, senderUid, room, gift, winGoldNum, afterMultiple);
|
||||
}
|
||||
|
||||
return recordService.buildRecord(senderUid, partitionId, gift, giftNum, null != room? room.getUid(): null,
|
||||
receiverUid, drawResult.getPoolId(), false,
|
||||
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("[lucky24] drawMultiple sender {} receiver {} 产出大于库存 winGoldNum {} afterStock {}",
|
||||
senderUid, receiverUid, winGoldNum, afterStock);
|
||||
afterStock = stockService.addStock(partitionId, winGoldNum);
|
||||
robotMsgService.pushStockNotEnough(partitionId, afterStock);
|
||||
}
|
||||
return enough;
|
||||
}
|
||||
|
||||
public BravoGiftConfig getConfig() {
|
||||
String configStr = sysConfService.getSysConfValueById(Constant.SysConfId.BRAVO_GIFT_CONFIG);
|
||||
if (!StringUtils.hasText(configStr)) {
|
||||
throw new ServiceException(BusiStatus.ALREADY_NOTEXISTS_CONFIG);
|
||||
}
|
||||
return JSON.parseObject(configStr, BravoGiftConfig.class);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
package com.accompany.business.service.gift;
|
||||
|
||||
import com.accompany.business.dto.lucky.BravoGiftConfig;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.message.BravoMessage;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.service.lucky.*;
|
||||
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.BravoRecord;
|
||||
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 BravoMessageService extends BaseService {
|
||||
|
||||
@Autowired
|
||||
private BravoRecordService recordService;
|
||||
@Autowired
|
||||
private BravoIncomeAllotService incomeAllotService;
|
||||
@Autowired
|
||||
private SuperLuckyGiftSendService superLuckyGiftSendService;
|
||||
@Autowired
|
||||
private Lucky24RobotMsgService robotMsgService;
|
||||
@Autowired
|
||||
private BravoGiftSendService sendService;
|
||||
@Autowired
|
||||
private RoomService roomService;
|
||||
@Autowired
|
||||
private GiftService giftService;
|
||||
|
||||
public void handleGiftMessage(BravoMessage giftMessage) {
|
||||
// 防止消息被重复消费
|
||||
if (!jedisLockService.isExist(RedisKey.lock_bravo_message.getKey(giftMessage.getMessId()), 30)) {
|
||||
logger.warn("handleBravoMessage giftMessage had handle, mess: " + giftMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jedisService.hexists(RedisKey.bravo_status.getKey(), giftMessage.getMessId())){
|
||||
logger.warn("handleBravoMessage giftMessage had handle, mess: " + giftMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("【处理Bravo 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());
|
||||
|
||||
BravoRecord record = insertRecord(giftMessage);
|
||||
|
||||
log.info("【处理Bravo mq】 record 插入成功 messId:{} recordId:{} record:{}",
|
||||
giftMessage.getMessId(), record.getId(), JSON.toJSONString(record));
|
||||
|
||||
// 收礼者收益
|
||||
BravoGiftConfig config = sendService.getConfig();
|
||||
BravoGiftConfig 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("【处理Bravo 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());
|
||||
}
|
||||
|
||||
// 删除该标识,表示消息已经消费过
|
||||
jedisService.hdel(RedisKey.bravo_status.getKey(), giftMessage.getMessId());
|
||||
}
|
||||
|
||||
private BravoRecord insertRecord(BravoMessage giftMessage) {
|
||||
BravoRecord record = new BravoRecord();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@@ -103,6 +103,8 @@ public class GiftSendService extends BaseService {
|
||||
private GiftSendConsumeGoldService giftSendConsumeGoldService;
|
||||
@Autowired
|
||||
private Lucky24GiftSendService lucky24GiftSendService;
|
||||
@Autowired
|
||||
private BravoGiftSendService bravoGiftSendService;
|
||||
|
||||
private List<LuckyBagGiftPrizePoolFilter> filters;
|
||||
|
||||
@@ -483,7 +485,6 @@ public class GiftSendService extends BaseService {
|
||||
giftNum, everyGoldNum, totalGiftNum, totalGoldNum,
|
||||
giftSource, sendType, sendGiftTime);
|
||||
|
||||
|
||||
// 福袋礼物
|
||||
if (gift.getGiftType() == Constant.GiftType.LUCKY_BAG) {
|
||||
return sendLuckyBagGift(sendUid, Arrays.asList(recvUids), room, gift, giftNum, giftSource, sendType, usersMap, sendGiftTime, u.getPartitionId(), after);
|
||||
@@ -493,6 +494,8 @@ public class GiftSendService extends BaseService {
|
||||
superLuckyGiftSendService.draw(sendUid, u.getPartitionId(), room, Arrays.asList(recvUids), gift, giftNum, sendGiftTime);
|
||||
} else if (gift.getGiftType() == Constant.GiftType.LUCKY_24) {
|
||||
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);
|
||||
}
|
||||
|
||||
Double everyGiftValue = gift.getGiftType() == Constant.GiftType.SUPER_LUCKY ?
|
||||
|
@@ -124,7 +124,8 @@ public class GiftTabService implements InitializingBean {
|
||||
return (userContext, giftVoList) -> giftVoList.stream()
|
||||
.filter(giftVo -> Constant.GiftType.LUCKY_BAG == giftVo.getGiftType()
|
||||
|| Constant.GiftType.LUCKY_BAG_LINEAR == giftVo.getGiftType()
|
||||
|| Constant.GiftType.SUPER_LUCKY == giftVo.getGiftType())
|
||||
|| Constant.GiftType.SUPER_LUCKY == giftVo.getGiftType()
|
||||
|| Constant.GiftType.BRAVO_GIFT == giftVo.getGiftType())
|
||||
.map(giftVo -> {
|
||||
if (Constant.GiftType.SUPER_LUCKY != giftVo.getGiftType()) {
|
||||
return giftVo;
|
||||
|
@@ -0,0 +1,51 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.dto.lucky.BravoGiftConfig;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.core.enumeration.BillObjTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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 BravoIncomeAllotService implements LuckyGiftIncomeAllotService {
|
||||
|
||||
public SuperLuckyGiftIncomeAllot calculate(BravoGiftConfig 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) {
|
||||
//todo
|
||||
}
|
||||
}
|
@@ -0,0 +1,265 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.constant.BravoPoolTypeEnum;
|
||||
import com.accompany.business.constant.Lucky24PoolTypeEnum;
|
||||
import com.accompany.business.dto.lucky.BravoGiftConfig;
|
||||
import com.accompany.business.dto.lucky.Lucky24GiftConfig;
|
||||
import com.accompany.business.dto.lucky.BravoResult;
|
||||
import com.accompany.business.model.lucky.BravoPool;
|
||||
import com.accompany.business.model.lucky.Lucky24Pool;
|
||||
import com.accompany.business.mybatismapper.lucky.BravoPoolMapper;
|
||||
import com.accompany.business.mybatismapper.lucky.Lucky24PoolMapper;
|
||||
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 com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
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 BravoPoolService {
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
@Autowired
|
||||
private Lucky24UserMetaService userMetaService;
|
||||
@Autowired
|
||||
private BravoPoolMapper bravoPoolMapper;
|
||||
|
||||
public BravoResult drawMultipleFromPool(BravoGiftConfig config, Long uid, int partitionId){
|
||||
RQueue<BravoResult> userPool = getUserPool(uid);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
BravoResult 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(BravoGiftConfig config, Long uid, int partitionId, RQueue<BravoResult> userPool) {
|
||||
boolean locked = false;
|
||||
RLock lock = redissonClient.getLock(RedisKey.bravo_user_lock.getKey(uid.toString()));
|
||||
try {
|
||||
locked = lock.tryLock(5,3, TimeUnit.SECONDS);
|
||||
|
||||
if (!userPool.isEmpty()){
|
||||
log.info("[bravo] genPool uid {} 已生成pool,无需再生成", uid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!locked) {
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY);
|
||||
}
|
||||
|
||||
BravoPoolTypeEnum poolType = selectPoolType(config, uid);
|
||||
|
||||
List<BravoPool> poolList = bravoPoolMapper.selectList(Wrappers.<BravoPool>lambdaQuery()
|
||||
.eq(BravoPool::getType, poolType.getType()));
|
||||
if (CollectionUtils.isEmpty(poolList)){
|
||||
throw new ServiceException(BusiStatus.SEIZE_TREASURE_POOL_CONFIG_ERROR);
|
||||
}
|
||||
|
||||
BravoGiftConfig partitionConfig = config.getRatioByPartitionId(partitionId);
|
||||
BravoPool pool = selectPool(config, partitionConfig, uid, poolType, poolList);
|
||||
|
||||
buildPool(config, uid, userPool, pool);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (locked) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPool(BravoGiftConfig config, Long uid, RQueue<BravoResult> userPool, BravoPool pool) {
|
||||
List<BravoPool.BravoPoolItem> 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("[bravo] genPool buildPool uid {}, winIndexList {}", uid, JSON.toJSONString(winIndexList));
|
||||
}
|
||||
|
||||
List<BravoResult> poolList = Arrays.stream(poolArray).boxed().map(output->{
|
||||
BravoResult result = new BravoResult();
|
||||
result.setPoolId(pool.getId());
|
||||
result.setOutput(BigDecimal.valueOf(output));
|
||||
return result;
|
||||
}).collect(Collectors.toList());
|
||||
userPool.addAll(poolList);
|
||||
}
|
||||
|
||||
private List<Integer> buildWinList(List<BravoPool.BravoPoolItem> poolItemList) {
|
||||
List<Integer> resultList = new ArrayList<>();
|
||||
|
||||
for (BravoPool.BravoPoolItem 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 BravoPool selectPool(BravoGiftConfig config, BravoGiftConfig partitionConfig,
|
||||
Long uid,
|
||||
BravoPoolTypeEnum poolType, List<BravoPool> poolList) {
|
||||
if (BravoPoolTypeEnum.NEW_USER_POOL.equals(poolType)){
|
||||
int randomIndex = RandomUtil.randomByRange(0, poolList.size());
|
||||
BravoPool randomPool = poolList.get(randomIndex);
|
||||
log.info("[bravo] genPool selectPool type {}, pooList {}, randomIndex {}, expect {}",
|
||||
poolType, JSON.toJSONString(poolList.stream().map(BravoPool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
|
||||
return randomPool;
|
||||
}
|
||||
|
||||
// 黑名单
|
||||
if (!CollectionUtils.isEmpty(config.getBlackUidList())
|
||||
&& config.getBlackUidList().contains(uid)){
|
||||
List<BravoPool> balckPoolList = poolList.stream()
|
||||
.sorted(Comparator.comparing(BravoPool::getExpect))
|
||||
.limit(1L)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int randomIndex = RandomUtil.randomByRange(0, balckPoolList.size());
|
||||
BravoPool randomPool = balckPoolList.get(randomIndex);
|
||||
log.info("[bravo] genPool selectPool type {}, blackPooList {}, randomIndex {}, expect {}",
|
||||
poolType, JSON.toJSONString(balckPoolList.stream().map(BravoPool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
|
||||
|
||||
return randomPool;
|
||||
}
|
||||
|
||||
BigDecimal thisExpect = calExpect(config, partitionConfig, uid);
|
||||
List<BravoPool> expectPoolList = BigDecimal.ZERO.compareTo(thisExpect) < 0 ?
|
||||
poolList.stream().filter(pool->pool.getExpect().compareTo(thisExpect) > 0).collect(Collectors.toList()):
|
||||
poolList.stream().filter(pool->thisExpect.negate().compareTo(pool.getExpect()) >= 0).collect(Collectors.toList());
|
||||
|
||||
int randomIndex = RandomUtil.randomByRange(0, expectPoolList.size());
|
||||
BravoPool randomPool = expectPoolList.get(randomIndex);
|
||||
log.info("[bravo] genPool selectPool type {}, pooList {}, randomIndex {}, expect {}",
|
||||
poolType, JSON.toJSONString(expectPoolList.stream().map(BravoPool::getExpect).collect(Collectors.toList())), randomIndex, randomPool.getExpect());
|
||||
return randomPool;
|
||||
}
|
||||
|
||||
private BigDecimal calExpect(BravoGiftConfig config, BravoGiftConfig partitionConfig, Long uid) {
|
||||
BigDecimal n = !CollectionUtils.isEmpty(config.getWhiteUidProductionRatioMap()) && config.getWhiteUidProductionRatioMap().containsKey(uid) ?
|
||||
config.getWhiteUidProductionRatioMap().get(uid): partitionConfig.getProductionRatio_N();
|
||||
|
||||
BigDecimal userLast2000ProductionRatio = userMetaService.getUserLast2000ProductionRatio(uid);
|
||||
if (userLast2000ProductionRatio.compareTo(n) <= 0){
|
||||
BigDecimal thisExpect = n.add(n).subtract(userLast2000ProductionRatio);
|
||||
log.info("[bravo] 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("[bravo] genPool calExpect uid {}, userTotalProductionRatio {} 小于等于配置基准 {},修正后期望值 {}",
|
||||
uid, userProductionRatio, n, thisExpect);
|
||||
return thisExpect;
|
||||
}
|
||||
log.info("[bravo] genPool calExpect uid {}, userLast2000ProductionRation {}, userTotalProductionRatio {} 都大于配置基准 {},修正后期望值 {}",
|
||||
uid, userLast2000ProductionRatio, userProductionRatio, n, n);
|
||||
return n.negate();
|
||||
}
|
||||
|
||||
private BravoPoolTypeEnum selectPoolType(BravoGiftConfig config, long senderUid) {
|
||||
if (!CollectionUtils.isEmpty(config.getBlackUidList()) && config.getBlackUidList().contains(senderUid)){
|
||||
log.info("[bravo] genPool selectPoolType uid {}, 黑名单", senderUid);
|
||||
return BravoPoolTypeEnum.BLACK_POOL;
|
||||
}
|
||||
if (config.getNewUserPoolCount() <= 0){
|
||||
log.info("[bravo] genPool selectPoolType uid {}, 配置里的newUserPoolCount小于等于0", senderUid);
|
||||
return BravoPoolTypeEnum.NORMAL_POOL;
|
||||
}
|
||||
long userTimes = userMetaService.getTimes(senderUid);
|
||||
BravoPoolTypeEnum typeEnum = userTimes < (long) config.getPoolSize() * config.getNewUserPoolCount()?
|
||||
BravoPoolTypeEnum.NEW_USER_POOL:
|
||||
BravoPoolTypeEnum.NORMAL_POOL;
|
||||
log.info("[bravo] genPool selectPoolType uid {}, userTimes {}, type {}",
|
||||
senderUid, userTimes, typeEnum);
|
||||
return typeEnum;
|
||||
}
|
||||
|
||||
private RDeque<BravoResult> getUserPool(Long uid) {
|
||||
return redissonClient.getDeque(RedisKey.bravo_user_pool.getKey(uid.toString()));
|
||||
}
|
||||
|
||||
public void updateUserMulti(Long uid) {
|
||||
RDeque<BravoResult> userPool = getUserPool(uid);
|
||||
if (userPool.isEmpty()){
|
||||
log.info("[bravo] updateUserMulti uid {} 未生成pool", uid);
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY, "用户未生成奖池");
|
||||
}
|
||||
boolean locked = false;
|
||||
RLock lock = redissonClient.getLock(RedisKey.bravo_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("[bravo] updateUserMulti uid {} 未生成pool", uid);
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY, "用户未生成奖池");
|
||||
}
|
||||
|
||||
List<BravoResult> list = userPool.poll(11);
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
BravoResult result = list.get(i);
|
||||
if (i >= list.size() - 1) {
|
||||
result.setIsSupplement(Boolean.TRUE);
|
||||
result.setOutput(BigDecimal.valueOf(1000));
|
||||
}
|
||||
userPool.addFirst(result);
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (locked) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,126 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.mybatismapper.lucky.BravoStatMapper;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.sharding.mapper.BravoRecordMapper;
|
||||
import com.accompany.sharding.model.BravoRecord;
|
||||
import com.accompany.sharding.vo.BravoPersonalStat;
|
||||
import com.accompany.sharding.vo.BravoPlatformStat;
|
||||
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.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class BravoRecordService extends ServiceImpl<BravoRecordMapper, BravoRecord> {
|
||||
|
||||
@Autowired
|
||||
private BravoStatMapper statMapper;
|
||||
@Resource(name = "bizExecutor")
|
||||
private ThreadPoolExecutor bizExecutor;
|
||||
|
||||
public BravoRecord buildRecord(long senderUid, int partitionId, Gift gift, int giftNum, Long roomUid, long receiverUid, Integer poolId,
|
||||
boolean isSupplement, BigDecimal drawMultiple, BigDecimal afterMultiple, Date sendGiftTime) {
|
||||
BravoRecord record = new BravoRecord();
|
||||
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(drawMultiple);
|
||||
record.setAfterMultiple(afterMultiple);
|
||||
|
||||
BigDecimal input = BigDecimal.valueOf(gift.getGoldPrice()).multiply(BigDecimal.valueOf(giftNum));
|
||||
BigDecimal output = afterMultiple.multiply(input);
|
||||
|
||||
record.setWinGoldNum(output);
|
||||
record.setCreateTime(sendGiftTime);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Async
|
||||
public void saveRecord(long senderUid, int partitionId, Gift gift, int giftNum, long receiverUid, int poolId,
|
||||
boolean isSupplement, BigDecimal drawMultiple, BigDecimal afterMultiple, Date sendGiftTime) {
|
||||
BravoRecord record = new BravoRecord();
|
||||
record.setUid(senderUid);
|
||||
record.setPartitionId(partitionId);
|
||||
record.setReceiverUid(receiverUid);
|
||||
record.setGiftId(gift.getGiftId());
|
||||
record.setGiftNum(giftNum);
|
||||
record.setGiftGoldPrice(gift.getGoldPrice());
|
||||
record.setPoolId(poolId);
|
||||
record.setIsSupplement(isSupplement);
|
||||
record.setDrawMultiple(drawMultiple);
|
||||
record.setAfterMultiple(afterMultiple);
|
||||
|
||||
BigDecimal input = BigDecimal.valueOf(gift.getGoldPrice()).multiply(BigDecimal.valueOf(giftNum));
|
||||
BigDecimal output = afterMultiple.multiply(input);
|
||||
|
||||
record.setWinGoldNum(output);
|
||||
record.setCreateTime(sendGiftTime);
|
||||
|
||||
insertRecord(record);
|
||||
}
|
||||
|
||||
public void insertRecord(BravoRecord record) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
try {
|
||||
record.setId(null);
|
||||
this.baseMapper.insert(record);
|
||||
return;
|
||||
} catch (DuplicateKeyException ignore) {
|
||||
log.error("[insertBravoRecord] 插入送礼记录失败", ignore);
|
||||
}
|
||||
}
|
||||
log.error(String.format("[insertBravoRecord] 插入送礼记录3次都失败 %s", JSON.toJSONString(record)));
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY);
|
||||
}
|
||||
|
||||
public void statDate(Integer partitionId, Date startTime, Date endTime, long zoneIdHour) {
|
||||
List<BravoPlatformStat> platformList = this.baseMapper.listPlatform(partitionId, startTime, endTime, zoneIdHour);
|
||||
log.info("[BravoRecordStat] 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 (BravoPlatformStat platformStat : platformList) {
|
||||
statMapper.savePlatform(platformStat);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<BravoPersonalStat> personalList = this.baseMapper.listPersonal(partitionId, null, startTime, endTime, zoneIdHour);
|
||||
log.info("[BravoRecordStat] 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<BravoPersonalStat>> list = Lists.partition(personalList, 20);
|
||||
for (List<BravoPersonalStat> personalStatList : list) {
|
||||
bizExecutor.execute(() -> {
|
||||
for (BravoPersonalStat personalStat : personalStatList) {
|
||||
statMapper.savePersonal(personalStat);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.dto.lucky.BravoGiftConfig;
|
||||
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 BravoSettlementService {
|
||||
|
||||
@Autowired
|
||||
private UserPurseService userPurseService;
|
||||
@Autowired
|
||||
private SuperLuckyGiftSendService superLuckyGiftSendService;
|
||||
|
||||
public void syncSendReward(BravoGiftConfig config, long senderUid, Room room, Gift gift, BigDecimal winGoldNum, BigDecimal afterMultiple){
|
||||
sendReward(config, senderUid, room, gift, winGoldNum, afterMultiple);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendReward(BravoGiftConfig config, long senderUid, Room room, Gift gift, BigDecimal winGoldNum, BigDecimal afterMultiple){
|
||||
// 道具奖励
|
||||
userPurseService.addDiamond(senderUid, winGoldNum.doubleValue(), BillObjTypeEnum.SUPER_LUCKY_GIFT_DIAMOND);
|
||||
|
||||
//飘屏
|
||||
if (null != room){
|
||||
superLuckyGiftSendService.sendTip(senderUid, room, winGoldNum, afterMultiple,
|
||||
afterMultiple.compareTo(config.getSpecialTipMulti()) >= 0 ? 2 : 1);
|
||||
|
||||
if (winGoldNum.compareTo(config.getAllRoomChatToastValue()) > 0){
|
||||
superLuckyGiftSendService.sendAllRoomScreen(senderUid, room, gift, winGoldNum);
|
||||
}
|
||||
|
||||
//飘屏
|
||||
if (winGoldNum.compareTo(config.getSpecialFloatValue()) >= 0
|
||||
|| afterMultiple.compareTo(config.getSpecialFloatMulti()) >= 0) {
|
||||
superLuckyGiftSendService.sendFloating(room, senderUid, gift, afterMultiple, winGoldNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void settlement(BravoGiftConfig config, long senderUid, Room room, Gift gift, int everyGiftNum, int totalGiftNum,
|
||||
Map<Long, Long> winGoldNumMap, SuperLuckyGiftIncomeAllot incomeAllot, Date sendGiftTime) {
|
||||
//适配SuperLuckyGift的结算modal
|
||||
/*Map<Long, Map<LuckyGiftReward, Integer>> userDrawResult = winGoldNumMap.entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey,
|
||||
entry-> {
|
||||
long winGoldNum = entry.getValue();
|
||||
LuckyGiftReward reward = new LuckyGiftReward();
|
||||
if (winGoldNum > 0) {
|
||||
reward.setRewardType(LuckyGiftRewardTypeEnum.DIAMOND.ordinal());
|
||||
reward.setRewardPrice(winGoldNum);
|
||||
reward.setRewardValue(winGoldNum);
|
||||
reward.setRewardIcon(config.getDiamondIcon());
|
||||
} else {
|
||||
reward.setRewardType(LuckyGiftRewardTypeEnum.NONE.ordinal());
|
||||
}
|
||||
return Map.of(reward, 1);
|
||||
}));*/
|
||||
|
||||
/*if (null != room) {
|
||||
//superLuckyGiftSendService.sendRoomScreen(senderUid, room, gift, everyGiftNum, userDrawResult);
|
||||
|
||||
long outputValue = winGoldNumMap.values().stream().mapToLong(Long::longValue).sum();
|
||||
if (outputValue >= config.getAllRoomChatToastValue()) {
|
||||
superLuckyGiftSendService.sendAllRoomScreen(senderUid, room, gift, BigDecimal.valueOf(outputValue));
|
||||
}
|
||||
}*/
|
||||
|
||||
superLuckyGiftSendService.settlement(senderUid, gift, everyGiftNum, totalGiftNum, room, incomeAllot, sendGiftTime);
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
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 BravoStockService implements StockService {
|
||||
|
||||
@Autowired
|
||||
protected RedissonClient redissonClient;
|
||||
|
||||
@Override
|
||||
public RedissonClient getRedissonClient() {
|
||||
return redissonClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisKey getRedisKey() {
|
||||
return RedisKey.bravo_stock;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.dto.lucky.BravoResult;
|
||||
import com.accompany.business.dto.lucky.Lucky24Result;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
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 BravoUserMetaService {
|
||||
|
||||
private static final String TIMES_KEY = "times";
|
||||
public static final String INPUT_KEY = "input";
|
||||
public static final String OUTPUT_KEY = "output";
|
||||
|
||||
private static final int HISTORY_QUEUE_SIZE = 2000;
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
public Long getTimes(Long uid) {
|
||||
return getUserMeta(uid).getOrDefault(TIMES_KEY, 0L).longValue();
|
||||
}
|
||||
|
||||
public BigDecimal getUserLast2000ProductionRatio(Long uid) {
|
||||
RList<BravoResult> historyQueue = getUserHistoryQueue(uid);
|
||||
int size = historyQueue.size();
|
||||
//保留20000条记录
|
||||
int num = Math.min(size, HISTORY_QUEUE_SIZE);
|
||||
List<BravoResult> historyResult = historyQueue.range(num);
|
||||
BigDecimal historyInput = historyResult.stream().limit(num).map(BravoResult::getInput).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal historyOutput = historyResult.stream().limit(num).map(BravoResult::getOutput).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal productionRatio = num > 0 ? historyOutput.divide(historyInput,2, RoundingMode.HALF_UP): BigDecimal.ZERO;
|
||||
log.info("[bravo] 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 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("[bravo] buildPool total uid {}, totalOutput {}, totalInput {}, productionRatio {}",
|
||||
uid, output, input, productionRatio);
|
||||
return productionRatio;
|
||||
}
|
||||
|
||||
public void updateUserMeta(long senderUid, BigDecimal input, BigDecimal output) {
|
||||
RList<BravoResult> historyQueue = getUserHistoryQueue(senderUid);
|
||||
historyQueue.add(0, new BravoResult(null, input, output, null));
|
||||
historyQueue.expireAsync(Duration.of(14, ChronoUnit.DAYS));
|
||||
|
||||
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("[bravo] updateUserMeta uid {} times {} totalInput {} totalOutput {}",
|
||||
senderUid, times, totalInput, totalOutput);
|
||||
}
|
||||
|
||||
public RMap<String, Number> getUserMeta(Long uid) {
|
||||
return redissonClient.getMap(RedisKey.bravo_user_meta.getKey(uid.toString()));
|
||||
}
|
||||
|
||||
public RList<BravoResult> getUserHistoryQueue(Long uid) {
|
||||
return redissonClient.getList(RedisKey.bravo_user_history.getKey(uid.toString()));
|
||||
}
|
||||
|
||||
}
|
@@ -3,17 +3,19 @@ package com.accompany.business.service.lucky;
|
||||
import com.accompany.business.dto.lucky.Lucky24GiftConfig;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.core.enumeration.BillObjTypeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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 Lucky24IncomeAllotService extends SuperLuckyIncomeAllotService {
|
||||
public class Lucky24IncomeAllotService implements LuckyGiftIncomeAllotService {
|
||||
|
||||
public SuperLuckyGiftIncomeAllot calculate(Lucky24GiftConfig config, Gift gift, int giftNum, List<Long> receiverUids) {
|
||||
BigDecimal giftNumB = BigDecimal.valueOf(giftNum);
|
||||
@@ -41,4 +43,9 @@ public class Lucky24IncomeAllotService extends SuperLuckyIncomeAllotService {
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -11,24 +11,18 @@ import java.math.BigDecimal;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class Lucky24StockService {
|
||||
public class Lucky24StockService implements StockService {
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
protected RedissonClient redissonClient;
|
||||
|
||||
public void setStock(Integer partitionId, BigDecimal value) {
|
||||
getPartitionStock(partitionId).set(value.doubleValue());
|
||||
@Override
|
||||
public RedissonClient getRedissonClient() {
|
||||
return redissonClient;
|
||||
}
|
||||
|
||||
public BigDecimal addStock(Integer partitionId, BigDecimal value) {
|
||||
return BigDecimal.valueOf(getPartitionStock(partitionId).addAndGet(value.doubleValue()));
|
||||
}
|
||||
|
||||
public BigDecimal subStock(Integer partitionId, BigDecimal value) {
|
||||
return BigDecimal.valueOf(getPartitionStock(partitionId).addAndGet(value.negate().doubleValue()));
|
||||
}
|
||||
|
||||
private RAtomicDouble getPartitionStock(Integer partitionId) {
|
||||
return redissonClient.getAtomicDouble(RedisKey.lucky_24_stock.getKey(partitionId.toString()));
|
||||
@Override
|
||||
public RedisKey getRedisKey() {
|
||||
return RedisKey.lucky_24_stock;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.event.ActivityOfDayConsumeEvent;
|
||||
import com.accompany.business.event.SuperLuckyGiftDiamondIncomeMessageEvent;
|
||||
import com.accompany.business.message.ActivityOfDayConsumeMessage;
|
||||
import com.accompany.business.message.SuperLuckyGiftDiamondIncomeMessage;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.core.base.SpringContextHolder;
|
||||
import com.accompany.core.enumeration.BillObjTypeEnum;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
public interface LuckyGiftIncomeAllotService {
|
||||
|
||||
default void send(long senderUid, Map<Long, BigDecimal> incomeMap, Long roomUid, BillObjTypeEnum billObjTypeEnum,
|
||||
Gift gift, int everyGiftNum, long everyTotalGoldNum, Date sendGiftTime) {
|
||||
for (Map.Entry<Long, BigDecimal> entry : incomeMap.entrySet()){
|
||||
send(senderUid, entry.getKey(), roomUid, entry.getValue(), billObjTypeEnum,
|
||||
gift.getGiftId(), everyGiftNum, everyTotalGoldNum, sendGiftTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
default void send(long senderUid, Long receiverUid, Long roomUid, BigDecimal income, BillObjTypeEnum billObjTypeEnum,
|
||||
int giftId, int everyGiftNum, long giftTotalGoldNum, Date sendGiftTime) {
|
||||
addIncome(receiverUid, income.doubleValue(), billObjTypeEnum, senderUid, roomUid, giftId, everyGiftNum, giftTotalGoldNum, sendGiftTime);
|
||||
|
||||
SuperLuckyGiftDiamondIncomeMessage diamondIncomeMessage = new SuperLuckyGiftDiamondIncomeMessage(senderUid, receiverUid, roomUid, giftId, everyGiftNum, giftTotalGoldNum, income.doubleValue(), sendGiftTime);
|
||||
SpringContextHolder.getApplicationContext().publishEvent(new SuperLuckyGiftDiamondIncomeMessageEvent(diamondIncomeMessage));
|
||||
SpringContextHolder.getApplicationContext().publishEvent(new ActivityOfDayConsumeEvent(ActivityOfDayConsumeMessage.builder()
|
||||
.sendUid(diamondIncomeMessage.getSenderUid())
|
||||
.messTime(new Date(diamondIncomeMessage.getCreateTime().getTime()))
|
||||
.totalDiamondNum(income.doubleValue()).consumeType("lucky gift").build()));
|
||||
}
|
||||
|
||||
/*default 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));
|
||||
}*/
|
||||
|
||||
void addIncome(Long receiverUid, double income, BillObjTypeEnum billObjTypeEnum,
|
||||
Long senderUid, Long roomUid, Integer giftId, Integer everyGiftNum, Long giftTotalGoldNum, Date sendGiftTime);
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import org.redisson.api.RAtomicDouble;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public interface StockService {
|
||||
|
||||
RedissonClient getRedissonClient();
|
||||
|
||||
RedisKey getRedisKey();
|
||||
|
||||
default void setStock(Integer partitionId, BigDecimal value) {
|
||||
getPartitionStock(partitionId).set(value.doubleValue());
|
||||
}
|
||||
|
||||
default BigDecimal addStock(Integer partitionId, BigDecimal value) {
|
||||
return BigDecimal.valueOf(getPartitionStock(partitionId).addAndGet(value.doubleValue()));
|
||||
}
|
||||
|
||||
default BigDecimal subStock(Integer partitionId, BigDecimal value) {
|
||||
return BigDecimal.valueOf(getPartitionStock(partitionId).addAndGet(value.negate().doubleValue()));
|
||||
}
|
||||
|
||||
default RAtomicDouble getPartitionStock(Integer partitionId) {
|
||||
return getRedissonClient().getAtomicDouble(getRedisKey().getKey(partitionId.toString()));
|
||||
}
|
||||
|
||||
}
|
@@ -2,10 +2,6 @@ package com.accompany.business.service.lucky;
|
||||
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftConfig;
|
||||
import com.accompany.business.dto.lucky.SuperLuckyGiftIncomeAllot;
|
||||
import com.accompany.business.event.ActivityOfDayConsumeEvent;
|
||||
import com.accompany.business.event.SuperLuckyGiftDiamondIncomeMessageEvent;
|
||||
import com.accompany.business.message.ActivityOfDayConsumeMessage;
|
||||
import com.accompany.business.message.SuperLuckyGiftDiamondIncomeMessage;
|
||||
import com.accompany.business.model.Gift;
|
||||
import com.accompany.business.service.purse.UserPurseService;
|
||||
import com.accompany.business.service.record.BillRecordService;
|
||||
@@ -13,7 +9,6 @@ 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.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -24,14 +19,12 @@ import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SuperLuckyIncomeAllotService {
|
||||
public class SuperLuckyIncomeAllotService implements LuckyGiftIncomeAllotService {
|
||||
|
||||
@Autowired
|
||||
private UserPurseService userPurseService;
|
||||
@Autowired
|
||||
private BillRecordService billRecordService;
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public SuperLuckyGiftIncomeAllot calculate(SuperLuckyGiftConfig config, Gift gift, int giftNum, List<Long> receiverUids, Room room) {
|
||||
BigDecimal giftNumB = BigDecimal.valueOf(giftNum);
|
||||
@@ -59,27 +52,11 @@ public class SuperLuckyIncomeAllotService {
|
||||
return new SuperLuckyGiftIncomeAllot(totalValue, receiverTotalIncome, receiverIncomeMap, roomOwnerTotalIncome, remainValue);
|
||||
}
|
||||
|
||||
public void send(long senderUid, Map<Long, BigDecimal> incomeMap, Long roomUid, BillObjTypeEnum billObjTypeEnum,
|
||||
Gift gift, int everyGiftNum, long everyTotalGoldNum, Date sendGiftTime) {
|
||||
for (Map.Entry<Long, BigDecimal> entry : incomeMap.entrySet()){
|
||||
send(senderUid, entry.getKey(), roomUid, entry.getValue(), billObjTypeEnum,
|
||||
gift.getGiftId(), everyGiftNum, everyTotalGoldNum, sendGiftTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void send(long senderUid, Long receiverUid, Long roomUid, BigDecimal income, BillObjTypeEnum billObjTypeEnum,
|
||||
int giftId, int everyGiftNum, long giftTotalGoldNum, Date sendGiftTime) {
|
||||
@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.doubleValue(), billObjTypeEnum,
|
||||
(userPurse)-> billRecordService.insertGiftSendBillRecord(receiverUid, senderUid, roomUid, null, billObjTypeEnum, income.doubleValue(),
|
||||
userPurseService.addGold(receiverUid, income, billObjTypeEnum,
|
||||
(userPurse)-> billRecordService.insertGiftSendBillRecord(receiverUid, senderUid, roomUid, null, billObjTypeEnum, income,
|
||||
giftId, everyGiftNum, giftTotalGoldNum, sendGiftTime, userPurse));
|
||||
|
||||
SuperLuckyGiftDiamondIncomeMessage diamondIncomeMessage = new SuperLuckyGiftDiamondIncomeMessage(senderUid, receiverUid, roomUid, giftId, everyGiftNum, giftTotalGoldNum, income.doubleValue(), sendGiftTime);
|
||||
applicationContext.publishEvent(new SuperLuckyGiftDiamondIncomeMessageEvent(diamondIncomeMessage));
|
||||
applicationContext.publishEvent(new ActivityOfDayConsumeEvent(ActivityOfDayConsumeMessage.builder()
|
||||
.sendUid(diamondIncomeMessage.getSenderUid())
|
||||
.messTime(new Date(diamondIncomeMessage.getCreateTime().getTime()))
|
||||
.totalDiamondNum(income.doubleValue()).consumeType("lucky gift").build()));
|
||||
}
|
||||
}
|
||||
|
@@ -73,6 +73,18 @@ public class RocketMQService {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendBatchBravoMessage(List<BravoMessage> bravoMessages) {
|
||||
List<Message<String>> messageList = bravoMessages.stream()
|
||||
.map(giftMessage -> MessageBuilder.withPayload(JSON.toJSONString(giftMessage)).build())
|
||||
.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);
|
||||
} else {
|
||||
log.error("sendLucky24Message fail result: {} message: {}", JSON.toJSONString(sendResult), messageList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送开箱子中奖消息
|
||||
*/
|
||||
@@ -120,4 +132,5 @@ public class RocketMQService {
|
||||
log.error("sendYidunIMTextAntiMsg fail message: {}", JSON.toJSONString(msg), throwable);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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.BravoPoolMapper">
|
||||
|
||||
</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.BravoStatMapper">
|
||||
<insert id="savePlatform">
|
||||
insert into bravo_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 bravo_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.Lucky24PlatformStat">
|
||||
select * from bravo_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.Lucky24PersonalStat">
|
||||
select * from bravo_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>
|
@@ -38,4 +38,7 @@ public interface MqConstant {
|
||||
String BILL_RECORD_TOPIC = "bill_record_topic";
|
||||
String BILL_RECORD_CONSUME_GROUP = "bill_record_consume_group";
|
||||
|
||||
String BRAVO_TOPIC = "bravo_topic";
|
||||
String BRAVO_CONSUME_GROUP = "bravo_consume_group";
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
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.BRAVO_TOPIC, consumerGroup = MqConstant.BRAVO_CONSUME_GROUP)
|
||||
public class BravoMessageConsumer extends AbstractMessageListener<BravoMessage> {
|
||||
|
||||
@Autowired
|
||||
private BravoMessageService bravoMessageService;
|
||||
|
||||
@Override
|
||||
public void onMessage(BravoMessage giftMessage) {
|
||||
log.info("onMessage bravoMessage: {}", giftMessage.toString());
|
||||
bravoMessageService.handleGiftMessage(giftMessage);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user