房间停留可获得礼物-修改倒计时方法,兼容跨重置周期

This commit is contained in:
2022-12-15 03:20:43 +08:00
parent 4b3cad8613
commit 1d9eebc6ff
4 changed files with 173 additions and 92 deletions

View File

@@ -1795,6 +1795,7 @@ public enum RedisKey {
user_in_out_room, //计算用户在房间内时长
room_free_gift_user_info, //房间免费礼物倒计时
room_free_gift_user_timestamp, //房间免费礼物倒计时
;

View File

@@ -5,14 +5,19 @@ import com.accompany.business.event.room.UserOutRoomEvent;
import com.accompany.business.message.room.UserOutRoomMessage;
import com.accompany.business.model.Gift;
import com.accompany.business.param.UserBackpackParam;
import com.accompany.business.service.ErBanNetEaseService;
import com.accompany.business.service.gift.GiftService;
import com.accompany.business.service.user.UserBackpackService;
import com.accompany.business.vo.room.RoomFreeGiftUserVo;
import com.accompany.common.constant.Attach;
import com.accompany.common.constant.Constant;
import com.accompany.common.redis.RedisKey;
import com.accompany.common.status.BusiStatus;
import com.accompany.common.utils.UUIDUitl;
import com.accompany.core.exception.ServiceException;
import com.accompany.core.model.Room;
import com.accompany.core.service.SysConfService;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -23,13 +28,19 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Component
@@ -46,8 +57,15 @@ public class RoomFreeGiftService implements InitializingBean, ApplicationListene
@Lazy
@Autowired
private UserBackpackService userBackpackService;
@Autowired
private RoomQueryService roomQueryService;
@Autowired
private TaskExecutor bizExecutor;
@Autowired
private ErBanNetEaseService erBanNetEaseService;
private RMap<String, Long> userInfoMap;
private RMap<String, Long> timestampMap;
public long getUserCurStage(Long uid){
String curStageKey = getCurStageKey(uid);
@@ -58,7 +76,7 @@ public class RoomFreeGiftService implements InitializingBean, ApplicationListene
RoomFreeGiftUserVo vo = new RoomFreeGiftUserVo();
RoomFreeGiftConfigDto config = getConfig();
if (null == config){
if (null == config || null == config.getGiftId()){
return vo;
}
@@ -81,54 +99,56 @@ public class RoomFreeGiftService implements InitializingBean, ApplicationListene
vo.setGoldPrice(freeGift.getGoldPrice());
vo.setResetTime(resetTime);
String countdownKey = getCountdownKey(uid);
long totalRemainSecond = userInfoMap.getOrDefault(countdownKey, 0L);
String lastTimestampKey = getLastTimestampKey(uid);
Long inRoomTimestamp = userInfoMap.get(lastTimestampKey);
// todo 考虑跨周期
if (null == inRoomTimestamp){
inRoomTimestamp = now.getTime();
}
long thisStageCountdown = (now.getTime() - inRoomTimestamp) / 1000;
userInfoMap.fastPutAsync(lastTimestampKey, inRoomTimestamp);
computeStage(uid, totalRemainSecond + thisStageCountdown, config, freeGift, vo);
return vo;
}
public void computeStage(Long uid, long remainSecond, RoomFreeGiftConfigDto config, Gift freeGift, RoomFreeGiftUserVo vo) {
long computeStage = 0L;
remainSecond -= config.getFirstStageSecond();
if (remainSecond >= 0L){
computeStage ++;
long otherStage = remainSecond / config.getOtherStageSecond();
long otherStageSecond = remainSecond %= config.getOtherStageSecond();
if (otherStage > 0L){
computeStage += Long.min(otherStage, config.getMaxStage()-1L);
}
if (otherStageSecond > 0L){
remainSecond = config.getOtherStageSecond() - otherStageSecond;
}
} else {
remainSecond = Math.abs(remainSecond);
}
String curStageKey = getCurStageKey(uid);
long curStage = userInfoMap.getOrDefault(curStageKey, 0L);
if (curStage < computeStage){
long freeGiftNum = computeStage - curStage;
addGiftToBackpack(uid, freeGift, freeGiftNum);
curStage = computeStage;
userInfoMap.fastPut(curStageKey, curStage);
}
if (null != vo){
if (curStage >= config.getMaxStage()){
vo.setCurStage(curStage);
vo.setCurStageSecond(remainSecond);
vo.setCurStageSecond(0L);
return vo;
}
long nowTimestamp = now.getTime();
String finishTimestampKey = getFinishTimestampKey(uid);
String lastTimestampKey = getLastTimestampKey(uid);
Long inRoomTimestamp = timestampMap.get(lastTimestampKey);
//该周期第一次来
if (null == inRoomTimestamp){
inRoomTimestamp = nowTimestamp;
timestampMap.fastPut(lastTimestampKey, inRoomTimestamp);
timestampMap.fastPut(finishTimestampKey, inRoomTimestamp + config.getFirstStageSecond() * 1000);
vo.setCurStage(curStage);
vo.setCurStageSecond(config.getFirstStageSecond());
return vo;
}
long finishTimestamp = timestampMap.get(finishTimestampKey);
if (nowTimestamp >= finishTimestamp){
inRoomTimestamp = nowTimestamp;
curStage = userInfoMap.addAndGet(curStageKey, 1L);
if (curStage >= config.getMaxStage()){
timestampMap.fastRemove(lastTimestampKey);
timestampMap.fastRemove(finishTimestampKey);
} else {
timestampMap.fastPut(lastTimestampKey, inRoomTimestamp);
timestampMap.fastPut(finishTimestampKey, inRoomTimestamp + config.getOtherStageSecond() * 1000);
}
addGiftToBackpack(uid, freeGift, 1);
vo.setCurStage(curStage);
vo.setCurStageSecond(config.getOtherStageSecond());
return vo;
}
String countdownKey = getCountdownKey(uid);
long thisStageCountdown = (nowTimestamp - inRoomTimestamp) / 1000;
userInfoMap.fastPut(countdownKey, thisStageCountdown);
vo.setCurStage(curStage);
vo.setCurStageSecond(curStage == 0L? config.getFirstStageSecond(): config.getOtherStageSecond() - thisStageCountdown);
return vo;
}
private void addGiftToBackpack(Long uid, Gift freeGift, long freeGiftNum) {
@@ -140,6 +160,7 @@ public class RoomFreeGiftService implements InitializingBean, ApplicationListene
userBackpackParam.setGiftSeq(freeGift.getSeqNo());
userBackpackService.saveOrUpdateUserBackpack(userBackpackParam);
// todo 记录和日志
log.info("[房间免费礼物] {} 获得 {} {}", uid, freeGiftNum, freeGift);
}
@Async
@@ -151,27 +172,77 @@ public class RoomFreeGiftService implements InitializingBean, ApplicationListene
return;
}
Long roomUid = userOutRoomMessage.getRoomUid();
//当前离开房间用户uid
RoomFreeGiftConfigDto config = getConfig();
if (null == config || null == config.getGiftId()){
return;
}
Gift freeGift = giftService.getValidGiftById(config.getGiftId());
if (null == freeGift){
return;
}
Long uid = userOutRoomMessage.getUid();
Long outTimestamp = userOutRoomMessage.getOutTimestamp();
Long remainMillisecond = userOutRoomMessage.getRemainMillisecond();
Long remainSecond = remainMillisecond / 1000;
String countdownKey = getCountdownKey(uid);
long totalRemainSecond = userInfoMap.addAndGet(countdownKey, remainSecond);
log.info("[房间免费礼物] {} 在 {} 离开房间 {} ,增加 {} 秒, 累计 {} 秒",uid, outTimestamp, roomUid, remainSecond, totalRemainSecond);
String curStageKey = getCurStageKey(uid);
long curStage = userInfoMap.getOrDefault(curStageKey, 0L);
if (curStage >= config.getMaxStage()){
return;
}
//清理
String lastTimestampKey = getLastTimestampKey(uid);
userInfoMap.fastRemove(lastTimestampKey);
Long lastTimestamp = timestampMap.get(lastTimestampKey);
String finishTimestampKey = getFinishTimestampKey(uid);
Long finishTimestamp = timestampMap.get(finishTimestampKey);
if (null == lastTimestamp || null == finishTimestamp){
return;
}
// todo 根据curStage和computeStage结算
Long roomUid = userOutRoomMessage.getRoomUid();
Long outTimestamp = userOutRoomMessage.getOutTimestamp();
if (outTimestamp >= finishTimestamp){
curStage = userInfoMap.addAndGet(curStageKey, 1L);
if (curStage >= config.getMaxStage()){
timestampMap.fastRemove(lastTimestampKey);
timestampMap.fastRemove(finishTimestampKey);
} else {
long inRoomTimestamp = Math.max(outTimestamp, lastTimestamp);
if (inRoomTimestamp > lastTimestamp){
timestampMap.fastPut(lastTimestampKey, inRoomTimestamp);
}
timestampMap.fastPut(finishTimestampKey, inRoomTimestamp + config.getOtherStageSecond() * 1000);
}
addGiftToBackpack(uid, freeGift, 1);
log.info("[房间免费礼物] {} 在 {} 离开房间 {} ,已上升到阶段 {}", uid, outTimestamp, roomUid, curStage);
return;
}
String countdownKey = getCountdownKey(uid);
long thisStageCountdown = (Math.max(outTimestamp,lastTimestamp) - lastTimestamp) / 1000;
userInfoMap.fastPut(countdownKey, thisStageCountdown);
log.info("[房间免费礼物] {} 在 {} 离开房间 {} ,当前阶段 {} 已累计倒计时 {} 秒", uid, outTimestamp, roomUid, curStage, thisStageCountdown);
}
public void resetFreeGift(){
userBackpackService.clearRoomFreeGift();
userInfoMap.clear();
//只清楚finish时间在重置时间前的记录
long nowTimestamp = System.currentTimeMillis();
List<Long> needClearUid = timestampMap.entrySet().stream().filter(entry-> entry.getKey().endsWith("finish_timestamp") && entry.getValue() < nowTimestamp)
.map(entry-> Long.parseLong(entry.getKey().split("_")[0])).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(needClearUid)){
List<String> keys = new ArrayList<>();
for (Long uid: needClearUid){
keys.add(getLastTimestampKey(uid));
keys.add(getFinishTimestampKey(uid));
}
String[] keyArray = keys.toArray(new String[keys.size()]);
timestampMap.fastRemove(keyArray);
}
}
public RoomFreeGiftConfigDto getConfig(){
@@ -199,9 +270,51 @@ public class RoomFreeGiftService implements InitializingBean, ApplicationListene
return String.format("%d_%s", uid, "last_timestamp");
}
private String getFinishTimestampKey(Long uid){
return String.format("%d_%s", uid, "finish_timestamp");
}
@Override
public void afterPropertiesSet() throws Exception {
userInfoMap = redissonClient.getMap(RedisKey.room_free_gift_user_info.getKey(), LongCodec.INSTANCE);
timestampMap = redissonClient.getMap(RedisKey.room_free_gift_user_timestamp.getKey(), LongCodec.INSTANCE);
}
public void sendRefreshChatRoomMsg() {
RoomFreeGiftConfigDto config = getConfig();
if (null == config || null == config.getGiftId()){
return;
}
Attach attach = new Attach();
attach.setFirst(Constant.DefMsgType.roomFreeGift);
attach.setSecond(Constant.DefMsgType.roomFreeGiftRest);
sendMessageToAllValidRooms(JSON.toJSONString(attach));
}
/**
* 发送所有有效房间消息
*
* @param msg
*/
public long sendMessageToAllValidRooms(final String msg) {
int BATCH_SIZE = 1000;
long count = this.roomQueryService.countValidRooms();
long times = count % BATCH_SIZE == 0 ? (count / BATCH_SIZE) : (count / BATCH_SIZE) + 1;
for (int i = 0; i < times; i++) {
Integer index = i * BATCH_SIZE;
List<Room> validRooms = this.roomQueryService.listValidRooms(index, BATCH_SIZE);
for (Room room : validRooms) {
bizExecutor.execute(() -> {
try {
this.erBanNetEaseService.sendChatRoomMsg(room.getRoomId(), UUIDUitl.get(), room.getUid().toString(), Constant.DefineProtocol.CUSTOM_MESS_DEFINE, msg, null);
} catch (Exception e) {
log.error("批量发送房间消息失败[room={}, message={}]", JSON.toJSONString(room), msg, e);
}
});
}
}
log.info("发送所有有效房间消息,房间数:{}", count);
return count;
}
}

