cp新功能
(cherry picked from commit 12647246d0b4a94923b991b54c260ebaac291706)
This commit is contained in:
parent
9ff70aa3e4
commit
2d61819a42
@ -70,7 +70,12 @@ public enum PropsCommodityType {
|
|||||||
/**
|
/**
|
||||||
* 红包封面.
|
* 红包封面.
|
||||||
*/
|
*/
|
||||||
RED_PACKET;
|
RED_PACKET,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CP戒指.
|
||||||
|
*/
|
||||||
|
CP_RING;
|
||||||
|
|
||||||
|
|
||||||
public boolean eq(String name) {
|
public boolean eq(String name) {
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.red.circle.other.inner.model.dto.user;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CP戒指信息.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class CpRingDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 道具资源ID.
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long propsId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 戒指名称.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 戒指封面图.
|
||||||
|
*/
|
||||||
|
private String cover;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过期时间戳(毫秒).
|
||||||
|
*/
|
||||||
|
private Long expireTime;
|
||||||
|
|
||||||
|
}
|
||||||
@ -82,4 +82,54 @@ public class CpSimpleUserProfileCO implements Serializable {
|
|||||||
|
|
||||||
private Timestamp createTime;
|
private Timestamp createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前CP等级(未达到Lv1门槛为null).
|
||||||
|
*/
|
||||||
|
private Integer cpLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下一级所需累计CP值(满级为null).
|
||||||
|
*/
|
||||||
|
private Long nextLevelExp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 爱心样式等级(0=无, 1-3).
|
||||||
|
*/
|
||||||
|
private Integer loveHeartLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邻麦动效等级(0=无, 1-3).
|
||||||
|
*/
|
||||||
|
private Integer micEffectLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资料卡背景等级(0=无, 1-3).
|
||||||
|
*/
|
||||||
|
private Integer profileCardLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赠礼飘窗等级(0=无, 1-3).
|
||||||
|
*/
|
||||||
|
private Integer giftWindowLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否解锁非邻麦动效.
|
||||||
|
*/
|
||||||
|
private Boolean hasNonMicEffect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否解锁CP头饰.
|
||||||
|
*/
|
||||||
|
private Boolean hasCpHeadwear;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 我方当前佩戴的CP戒指(未佩戴为null).
|
||||||
|
*/
|
||||||
|
private CpRingDTO selfRing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CP方当前佩戴的CP戒指(未佩戴为null).
|
||||||
|
*/
|
||||||
|
private CpRingDTO cpRing;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,15 @@ package com.red.circle.other.app.command.material;
|
|||||||
|
|
||||||
import com.red.circle.other.app.dto.cmd.material.SwitchUsePropsCmd;
|
import com.red.circle.other.app.dto.cmd.material.SwitchUsePropsCmd;
|
||||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||||
import java.util.Objects;
|
import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService;
|
||||||
|
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
|
||||||
|
import com.red.circle.other.inner.enums.material.PropsCommodityType;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户切换使用道具.
|
* 用户切换使用道具.
|
||||||
*
|
*
|
||||||
@ -16,6 +21,8 @@ import org.springframework.stereotype.Component;
|
|||||||
public class UserSwitchUsePropsCmdExe {
|
public class UserSwitchUsePropsCmdExe {
|
||||||
|
|
||||||
private final UserProfileGateway userProfileGateway;
|
private final UserProfileGateway userProfileGateway;
|
||||||
|
private final CpRelationshipService cpRelationshipService;
|
||||||
|
private final CpRelationshipCacheService cpRelationshipCacheService;
|
||||||
|
|
||||||
public void execute(SwitchUsePropsCmd cmd) {
|
public void execute(SwitchUsePropsCmd cmd) {
|
||||||
if (Objects.equals(cmd.getUnload(), Boolean.TRUE)) {
|
if (Objects.equals(cmd.getUnload(), Boolean.TRUE)) {
|
||||||
@ -23,13 +30,21 @@ public class UserSwitchUsePropsCmdExe {
|
|||||||
cmd.requiredReqUserId(),
|
cmd.requiredReqUserId(),
|
||||||
Objects.toString(cmd.getType())
|
Objects.toString(cmd.getType())
|
||||||
);
|
);
|
||||||
return;
|
} else {
|
||||||
|
userProfileGateway.switchUseProps(
|
||||||
|
cmd.requiredReqUserId(),
|
||||||
|
cmd.getPropsId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CP戒指切换后清除双方缓存
|
||||||
|
if (Objects.equals(cmd.getType(), PropsCommodityType.CP_RING)) {
|
||||||
|
List<Long> cpUserIds = cpRelationshipService.getCpUserId(cmd.requiredReqUserId());
|
||||||
|
cpRelationshipCacheService.remove(List.of(cmd.requiredReqUserId()));
|
||||||
|
if (cpUserIds != null && !cpUserIds.isEmpty()) {
|
||||||
|
cpRelationshipCacheService.remove(cpUserIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
userProfileGateway.switchUseProps(
|
|
||||||
cmd.requiredReqUserId(),
|
|
||||||
cmd.getPropsId()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,18 @@ import com.alibaba.fastjson.JSON;
|
|||||||
import com.red.circle.component.redis.service.RedisService;
|
import com.red.circle.component.redis.service.RedisService;
|
||||||
import com.red.circle.other.infra.database.cache.key.user.UserKey;
|
import com.red.circle.other.infra.database.cache.key.user.UserKey;
|
||||||
import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService;
|
import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService;
|
||||||
|
import com.red.circle.other.infra.database.rds.entity.props.PropsBackpack;
|
||||||
|
import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord;
|
||||||
import com.red.circle.other.infra.database.rds.entity.user.user.BaseInfo;
|
import com.red.circle.other.infra.database.rds.entity.user.user.BaseInfo;
|
||||||
|
import com.red.circle.other.infra.database.rds.entity.user.user.CpLevelConfig;
|
||||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
|
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
|
||||||
|
import com.red.circle.other.infra.database.rds.service.props.PropsBackpackService;
|
||||||
|
import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService;
|
||||||
import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService;
|
import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService;
|
||||||
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
|
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
|
||||||
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
|
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
|
||||||
|
import com.red.circle.other.inner.enums.material.PropsCommodityType;
|
||||||
|
import com.red.circle.other.inner.model.dto.user.CpRingDTO;
|
||||||
import com.red.circle.other.inner.model.dto.user.CpSimpleUserProfileCO;
|
import com.red.circle.other.inner.model.dto.user.CpSimpleUserProfileCO;
|
||||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||||
import com.red.circle.tool.core.date.DateUtils;
|
import com.red.circle.tool.core.date.DateUtils;
|
||||||
@ -35,6 +42,8 @@ public class CpRelationshipCacheServiceImpl implements CpRelationshipCacheServic
|
|||||||
private final RedisService redisService;
|
private final RedisService redisService;
|
||||||
private final BaseInfoService baseInfoService;
|
private final BaseInfoService baseInfoService;
|
||||||
private final CpValueService cpValueService;
|
private final CpValueService cpValueService;
|
||||||
|
private final PropsBackpackService propsBackpackService;
|
||||||
|
private final PropsSourceRecordService propsSourceRecordService;
|
||||||
|
|
||||||
|
|
||||||
private static final long CACHE_EXPIRE_SECONDS = 7200L;
|
private static final long CACHE_EXPIRE_SECONDS = 7200L;
|
||||||
@ -84,7 +93,7 @@ public class CpRelationshipCacheServiceImpl implements CpRelationshipCacheServic
|
|||||||
String firstDay = cpRelationship.getCreateTime().toInstant()
|
String firstDay = cpRelationship.getCreateTime().toInstant()
|
||||||
.atZone(ZoneId.systemDefault()).toLocalDate().toString();
|
.atZone(ZoneId.systemDefault()).toLocalDate().toString();
|
||||||
|
|
||||||
return new CpSimpleUserProfileCO()
|
CpSimpleUserProfileCO co = new CpSimpleUserProfileCO()
|
||||||
.setMeUserId(baseInfo.getId())
|
.setMeUserId(baseInfo.getId())
|
||||||
.setMeAccount(baseInfo.getAccount())
|
.setMeAccount(baseInfo.getAccount())
|
||||||
.setMeUserAvatar(baseInfo.getUserAvatar())
|
.setMeUserAvatar(baseInfo.getUserAvatar())
|
||||||
@ -98,6 +107,25 @@ public class CpRelationshipCacheServiceImpl implements CpRelationshipCacheServic
|
|||||||
.setCreateTime(cpRelationship.getCreateTime())
|
.setCreateTime(cpRelationship.getCreateTime())
|
||||||
.setDays(days)
|
.setDays(days)
|
||||||
.setFirstDay(firstDay);
|
.setFirstDay(firstDay);
|
||||||
|
|
||||||
|
// 填充等级权益
|
||||||
|
CpLevelConfig levelConfig = CpLevelConfig.of(cpValue);
|
||||||
|
if (levelConfig != null) {
|
||||||
|
co.setCpLevel(levelConfig.getLevel())
|
||||||
|
.setNextLevelExp(levelConfig.nextExpThreshold())
|
||||||
|
.setLoveHeartLevel(levelConfig.getLoveHeartLevel())
|
||||||
|
.setMicEffectLevel(levelConfig.getMicEffectLevel())
|
||||||
|
.setProfileCardLevel(levelConfig.getProfileCardLevel())
|
||||||
|
.setGiftWindowLevel(levelConfig.getGiftWindowLevel())
|
||||||
|
.setHasNonMicEffect(levelConfig.isHasNonMicEffect())
|
||||||
|
.setHasCpHeadwear(levelConfig.isHasCpHeadwear());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实时查双方背包中正在使用的CP戒指
|
||||||
|
co.setSelfRing(buildCpRing(userId));
|
||||||
|
co.setCpRing(buildCpRing(cpRelationship.getCpUserId()));
|
||||||
|
|
||||||
|
return co;
|
||||||
})
|
})
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.sorted(Comparator.comparing(CpSimpleUserProfileCO::getCpValue).reversed()
|
.sorted(Comparator.comparing(CpSimpleUserProfileCO::getCpValue).reversed()
|
||||||
@ -117,6 +145,23 @@ public class CpRelationshipCacheServiceImpl implements CpRelationshipCacheServic
|
|||||||
return UserKey.CP.getKey(userId);
|
return UserKey.CP.getKey(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CpRingDTO buildCpRing(Long userId) {
|
||||||
|
PropsBackpack backpack = propsBackpackService
|
||||||
|
.getUserNotExpiredUseProps(userId, PropsCommodityType.CP_RING);
|
||||||
|
if (backpack == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PropsSourceRecord source = propsSourceRecordService.getById(backpack.getPropsId());
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new CpRingDTO()
|
||||||
|
.setPropsId(backpack.getPropsId())
|
||||||
|
.setName(source.getName())
|
||||||
|
.setCover(source.getCover())
|
||||||
|
.setExpireTime(backpack.getExpireTime().getTime());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(List<Long> userIds) {
|
public void remove(List<Long> userIds) {
|
||||||
userIds.forEach(userId -> {
|
userIds.forEach(userId -> {
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
package com.red.circle.other.infra.database.rds.entity.user.user;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CP等级配置.
|
||||||
|
* <p>
|
||||||
|
* 字段说明:
|
||||||
|
* - expThreshold : 达到该等级所需的累计CP值(对应 user_cp_value.cp_val)
|
||||||
|
* - loveHeartLevel : 爱心样式等级(0=无, 1-3=对应样式,展示在资料卡和用户头像旁)
|
||||||
|
* - micEffectLevel : 邻麦动效等级(0=无, 1-3,仅在左右相邻麦位时触发)
|
||||||
|
* - profileCardLevel: 资料卡动态背景等级(0=无, 1-3,展示在CP资料卡内层元素)
|
||||||
|
* - giftWindowLevel : 赠礼飘窗等级(0=无, 1-3,双方赠送CP礼物时触发)
|
||||||
|
* - hasNonMicEffect : 是否解锁非邻麦动效(true=解锁,非相邻时也可展示动效)
|
||||||
|
* - hasCpHeadwear : 是否解锁CP头饰(true=解锁,展示在双方头像周期性飞出效果)
|
||||||
|
*
|
||||||
|
* @author tf
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum CpLevelConfig {
|
||||||
|
|
||||||
|
LV1 (1, 500000L, 1, 1, 0, 0, false, false),
|
||||||
|
LV2 (2, 1000000L, 2, 1, 0, 0, false, false),
|
||||||
|
LV3 (3, 3000000L, 2, 2, 1, 0, false, false),
|
||||||
|
LV4 (4, 5000000L, 2, 2, 1, 1, false, false),
|
||||||
|
LV5 (5, 15500000L, 3, 2, 2, 1, true, true ),
|
||||||
|
LV6 (6, 40000000L, 3, 3, 2, 1, true, true ),
|
||||||
|
LV7 (7, 70000000L, 3, 3, 3, 1, true, true ),
|
||||||
|
LV8 (8, 100000000L, 3, 3, 3, 2, true, true ),
|
||||||
|
LV9 (9, 130000000L, 3, 3, 3, 3, true, true ),
|
||||||
|
LV10(10, 200000000L, 3, 3, 3, 3, true, true );
|
||||||
|
|
||||||
|
private final int level;
|
||||||
|
private final long expThreshold;
|
||||||
|
private final int loveHeartLevel;
|
||||||
|
private final int micEffectLevel;
|
||||||
|
private final int profileCardLevel;
|
||||||
|
private final int giftWindowLevel;
|
||||||
|
private final boolean hasNonMicEffect;
|
||||||
|
private final boolean hasCpHeadwear;
|
||||||
|
|
||||||
|
CpLevelConfig(int level, long expThreshold,
|
||||||
|
int loveHeartLevel, int micEffectLevel,
|
||||||
|
int profileCardLevel, int giftWindowLevel,
|
||||||
|
boolean hasNonMicEffect, boolean hasCpHeadwear) {
|
||||||
|
this.level = level;
|
||||||
|
this.expThreshold = expThreshold;
|
||||||
|
this.loveHeartLevel = loveHeartLevel;
|
||||||
|
this.micEffectLevel = micEffectLevel;
|
||||||
|
this.profileCardLevel = profileCardLevel;
|
||||||
|
this.giftWindowLevel = giftWindowLevel;
|
||||||
|
this.hasNonMicEffect = hasNonMicEffect;
|
||||||
|
this.hasCpHeadwear = hasCpHeadwear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 按 expThreshold 降序,用于 of() 从高到低匹配. */
|
||||||
|
private static final List<CpLevelConfig> DESC = Arrays.stream(values())
|
||||||
|
.sorted(Comparator.comparingLong(CpLevelConfig::getExpThreshold).reversed())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据累计CP值推算当前等级,未达到Lv1门槛返回 null.
|
||||||
|
*/
|
||||||
|
public static CpLevelConfig of(BigDecimal cpVal) {
|
||||||
|
if (cpVal == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long val = cpVal.longValue();
|
||||||
|
return DESC.stream()
|
||||||
|
.filter(lv -> val >= lv.expThreshold)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一等级经验门槛,满级返回 null.
|
||||||
|
*/
|
||||||
|
public Long nextExpThreshold() {
|
||||||
|
int nextOrdinal = this.ordinal() + 1;
|
||||||
|
CpLevelConfig[] all = values();
|
||||||
|
return nextOrdinal < all.length ? all[nextOrdinal].expThreshold : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user