refactor: extract GameEventService for game event common logic, add broadcast/ranking/task to HkysServiceImpl
This commit is contained in:
parent
03a276d022
commit
b450ced3cf
@ -6,6 +6,8 @@ public enum GameOriginEnum {
|
||||
|
||||
HOTGAME,
|
||||
|
||||
HKYS,
|
||||
|
||||
LINGXIAN,
|
||||
|
||||
YOMI
|
||||
|
||||
@ -14,170 +14,133 @@ import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.core.exception.ResponseException;
|
||||
import com.red.circle.framework.core.security.UserCredential;
|
||||
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.app.enums.violation.GameOriginEnum;
|
||||
import com.red.circle.other.app.service.game.common.GameEventService;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
|
||||
import com.red.circle.other.infra.database.rds.entity.sys.GameListConfig;
|
||||
import com.red.circle.other.infra.database.rds.service.sys.GameListConfigService;
|
||||
import com.red.circle.other.inner.enums.config.EnumConfigKey;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.tool.core.date.LocalDateTimeUtils;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import com.red.circle.tool.core.num.ArithmeticUtils;
|
||||
import com.red.circle.tool.core.parse.DataTypeUtils;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
|
||||
import com.red.circle.wallet.inner.error.WalletErrorCode;
|
||||
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd.GoldReceiptCmdBuilder;
|
||||
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Hkys 游戏服务 实现.
|
||||
* Hkys 游戏服务实现
|
||||
*
|
||||
* @author pengliang on 2022/9/20
|
||||
* @author tf
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class HkysServiceImpl implements GameHkysService {
|
||||
|
||||
private final AuthClient authClient;
|
||||
private final RedisService redisService;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final EnumConfigCacheService enumConfigCacheService;
|
||||
private final UserProfileAppConvertor userProfileAppConvertor;
|
||||
private final AuthClient authClient;
|
||||
private final RedisService redisService;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final EnumConfigCacheService enumConfigCacheService;
|
||||
private final UserProfileAppConvertor userProfileAppConvertor;
|
||||
private final GameListConfigService gameListConfigService;
|
||||
private final GameActivityService gameActivityService;
|
||||
private final GameEventService gameEventService;
|
||||
|
||||
@Override
|
||||
public HkysResponse<HkysUserProfileResponse> getUserProfile(HkysUserProfileQuery query) {
|
||||
|
||||
if (!UserCredential.checkToken(query.getToken(), query.longUid())) {
|
||||
return new HkysResponse<HkysUserProfileResponse>()
|
||||
.setErrorCode(HyksErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
|
||||
@Override
|
||||
public HkysResponse<HkysUserProfileResponse> getUserProfile(HkysUserProfileQuery query) {
|
||||
if (!UserCredential.checkToken(query.getToken(), query.longUid())) {
|
||||
return new HkysResponse<HkysUserProfileResponse>()
|
||||
.setErrorCode(HyksErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
|
||||
}
|
||||
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
|
||||
userProfileGateway.getByUserId(query.longUid()));
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return new HkysResponse<HkysUserProfileResponse>()
|
||||
.setErrorCode(HyksErrorEnum.USER_NOT_FOUND.getErrorCode());
|
||||
}
|
||||
if (StringUtils.isBlank(userProfile.getUserAvatar())) {
|
||||
userProfile.setUserAvatar(enumConfigCacheService.getValue(
|
||||
EnumConfigKey.DEFAULT_USER_AVATAR, userProfile.getOriginSys()));
|
||||
}
|
||||
return new HkysResponse<HkysUserProfileResponse>()
|
||||
.setErrorCode(HyksErrorEnum.SUCCESS.getErrorCode())
|
||||
.setData(new HkysUserProfileResponse()
|
||||
.setUid(Objects.toString(userProfile.getId()))
|
||||
.setNickname(userProfile.getUserNickname())
|
||||
.setAvatar(userProfile.getUserAvatar())
|
||||
.setCoin(ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
|
||||
.getDollarAmount().longValue())
|
||||
);
|
||||
}
|
||||
|
||||
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
|
||||
userProfileGateway.getByUserId(query.longUid())
|
||||
);
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return new HkysResponse<HkysUserProfileResponse>()
|
||||
.setErrorCode(HyksErrorEnum.USER_NOT_FOUND.getErrorCode());
|
||||
}
|
||||
@Override
|
||||
public HkysResponse<UpdateUserCoinResponse> updateUserCoin(HkysUserCoinUpdate param) {
|
||||
long startTime = LocalDateTimeUtils.nowEpochMilli();
|
||||
Long userId = param.longUid();
|
||||
|
||||
if (StringUtils.isBlank(userProfile.getUserAvatar())) {
|
||||
userProfile.setUserAvatar(
|
||||
enumConfigCacheService.getValue(EnumConfigKey.DEFAULT_USER_AVATAR,
|
||||
userProfile.getOriginSys()));
|
||||
}
|
||||
if (!UserCredential.checkToken(param.getToken(), userId)) {
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(HyksErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
|
||||
}
|
||||
|
||||
return new HkysResponse<HkysUserProfileResponse>()
|
||||
.setErrorCode(HyksErrorEnum.SUCCESS.getErrorCode())
|
||||
.setData(new HkysUserProfileResponse()
|
||||
.setUid(Objects.toString(userProfile.getId()))
|
||||
.setNickname(userProfile.getUserNickname())
|
||||
.setAvatar(userProfile.getUserAvatar())
|
||||
.setCoin(
|
||||
ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
|
||||
.getDollarAmount().longValue())
|
||||
);
|
||||
}
|
||||
// 幂等检查
|
||||
if (!redisService.setIfAbsent("GHYKS:" + param.getOrderId(), 5, TimeUnit.MINUTES)) {
|
||||
log.warn("重复触发消息: {}", JacksonUtils.toJson(param));
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(406);
|
||||
}
|
||||
|
||||
try {
|
||||
long walletStartTime = LocalDateTimeUtils.nowEpochMilli();
|
||||
WalletReceiptResDTO res = walletGoldClient.changeBalance(
|
||||
gameEventService.buildReceipt(param.getToken(), userId, param.getType(),
|
||||
GoldOrigin.HKYS_GAME, param.getGameId(), param.getOrderId(), param.getCoin())
|
||||
).getBody();
|
||||
long walletCost = LocalDateTimeUtils.nowEpochMilli() - walletStartTime;
|
||||
long totalCost = LocalDateTimeUtils.nowEpochMilli() - startTime;
|
||||
if (totalCost >= 500 || walletCost >= 500) {
|
||||
log.error("(hkys) totalTime={}, walletTime={}, type={}", totalCost, walletCost, param.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HkysResponse<UpdateUserCoinResponse> updateUserCoin(HkysUserCoinUpdate param) {
|
||||
long startTime = LocalDateTimeUtils.nowEpochMilli();
|
||||
Long userId = param.longUid();
|
||||
if (!UserCredential.checkToken(param.getToken(), userId)) {
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(HyksErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
|
||||
}
|
||||
String sysOrigin = UserCredential.parseToken(param.getToken()).getSysOrigin();
|
||||
GameListConfig gameConfig = gameListConfigService.getByGameId(
|
||||
param.getGameId(), GameOriginEnum.HKYS.name(), sysOrigin);
|
||||
|
||||
if (!redisService.setIfAbsent("GHYKS:" + param.getOrderId(), 5,
|
||||
TimeUnit.MINUTES)) {
|
||||
log.warn("重复触发消息: {}", JacksonUtils.toJson(param));
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(406);
|
||||
}
|
||||
if (param.getType() == 2 && param.getCoin() > 0) {
|
||||
gameEventService.onWinEvent(userId, param.getCoin(), GameOriginEnum.HKYS.name(), gameConfig);
|
||||
gameActivityService.handleGameSpendTask(userId, param.getCoin());
|
||||
gameActivityService.incKingGameDaily(userId, param.getCoin(), param.getGameId());
|
||||
} else {
|
||||
gameEventService.onBetEvent(userId, param.getCoin());
|
||||
}
|
||||
|
||||
long walletStartTime = LocalDateTimeUtils.nowEpochMilli();
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(HyksErrorEnum.SUCCESS.getErrorCode())
|
||||
.setData(new UpdateUserCoinResponse()
|
||||
.setEventId(res.getAssetRecordId().toString())
|
||||
.setBalance(res.getBalance().getDollarAmount().stripTrailingZeros())
|
||||
);
|
||||
} catch (ResponseException ex) {
|
||||
if (Objects.equals(ex.getErrorCode(), WalletErrorCode.INSUFFICIENT_BALANCE.getCode())) {
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(4004);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("ex:{}", Throwables.getStackTraceAsString(ex));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
WalletReceiptResDTO res = walletGoldClient.changeBalance(createReceipt(param)).getBody();
|
||||
|
||||
long walletEndTime = LocalDateTimeUtils.nowEpochMilli() - walletStartTime;
|
||||
|
||||
long totalTime = LocalDateTimeUtils.nowEpochMilli() - startTime;
|
||||
|
||||
if (totalTime >= 500 || walletEndTime >= 500) {
|
||||
log.error("totalTime={},walletEndTim={},type={}"
|
||||
, totalTime
|
||||
, walletEndTime
|
||||
, param.getType());
|
||||
}
|
||||
|
||||
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(HyksErrorEnum.SUCCESS.getErrorCode())
|
||||
.setData(new UpdateUserCoinResponse()
|
||||
.setEventId(res.getAssetRecordId().toString())
|
||||
.setBalance(res.getBalance().getDollarAmount().stripTrailingZeros())
|
||||
);
|
||||
} catch (ResponseException ex) {
|
||||
if (Objects.equals(ex.getErrorCode(), WalletErrorCode.INSUFFICIENT_BALANCE.getCode())) {
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(4004);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("ex:{}", Throwables.getStackTraceAsString(ex));
|
||||
.setErrorCode(HyksErrorEnum.SERVER_ERROR.getErrorCode());
|
||||
}
|
||||
|
||||
return new HkysResponse<UpdateUserCoinResponse>()
|
||||
.setErrorCode(HyksErrorEnum.SERVER_ERROR.getErrorCode());
|
||||
}
|
||||
|
||||
private GoldReceiptCmd createReceipt(HkysUserCoinUpdate param) {
|
||||
UserCredential userCredential = UserCredential.parseToken(param.getToken());
|
||||
return createGoldReceiptCmdBuilder(param.getType())
|
||||
.userId(param.longUid())
|
||||
.sysOrigin(userCredential.getSysOrigin())
|
||||
.origin(GoldOrigin.HKYS_GAME.name() + "_" + param.getGameId())
|
||||
.originDescribe(GoldOrigin.HKYS_GAME.getDesc() + "[" + param.getGameId() + "]")
|
||||
.eventId(param.getOrderId())
|
||||
.amount(ArithmeticUtils.toBigDecimal(param.getCoin()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private GoldReceiptCmdBuilder createGoldReceiptCmdBuilder(Integer type) {
|
||||
if (Objects.equals(type, 1)) {
|
||||
return GoldReceiptCmd.builder().appExpenditure();
|
||||
}
|
||||
|
||||
if (Objects.equals(type, 2)) {
|
||||
return GoldReceiptCmd.builder().appIncome();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("3rd party param [type] error: " + type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制验证令牌.
|
||||
*
|
||||
* @return true 通过,false 不通过
|
||||
*/
|
||||
private boolean checkTokenStrict(String token, Long userId) {
|
||||
|
||||
UserCredential tokenContent = UserCredential.parseToken(token);
|
||||
|
||||
UserCredential registerCredential = ResponseAssert.requiredSuccess(
|
||||
authClient.getUserCredential(DataTypeUtils.toLong(userId)));
|
||||
|
||||
return UserCredential.checkToken(tokenContent, registerCredential)
|
||||
&& Objects.equals(tokenContent.getUserId(), userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -8,56 +8,48 @@ import com.red.circle.component.game.hotgame.response.HotGameCoin;
|
||||
import com.red.circle.component.game.hotgame.response.HotGameErrorEnum;
|
||||
import com.red.circle.component.game.hotgame.response.HotGameResponse;
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.BroadcastGroupMsgBodyCmd;
|
||||
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.framework.core.exception.ResponseException;
|
||||
import com.red.circle.framework.core.security.UserCredential;
|
||||
import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
|
||||
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
|
||||
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
|
||||
import com.red.circle.other.app.enums.violation.GameOriginEnum;
|
||||
import com.red.circle.other.app.service.activity.ActivityRechargeTicketService;
|
||||
import com.red.circle.other.app.service.activity.RankingActivityService;
|
||||
import com.red.circle.other.app.service.game.common.GameEventService;
|
||||
import com.red.circle.other.app.service.game.override.domain.HotGameUserProfile;
|
||||
import com.red.circle.other.app.service.game.override.service.HotGameService;
|
||||
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
|
||||
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.other.app.util.OfficialNoticeUtils;
|
||||
import com.red.circle.other.domain.game.GameRankingIncrementCmd;
|
||||
import com.red.circle.other.domain.gateway.game.GameRankingGateway;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.ranking.RankingActivityType;
|
||||
import com.red.circle.other.domain.ranking.RankingCycleType;
|
||||
import com.red.circle.other.domain.ranking.RankingDimension;
|
||||
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
|
||||
import com.red.circle.other.infra.database.cache.service.other.GameListCacheService;
|
||||
import com.red.circle.other.infra.database.rds.dto.game.WeeklyVipInfo;
|
||||
import com.red.circle.other.infra.database.rds.entity.sys.GameListConfig;
|
||||
import com.red.circle.other.infra.database.rds.service.activity.UserActivityRechargeService;
|
||||
import com.red.circle.other.infra.database.rds.dto.game.WeeklyVipInfo;
|
||||
import com.red.circle.other.infra.database.rds.service.game.GameVipLevelWeeklyService;
|
||||
import com.red.circle.other.infra.database.rds.service.sys.GameListConfigService;
|
||||
import com.red.circle.other.inner.enums.config.EnumConfigKey;
|
||||
import com.red.circle.other.inner.enums.game.VipLevelRule;
|
||||
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.tool.core.date.LocalDateTimeUtils;
|
||||
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.tool.core.date.ZonedId;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import com.red.circle.tool.core.num.ArithmeticUtils;
|
||||
import com.red.circle.tool.core.parse.DataTypeUtils;
|
||||
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
|
||||
import com.red.circle.wallet.inner.error.WalletErrorCode;
|
||||
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd.GoldReceiptCmdBuilder;
|
||||
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
@ -66,14 +58,15 @@ import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* HotGame 游戏服务实现
|
||||
*
|
||||
* @author tf
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ -84,10 +77,7 @@ public class HotGameServiceImpl implements HotGameService {
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final EnumConfigCacheService enumConfigCacheService;
|
||||
private final UserProfileAppConvertor userProfileAppConvertor;
|
||||
private final TaskMqMessage taskMqMessage;
|
||||
private final ImGroupClient imGroupClient;
|
||||
private final GameListConfigService gameListConfigService;
|
||||
private final GameRankingGateway gameRankingGateway;
|
||||
private final GameListCacheService gameListCacheService;
|
||||
private final RankingActivityService rankingActivityService;
|
||||
private final GameActivityService gameActivityService;
|
||||
@ -95,9 +85,10 @@ public class HotGameServiceImpl implements HotGameService {
|
||||
private final GameVipLevelWeeklyService gameVipLevelWeeklyService;
|
||||
private final ActivityRechargeTicketService activityRechargeTicketService;
|
||||
private final RoomDailyTaskProgressService roomDailyTaskProgressService;
|
||||
|
||||
private final GameEventService gameEventService;
|
||||
|
||||
@Value("${red-circle.game-rank.gameid}")
|
||||
private String gameId;
|
||||
private String rankGameId;
|
||||
@Value("${red-circle.game-rank.endTime}")
|
||||
private String endTime;
|
||||
|
||||
@ -106,24 +97,20 @@ public class HotGameServiceImpl implements HotGameService {
|
||||
|
||||
@Override
|
||||
public HotGameResponse<HotGameUserProfile> getUserProfile(HotGameUserProfileQuery query) {
|
||||
|
||||
if (!UserCredential.checkToken(query.getToken(), query.longUid())) {
|
||||
return new HotGameResponse<HotGameUserProfile>()
|
||||
.setErrorCode(HotGameErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
|
||||
}
|
||||
|
||||
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
|
||||
userProfileGateway.getByUserId(query.longUid()));
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return new HotGameResponse<HotGameUserProfile>()
|
||||
.setErrorCode(HotGameErrorEnum.USER_NOT_FOUND.getErrorCode());
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(userProfile.getUserAvatar())) {
|
||||
userProfile.setUserAvatar(enumConfigCacheService.getValue(EnumConfigKey.DEFAULT_USER_AVATAR,
|
||||
userProfile.getOriginSys()));
|
||||
userProfile.setUserAvatar(enumConfigCacheService.getValue(
|
||||
EnumConfigKey.DEFAULT_USER_AVATAR, userProfile.getOriginSys()));
|
||||
}
|
||||
|
||||
Integer vipLevel = getVipLevel(query.longUid(), userProfile.getCountryCode());
|
||||
return new HotGameResponse<HotGameUserProfile>()
|
||||
.setErrorCode(HotGameErrorEnum.SUCCESS.getErrorCode())
|
||||
@ -132,214 +119,57 @@ public class HotGameServiceImpl implements HotGameService {
|
||||
.uid(Objects.toString(userProfile.getId()))
|
||||
.nickname(userProfile.getUserNickname())
|
||||
.avatar(userProfile.getUserAvatar())
|
||||
.coin(
|
||||
ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
|
||||
.getDollarAmount().longValue()).build()
|
||||
.coin(ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
|
||||
.getDollarAmount().longValue())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public Integer getVipLevel(Long userId, String countryCode) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
// 巴基斯坦返回0 其他正常
|
||||
if ("PK".equals(countryCode)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 【新增】检查是否为每月的 1-7号 或 16-22号
|
||||
// int dayOfMonth = now.getDayOfMonth();
|
||||
// if ((dayOfMonth >= 1 && dayOfMonth <= 7) || (dayOfMonth >= 16 && dayOfMonth <= 22)) {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
try {
|
||||
String cacheKey = VIP_LEVEL_CACHE_KEY + userId;
|
||||
Object cachedLevel = redisService.redisTemplate().opsForValue().get(cacheKey);
|
||||
if (cachedLevel != null) {
|
||||
return Integer.parseInt(cachedLevel.toString());
|
||||
}
|
||||
|
||||
LocalDate firstRechargeDate = userActivityRechargeService.getFirstRechargeDate(userId, VIP_ACTIVITY_ID);
|
||||
if (firstRechargeDate == null) {
|
||||
redisService.redisTemplate().opsForValue().set(cacheKey, 0, 1, TimeUnit.DAYS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int[] weekDurations = VipLevelRule.calculateWeekDurations(userId, firstRechargeDate);
|
||||
int week1Days = weekDurations[0];
|
||||
int week2Days = weekDurations[1];
|
||||
int week3Days = weekDurations[2];
|
||||
|
||||
LocalDate today = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
||||
long daysSinceFirst = ChronoUnit.DAYS.between(firstRechargeDate, today);
|
||||
long completedCycles = daysSinceFirst / 21;
|
||||
long dayInCycle = daysSinceFirst % 21;
|
||||
|
||||
int normalizedWeek;
|
||||
int weekInCycle;
|
||||
LocalDate weekStartDate;
|
||||
LocalDate weekEndDate;
|
||||
|
||||
if (dayInCycle < week1Days) {
|
||||
normalizedWeek = 1;
|
||||
weekInCycle = 1;
|
||||
weekStartDate = firstRechargeDate.plusDays(completedCycles * 21);
|
||||
weekEndDate = weekStartDate.plusDays(week1Days - 1);
|
||||
} else if (dayInCycle < week1Days + week2Days) {
|
||||
normalizedWeek = 2;
|
||||
weekInCycle = 2;
|
||||
weekStartDate = firstRechargeDate.plusDays(completedCycles * 21 + week1Days);
|
||||
weekEndDate = weekStartDate.plusDays(week2Days - 1);
|
||||
} else {
|
||||
normalizedWeek = 3;
|
||||
weekInCycle = 3;
|
||||
weekStartDate = firstRechargeDate.plusDays(completedCycles * 21 + week1Days + week2Days);
|
||||
weekEndDate = weekStartDate.plusDays(week3Days - 1);
|
||||
}
|
||||
|
||||
LocalDate cycleStartDate = firstRechargeDate.plusDays(completedCycles * 21);
|
||||
LocalDate cycleEndDate = cycleStartDate.plusDays(20);
|
||||
|
||||
WeeklyVipInfo lastWeekInfo = gameVipLevelWeeklyService.getLastWeekInfo(userId, VIP_ACTIVITY_ID, weekStartDate);
|
||||
|
||||
BigDecimal lastWeekInherited = calculateLastWeekInherited(lastWeekInfo, weekInCycle, completedCycles);
|
||||
|
||||
BigDecimal thisWeekRecharge = userActivityRechargeService.getRechargeAmountByDateRange(
|
||||
userId, VIP_ACTIVITY_ID, weekStartDate, weekEndDate
|
||||
);
|
||||
|
||||
int displayLevel = VipLevelRule.calculateVipLevelWithInheritance(
|
||||
normalizedWeek, thisWeekRecharge, lastWeekInherited
|
||||
);
|
||||
// 应用保底逻辑
|
||||
int startLevel = calculateStartLevel(lastWeekInfo, weekInCycle, completedCycles);
|
||||
displayLevel = Math.max(displayLevel, startLevel);
|
||||
|
||||
BigDecimal thisWeekInherited = thisWeekRecharge.add(lastWeekInherited);
|
||||
|
||||
gameVipLevelWeeklyService.saveOrUpdateWeeklyLevel(
|
||||
userId, VIP_ACTIVITY_ID, (int) (completedCycles * 3 + normalizedWeek),
|
||||
weekStartDate, weekEndDate, displayLevel, thisWeekRecharge,
|
||||
normalizedWeek, thisWeekInherited
|
||||
);
|
||||
|
||||
redisService.redisTemplate().opsForValue().set(cacheKey, displayLevel, 1, TimeUnit.DAYS);
|
||||
|
||||
return displayLevel;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("获取VIP等级异常, userId:{}, error:{}", userId, e.getMessage(), e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private BigDecimal calculateLastWeekInherited(WeeklyVipInfo lastWeekInfo, int weekInCycle, long completedCycles) {
|
||||
BigDecimal lastWeekInherited = Optional.ofNullable(lastWeekInfo)
|
||||
.map(WeeklyVipInfo::getInheritedAmount)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
|
||||
// 第二个周期及以后,第一周需要除3再除2
|
||||
if (completedCycles > 0 && weekInCycle == 1) {
|
||||
return lastWeekInherited
|
||||
.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP)
|
||||
.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
if (completedCycles > 0 && weekInCycle == 2) {
|
||||
BigDecimal lastCycleInherited = lastWeekInherited.subtract(Optional.ofNullable(lastWeekInfo).map(WeeklyVipInfo::getRechargeAmount).orElse(BigDecimal.ZERO)).max(BigDecimal.ZERO);
|
||||
return lastWeekInherited.subtract(lastCycleInherited).divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
// 原有逻辑:第二周除2,第三周除2再除2
|
||||
if (weekInCycle == 2) {
|
||||
return lastWeekInherited.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
} else if (weekInCycle == 3) {
|
||||
return lastWeekInherited.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP)
|
||||
.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
return lastWeekInherited;
|
||||
}
|
||||
|
||||
private Integer calculateStartLevel(WeeklyVipInfo lastWeekInfo, int weekInCycle, long completedCycles) {
|
||||
// 只在新周期的第一周判断
|
||||
if (completedCycles > 0 && weekInCycle == 1 && lastWeekInfo != null) {
|
||||
// 检查上周期是否达到过V3
|
||||
if (lastWeekInfo.getFinalLevel() != null && lastWeekInfo.getFinalLevel() >= 3) {
|
||||
return 2; // 保底V2
|
||||
}
|
||||
}
|
||||
return 0; // 默认无保底
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HotGameResponse<HotGameCoin> updateUserCoin(HotGameUserCoinUpdate param) {
|
||||
long startTime = LocalDateTimeUtils.nowEpochMilli();
|
||||
Long userId = param.longUid();
|
||||
|
||||
if (!UserCredential.checkToken(param.getToken(), userId)) {
|
||||
return new HotGameResponse<HotGameCoin>()
|
||||
.setData(new HotGameCoin()
|
||||
.setBalanceCoin(0L)
|
||||
.setResponseId(IdWorkerUtils.getIdStr())
|
||||
)
|
||||
.setData(new HotGameCoin().setBalanceCoin(0L).setResponseId(IdWorkerUtils.getIdStr()))
|
||||
.setErrorCode(HotGameErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
|
||||
}
|
||||
|
||||
if (!redisService.setIfAbsent("GHOT:" + param.getOrderId(), 5,
|
||||
TimeUnit.MINUTES)) {
|
||||
// 幂等检查
|
||||
String idempotentKey = "GHOT:" + param.getOrderId();
|
||||
if (!redisService.setIfAbsent(idempotentKey, 5, TimeUnit.MINUTES)) {
|
||||
log.warn("重复触发消息: {}", JacksonUtils.toJson(param));
|
||||
return new HotGameResponse<HotGameCoin>()
|
||||
.setData(new HotGameCoin()
|
||||
.setBalanceCoin(0L)
|
||||
.setResponseId("0")
|
||||
).setErrorCode(406);
|
||||
.setData(new HotGameCoin().setBalanceCoin(0L).setResponseId("0"))
|
||||
.setErrorCode(406);
|
||||
}
|
||||
|
||||
try {
|
||||
long walletStartTime = LocalDateTimeUtils.nowEpochMilli();
|
||||
|
||||
WalletReceiptResDTO res = walletGoldClient.changeBalance(createReceipt(param)).getBody();
|
||||
|
||||
long walletEndTime = LocalDateTimeUtils.nowEpochMilli() - walletStartTime;
|
||||
|
||||
long totalTime = LocalDateTimeUtils.nowEpochMilli() - startTime;
|
||||
|
||||
if (totalTime >= 500 || walletEndTime >= 500) {
|
||||
log.error("(hotgame) totalTime={},walletEndTim={},type={}"
|
||||
, totalTime
|
||||
, walletEndTime
|
||||
, param.getType());
|
||||
WalletReceiptResDTO res = walletGoldClient.changeBalance(
|
||||
gameEventService.buildReceipt(param.getToken(), userId, param.getType(),
|
||||
GoldOrigin.HOT_GAME, param.getGameId(), param.getOrderId(), param.getCoin())
|
||||
).getBody();
|
||||
long walletCost = LocalDateTimeUtils.nowEpochMilli() - walletStartTime;
|
||||
long totalCost = LocalDateTimeUtils.nowEpochMilli() - startTime;
|
||||
if (totalCost >= 500 || walletCost >= 500) {
|
||||
log.error("(hotgame) totalTime={}, walletTime={}, type={}", totalCost, walletCost, param.getType());
|
||||
}
|
||||
|
||||
String sysOrigin = UserCredential.parseToken(param.getToken()).getSysOrigin();
|
||||
GameListConfig gameListConfig = gameListCacheService.getByGameIdWithCache(
|
||||
param.getGameId(),
|
||||
GameOriginEnum.HOTGAME.name(),
|
||||
sysOrigin,
|
||||
GameListConfig gameConfig = gameListCacheService.getByGameIdWithCache(
|
||||
param.getGameId(), GameOriginEnum.HOTGAME.name(), sysOrigin,
|
||||
() -> gameListConfigService.getByGameId(param.getGameId(), GameOriginEnum.HOTGAME.name(), sysOrigin)
|
||||
);
|
||||
// type = 1 支出 type = 2 收入
|
||||
|
||||
sendBroadcast(param, gameListConfig);
|
||||
//累计游戏排行榜
|
||||
incGameRankingRecord(param, gameListConfig);
|
||||
//游戏参与打榜活动
|
||||
incGameRankingActivity(param);
|
||||
|
||||
//FruitParty
|
||||
incGameRankingTmpActivity(param);
|
||||
|
||||
//处理游戏获得任务
|
||||
if (param.getType() == 2) {
|
||||
if (param.getType() == 2 && param.getCoin() > 0) {
|
||||
gameEventService.onWinEvent(userId, param.getCoin(), GameOriginEnum.HOTGAME.name(), gameConfig);
|
||||
gameActivityService.handleGameSpendTask(userId, param.getCoin());
|
||||
|
||||
// 累加用户获得的金币并发送抽奖券
|
||||
// activityRechargeTicketService.accumulatedRechargeAndAddTicket(userId, Math.abs(param.getCoin()), MonthlyRechargeType.UNDEFINED);
|
||||
}
|
||||
|
||||
// 处理游戏消费人任务
|
||||
else {
|
||||
updateRoomDailyTask(param.getCoin(), userId);
|
||||
gameActivityService.incKingGameDaily(userId, param.getCoin(), param.getGameId());
|
||||
incFruitPartyRankingActivity(param);
|
||||
} else {
|
||||
gameEventService.onBetEvent(userId, param.getCoin());
|
||||
}
|
||||
|
||||
return new HotGameResponse<HotGameCoin>()
|
||||
@ -362,133 +192,137 @@ public class HotGameServiceImpl implements HotGameService {
|
||||
.setErrorCode(HotGameErrorEnum.SERVER_ERROR.getErrorCode());
|
||||
}
|
||||
|
||||
private void updateRoomDailyTask(Long coins, Long userId) {
|
||||
RoomDailyTaskCode taskType = RoomDailyTaskCode.PERSONAL_GAME_CONSUME;
|
||||
String redisKey = "room:daily:task:progress:" + taskType.name() + ":" + userId + ":" + ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
||||
Long total = redisService.increment(redisKey, Math.abs(coins.intValue()));
|
||||
redisService.expire(redisKey, DateTimeAsiaRiyadhUtils.getSecondsUntilMidnight(), TimeUnit.SECONDS);
|
||||
roomDailyTaskProgressService.updateTaskProgress(
|
||||
new RoomDailyTaskProgressUpdateCmd()
|
||||
.setUserId(userId)
|
||||
.setTaskCode(taskType.name())
|
||||
.setProgressValue(total.intValue())
|
||||
// ------------------------------------------------------------------ //
|
||||
// HotGame 特有:FruitParty 临时排行榜
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
private void incFruitPartyRankingActivity(HotGameUserCoinUpdate param) {
|
||||
if (ZonedDateTimeAsiaRiyadhUtils.now().isAfter(getFruitPartyFinishTime())) {
|
||||
return;
|
||||
}
|
||||
if (rankGameId == null || !rankGameId.equals(param.getGameId())) {
|
||||
return;
|
||||
}
|
||||
RankingDataUpdateCmd cmd = new RankingDataUpdateCmd();
|
||||
cmd.setUserId(DataTypeUtils.toLong(param.getUid()));
|
||||
cmd.setUserSex(1);
|
||||
cmd.setActivityType(RankingActivityType.FRUIT_PARTY.getCode());
|
||||
cmd.setCycleType(RankingCycleType.WEEKLY.getCode());
|
||||
cmd.setCycleKey("20260129");
|
||||
cmd.setDimension(RankingDimension.GAME_WIN.getCode());
|
||||
cmd.setIncrementQuantity(param.getCoin());
|
||||
cmd.setBizNo(param.getGameId());
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name()));
|
||||
rankingActivityService.accumulateRankingData(cmd);
|
||||
}
|
||||
|
||||
private ZonedDateTime getFruitPartyFinishTime() {
|
||||
return ZonedDateTime.of(
|
||||
LocalDateTime.parse(endTime, DateTimeFormatter.ISO_LOCAL_DATE_TIME),
|
||||
ZonedId.ASIA_RIYADH.getZonedId()
|
||||
);
|
||||
}
|
||||
|
||||
private void incGameRankingTmpActivity(HotGameUserCoinUpdate request) {
|
||||
if (ZonedDateTimeAsiaRiyadhUtils.now().isAfter(getFinishTime())) {
|
||||
return;
|
||||
}
|
||||
// ------------------------------------------------------------------ //
|
||||
// HotGame 特有:VIP 等级
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
if (gameId != null && gameId.equals(request.getGameId()) && request.getType() == 2 && request.getCoin() > 0) {
|
||||
RankingDataUpdateCmd receiverCmd = new RankingDataUpdateCmd();
|
||||
receiverCmd.setUserId(DataTypeUtils.toLong(request.getUid()));
|
||||
receiverCmd.setUserSex(1);
|
||||
receiverCmd.setActivityType(RankingActivityType.FRUIT_PARTY.getCode());
|
||||
receiverCmd.setCycleType(RankingCycleType.WEEKLY.getCode());
|
||||
receiverCmd.setCycleKey("20260129");
|
||||
receiverCmd.setDimension(RankingDimension.GAME_WIN.getCode());
|
||||
receiverCmd.setIncrementQuantity(request.getCoin());
|
||||
receiverCmd.setBizNo(request.getGameId());
|
||||
receiverCmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name()));
|
||||
rankingActivityService.accumulateRankingData(receiverCmd);
|
||||
public Integer getVipLevel(Long userId, String countryCode) {
|
||||
if ("PK".equals(countryCode)) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
String cacheKey = VIP_LEVEL_CACHE_KEY + userId;
|
||||
Object cachedLevel = redisService.redisTemplate().opsForValue().get(cacheKey);
|
||||
if (cachedLevel != null) {
|
||||
return Integer.parseInt(cachedLevel.toString());
|
||||
}
|
||||
LocalDate firstRechargeDate = userActivityRechargeService.getFirstRechargeDate(userId, VIP_ACTIVITY_ID);
|
||||
if (firstRechargeDate == null) {
|
||||
redisService.redisTemplate().opsForValue().set(cacheKey, 0, 1, TimeUnit.DAYS);
|
||||
return 0;
|
||||
}
|
||||
int[] weekDurations = VipLevelRule.calculateWeekDurations(userId, firstRechargeDate);
|
||||
int week1Days = weekDurations[0];
|
||||
int week2Days = weekDurations[1];
|
||||
int week3Days = weekDurations[2];
|
||||
LocalDate today = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
||||
long daysSinceFirst = ChronoUnit.DAYS.between(firstRechargeDate, today);
|
||||
long completedCycles = daysSinceFirst / 21;
|
||||
long dayInCycle = daysSinceFirst % 21;
|
||||
int normalizedWeek;
|
||||
int weekInCycle;
|
||||
LocalDate weekStartDate;
|
||||
LocalDate weekEndDate;
|
||||
if (dayInCycle < week1Days) {
|
||||
normalizedWeek = 1;
|
||||
weekInCycle = 1;
|
||||
weekStartDate = firstRechargeDate.plusDays(completedCycles * 21);
|
||||
weekEndDate = weekStartDate.plusDays(week1Days - 1);
|
||||
} else if (dayInCycle < week1Days + week2Days) {
|
||||
normalizedWeek = 2;
|
||||
weekInCycle = 2;
|
||||
weekStartDate = firstRechargeDate.plusDays(completedCycles * 21 + week1Days);
|
||||
weekEndDate = weekStartDate.plusDays(week2Days - 1);
|
||||
} else {
|
||||
normalizedWeek = 3;
|
||||
weekInCycle = 3;
|
||||
weekStartDate = firstRechargeDate.plusDays(completedCycles * 21 + week1Days + week2Days);
|
||||
weekEndDate = weekStartDate.plusDays(week3Days - 1);
|
||||
}
|
||||
WeeklyVipInfo lastWeekInfo = gameVipLevelWeeklyService.getLastWeekInfo(userId, VIP_ACTIVITY_ID, weekStartDate);
|
||||
BigDecimal lastWeekInherited = calculateLastWeekInherited(lastWeekInfo, weekInCycle, completedCycles);
|
||||
BigDecimal thisWeekRecharge = userActivityRechargeService.getRechargeAmountByDateRange(
|
||||
userId, VIP_ACTIVITY_ID, weekStartDate, weekEndDate);
|
||||
int displayLevel = VipLevelRule.calculateVipLevelWithInheritance(
|
||||
normalizedWeek, thisWeekRecharge, lastWeekInherited);
|
||||
int startLevel = calculateStartLevel(lastWeekInfo, weekInCycle, completedCycles);
|
||||
displayLevel = Math.max(displayLevel, startLevel);
|
||||
BigDecimal thisWeekInherited = thisWeekRecharge.add(lastWeekInherited);
|
||||
gameVipLevelWeeklyService.saveOrUpdateWeeklyLevel(
|
||||
userId, VIP_ACTIVITY_ID, (int) (completedCycles * 3 + normalizedWeek),
|
||||
weekStartDate, weekEndDate, displayLevel, thisWeekRecharge,
|
||||
normalizedWeek, thisWeekInherited);
|
||||
redisService.redisTemplate().opsForValue().set(cacheKey, displayLevel, 1, TimeUnit.DAYS);
|
||||
return displayLevel;
|
||||
} catch (Exception e) {
|
||||
log.error("获取VIP等级异常, userId:{}, error:{}", userId, e.getMessage(), e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private ZonedDateTime getFinishTime() {
|
||||
return ZonedDateTime.of(LocalDateTime.parse(endTime,
|
||||
DateTimeFormatter.ISO_LOCAL_DATE_TIME),
|
||||
ZonedId.ASIA_RIYADH.getZonedId());
|
||||
private BigDecimal calculateLastWeekInherited(WeeklyVipInfo lastWeekInfo, int weekInCycle, long completedCycles) {
|
||||
BigDecimal lastWeekInherited = Optional.ofNullable(lastWeekInfo)
|
||||
.map(WeeklyVipInfo::getInheritedAmount)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
if (completedCycles > 0 && weekInCycle == 1) {
|
||||
return lastWeekInherited
|
||||
.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP)
|
||||
.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
if (completedCycles > 0 && weekInCycle == 2) {
|
||||
BigDecimal lastCycleInherited = lastWeekInherited
|
||||
.subtract(Optional.ofNullable(lastWeekInfo).map(WeeklyVipInfo::getRechargeAmount).orElse(BigDecimal.ZERO))
|
||||
.max(BigDecimal.ZERO);
|
||||
return lastWeekInherited.subtract(lastCycleInherited)
|
||||
.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
if (weekInCycle == 2) {
|
||||
return lastWeekInherited.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
} else if (weekInCycle == 3) {
|
||||
return lastWeekInherited
|
||||
.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP)
|
||||
.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
return lastWeekInherited;
|
||||
}
|
||||
|
||||
private void incGameRankingRecord(HotGameUserCoinUpdate request, GameListConfig gameListConfig) {
|
||||
if (request.getType() != 2 || gameListConfig == null || request.getCoin() <= 0) {
|
||||
return;
|
||||
private Integer calculateStartLevel(WeeklyVipInfo lastWeekInfo, int weekInCycle, long completedCycles) {
|
||||
if (completedCycles > 0 && weekInCycle == 1 && lastWeekInfo != null) {
|
||||
if (lastWeekInfo.getFinalLevel() != null && lastWeekInfo.getFinalLevel() >= 3) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
GameRankingIncrementCmd build = GameRankingIncrementCmd.builder()
|
||||
.gameOrigin(GameOriginEnum.HOTGAME.name())
|
||||
.gameId(gameListConfig.getGameId())
|
||||
.gameName(gameListConfig.getName())
|
||||
.gameCover(gameListConfig.getCover())
|
||||
.gameUrl(gameListConfig.getGameCode())
|
||||
.sysOrigin(gameListConfig.getSysOrigin())
|
||||
.amount(request.getCoin())
|
||||
.periodType("ALL")
|
||||
.build();
|
||||
gameRankingGateway.incrementPrizeAmount(build);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void incGameRankingActivity(HotGameUserCoinUpdate param) {
|
||||
if (param.getType() != null && param.getType() == 2 && param.getCoin() > 0) {
|
||||
RankingDataUpdateCmd receiverCmd = new RankingDataUpdateCmd();
|
||||
receiverCmd.setUserId(DataTypeUtils.toLong(param.getUid()));
|
||||
receiverCmd.setUserSex(1);
|
||||
receiverCmd.setActivityType(RankingActivityType.KING_GAMES.getCode());
|
||||
receiverCmd.setCycleType(RankingCycleType.WEEKLY.getCode());
|
||||
receiverCmd.setCycleKey(ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt().toString());
|
||||
receiverCmd.setDimension(RankingDimension.GAME_WIN.getCode());
|
||||
receiverCmd.setIncrementQuantity(param.getCoin());
|
||||
receiverCmd.setBizNo(param.getGameId());
|
||||
receiverCmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name()));
|
||||
rankingActivityService.accumulateRankingData(receiverCmd);
|
||||
|
||||
//游戏王日榜
|
||||
gameActivityService.incKingGameDaily(DataTypeUtils.toLong(param.getUid()), param.getCoin(), param.getGameId());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendBroadcast(HotGameUserCoinUpdate request, GameListConfig gameListConfig) {
|
||||
if (gameListConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal cost = enumConfigCacheService
|
||||
.getValueBigDecimal(EnumConfigKey.GAME_BROADCAST_AMOUNT, gameListConfig.getSysOrigin()) ;
|
||||
if (cost == null || cost.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
cost = BigDecimal.valueOf(5000L);
|
||||
}
|
||||
|
||||
if (request.getType() != null && request.getType() == 2 && request.getCoin() >= cost.longValue()) {
|
||||
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
|
||||
userProfileGateway.getByUserId(DataTypeUtils.toLong(request.getUid())));
|
||||
|
||||
Map<Object, Object> build = OfficialNoticeUtils.buildUserProfile(userProfile);
|
||||
build.put("gameUrl", gameListConfig.getCover());
|
||||
build.put("currencyDiff", request.getCoin());
|
||||
|
||||
imGroupClient.sendMessageBroadcast(
|
||||
BroadcastGroupMsgBodyCmd.builder()
|
||||
.toPlatform(SysOriginPlatformEnum.ATYOU)
|
||||
.type(GroupMessageTypeEnum.GAME_BAISHUN_WIN)
|
||||
.data(build)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private GoldReceiptCmdBuilder createGoldReceiptCmdBuilder(Integer type) {
|
||||
if (Objects.equals(type, 1)) {
|
||||
return GoldReceiptCmd.builder().appExpenditure();
|
||||
}
|
||||
|
||||
if (Objects.equals(type, 2)) {
|
||||
return GoldReceiptCmd.builder().appIncome();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("3rd party param [type] error: " + type);
|
||||
}
|
||||
|
||||
private GoldReceiptCmd createReceipt(HotGameUserCoinUpdate param) {
|
||||
UserCredential userCredential = UserCredential.parseToken(param.getToken());
|
||||
return createGoldReceiptCmdBuilder(param.getType())
|
||||
.userId(param.longUid())
|
||||
.sysOrigin(userCredential.getSysOrigin())
|
||||
.origin(GoldOrigin.HOT_GAME.name() + "_" + param.getGameId())
|
||||
.originDescribe(GoldOrigin.HOT_GAME.getDesc() + "[" + param.getGameId() + "]")
|
||||
.eventId(param.getOrderId())
|
||||
.amount(ArithmeticUtils.toBigDecimal(param.getCoin()))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,25 +3,18 @@ package com.red.circle.other.app.service.game;
|
||||
import com.red.circle.auth.inner.endpoint.AuthClient;
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.BroadcastGroupMsgBodyCmd;
|
||||
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.framework.core.security.UserCredential;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
|
||||
import com.red.circle.other.app.dto.cmd.party3rd.*;
|
||||
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
|
||||
import com.red.circle.other.app.enums.violation.GameOriginEnum;
|
||||
import com.red.circle.other.app.service.activity.ActivityRechargeTicketService;
|
||||
import com.red.circle.other.app.service.activity.RankingActivityService;
|
||||
import com.red.circle.other.app.service.game.common.GameEventService;
|
||||
import com.red.circle.other.app.service.game.override.service.YomiGameService;
|
||||
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
|
||||
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.other.app.util.OfficialNoticeUtils;
|
||||
import com.red.circle.other.domain.game.GameRankingIncrementCmd;
|
||||
import com.red.circle.other.domain.gateway.game.GameRankingGateway;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
import com.red.circle.other.domain.ranking.RankingActivityType;
|
||||
@ -31,14 +24,8 @@ import com.red.circle.other.infra.config.YomiConfig;
|
||||
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
|
||||
import com.red.circle.other.infra.database.rds.entity.sys.GameListConfig;
|
||||
import com.red.circle.other.infra.database.rds.service.sys.GameListConfigService;
|
||||
import com.red.circle.other.inner.enums.config.EnumConfigKey;
|
||||
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.tool.core.num.ArithmeticUtils;
|
||||
import com.red.circle.tool.core.tuple.PennyAmount;
|
||||
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
|
||||
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
|
||||
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -46,12 +33,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Yomi游戏服务实现
|
||||
*
|
||||
* @author tf
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -67,15 +54,11 @@ public class YomiGameServiceImpl implements YomiGameService {
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
private final GameListConfigService gameListConfigService;
|
||||
private final EnumConfigCacheService enumConfigCacheService;
|
||||
private final ImGroupClient imGroupClient;
|
||||
private final UserProfileAppConvertor userProfileAppConvertor;
|
||||
private final RankingActivityService rankingActivityService;
|
||||
private final GameActivityService gameActivityService;
|
||||
private final GameRankingGateway gameRankingGateway;
|
||||
private final ActivityRechargeTicketService activityRechargeTicketService;
|
||||
private final RoomDailyTaskProgressService roomDailyTaskProgressService;
|
||||
private final RedisService redisService;
|
||||
private final GameEventService gameEventService;
|
||||
|
||||
@Override
|
||||
public YomiTokenCO getToken(YomiGetTokenCmd cmd) {
|
||||
@ -94,12 +77,10 @@ public class YomiGameServiceImpl implements YomiGameService {
|
||||
if (balanceResp == null || balanceResp.getBody() == null) {
|
||||
throw new RuntimeException("查询用户余额失败");
|
||||
}
|
||||
|
||||
String token = credResp.getBody().token();
|
||||
long expireAt = System.currentTimeMillis() / 1000 + yomiConfig.getTokenExpireSeconds();
|
||||
redisTemplate.opsForValue().set(buildTokenCacheKey(token), cmd.getPlatUserId(),
|
||||
yomiConfig.getTokenExpireSeconds(), TimeUnit.SECONDS);
|
||||
|
||||
return YomiTokenCO.builder()
|
||||
.platUserId(cmd.getPlatUserId())
|
||||
.platUserOpenId("") // 项目暂无 openId 概念,按协议传空
|
||||
@ -154,10 +135,9 @@ public class YomiGameServiceImpl implements YomiGameService {
|
||||
throw new Exception("token与用户ID不匹配");
|
||||
}
|
||||
|
||||
// 幂等检查:requestId 已处理则直接返回当前余额
|
||||
// 幂等检查
|
||||
String idempotentKey = IDEMPOTENT_KEY_PREFIX + cmd.getRequestId();
|
||||
String processed = redisTemplate.opsForValue().get(idempotentKey);
|
||||
if (processed != null) {
|
||||
if (gameEventService.isProcessed(idempotentKey)) {
|
||||
log.info("yomi余额变动重复请求,直接返回: requestId={}", cmd.getRequestId());
|
||||
ResultResponse<PennyAmount> balanceResp = walletGoldClient.getBalance(userId);
|
||||
if (balanceResp == null || balanceResp.getBody() == null) {
|
||||
@ -167,29 +147,32 @@ public class YomiGameServiceImpl implements YomiGameService {
|
||||
return new YomiBalanceCO(balanceResp.getBody().getDollarAmount().longValue());
|
||||
}
|
||||
|
||||
WalletReceiptResDTO res = walletGoldClient.changeBalance(createReceipt(cmd)).getBody();
|
||||
WalletReceiptResDTO res = walletGoldClient.changeBalance(
|
||||
gameEventService.buildReceipt(cmd.getPlatToken(), userId, cmd.getReasonType(),
|
||||
GoldOrigin.YOMI_GAME, gameIdStr,
|
||||
String.valueOf(cmd.getGameRoundId()), cmd.getGold())
|
||||
).getBody();
|
||||
if (res == null || res.getBalance() == null) {
|
||||
throw new Exception("wallet changeBalance failed, response is null");
|
||||
}
|
||||
long currentBalance = res.getBalance().getDollarAmount().longValue();
|
||||
|
||||
// 执行成功后写入幂等标记
|
||||
redisTemplate.opsForValue().set(idempotentKey, "1", IDEMPOTENT_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||
gameEventService.markProcessed(idempotentKey, IDEMPOTENT_EXPIRE_SECONDS);
|
||||
|
||||
String sysOrigin = UserCredential.parseToken(cmd.getPlatToken()).getSysOrigin();
|
||||
GameListConfig gameConfig = gameListConfigService.getByGameId(gameIdStr, GameOriginEnum.YOMI.name(), sysOrigin);
|
||||
GameListConfig gameConfig = gameListConfigService.getByGameId(
|
||||
gameIdStr, GameOriginEnum.YOMI.name(), sysOrigin);
|
||||
if (gameConfig == null) {
|
||||
log.error("游戏配置不存在, gameUid={}", cmd.getGameUid());
|
||||
return new YomiBalanceCO(currentBalance);
|
||||
}
|
||||
|
||||
if (!isBet(cmd)) {
|
||||
sendBroadcast(userId, cmd.getGold().intValue(), gameConfig.getCover());
|
||||
incGameRankingRecord(cmd.getGold().intValue(), gameConfig);
|
||||
incGameRankingActivity(userId, cmd.getGold().intValue(), gameIdStr);
|
||||
gameEventService.onWinEvent(userId, cmd.getGold(), GameOriginEnum.YOMI.name(), gameConfig);
|
||||
gameActivityService.handleGameSpendTask(userId, cmd.getGold());
|
||||
gameActivityService.incKingGameDaily(userId, cmd.getGold(), gameIdStr);
|
||||
} else {
|
||||
updateRoomDailyTask(cmd.getGold(), userId);
|
||||
gameEventService.onBetEvent(userId, cmd.getGold());
|
||||
}
|
||||
|
||||
return new YomiBalanceCO(currentBalance);
|
||||
@ -200,10 +183,6 @@ public class YomiGameServiceImpl implements YomiGameService {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// 私有方法
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
private static boolean isBet(YomiChangeBalanceCmd cmd) {
|
||||
return Integer.valueOf(1).equals(cmd.getReasonType());
|
||||
}
|
||||
@ -211,91 +190,4 @@ public class YomiGameServiceImpl implements YomiGameService {
|
||||
private String buildTokenCacheKey(String token) {
|
||||
return "yomi:token:" + token;
|
||||
}
|
||||
|
||||
private GoldReceiptCmd createReceipt(YomiChangeBalanceCmd cmd) {
|
||||
UserCredential userCredential = UserCredential.parseToken(cmd.getPlatToken());
|
||||
String gameUidStr = String.valueOf(cmd.getGameUid());
|
||||
return createGoldReceiptCmdBuilder(cmd.getReasonType())
|
||||
.userId(Long.parseLong(cmd.getPlatUserId()))
|
||||
.sysOrigin(userCredential.getSysOrigin())
|
||||
.origin(GoldOrigin.YOMI_GAME.name() + "_" + gameUidStr)
|
||||
.originDescribe(GoldOrigin.YOMI_GAME.getDesc() + "[" + gameUidStr + "]")
|
||||
.eventId(String.valueOf(cmd.getGameRoundId()))
|
||||
.amount(ArithmeticUtils.toBigDecimal(Math.abs(cmd.getGold())))
|
||||
.build();
|
||||
}
|
||||
|
||||
private GoldReceiptCmd.GoldReceiptCmdBuilder createGoldReceiptCmdBuilder(Integer reasonType) {
|
||||
if (Integer.valueOf(1).equals(reasonType)) {
|
||||
return GoldReceiptCmd.builder().appExpenditure();
|
||||
}
|
||||
if (Integer.valueOf(2).equals(reasonType)) {
|
||||
return GoldReceiptCmd.builder().appIncome();
|
||||
}
|
||||
throw new IllegalArgumentException("yomi game param [reasonType] error: " + reasonType);
|
||||
}
|
||||
|
||||
private void updateRoomDailyTask(Long coins, Long userId) {
|
||||
RoomDailyTaskCode taskType = RoomDailyTaskCode.PERSONAL_GAME_CONSUME;
|
||||
String redisKey = "room:daily:task:progress:" + taskType.name() + ":" + userId + ":" + ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
||||
Long total = redisService.increment(redisKey, Math.abs(coins.intValue()));
|
||||
redisService.expire(redisKey, DateTimeAsiaRiyadhUtils.getSecondsUntilMidnight(), TimeUnit.SECONDS);
|
||||
roomDailyTaskProgressService.updateTaskProgress(
|
||||
new RoomDailyTaskProgressUpdateCmd()
|
||||
.setUserId(userId)
|
||||
.setTaskCode(taskType.name())
|
||||
.setProgressValue(total.intValue())
|
||||
);
|
||||
}
|
||||
|
||||
private void sendBroadcast(Long userId, Integer rewardAmount, String gameUrl) {
|
||||
if (rewardAmount == null || rewardAmount <= 0) {
|
||||
return;
|
||||
}
|
||||
BigDecimal cost = enumConfigCacheService.getValueBigDecimal(EnumConfigKey.GAME_BROADCAST_AMOUNT, SysOriginPlatformEnum.ATYOU.name());
|
||||
int thresholdAmount = Optional.ofNullable(cost).orElse(new BigDecimal("5000")).intValue();
|
||||
if (rewardAmount < thresholdAmount) {
|
||||
return;
|
||||
}
|
||||
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(userProfileGateway.getByUserId(userId));
|
||||
java.util.Map<Object, Object> build = OfficialNoticeUtils.buildUserProfile(userProfile);
|
||||
build.put("gameUrl", gameUrl);
|
||||
build.put("currencyDiff", rewardAmount);
|
||||
imGroupClient.sendMessageBroadcast(
|
||||
BroadcastGroupMsgBodyCmd.builder()
|
||||
.toPlatform(SysOriginPlatformEnum.ATYOU)
|
||||
.type(GroupMessageTypeEnum.GAME_BAISHUN_WIN)
|
||||
.data(build)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private void incGameRankingRecord(Integer rewardAmount, GameListConfig gameListConfig) {
|
||||
GameRankingIncrementCmd build = GameRankingIncrementCmd.builder()
|
||||
.gameOrigin(GameOriginEnum.YOMI.name())
|
||||
.gameId(gameListConfig.getGameId())
|
||||
.gameName(gameListConfig.getName())
|
||||
.gameCover(gameListConfig.getCover())
|
||||
.gameUrl(gameListConfig.getGameCode())
|
||||
.sysOrigin(gameListConfig.getSysOrigin())
|
||||
.amount(rewardAmount.longValue())
|
||||
.periodType("ALL")
|
||||
.build();
|
||||
gameRankingGateway.incrementPrizeAmount(build);
|
||||
}
|
||||
|
||||
private void incGameRankingActivity(Long userId, Integer rewardAmount, String gameId) {
|
||||
RankingDataUpdateCmd receiverCmd = new RankingDataUpdateCmd();
|
||||
receiverCmd.setUserId(userId);
|
||||
receiverCmd.setUserSex(1);
|
||||
receiverCmd.setActivityType(RankingActivityType.KING_GAMES.getCode());
|
||||
receiverCmd.setCycleType(RankingCycleType.WEEKLY.getCode());
|
||||
receiverCmd.setCycleKey(ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt().toString());
|
||||
receiverCmd.setDimension(RankingDimension.GAME_WIN.getCode());
|
||||
receiverCmd.setIncrementQuantity(rewardAmount.longValue());
|
||||
receiverCmd.setBizNo(gameId);
|
||||
receiverCmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name()));
|
||||
rankingActivityService.accumulateRankingData(receiverCmd);
|
||||
gameActivityService.incKingGameDaily(userId, rewardAmount.longValue(), gameId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,208 @@
|
||||
package com.red.circle.other.app.service.game.common;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.component.redis.service.RedisService;
|
||||
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.BroadcastGroupMsgBodyCmd;
|
||||
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.framework.core.security.UserCredential;
|
||||
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
|
||||
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
|
||||
import com.red.circle.other.app.service.activity.RankingActivityService;
|
||||
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
|
||||
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.other.app.util.OfficialNoticeUtils;
|
||||
import com.red.circle.other.domain.game.GameRankingIncrementCmd;
|
||||
import com.red.circle.other.domain.gateway.game.GameRankingGateway;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.ranking.RankingActivityType;
|
||||
import com.red.circle.other.domain.ranking.RankingCycleType;
|
||||
import com.red.circle.other.domain.ranking.RankingDimension;
|
||||
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
|
||||
import com.red.circle.other.infra.database.rds.entity.sys.GameListConfig;
|
||||
import com.red.circle.other.inner.enums.config.EnumConfigKey;
|
||||
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.tool.core.num.ArithmeticUtils;
|
||||
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
|
||||
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 第三方游戏公共事件处理
|
||||
*
|
||||
* @author tf
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GameEventService {
|
||||
|
||||
private final RedisTemplate<String, String> redisTemplate;
|
||||
private final RedisService redisService;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final UserProfileAppConvertor userProfileAppConvertor;
|
||||
private final EnumConfigCacheService enumConfigCacheService;
|
||||
private final ImGroupClient imGroupClient;
|
||||
private final GameRankingGateway gameRankingGateway;
|
||||
private final RankingActivityService rankingActivityService;
|
||||
private final RoomDailyTaskProgressService roomDailyTaskProgressService;
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// 幂等
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
/**
|
||||
* 幂等检查,true 表示已处理过
|
||||
*/
|
||||
public boolean isProcessed(String idempotentKey) {
|
||||
return redisTemplate.opsForValue().get(idempotentKey) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记已处理
|
||||
*/
|
||||
public void markProcessed(String idempotentKey, long ttlSeconds) {
|
||||
redisTemplate.opsForValue().set(idempotentKey, "1", ttlSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// 钱包收支命令构建
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
/**
|
||||
* 构建钱包收支命令
|
||||
*
|
||||
* @param type 1=支出(投注) 2=收入(返奖)
|
||||
* @param origin 游戏来源枚举
|
||||
* @param gameId 游戏ID字符串
|
||||
* @param eventId 幂等事件ID(orderId / roundId)
|
||||
* @param amount 金额绝对值
|
||||
*/
|
||||
public GoldReceiptCmd buildReceipt(String token, Long userId, Integer type,
|
||||
GoldOrigin origin, String gameId,
|
||||
String eventId, long amount) {
|
||||
UserCredential credential = UserCredential.parseToken(token);
|
||||
return buildReceiptCmdBuilder(type)
|
||||
.userId(userId)
|
||||
.sysOrigin(credential.getSysOrigin())
|
||||
.origin(origin.name() + "_" + gameId)
|
||||
.originDescribe(origin.getDesc() + "[" + gameId + "]")
|
||||
.eventId(eventId)
|
||||
.amount(ArithmeticUtils.toBigDecimal(Math.abs(amount)))
|
||||
.build();
|
||||
}
|
||||
|
||||
private GoldReceiptCmd.GoldReceiptCmdBuilder buildReceiptCmdBuilder(Integer type) {
|
||||
if (Integer.valueOf(1).equals(type)) {
|
||||
return GoldReceiptCmd.builder().appExpenditure();
|
||||
}
|
||||
if (Integer.valueOf(2).equals(type)) {
|
||||
return GoldReceiptCmd.builder().appIncome();
|
||||
}
|
||||
throw new IllegalArgumentException("3rd game param [type] error: " + type);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// 返奖事件(type=2)
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
/**
|
||||
* 返奖后的业务增强:广播 + 游戏排行榜记录 + 打榜活动 + 游戏王日榜 + 获得任务
|
||||
*/
|
||||
public void onWinEvent(Long userId, Long gold, String gameOrigin, GameListConfig gameConfig) {
|
||||
if (gold == null || gold <= 0 || gameConfig == null) {
|
||||
return;
|
||||
}
|
||||
sendBroadcast(userId, gold, gameConfig);
|
||||
incGameRankingRecord(gold, gameOrigin, gameConfig);
|
||||
incGameRankingActivity(userId, gold, gameConfig.getGameId(), gameConfig.getSysOrigin());
|
||||
}
|
||||
|
||||
private void sendBroadcast(Long userId, Long gold, GameListConfig gameConfig) {
|
||||
BigDecimal cost = enumConfigCacheService.getValueBigDecimal(
|
||||
EnumConfigKey.GAME_BROADCAST_AMOUNT, gameConfig.getSysOrigin());
|
||||
long threshold = Optional.ofNullable(cost)
|
||||
.filter(c -> c.compareTo(BigDecimal.ZERO) > 0)
|
||||
.orElse(BigDecimal.valueOf(5000L))
|
||||
.longValue();
|
||||
if (gold < threshold) {
|
||||
return;
|
||||
}
|
||||
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
|
||||
userProfileGateway.getByUserId(userId));
|
||||
Map<Object, Object> data = OfficialNoticeUtils.buildUserProfile(userProfile);
|
||||
data.put("gameUrl", gameConfig.getCover());
|
||||
data.put("currencyDiff", gold);
|
||||
imGroupClient.sendMessageBroadcast(
|
||||
BroadcastGroupMsgBodyCmd.builder()
|
||||
.toPlatform(SysOriginPlatformEnum.ATYOU)
|
||||
.type(GroupMessageTypeEnum.GAME_BAISHUN_WIN)
|
||||
.data(data)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private void incGameRankingRecord(Long gold, String gameOrigin, GameListConfig gameConfig) {
|
||||
gameRankingGateway.incrementPrizeAmount(
|
||||
GameRankingIncrementCmd.builder()
|
||||
.gameOrigin(gameOrigin)
|
||||
.gameId(gameConfig.getGameId())
|
||||
.gameName(gameConfig.getName())
|
||||
.gameCover(gameConfig.getCover())
|
||||
.gameUrl(gameConfig.getGameCode())
|
||||
.sysOrigin(gameConfig.getSysOrigin())
|
||||
.amount(gold)
|
||||
.periodType("ALL")
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private void incGameRankingActivity(Long userId, Long gold, String gameId, String sysOrigin) {
|
||||
RankingDataUpdateCmd cmd = new RankingDataUpdateCmd();
|
||||
cmd.setUserId(userId);
|
||||
cmd.setUserSex(1);
|
||||
cmd.setActivityType(RankingActivityType.KING_GAMES.getCode());
|
||||
cmd.setCycleType(RankingCycleType.WEEKLY.getCode());
|
||||
cmd.setCycleKey(ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt().toString());
|
||||
cmd.setDimension(RankingDimension.GAME_WIN.getCode());
|
||||
cmd.setIncrementQuantity(gold);
|
||||
cmd.setBizNo(gameId);
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of(sysOrigin));
|
||||
rankingActivityService.accumulateRankingData(cmd);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// 投注事件(type=1)
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
/**
|
||||
* 投注后的业务增强:房间日任务
|
||||
*/
|
||||
public void onBetEvent(Long userId, Long gold) {
|
||||
RoomDailyTaskCode taskType = RoomDailyTaskCode.PERSONAL_GAME_CONSUME;
|
||||
String redisKey = "room:daily:task:progress:" + taskType.name() + ":" + userId
|
||||
+ ":" + ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
|
||||
Long total = redisService.increment(redisKey, Math.abs(gold.intValue()));
|
||||
redisService.expire(redisKey, DateTimeAsiaRiyadhUtils.getSecondsUntilMidnight(), TimeUnit.SECONDS);
|
||||
roomDailyTaskProgressService.updateTaskProgress(
|
||||
new RoomDailyTaskProgressUpdateCmd()
|
||||
.setUserId(userId)
|
||||
.setTaskCode(taskType.name())
|
||||
.setProgressValue(total.intValue())
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user