View File

@@ -218,7 +218,7 @@ public class UserBackpackService extends BaseService {
if (consumeType.equals(Constant.GiftConsumeType.ROOM_FREE_GIFT)){
//理论上只有一个当用户未领完并且背包没有免费礼物时mock一个
RoomFreeGiftConfigDto config = roomFreeGiftService.getConfig();
if (null != config){
if (null != config && null != config.getGiftId()){
long curStage = roomFreeGiftService.getUserCurStage(uid);
if (curStage < config.getMaxStage() && CollectionUtils.isEmpty(tempList)){
//规定是一个

View File

@@ -28,21 +28,12 @@ public class RoomFreeGiftTask implements InitializingBean {
@Autowired
private RoomFreeGiftService roomFreeGiftService;
@Autowired
private RoomQueryService roomQueryService;
@Autowired
private TaskExecutor bizExecutor;
@Autowired
private ErBanNetEaseService erBanNetEaseService;
@Scheduled(cron = "0 * * * * ?")
public void clanGoldSettlement() throws InterruptedException {
roomFreeGiftService.resetFreeGift();
Attach attach = new Attach();
attach.setFirst(Constant.DefMsgType.roomFreeGift);
attach.setSecond(Constant.DefMsgType.roomFreeGiftRest);
sendMessageToAllValidRooms(JSON.toJSONString(attach));
roomFreeGiftService.sendRefreshChatRoomMsg();
}
@@ -71,29 +62,5 @@ public class RoomFreeGiftTask implements InitializingBean {
return new CronSequenceGenerator(config.getResetTimeCron());
}
/**
* 发送所有有效房间消息
*
* @param msg
*/
public long sendMessageToAllValidRooms(final String msg) {
int BATCH_SIZE = 1000;
long count = this.roomQueryService.countValidRooms();
long times = count % BATCH_SIZE == 0 ? (count / BATCH_SIZE) : (count / BATCH_SIZE) + 1;
for (int i = 0; i < times; i++) {
Integer index = i * BATCH_SIZE;
List<Room> validRooms = this.roomQueryService.listValidRooms(index, BATCH_SIZE);
for (Room room : validRooms) {
bizExecutor.execute(() -> {
try {
this.erBanNetEaseService.sendChatRoomMsg(room.getRoomId(), UUIDUitl.get(), room.getUid().toString(), Constant.DefineProtocol.CUSTOM_MESS_DEFINE, msg, null);
} catch (Exception e) {
log.error("批量发送房间消息失败[room={}, message={}]", JSON.toJSONString(room), msg, e);
}
});
}
}
log.info("发送所有有效房间消息,房间数:{}", count);
return count;
}
}