新增-财富升级发送VIP
This commit is contained in:
parent
14b63e3983
commit
6fdf9c5f09
@ -0,0 +1,77 @@
|
||||
package com.red.circle.other.app.enums.user;
|
||||
|
||||
import com.red.circle.other.inner.enums.material.NobleVipEnum;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 财富等级升级 VIP 奖励配置.
|
||||
* 升到对应等级时发放奖励,等级 40+ 暂无奖励。
|
||||
*/
|
||||
public enum WealthLevelRewardEnum {
|
||||
|
||||
LEVEL_2(2, List.of(new VipReward(NobleVipEnum.VISCOUNT, 3))),
|
||||
LEVEL_3(3, List.of(new VipReward(NobleVipEnum.VISCOUNT, 5))),
|
||||
LEVEL_4(4, List.of(new VipReward(NobleVipEnum.VISCOUNT, 7))),
|
||||
LEVEL_5(5, List.of(new VipReward(NobleVipEnum.VISCOUNT, 15))),
|
||||
LEVEL_6(6, List.of(new VipReward(NobleVipEnum.EARL, 2))),
|
||||
LEVEL_7(7, List.of(new VipReward(NobleVipEnum.EARL, 2))),
|
||||
LEVEL_8(8, List.of(new VipReward(NobleVipEnum.EARL, 3))),
|
||||
LEVEL_9(9, List.of(new VipReward(NobleVipEnum.EARL, 3))),
|
||||
LEVEL_10(10, List.of(new VipReward(NobleVipEnum.EARL, 3), new VipReward(NobleVipEnum.VISCOUNT, 999))),
|
||||
LEVEL_11(11, List.of(new VipReward(NobleVipEnum.EARL, 5))),
|
||||
LEVEL_12(12, List.of(new VipReward(NobleVipEnum.EARL, 7))),
|
||||
LEVEL_13(13, List.of(new VipReward(NobleVipEnum.EARL, 14))),
|
||||
LEVEL_14(14, List.of(new VipReward(NobleVipEnum.EARL, 20))),
|
||||
LEVEL_15(15, List.of(new VipReward(NobleVipEnum.MARQUIS, 5))),
|
||||
LEVEL_16(16, List.of(new VipReward(NobleVipEnum.MARQUIS, 5))),
|
||||
LEVEL_17(17, List.of(new VipReward(NobleVipEnum.MARQUIS, 7))),
|
||||
LEVEL_18(18, List.of(new VipReward(NobleVipEnum.MARQUIS, 7))),
|
||||
LEVEL_19(19, List.of(new VipReward(NobleVipEnum.MARQUIS, 14))),
|
||||
LEVEL_20(20, List.of(new VipReward(NobleVipEnum.MARQUIS, 14), new VipReward(NobleVipEnum.EARL, 999))),
|
||||
LEVEL_21(21, List.of(new VipReward(NobleVipEnum.MARQUIS, 15))),
|
||||
LEVEL_22(22, List.of(new VipReward(NobleVipEnum.MARQUIS, 20))),
|
||||
LEVEL_23(23, List.of(new VipReward(NobleVipEnum.MARQUIS, 25))),
|
||||
LEVEL_24(24, List.of(new VipReward(NobleVipEnum.MARQUIS, 30))),
|
||||
LEVEL_25(25, List.of(new VipReward(NobleVipEnum.DUKE, 15))),
|
||||
LEVEL_26(26, List.of(new VipReward(NobleVipEnum.DUKE, 15))),
|
||||
LEVEL_27(27, List.of(new VipReward(NobleVipEnum.DUKE, 20))),
|
||||
LEVEL_28(28, List.of(new VipReward(NobleVipEnum.DUKE, 20))),
|
||||
LEVEL_29(29, List.of(new VipReward(NobleVipEnum.DUKE, 30))),
|
||||
LEVEL_30(30, List.of(new VipReward(NobleVipEnum.DUKE, 30), new VipReward(NobleVipEnum.MARQUIS, 999))),
|
||||
LEVEL_31(31, List.of(new VipReward(NobleVipEnum.DUKE, 30))),
|
||||
LEVEL_32(32, List.of(new VipReward(NobleVipEnum.DUKE, 30))),
|
||||
LEVEL_33(33, List.of(new VipReward(NobleVipEnum.DUKE, 30))),
|
||||
LEVEL_34(34, List.of(new VipReward(NobleVipEnum.DUKE, 30))),
|
||||
LEVEL_35(35, List.of(new VipReward(NobleVipEnum.KING, 30))),
|
||||
LEVEL_36(36, List.of(new VipReward(NobleVipEnum.KING, 30))),
|
||||
LEVEL_37(37, List.of(new VipReward(NobleVipEnum.KING, 30))),
|
||||
LEVEL_38(38, List.of(new VipReward(NobleVipEnum.KING, 30))),
|
||||
LEVEL_39(39, List.of(new VipReward(NobleVipEnum.KING, 30))),
|
||||
LEVEL_40(40, List.of(new VipReward(NobleVipEnum.KING, 30), new VipReward(NobleVipEnum.DUKE, 999)));
|
||||
|
||||
private final int level;
|
||||
private final List<VipReward> rewards;
|
||||
|
||||
WealthLevelRewardEnum(int level, List<VipReward> rewards) {
|
||||
this.level = level;
|
||||
this.rewards = rewards;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public List<VipReward> getRewards() {
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public static Optional<WealthLevelRewardEnum> of(int level) {
|
||||
return Arrays.stream(values())
|
||||
.filter(e -> e.level == level)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public record VipReward(NobleVipEnum vipType, int days) {}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.framework.mybatis.service.BaseService;
|
||||
import com.red.circle.other.infra.database.rds.entity.props.PropsCommodityStore;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -60,4 +61,12 @@ public interface PropsCommodityStoreService extends BaseService<PropsCommoditySt
|
||||
PropsCommodityStore getBySourceId(SysOriginPlatformEnum sysOrigin, Long sourceId,
|
||||
String propsType);
|
||||
|
||||
/**
|
||||
* 获取 NOBLE_VIP 类型上架未删除的商品,key=sourceId,value=商品信息.
|
||||
*
|
||||
* @param sysOrigin 平台
|
||||
* @return map,key 即 props_noble_vip_ability.id
|
||||
*/
|
||||
Map<Long, PropsCommodityStore> mapNobleVipBySourceId(SysOriginPlatformEnum sysOrigin);
|
||||
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import com.red.circle.other.inner.enums.material.PropsCommodityType;
|
||||
import com.red.circle.other.inner.enums.material.PropsCurrencyType;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -98,4 +99,16 @@ public class PropsCommodityStoreServiceImpl extends
|
||||
.getOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, PropsCommodityStore> mapNobleVipBySourceId(SysOriginPlatformEnum sysOrigin) {
|
||||
return query()
|
||||
.eq(PropsCommodityStore::getSysOrigin, sysOrigin)
|
||||
.eq(PropsCommodityStore::getPropsType, PropsCommodityType.NOBLE_VIP)
|
||||
.eq(PropsCommodityStore::getShelfStatus, Boolean.TRUE)
|
||||
.eq(PropsCommodityStore::getDel, Boolean.FALSE)
|
||||
.list()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(PropsCommodityStore::getSourceId, v -> v));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -55,4 +55,5 @@ public class PropsNobleVipAbilityServiceImpl extends
|
||||
.in(ids.size() > 1, PropsNobleVipAbility::getId, ids)
|
||||
.list();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -57,9 +57,6 @@ public class ConsumptionLevelServiceImpl extends
|
||||
|
||||
@Override
|
||||
public BigDecimal getConsumptionGolds(Long userId) {
|
||||
if(userId==1807604920249827329L||userId ==1808800573464178690L){
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return Optional.ofNullable(
|
||||
query().select(ConsumptionLevel::getConsumptionGolds)
|
||||
.eq(ConsumptionLevel::getUserId, userId)
|
||||
|
||||
@ -0,0 +1,200 @@
|
||||
package com.red.circle.other.app.inner.service.user.user;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.common.business.core.level.LevelUtils;
|
||||
import com.red.circle.common.business.enums.SendPropsOrigin;
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateCustomizeCmd;
|
||||
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
|
||||
import com.red.circle.other.app.enums.user.WealthLevelRewardEnum;
|
||||
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
|
||||
import com.red.circle.other.domain.model.user.ability.RegionConfig;
|
||||
import com.red.circle.other.infra.database.rds.entity.props.PropsCommodityStore;
|
||||
import com.red.circle.other.infra.database.rds.entity.props.PropsNobleVipAbility;
|
||||
import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord;
|
||||
import com.red.circle.other.infra.database.rds.service.props.PropsCommodityStoreService;
|
||||
import com.red.circle.other.infra.database.rds.service.props.PropsNobleVipAbilityService;
|
||||
import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService;
|
||||
import com.red.circle.other.infra.utils.I18nUtils;
|
||||
import com.red.circle.other.inner.endpoint.material.props.PropsBackpackClient;
|
||||
import com.red.circle.other.inner.endpoint.material.props.PropsNobleVipClient;
|
||||
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
|
||||
import com.red.circle.other.inner.enums.material.ConsolePropsTypeEnum;
|
||||
import com.red.circle.other.inner.enums.material.PropsCommodityType;
|
||||
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
|
||||
import com.red.circle.other.inner.model.dto.material.props.PropsNobleVipAbilityDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 财富等级升级奖励服务.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WealthLevelUpRewardService {
|
||||
|
||||
private static final String REWARD_KEY_PREFIX = "wealth:level_reward:";
|
||||
|
||||
private final RedisService redisService;
|
||||
private final PropsBackpackClient propsBackpackClient;
|
||||
private final PropsNobleVipClient propsNobleVipClient;
|
||||
private final PropsNobleVipAbilityService propsNobleVipAbilityService;
|
||||
private final PropsCommodityStoreService propsCommodityStoreService;
|
||||
private final PropsSourceRecordService propsSourceRecordService;
|
||||
private final UserProfileClient userProfileClient;
|
||||
private final UserRegionGateway userRegionGateway;
|
||||
private final OfficialNoticeClient officialNoticeClient;
|
||||
|
||||
/**
|
||||
* 检查财富等级是否升级,并发放对应 VIP 奖励.
|
||||
*
|
||||
* @param userId 用户id
|
||||
* @param beforeExp 累加前的经验值
|
||||
* @param addedExp 本次累加量
|
||||
*/
|
||||
public void checkAndReward(Long userId, BigDecimal beforeExp, BigDecimal addedExp) {
|
||||
int oldLevel = LevelUtils.getWealthLevel(SysOriginPlatformEnum.ATYOU, beforeExp.longValue()).getLevel();
|
||||
int newLevel = LevelUtils.getWealthLevel(SysOriginPlatformEnum.ATYOU, beforeExp.add(addedExp).longValue()).getLevel();
|
||||
|
||||
if (newLevel <= oldLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// key=sourceId(即 ability.id),一次加载,复用
|
||||
Map<Long, PropsCommodityStore> nobleVipStoreMap = propsCommodityStoreService.mapNobleVipBySourceId(SysOriginPlatformEnum.ATYOU);
|
||||
|
||||
// 批量加载所有 ability,key=ability.id
|
||||
Set<Long> abilityIds = nobleVipStoreMap.keySet();
|
||||
Map<Long, PropsNobleVipAbility> abilityMap = propsNobleVipAbilityService.mapByIds(abilityIds);
|
||||
|
||||
// vipType -> store,发奖时直接 O(1) 取
|
||||
Map<String, PropsCommodityStore> vipTypeStoreMap = abilityMap.values().stream()
|
||||
.filter(a -> nobleVipStoreMap.containsKey(a.getId()))
|
||||
.collect(Collectors.toMap(PropsNobleVipAbility::getVipType, a -> nobleVipStoreMap.get(a.getId())));
|
||||
|
||||
RegionConfig region = userRegionGateway.getRegionConfigByUserId(userId);
|
||||
|
||||
for (int level = oldLevel + 1; level <= newLevel; level++) {
|
||||
WealthLevelRewardEnum.of(level).ifPresent(reward -> sendRewards(userId, reward, vipTypeStoreMap, abilityMap, region));
|
||||
}
|
||||
|
||||
useMaxNobleVip(userId);
|
||||
}
|
||||
|
||||
private void sendRewards(Long userId, WealthLevelRewardEnum reward,
|
||||
Map<String, PropsCommodityStore> vipTypeStoreMap,
|
||||
Map<Long, PropsNobleVipAbility> abilityMap,
|
||||
RegionConfig region) {
|
||||
for (WealthLevelRewardEnum.VipReward vipReward : reward.getRewards()) {
|
||||
String idempotentKey = REWARD_KEY_PREFIX + userId + ":" + reward.getLevel() + ":" + vipReward.vipType().name();
|
||||
if (!redisService.setIfAbsent(idempotentKey, "1")) {
|
||||
log.warn("wealth level reward already sent, skip. userId={}, level={}, vipType={}", userId, reward.getLevel(), vipReward.vipType());
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
giveVipWithAttachments(userId, vipReward, vipTypeStoreMap, abilityMap, region);
|
||||
} catch (Exception e) {
|
||||
redisService.delete(idempotentKey);
|
||||
log.error("wealth level reward send failed. userId={}, level={}, vipType={}", userId, reward.getLevel(), vipReward.vipType(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void giveVipWithAttachments(Long userId, WealthLevelRewardEnum.VipReward vipReward,
|
||||
Map<String, PropsCommodityStore> vipTypeStoreMap,
|
||||
Map<Long, PropsNobleVipAbility> abilityMap,
|
||||
RegionConfig region) {
|
||||
PropsCommodityStore store = vipTypeStoreMap.get(vipReward.vipType().name());
|
||||
if (Objects.isNull(store)) {
|
||||
log.error("wealth level reward: noble vip store not found. userId={}, vipType={}", userId, vipReward.vipType());
|
||||
return;
|
||||
}
|
||||
|
||||
PropsNobleVipAbility ability = abilityMap.get(store.getSourceId());
|
||||
if (Objects.isNull(ability)) {
|
||||
log.error("wealth level reward: ability not found. userId={}, sourceId={}", userId, store.getSourceId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 主 VIP
|
||||
sendPropsRecord(userId, store.getSourceId(), PropsCommodityType.NOBLE_VIP.name(), vipReward.days(), region);
|
||||
|
||||
// 座驾
|
||||
if (Objects.nonNull(ability.getCarId()) && !Objects.equals(ability.getCarId(), 0L)) {
|
||||
sendPropsRecord(userId, ability.getCarId(), ConsolePropsTypeEnum.RIDE.getName(), vipReward.days(), region);
|
||||
}
|
||||
|
||||
// 头像框
|
||||
if (Objects.nonNull(ability.getAvatarFrameId()) && !Objects.equals(ability.getAvatarFrameId(), 0L)) {
|
||||
sendPropsRecord(userId, ability.getAvatarFrameId(), ConsolePropsTypeEnum.AVATAR_FRAME.getName(), vipReward.days(), region);
|
||||
}
|
||||
|
||||
// 聊天气泡
|
||||
if (Objects.nonNull(ability.getChatBubbleId()) && !Objects.equals(ability.getChatBubbleId(), 0L)) {
|
||||
sendPropsRecord(userId, ability.getChatBubbleId(), ConsolePropsTypeEnum.CHAT_BUBBLE.getName(), vipReward.days(), region);
|
||||
}
|
||||
|
||||
// 资料卡
|
||||
if (Objects.nonNull(ability.getDataCardId()) && !Objects.equals(ability.getDataCardId(), 0L)) {
|
||||
sendPropsRecord(userId, ability.getDataCardId(), ConsolePropsTypeEnum.DATA_CARD.getName(), vipReward.days(), region);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPropsRecord(Long userId, Long propsId, String type, int days, RegionConfig region) {
|
||||
propsBackpackClient.giveProps(new GivePropsBackpackCmd()
|
||||
.setAcceptUserId(userId)
|
||||
.setPropsId(propsId)
|
||||
.setType(type)
|
||||
.setOrigin(SendPropsOrigin.ACTIVITY_REWARD.name())
|
||||
.setOriginDesc(SendPropsOrigin.ACTIVITY_REWARD.getDesc())
|
||||
.setDays(days)
|
||||
.setUseProps(Boolean.TRUE)
|
||||
.setAllowGive(Boolean.FALSE)
|
||||
.setOpUser(userId));
|
||||
|
||||
try {
|
||||
if (region == null) {
|
||||
return;
|
||||
}
|
||||
String url = null;
|
||||
PropsSourceRecord props = propsSourceRecordService.getPropsById(propsId);
|
||||
if (props != null) {
|
||||
url = props.getCover();
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("propType", type);
|
||||
officialNoticeClient.send(
|
||||
NoticeExtTemplateCustomizeCmd.builder()
|
||||
.toAccount(userId)
|
||||
.noticeType(OfficialNoticeTypeEnum.GIVE_AWAY_PROPS)
|
||||
.content(I18nUtils.getPropsRewardContent(type, days, I18nUtils.getLanguageByRegion(region.getRegionName())))
|
||||
.title(JSON.toJSONString(map))
|
||||
.expand(url)
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error("wealth level reward notice failed. userId={}, propsId={}, type={}", userId, propsId, type, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void useMaxNobleVip(Long userId) {
|
||||
PropsNobleVipAbilityDTO abilityDTO = propsNobleVipClient.getUserMaxAbilityDTO(userId).getBody();
|
||||
if (Objects.isNull(abilityDTO)) {
|
||||
return;
|
||||
}
|
||||
propsBackpackClient.switchUseProps(userId, abilityDTO.getId());
|
||||
propsBackpackClient.switchUseProps(userId, abilityDTO.getAvatarFrameId());
|
||||
propsBackpackClient.switchUseProps(userId, abilityDTO.getDataCardId());
|
||||
propsBackpackClient.switchUseProps(userId, abilityDTO.getCarId());
|
||||
userProfileClient.removeCacheAll(userId);
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import com.red.circle.other.infra.database.rds.service.user.user.ConsumptionLeve
|
||||
import com.red.circle.other.app.inner.convertor.user.UserProfileInnerConvertor;
|
||||
import com.red.circle.other.inner.model.dto.user.WealthAndCharmExpLevelDTO;
|
||||
import com.red.circle.other.app.inner.service.user.user.UserLevelClientService;
|
||||
import com.red.circle.other.app.inner.service.user.user.WealthLevelUpRewardService;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -25,6 +26,7 @@ public class UserLevelClientServiceImpl implements UserLevelClientService {
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final ConsumptionLevelService consumptionLevelService;
|
||||
private final UserProfileInnerConvertor userProfileInnerConvertor;
|
||||
private final WealthLevelUpRewardService wealthLevelUpRewardService;
|
||||
|
||||
@Override
|
||||
public WealthAndCharmExpLevelDTO getUserLevelExp(SysOriginPlatformEnum sysOrigin, Long userId) {
|
||||
@ -45,7 +47,9 @@ public class UserLevelClientServiceImpl implements UserLevelClientService {
|
||||
|
||||
@Override
|
||||
public void incrWealthExp(Long userId, BigDecimal quantity) {
|
||||
BigDecimal beforeExp = consumptionLevelService.getConsumptionGolds(userId);
|
||||
consumptionLevelService.incrConsumptionGolds(userId, quantity);
|
||||
wealthLevelUpRewardService.checkAndReward(userId, beforeExp, quantity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user