增加游戏接入

This commit is contained in:
hy001 2026-04-22 19:59:00 +08:00
parent bd27726301
commit dcb77bda37
31 changed files with 1552 additions and 353 deletions

View File

@ -62,10 +62,19 @@ public class UserBaseInfoRestController extends BaseController {
/** /**
* 获取用户详情. * 获取用户详情.
*/ */
@GetMapping("/account/{account}") @GetMapping("/account/{account}")
public List<UserProfileDTO> getAccount(@PathVariable String account) { public List<UserProfileDTO> getAccount(@PathVariable String account) {
return userBaseInfoService.getByAccount(account); return userBaseInfoService.getByAccount(account);
} }
/**
* 根据系统和短号获取用户详情.
*/
@GetMapping("/{sysOrigin}/account/{account}")
public UserProfileDTO getAccount(@PathVariable String sysOrigin,
@PathVariable String account) {
return userBaseInfoService.getByAccount(sysOrigin, account);
}
@OpsOperationReqLog(value = "修改APP用户信息") @OpsOperationReqLog(value = "修改APP用户信息")
@PutMapping @PutMapping

View File

@ -190,9 +190,14 @@ public class UserBaseInfoServiceImpl implements
} }
@Override @Override
public List<UserProfileDTO> getByAccount(String account) { public List<UserProfileDTO> getByAccount(String account) {
return ResponseAssert.requiredSuccess(userProfileClient.getByAccount(account)); return ResponseAssert.requiredSuccess(userProfileClient.getByAccount(account));
} }
@Override
public UserProfileDTO getByAccount(String sysOrigin, String account) {
return ResponseAssert.requiredSuccess(userProfileClient.getByAccount(sysOrigin, account));
}
@Override @Override
public void updateUserProfile(UserProfileUpdateCmd param) { public void updateUserProfile(UserProfileUpdateCmd param) {

View File

@ -35,10 +35,10 @@ public class UserBeautifulNumberServiceImpl implements
ResponseAssert.requiredSuccess(userBeautifulNumberApplyClient.updateBeautifulNumber(cmd)); ResponseAssert.requiredSuccess(userBeautifulNumberApplyClient.updateBeautifulNumber(cmd));
} }
@Override @Override
public void addBeautifulNumber(UserBeautifulNumberCmd cmd) { public void addBeautifulNumber(UserBeautifulNumberCmd cmd) {
ResponseAssert.requiredSuccess(userBeautifulNumberApplyClient.updateBeautifulNumber(cmd)); ResponseAssert.requiredSuccess(userBeautifulNumberApplyClient.addBeautifulNumber(cmd));
} }
@Override @Override
public void deleteBeautifulNumber(Long id) { public void deleteBeautifulNumber(Long id) {

View File

@ -51,15 +51,20 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
private final UserService userService; private final UserService userService;
private final UserAppConvertor userAppConvertor; private final UserAppConvertor userAppConvertor;
private final UserProfileClient userProfileClient; private final UserProfileClient userProfileClient;
private final UserSpecialIdClient userSpecialIdClient; private final UserSpecialIdClient userSpecialIdClient;
private final SysUserSpecialIdLogClient userSpecialIdLogClient; private final SysUserSpecialIdLogClient userSpecialIdLogClient;
@Override @Override
public void addOrUpdate(UserSpecialIdCmd param) { public void addOrUpdate(UserSpecialIdCmd param) {
ResponseAssert.isTrue(UserErrorCode.USER_INFO_NOT_FOUND, ResponseAssert.isTrue(UserErrorCode.USER_INFO_NOT_FOUND,
Objects.nonNull(param.getUserId()) && param.getUserId() > 0); Objects.nonNull(param.getUserId()) && param.getUserId() > 0);
UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
userProfileClient.removeCache(param.getUserId()); userProfileClient.getByUserId(param.getUserId()));
ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
ResponseAssert.isTrue(UserErrorCode.USER_INFO_NOT_FOUND,
userProfile.eqSysOrigin(param.getSysOrigin()));
userProfileClient.removeCache(param.getUserId());
if (StringUtils.isBlank(param.getId())) { if (StringUtils.isBlank(param.getId())) {
add(param); add(param);
return; return;
@ -78,14 +83,11 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
ResponseAssert.isNull(ConsoleErrorCode.RESOURCES_ALREADY_EXISTS); ResponseAssert.isNull(ConsoleErrorCode.RESOURCES_ALREADY_EXISTS);
} }
userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() userSpecialIdLogClient.addSpecialIdLog(
.setAccount(param.getAccount()) buildSpecialIdLogCmd(param.getAccount(), param.getUserId(), param.getSysOrigin(),
.setUserId(param.getUserId()) getUpdateEventDesc(param), param.getOptUserId()));
.setSysOrigin(param.getSysOrigin()) userSpecialIdClient.updateSelective(toUserSpecialId(param));
.setEventDesc(getUpdateEventDesc(param)) }
);
userSpecialIdClient.updateSelective(toUserSpecialId(param));
}
private String getUpdateEventDesc(UserSpecialIdCmd param) { private String getUpdateEventDesc(UserSpecialIdCmd param) {
String desc = "【修改】靓号"; String desc = "【修改】靓号";
@ -110,16 +112,13 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
UserErrorCode.USER_RESOURCES_ALREADY_EXISTS, UserErrorCode.USER_RESOURCES_ALREADY_EXISTS,
ResponseAssert.requiredSuccess(userSpecialIdClient.existsUserId(param.getUserId()))); ResponseAssert.requiredSuccess(userSpecialIdClient.existsUserId(param.getUserId())));
userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() userSpecialIdLogClient.addSpecialIdLog(
.setAccount(param.getAccount()) buildSpecialIdLogCmd(param.getAccount(), param.getUserId(), param.getSysOrigin(),
.setUserId(param.getUserId()) "【添加】靓号" + (StringUtils.isNotBlank(param.getRemark()) ? "" + param.getRemark()
.setSysOrigin(param.getSysOrigin()) : ""),
.setEventDesc( param.getOptUserId()));
"【添加】靓号" + (StringUtils.isNotBlank(param.getRemark()) ? "" + param.getRemark() userSpecialIdClient.add(toUserSpecialId(param));
: "")) }
);
userSpecialIdClient.add(toUserSpecialId(param));
}
@Override @Override
public void updateExpiredTime(UserSpecialIdExpiredTimeCmd param) { public void updateExpiredTime(UserSpecialIdExpiredTimeCmd param) {
@ -129,15 +128,12 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
CommonErrorCode.NOT_FOUND_RECORD_INFO, userSpecialId); CommonErrorCode.NOT_FOUND_RECORD_INFO, userSpecialId);
userProfileClient.removeCache(userSpecialId.getUserId()); userProfileClient.removeCache(userSpecialId.getUserId());
if (Objects.equals(param.getExpiredDays(), 0)) { if (Objects.equals(param.getExpiredDays(), 0)) {
userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() userSpecialIdLogClient.addSpecialIdLog(
.setAccount(userSpecialId.getAccount()) buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(),
.setUserId(userSpecialId.getUserId()) userSpecialId.getSysOrigin(), "【修改】账号为永久", param.getOptUserId()));
.setSysOrigin(userSpecialId.getSysOrigin()) userSpecialIdClient.removeExpiredTime(userSpecialId.getSysOrigin(),
.setEventDesc("【修改】账号为永久") userSpecialId.getAccount());
); return;
userSpecialIdClient.removeExpiredTime(userSpecialId.getSysOrigin(),
userSpecialId.getAccount());
return;
} }
if (param.getExpiredDays() > 0) { if (param.getExpiredDays() > 0) {
@ -151,14 +147,12 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
} else if (Objects.isNull(userSpecialId.getExpiredTime())) { } else if (Objects.isNull(userSpecialId.getExpiredTime())) {
userSpecialId.setExpiredTime(TimestampUtils.now()); userSpecialId.setExpiredTime(TimestampUtils.now());
} }
userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() userSpecialIdLogClient.addSpecialIdLog(
.setAccount(userSpecialId.getAccount()) buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(),
.setUserId(userSpecialId.getUserId()) userSpecialId.getSysOrigin(), "【修改】设置账号过期时间: " + userSpecialId.getExpiredTime(),
.setSysOrigin(userSpecialId.getSysOrigin()) param.getOptUserId()));
.setEventDesc("【修改】设置账号过期时间: " + userSpecialId.getExpiredTime()) userSpecialIdClient.updateSelective(userAppConvertor.toUserSpecialIdCmd(userSpecialId));
); }
userSpecialIdClient.updateSelective(userAppConvertor.toUserSpecialIdCmd(userSpecialId));
}
@Override @Override
public void remove(String id, Long opeUserId) { public void remove(String id, Long opeUserId) {
@ -167,15 +161,12 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
if (Objects.isNull(userSpecialId)) { if (Objects.isNull(userSpecialId)) {
return; return;
} }
userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() userSpecialIdLogClient.addSpecialIdLog(
.setAccount(userSpecialId.getAccount()) buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(),
.setUserId(userSpecialId.getUserId()) userSpecialId.getSysOrigin(), "【移除】靓号", opeUserId));
.setSysOrigin(userSpecialId.getSysOrigin()) userSpecialIdClient.remove(id);
.setEventDesc("【移除】靓号") userProfileClient.removeCache(userSpecialId.getUserId());
); userProfileClient.removeRunProfileById(userSpecialId.getUserId());
userSpecialIdClient.remove(id);
userProfileClient.removeCache(userSpecialId.getUserId());
userProfileClient.removeRunProfileById(userSpecialId.getUserId());
} }
@Override @Override
@ -208,33 +199,30 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
} }
@Override @Override
public List<SpecialIdLogCO> listLatestLog(Long userId) { public List<SpecialIdLogCO> listLatestLog(Long userId) {
List<SysUserSpecialIdLogDTO> sysUserSpecialIdLogs = ResponseAssert.requiredSuccess( List<SysUserSpecialIdLogDTO> sysUserSpecialIdLogs = ResponseAssert.requiredSuccess(
userSpecialIdLogClient.listLatest(userId, userSpecialIdLogClient.listLatest(userId,
20)); 20));
if (CollectionUtils.isEmpty(sysUserSpecialIdLogs)) { if (CollectionUtils.isEmpty(sysUserSpecialIdLogs)) {
return Lists.newArrayList(); return Lists.newArrayList();
} }
Map<Long, UserProfileDTO> userBaseInfoMap = ResponseAssert.requiredSuccess( Map<Long, UserProfileDTO> userBaseInfoMap = getUserBaseInfoMap(
userProfileClient.mapByUserIds( sysUserSpecialIdLogs.stream().map(SysUserSpecialIdLogDTO::getUserId)
sysUserSpecialIdLogs.stream().map(SysUserSpecialIdLogDTO::getUserId) .collect(Collectors.toSet()));
.collect(Collectors.toSet())) Map<Long, User> backUserInfoMap = userService.mapBackUser(sysUserSpecialIdLogs.stream()
); .map(this::getOperateUserId)
.filter(Objects::nonNull)
Map<Long, User> backUserInfoMap = userService.mapBackUser(sysUserSpecialIdLogs.stream() .collect(Collectors.toSet()));
.filter(log -> Objects.nonNull(log.getAccount()))
.map(SysUserSpecialIdLogDTO::getCreateUser) return sysUserSpecialIdLogs.stream()
.collect(Collectors.toSet())); .map(log -> userAppConvertor.toSpecialIdLogCO(log)
.setUserBaseInfo(userBaseInfoMap.get(log.getUserId()))
return sysUserSpecialIdLogs.stream().map(log -> { .setOptUserNickname(Optional.ofNullable(backUserInfoMap.get(getOperateUserId(log)))
SpecialIdLogCO logVo = userAppConvertor.toSpecialIdLogCO(log); .map(User::getNickname)
logVo.setUserBaseInfo(userBaseInfoMap.get(logVo.getUserId())); .orElse(StringPool.UNKNOWN)))
User user = backUserInfoMap.get(log.getCreateUser()); .collect(Collectors.toList());
logVo.setOptUserNickname(Objects.nonNull(user) ? user.getNickname() : StringPool.UNKNOWN); }
return logVo;
}).collect(Collectors.toList());
}
@Override @Override
public void updateAccount(UpdateUserSpecialIdCmd param) { public void updateAccount(UpdateUserSpecialIdCmd param) {
@ -256,16 +244,14 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
userSpecialIdClient.remove(userSpecialId.getId()); userSpecialIdClient.remove(userSpecialId.getId());
userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() userSpecialIdLogClient.addSpecialIdLog(
.setAccount(param.getNewAccount()) buildSpecialIdLogCmd(param.getNewAccount(), userSpecialId.getUserId(),
.setUserId(userSpecialId.getUserId()) userSpecialId.getSysOrigin(),
.setSysOrigin(userSpecialId.getSysOrigin()) "【修改】靓号" + ",由" + userSpecialId.getAccount() + "修改成: " + param.getNewAccount(),
.setEventDesc( param.getOptUserId()));
"【修改】靓号" + ",由" + userSpecialId.getAccount() + "修改成: " + param.getNewAccount()) userSpecialId.setAccount(param.getNewAccount());
); userSpecialId.setTimeId(IdWorkerUtils.getId());
userSpecialId.setAccount(param.getNewAccount()); userSpecialId.setExpiredTime(TimestampUtils.nowPlusDays(param.getExpiredDays()));
userSpecialId.setTimeId(IdWorkerUtils.getId());
userSpecialId.setExpiredTime(TimestampUtils.nowPlusDays(param.getExpiredDays()));
userSpecialId.setCreateTime(TimestampUtils.now()); userSpecialId.setCreateTime(TimestampUtils.now());
userSpecialId.setUpdateTime(TimestampUtils.now()); userSpecialId.setUpdateTime(TimestampUtils.now());
userSpecialIdClient.add(userAppConvertor.toUserSpecialIdCmd(userSpecialId)); userSpecialIdClient.add(userAppConvertor.toUserSpecialIdCmd(userSpecialId));
@ -273,42 +259,58 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ
} }
@Override @Override
public PageResult<SpecialIdLogCO> pageSpecialIdLog(PageUserSpecialIdCmd param) { public PageResult<SpecialIdLogCO> pageSpecialIdLog(PageUserSpecialIdCmd param) {
PageResult<SysUserSpecialIdLogDTO> pageResult = ResponseAssert.requiredSuccess( PageResult<SysUserSpecialIdLogDTO> pageResult = ResponseAssert.requiredSuccess(
userSpecialIdLogClient.pageSpecialIdLog( userSpecialIdLogClient.pageSpecialIdLog(
param)); param));
if (CollectionUtils.isEmpty(pageResult.getRecords())) {
PageResult<SpecialIdLogCO> result = pageResult.convert(userAppConvertor::toSpecialIdLogCO); return pageResult.convert(userAppConvertor::toSpecialIdLogCO);
if (CollectionUtils.isEmpty(result.getRecords())) { }
return result;
} Set<Long> userIds = pageResult.getRecords().stream()
.map(SysUserSpecialIdLogDTO::getUserId)
Set<Long> userIds = Stream .filter(Objects::nonNull)
.of(result.getRecords().stream().map(SpecialIdLogCO::getUserId) .collect(Collectors.toSet());
.collect(Collectors.toSet())).flatMap(Collection::stream)
.collect(Collectors.toSet()); Map<Long, UserProfileDTO> userBaseInfoMap = getUserBaseInfoMap(userIds);
Map<Long, UserProfileDTO> userBaseInfoMap = ResponseAssert.requiredSuccess( Map<Long, User> backUserInfoMap = userService.mapBackUser(pageResult.getRecords().stream()
userProfileClient.mapByUserIds(userIds)); .map(this::getOperateUserId)
.filter(Objects::nonNull)
Map<Long, User> backUserInfoMap = userService.mapBackUser(result.getRecords().stream() .collect(Collectors.toSet()));
.filter(log -> Objects.nonNull(log.getAccount()))
.map(SpecialIdLogCO::getCreateUser) return pageResult.convert(log -> userAppConvertor.toSpecialIdLogCO(log)
.collect(Collectors.toSet())); .setUserBaseInfo(userBaseInfoMap.get(log.getUserId()))
.setOptUserNickname(Optional.ofNullable(backUserInfoMap.get(getOperateUserId(log)))
return result.convert(specialLog -> { .map(User::getNickname)
UserProfileDTO UserProfileDTO = userBaseInfoMap.get(specialLog.getUserId()); .orElse(StringPool.UNKNOWN)));
User user = backUserInfoMap.get(specialLog.getCreateUser()); }
specialLog.setUserBaseInfo(UserProfileDTO);
specialLog.setOptUserNickname( private SysUserSpecialIdLogCmd buildSpecialIdLogCmd(String account, Long userId, String sysOrigin,
Objects.nonNull(user) ? user.getNickname() : StringPool.UNKNOWN); String eventDesc, Long operateUserId) {
return specialLog; return new SysUserSpecialIdLogCmd()
}); .setAccount(account)
} .setUserId(userId)
.setSysOrigin(sysOrigin)
private UserSpecialIdCmd toUserSpecialId(UserSpecialIdCmd param) { .setEventDesc(eventDesc)
return new UserSpecialIdCmd() .setCreateUser(operateUserId)
.setUpdateUser(operateUserId);
}
private Map<Long, UserProfileDTO> getUserBaseInfoMap(Set<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return CollectionUtils.newHashMap();
}
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds));
}
private Long getOperateUserId(SysUserSpecialIdLogDTO log) {
return Objects.nonNull(log.getUpdateUser()) ? log.getUpdateUser() : log.getCreateUser();
}
private UserSpecialIdCmd toUserSpecialId(UserSpecialIdCmd param) {
return new UserSpecialIdCmd()
.setId(param.getId()) .setId(param.getId())
.setTimeId(IdWorkerUtils.getId()) .setTimeId(IdWorkerUtils.getId())
.setSysOrigin(param.getSysOrigin()) .setSysOrigin(param.getSysOrigin())

View File

@ -43,10 +43,15 @@ public interface UserBaseInfoService {
*/ */
UserProfileDetailCO getByUserId(Long userId); UserProfileDetailCO getByUserId(Long userId);
/** /**
* 根据账号查询用户信息 * 根据账号查询用户信息
*/ */
List<UserProfileDTO> getByAccount(String account); List<UserProfileDTO> getByAccount(String account);
/**
* 根据系统和账号查询单个用户信息
*/
UserProfileDTO getByAccount(String sysOrigin, String account);
/** /**
* 修改APP用户信息 * 修改APP用户信息

View File

@ -88,18 +88,19 @@ public class LiveMicRestController extends BaseController {
liveMicrophoneService.bannedLiveUser(cmd); liveMicrophoneService.bannedLiveUser(cmd);
} }
/** /**
* 下麦克风. * 下麦克风.
* *
* @eo.name 下麦克风. * @eo.name 下麦克风.
* @eo.url /go-down * @eo.url /go-down
* @eo.method post * @eo.method post
* @eo.request-type json * @eo.request-type json
*/ */
@PostMapping("/go-down") @PostMapping("/go-down")
public void goDown(@Validated @RequestBody MicUpDownCmd cmd) { public Boolean goDown(@Validated @RequestBody MicUpDownCmd cmd) {
liveMicrophoneService.goDown(cmd); liveMicrophoneService.goDown(cmd);
} return Boolean.TRUE;
}
/** /**
* T麦克风房主&管理员. * T麦克风房主&管理员.

View File

@ -0,0 +1,79 @@
package com.red.circle.live.adapter.app;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import com.red.circle.common.business.dto.cmd.app.AppRoomIdCmd;
import com.red.circle.live.app.dto.clientobject.LiveMicrophoneCO;
import com.red.circle.live.app.dto.cmd.MicBlockCmd;
import com.red.circle.live.app.dto.cmd.MicKillCmd;
import com.red.circle.live.app.dto.cmd.MicLockCmd;
import com.red.circle.live.app.dto.cmd.MicMuteCmd;
import com.red.circle.live.app.dto.cmd.MicUpDownCmd;
import com.red.circle.live.app.service.LiveMicrophoneService;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
public class LiveMicRestControllerTest {
@Test
public void goDownShouldReturnTrueAfterServiceCall() {
MicUpDownCmd cmd = new MicUpDownCmd();
RecordingLiveMicrophoneService service = new RecordingLiveMicrophoneService();
LiveMicRestController controller = new LiveMicRestController(service, null);
Boolean result = controller.goDown(cmd);
assertTrue(result);
assertEquals(1, service.goDownCallCount);
assertSame(cmd, service.lastGoDownCmd);
}
private static class RecordingLiveMicrophoneService implements LiveMicrophoneService {
private int goDownCallCount;
private MicUpDownCmd lastGoDownCmd;
@Override
public LiveMicrophoneCO goUp(MicUpDownCmd cmd) {
return null;
}
@Override
public void goDown(MicUpDownCmd cmd) {
goDownCallCount++;
lastGoDownCmd = cmd;
}
@Override
public List<LiveMicrophoneCO> listMic(AppRoomIdCmd cmd) {
return Collections.emptyList();
}
@Override
public void lock(MicLockCmd cmd) {
}
@Override
public void mute(MicMuteCmd cmd) {
}
@Override
public void kill(MicKillCmd cmd) {
}
@Override
public void init(AppRoomIdCmd cmd) {
}
@Override
public void bannedLiveUser(String account) {
}
@Override
public void bannedLiveUser(MicBlockCmd cmd) {
}
}
}

View File

@ -1,12 +1,11 @@
package com.red.circle.other.app.command.game; package com.red.circle.other.app.command.game;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.red.circle.auth.inner.endpoint.AuthClient; import com.red.circle.auth.inner.endpoint.AuthClient;
import com.red.circle.component.game.baishun.error.BaishunErrorCode; import com.red.circle.component.game.baishun.error.BaishunErrorCode;
import com.red.circle.component.game.baishun.props.GameBaishunProperties; import com.red.circle.component.game.baishun.request.BaishunUserProfileRequest;
import com.red.circle.component.game.baishun.request.BaishunUserProfileRequest; import com.red.circle.component.game.baishun.response.BaishunResponse;
import com.red.circle.component.game.baishun.response.BaishunResponse;
import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.security.UserCredential; import com.red.circle.framework.core.security.UserCredential;
import com.red.circle.other.app.dto.cmd.game.GameBaishunReportCmd; import com.red.circle.other.app.dto.cmd.game.GameBaishunReportCmd;
@ -35,12 +34,11 @@ import org.springframework.stereotype.Component;
public class GameBaishunReportCmdExe { public class GameBaishunReportCmdExe {
private static final String REPORT_TYPE_START = "game_start"; private static final String REPORT_TYPE_START = "game_start";
private static final String REPORT_TYPE_SETTLE = "game_settle"; private static final String REPORT_TYPE_SETTLE = "game_settle";
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final GameBaishunProperties gameBaishunProperties; private final WalletGoldClient walletGoldClient;
private final WalletGoldClient walletGoldClient; private final AuthClient authClient;
private final AuthClient authClient;
public BaishunResponse<Void> execute(GameBaishunReportCmd cmd) { public BaishunResponse<Void> execute(GameBaishunReportCmd cmd) {
// if (!checkTokenStrict(cmd)) { // if (!checkTokenStrict(cmd)) {

View File

@ -1,19 +1,21 @@
package com.red.circle.other.app.command.game.barrage; package com.red.circle.other.app.command.game.barrage;
import com.red.circle.component.game.baishun.props.GameBaishunProperties; import com.red.circle.external.inner.endpoint.baishun.BarrageGameClient;
import com.red.circle.external.inner.endpoint.baishun.BarrageGameClient; import com.red.circle.external.inner.model.cmd.game.baishun.BarrageGameCmd;
import com.red.circle.external.inner.model.cmd.game.baishun.BarrageGameCmd; import com.red.circle.external.inner.model.cmd.game.baishun.CloseRoomCmd;
import com.red.circle.external.inner.model.cmd.game.baishun.CloseRoomCmd; import com.red.circle.external.inner.model.cmd.game.baishun.UserEffectsCmd;
import com.red.circle.external.inner.model.cmd.game.baishun.UserEffectsCmd; import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.core.response.CommonErrorCode; import com.red.circle.other.app.dto.cmd.game.barrage.GameCloseRoomCmd;
import com.red.circle.other.app.dto.cmd.game.barrage.GameCloseRoomCmd; import com.red.circle.other.app.dto.cmd.game.barrage.GameUserEffectsCmd;
import com.red.circle.other.app.dto.cmd.game.barrage.GameUserEffectsCmd; import com.red.circle.other.app.service.game.BaishunRuntimeConfig;
import com.red.circle.other.infra.database.rds.service.live.RoomMemberService; import com.red.circle.other.app.service.game.BaishunRuntimeConfigResolver;
import com.red.circle.other.inner.enums.live.RoomUserRolesEnum; import com.red.circle.other.infra.database.rds.service.live.RoomMemberService;
import com.red.circle.tool.core.text.StringUtils; import com.red.circle.other.inner.enums.live.RoomUserRolesEnum;
import lombok.RequiredArgsConstructor; import com.red.circle.tool.core.parse.DataTypeUtils;
import lombok.extern.slf4j.Slf4j; import com.red.circle.tool.core.text.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -33,55 +35,59 @@ import java.util.Random;
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class BarrageGameBaishunExe { public class BarrageGameBaishunExe {
private final BarrageGameClient barrageGameClient; private static final String DEFAULT_SYS_ORIGIN = "LIKEI";
private final RoomMemberService roomMemberService;
private final GameBaishunProperties gameBaishunProperties; private final BarrageGameClient barrageGameClient;
private final RoomMemberService roomMemberService;
public void userEffects(GameUserEffectsCmd cmd) { private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver;
UserEffectsCmd userEffectsCmd = getUserEffectsCmd(cmd);
barrageGameClient.userEffects(userEffectsCmd); public void userEffects(GameUserEffectsCmd cmd) {
} UserEffectsCmd userEffectsCmd = getUserEffectsCmd(cmd,
baishunRuntimeConfigResolver.resolveBySysOrigin(DEFAULT_SYS_ORIGIN));
barrageGameClient.userEffects(userEffectsCmd);
}
public void closeRoom(GameCloseRoomCmd cmd) { public void closeRoom(GameCloseRoomCmd cmd) {
log.info("关闭游戏接⼝ closeRoom: {}", cmd); log.info("关闭游戏接⼝ closeRoom: {}", cmd);
// 判断是否是房主或房间管理员 // 判断是否是房主或房间管理员
RoomUserRolesEnum roomUserRoles = roomMemberService.getRoomUserRoles(cmd.requiredReqUserId(), cmd.getRoomId()); RoomUserRolesEnum roomUserRoles = roomMemberService.getRoomUserRoles(cmd.requiredReqUserId(), cmd.getRoomId());
// 权限不足: 只有管理员和房主能往下走 // 权限不足: 只有管理员和房主能往下走
ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION, ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
Objects.nonNull(roomUserRoles) && roomUserRoles.checkHomeownerOrAdmin()); Objects.nonNull(roomUserRoles) && roomUserRoles.checkHomeownerOrAdmin());
CloseRoomCmd closeRoomCmd = getCloseRoomCmd(cmd); CloseRoomCmd closeRoomCmd = getCloseRoomCmd(cmd,
barrageGameClient.closeRoom(closeRoomCmd); baishunRuntimeConfigResolver.resolveBySysOrigin(cmd.requireReqSysOrigin()));
} barrageGameClient.closeRoom(closeRoomCmd);
}
@NotNull
private UserEffectsCmd getUserEffectsCmd(GameUserEffectsCmd cmd) { @NotNull
UserEffectsCmd userEffectsCmd = new UserEffectsCmd(); private UserEffectsCmd getUserEffectsCmd(GameUserEffectsCmd cmd, BaishunRuntimeConfig runtimeConfig) {
userEffectsCmd.setGameId(cmd.getGameId()); UserEffectsCmd userEffectsCmd = new UserEffectsCmd();
userEffectsCmd.setUserId(String.valueOf(cmd.getUserId())); userEffectsCmd.setGameId(cmd.getGameId());
userEffectsCmd.setEffectsId(cmd.getEffectsId()); userEffectsCmd.setUserId(String.valueOf(cmd.getUserId()));
userEffectsCmd.setGiftPrice(cmd.getGiftPrice()); userEffectsCmd.setEffectsId(cmd.getEffectsId());
userEffectsCmd.setGiftNum(cmd.getGiftNum()); userEffectsCmd.setGiftPrice(cmd.getGiftPrice());
userEffectsCmd.setAppId(Long.parseLong(gameBaishunProperties.getAppId())); userEffectsCmd.setGiftNum(cmd.getGiftNum());
BarrageGameCmd barrageGameCmd = getBarrageGameCmd(gameBaishunProperties.getAppKey()); userEffectsCmd.setAppId(DataTypeUtils.toLong(runtimeConfig.getAppId()));
userEffectsCmd.setTimestamp(barrageGameCmd.getTimestamp()); BarrageGameCmd barrageGameCmd = getBarrageGameCmd(runtimeConfig.getAppKey());
userEffectsCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce()); userEffectsCmd.setTimestamp(barrageGameCmd.getTimestamp());
userEffectsCmd.setSignature(barrageGameCmd.getSignature()); userEffectsCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce());
return userEffectsCmd; userEffectsCmd.setSignature(barrageGameCmd.getSignature());
} return userEffectsCmd;
}
@NotNull
private CloseRoomCmd getCloseRoomCmd(GameCloseRoomCmd cmd) { @NotNull
CloseRoomCmd closeRoomCmd = new CloseRoomCmd(); private CloseRoomCmd getCloseRoomCmd(GameCloseRoomCmd cmd, BaishunRuntimeConfig runtimeConfig) {
closeRoomCmd.setRoomId(String.valueOf(cmd.getRoomId())); CloseRoomCmd closeRoomCmd = new CloseRoomCmd();
closeRoomCmd.setGameId(cmd.getGameId()); closeRoomCmd.setRoomId(String.valueOf(cmd.getRoomId()));
closeRoomCmd.setAppId(Long.parseLong(gameBaishunProperties.getAppId())); closeRoomCmd.setGameId(cmd.getGameId());
closeRoomCmd.setAppChannel(gameBaishunProperties.getAppChannel()); closeRoomCmd.setAppId(DataTypeUtils.toLong(runtimeConfig.getAppId()));
BarrageGameCmd barrageGameCmd = getBarrageGameCmd(gameBaishunProperties.getAppKey()); closeRoomCmd.setAppChannel(runtimeConfig.getAppChannel());
closeRoomCmd.setTimestamp(barrageGameCmd.getTimestamp()); BarrageGameCmd barrageGameCmd = getBarrageGameCmd(runtimeConfig.getAppKey());
closeRoomCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce()); closeRoomCmd.setTimestamp(barrageGameCmd.getTimestamp());
closeRoomCmd.setSignature(barrageGameCmd.getSignature()); closeRoomCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce());
closeRoomCmd.setSignature(barrageGameCmd.getSignature());
return closeRoomCmd; return closeRoomCmd;
} }

View File

@ -7,9 +7,10 @@ import com.red.circle.mq.business.model.event.BadgeOperateEvent;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage; import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService; import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.other.app.dto.cmd.team.DbInviteMessageProcessCmd; import com.red.circle.other.app.dto.cmd.team.DbInviteMessageProcessCmd;
import com.red.circle.other.infra.database.mongo.entity.team.bd.BdInviteAgentMessage; import com.red.circle.other.infra.database.cache.service.team.BdTeamCacheService;
import com.red.circle.other.infra.database.mongo.entity.team.bd.MessageInviteGroup; import com.red.circle.other.infra.database.mongo.entity.team.bd.BdInviteAgentMessage;
import com.red.circle.other.infra.database.mongo.entity.team.bd.MessageInviteGroup;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamApplicationProcessApproval; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamApplicationProcessApproval;
import com.red.circle.other.infra.database.mongo.service.team.bd.BdInviteAgentMessageService; import com.red.circle.other.infra.database.mongo.service.team.bd.BdInviteAgentMessageService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamApplicationProcessApprovalService; import com.red.circle.other.infra.database.mongo.service.team.team.TeamApplicationProcessApprovalService;
@ -46,10 +47,11 @@ public class BdLeaderInviteMessageProcessExe {
private final UserHistoryIdentityService userHistoryIdentityService; private final UserHistoryIdentityService userHistoryIdentityService;
private final BdInviteAgentMessageService bdInviteAgentMessageService; private final BdInviteAgentMessageService bdInviteAgentMessageService;
private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService;
private final TeamApplicationProcessApprovalService teamApplicationProcessApprovalService; private final TeamApplicationProcessApprovalService teamApplicationProcessApprovalService;
private final UserMqMessageService userMqMessageService; private final UserMqMessageService userMqMessageService;
private final AdministratorService administratorService; private final AdministratorService administratorService;
private final TaskMqMessage taskMqMessage; private final TaskMqMessage taskMqMessage;
private final BdTeamCacheService bdTeamCacheService;
public void execute(DbInviteMessageProcessCmd cmd) { public void execute(DbInviteMessageProcessCmd cmd) {
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getStatus() > 0); ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getStatus() > 0);
@ -99,10 +101,14 @@ public class BdLeaderInviteMessageProcessExe {
.setCreateUserOrigin(0) .setCreateUserOrigin(0)
); );
userHistoryIdentityService.saveBd(cmd.requiredReqUserId()); userHistoryIdentityService.saveBd(cmd.requiredReqUserId());
bdTeamCacheService.clearRelationChangeCaches(
// 发送管理员任务消息 message.getUserId(),
sendAdminTaskMessage(message.getUserId(), 14); message.getUserId(),
cmd.requiredReqUserId());
// 发送管理员任务消息
sendAdminTaskMessage(message.getUserId(), 14);
userMqMessageService.sendBadgeOperateEvent(cmd.requiredReqUserId(), BadgeKeyEnum.BD.name(), BadgeOperateEvent.OperationType.WEAR.name()); userMqMessageService.sendBadgeOperateEvent(cmd.requiredReqUserId(), BadgeKeyEnum.BD.name(), BadgeOperateEvent.OperationType.WEAR.name());
} }
@ -161,19 +167,25 @@ public class BdLeaderInviteMessageProcessExe {
.setContact(StringUtils.isBlank(cmd.getContact()) ? StringUtils.EMPTY : cmd.getContact()) .setContact(StringUtils.isBlank(cmd.getContact()) ? StringUtils.EMPTY : cmd.getContact())
); );
teamApplicationProcessApprovalService.add(new TeamApplicationProcessApproval() teamApplicationProcessApprovalService.add(new TeamApplicationProcessApproval()
.setSysOrigin(cmd.requireReqSysOrigin()) .setSysOrigin(cmd.requireReqSysOrigin())
.setType(TeamApplicationType.BD) .setType(TeamApplicationType.BD)
.setReason(TeamAppProcessApprovalReason.ACCEPT_BDLEADER_INVITE) .setReason(TeamAppProcessApprovalReason.ACCEPT_BDLEADER_INVITE)
.setAssociateId(message.getUserId()) .setAssociateId(message.getUserId())
.setBeProcessUserId(message.getInviteUserId()) .setBeProcessUserId(message.getInviteUserId())
.setCreateTime(TimestampUtils.now()) .setCreateTime(TimestampUtils.now())
.setCreateUser(cmd.requiredReqUserId()) .setCreateUser(cmd.requiredReqUserId())
.setCreateUserOrigin(0) .setCreateUserOrigin(0)
); );
// 发送管理员任务消息 bdTeamCacheService.clearRelationChangeCaches(
sendAdminTaskMessage(message.getUserId(), 13); message.getUserId(),
message.getUserId(),
cmd.requiredReqUserId());
bdTeamCacheService.clearLeaderRelatedCaches(cmd.requiredReqUserId());
// 发送管理员任务消息
sendAdminTaskMessage(message.getUserId(), 13);
userMqMessageService.sendBadgeOperateEvent(cmd.requiredReqUserId(), BadgeKeyEnum.BD.name(), BadgeOperateEvent.OperationType.WEAR.name()); userMqMessageService.sendBadgeOperateEvent(cmd.requiredReqUserId(), BadgeKeyEnum.BD.name(), BadgeOperateEvent.OperationType.WEAR.name());
userMqMessageService.sendBadgeOperateEvent(cmd.requiredReqUserId(), BadgeKeyEnum.BD_LEADER.name(), BadgeOperateEvent.OperationType.WEAR.name()); userMqMessageService.sendBadgeOperateEvent(cmd.requiredReqUserId(), BadgeKeyEnum.BD_LEADER.name(), BadgeOperateEvent.OperationType.WEAR.name());

View File

@ -29,12 +29,13 @@ import com.red.circle.mq.rocket.business.streams.GameLuckyGiftBusinessSink;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType; import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.app.convertor.material.GiftAppConvertor; import com.red.circle.other.app.convertor.material.GiftAppConvertor;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.game.GameLuckyGiftMsgCO; import com.red.circle.other.app.dto.clientobject.game.GameLuckyGiftMsgCO;
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd; import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd; import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
import com.red.circle.other.app.service.activity.ActivityRechargeTicketService; import com.red.circle.other.app.service.game.BaishunRuntimeConfigResolver;
import com.red.circle.other.app.service.activity.RankingActivityService; 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.room.RoomProfileService; import com.red.circle.other.app.service.room.RoomProfileService;
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService; import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils; import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
@ -117,11 +118,12 @@ public class GameLuckyGiftCommon {
private final UserProfileAppConvertor userProfileAppConvertor; private final UserProfileAppConvertor userProfileAppConvertor;
private final GameLuckyGiftCountService gameLuckyGiftCountService; private final GameLuckyGiftCountService gameLuckyGiftCountService;
private final GameLuckyGiftCacheService gameLuckyGiftCacheService; private final GameLuckyGiftCacheService gameLuckyGiftCacheService;
private final LuckyGiftProbabilityService luckyGiftProbabilityService; private final LuckyGiftProbabilityService luckyGiftProbabilityService;
private final GameLuckyGiftRuleConfigService gameLuckyGiftRuleConfigService; private final GameLuckyGiftRuleConfigService gameLuckyGiftRuleConfigService;
private final LuckyGiftProbabilityDetailsService luckyGiftProbabilityDetailsService; private final LuckyGiftProbabilityDetailsService luckyGiftProbabilityDetailsService;
private final LuckyGiftApiConfig luckyGiftApiConfig; private final LuckyGiftApiConfig luckyGiftApiConfig;
private final RankingActivityService rankingActivityService; private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver;
private final RankingActivityService rankingActivityService;
private final GiftMqMessage giftMqMessage; private final GiftMqMessage giftMqMessage;
private final GiftAppConvertor giftAppConvertor; private final GiftAppConvertor giftAppConvertor;
private final ActivityRechargeTicketService activityRechargeTicketService; private final ActivityRechargeTicketService activityRechargeTicketService;
@ -820,10 +822,11 @@ public class GameLuckyGiftCommon {
* 调用第三方幸运礼物API. * 调用第三方幸运礼物API.
*/ */
private ThirdPartyLotteryResult callThirdPartyLotteryApi(GameLuckyGiftParam param, List<String> orderIds) { private ThirdPartyLotteryResult callThirdPartyLotteryApi(GameLuckyGiftParam param, List<String> orderIds) {
try { try {
// 根据 standardId 获取对应的API配置 // 根据 standardId 获取对应的API配置
ApiAccount apiConfig = luckyGiftApiConfig.getConfigByStandardId(param.getStandardId()); ApiAccount apiConfig = baishunRuntimeConfigResolver.resolveLuckyGiftApiAccount(
log.info("使用API配置, standardId: {}, config: {}", param.getStandardId(), apiConfig); param.getSysOrigin(), param.getStandardId());
log.info("使用API配置, standardId: {}, config: {}", param.getStandardId(), apiConfig);
Map<String, Object> requestData = new HashMap<>(); Map<String, Object> requestData = new HashMap<>();
requestData.put("room_id", param.getRoomId().toString()); requestData.put("room_id", param.getRoomId().toString());

View File

@ -0,0 +1,140 @@
package com.red.circle.other.app.common.gift;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.live.LiveHeartbeatCache;
import com.red.circle.other.infra.database.cache.service.other.LiveHeartbeatCacheService;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomContributionCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomContributionRankCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.service.live.RoomContributionBalanceService;
import com.red.circle.tool.core.collection.CollectionUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class LuckyGiftRewardSettlementService {
private static final BigDecimal REWARD_RATIO = new BigDecimal("0.1");
private final UserRegionGateway userRegionGateway;
private final LiveHeartbeatCacheService liveHeartbeatCacheService;
private final RoomProfileManagerService roomProfileManagerService;
private final RoomContributionRankCountService roomContributionRankCountService;
private final RoomContributionBalanceService roomContributionBalanceService;
private final RoomContributionCountService roomContributionCountService;
private final RoomContributionActivityCountService roomContributionActivityCountService;
public void settle(LuckyGiftRewardStreamEvent event) {
if (event == null || event.getRoomId() == null || event.getSendUserId() == null
|| CollectionUtils.isEmpty(event.getResults())) {
return;
}
applyHeartbeat(event);
applyRoomContribution(event);
}
private void applyHeartbeat(LuckyGiftRewardStreamEvent event) {
Map<Long, Long> increments = new HashMap<>();
for (LuckyGiftRewardStreamEvent.Result result : event.getResults()) {
if (result == null || result.getAcceptUserId() == null || result.getRewardNum() == null
|| result.getRewardNum() <= 0) {
continue;
}
long increment = calcRewardIncrement(result.getRewardNum());
if (increment <= 0) {
continue;
}
increments.merge(result.getAcceptUserId(), increment, Long::sum);
}
if (increments.isEmpty()) {
return;
}
List<LiveHeartbeatCache> liveHeartbeatCaches = liveHeartbeatCacheService.listLiveHeartbeat(
event.getRoomId());
Map<Long, LiveHeartbeatCache> heartbeatMap = mapHeartbeatCaches(liveHeartbeatCaches);
increments.forEach((userId, increment) -> heartbeatMap.compute(userId, (key, current) -> {
LiveHeartbeatCache target = current == null
? new LiveHeartbeatCache().setId(userId).setHeartbeatVal(0L)
: current;
long total = Objects.requireNonNullElse(target.getHeartbeatVal(), 0L) + increment;
target.setHeartbeatVal(total);
return target;
}));
liveHeartbeatCacheService.create(event.getRoomId(), new ArrayList<>(heartbeatMap.values()));
}
private void applyRoomContribution(LuckyGiftRewardStreamEvent event) {
RoomProfile roomProfile = roomProfileManagerService.getProfileById(event.getRoomId());
if (roomProfile == null) {
log.warn("skip lucky gift room contribution because room profile missing. eventId={} roomId={}",
event.getEventId(), event.getRoomId());
return;
}
String roomCode = userRegionGateway.getRegionCode(roomProfile.getUserId());
String sendUserCode = userRegionGateway.getRegionCode(event.getSendUserId());
if (!Objects.equals(roomCode, sendUserCode)) {
return;
}
long totalReward = event.getResults().stream()
.filter(Objects::nonNull)
.map(LuckyGiftRewardStreamEvent.Result::getRewardNum)
.filter(Objects::nonNull)
.filter(rewardNum -> rewardNum > 0)
.mapToLong(Long::longValue)
.sum();
long contributionQuantity = calcRewardIncrement(totalReward);
if (contributionQuantity <= 0) {
return;
}
BigDecimal quantity = BigDecimal.valueOf(contributionQuantity);
roomContributionRankCountService.incr(roomProfile.getSysOrigin(), roomProfile.getId(),
event.getSendUserId(), quantity);
roomContributionBalanceService.incrTotalQuantity(roomProfile.getId(), roomProfile.getUserId(),
quantity);
roomContributionCountService.incrQuantity(roomProfile.getSysOrigin(), roomProfile.getId(),
roomProfile.getUserId(), quantity);
roomContributionActivityCountService.incrQuantity(roomProfile.getId(), quantity);
}
private long calcRewardIncrement(Long rewardNum) {
if (rewardNum == null || rewardNum <= 0) {
return 0L;
}
return BigDecimal.valueOf(rewardNum)
.multiply(REWARD_RATIO)
.setScale(0, RoundingMode.DOWN)
.longValue();
}
private Map<Long, LiveHeartbeatCache> mapHeartbeatCaches(List<LiveHeartbeatCache> list) {
if (CollectionUtils.isEmpty(list)) {
return new HashMap<>();
}
Map<Long, LiveHeartbeatCache> result = new HashMap<>(list.size());
for (LiveHeartbeatCache cache : list) {
if (cache != null && cache.getId() != null) {
result.put(cache.getId(), cache);
}
}
return result;
}
}

View File

@ -0,0 +1,32 @@
package com.red.circle.other.app.common.gift;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class LuckyGiftRewardStreamEvent implements Serializable {
private String eventId;
private String businessId;
private String sysOrigin;
private Long roomId;
private Long sendUserId;
private Long giftId;
private Long giftPrice;
private Long giftNum;
private Long rewardTotal;
private Long publishedAt;
private List<Result> results;
@Data
@Accessors(chain = true)
public static class Result implements Serializable {
private String orderSeed;
private Long acceptUserId;
private Long rewardNum;
}
}

View File

@ -0,0 +1,119 @@
package com.red.circle.other.app.scheduler;
import com.alibaba.fastjson.JSON;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.app.common.gift.LuckyGiftRewardSettlementService;
import com.red.circle.other.app.common.gift.LuckyGiftRewardStreamEvent;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class LuckyGiftRewardStreamTask {
private static final String CONSUMER_LOCK_KEY = "lucky_gift:reward:consumer:lock";
private static final String LAST_ID_KEY_PREFIX = "lucky_gift:reward:last-id:";
private static final String PROCESSED_KEY_PREFIX = "lucky_gift:reward:processed:";
private final RedisTemplate<String, Object> redisTemplate;
private final RedisService redisService;
private final LuckyGiftRewardSettlementService luckyGiftRewardSettlementService;
@Value("${red-circle.lucky-gift.reward.stream-key:lucky-gift:reward}")
private String streamKey;
@Value("${red-circle.lucky-gift.reward.batch-size:20}")
private Long batchSize;
@Value("${red-circle.lucky-gift.reward.read-block-millis:200}")
private Long readBlockMillis;
@Scheduled(fixedDelayString = "${red-circle.lucky-gift.reward.poll-delay-millis:1000}")
public void consume() {
if (StringUtils.isBlank(streamKey) || !redisService.lock(CONSUMER_LOCK_KEY, 30)) {
return;
}
try {
String lastId = redisService.getString(lastIdKey());
if (StringUtils.isBlank(lastId)) {
lastId = "0-0";
}
StreamReadOptions readOptions = StreamReadOptions.empty()
.count(Objects.requireNonNullElse(batchSize, 20L))
.block(Duration.ofMillis(Objects.requireNonNullElse(readBlockMillis, 200L)));
List<MapRecord<String, Object, Object>> records = redisTemplate.opsForStream().read(
readOptions,
StreamOffset.create(streamKey, ReadOffset.from(lastId))
);
if (CollectionUtils.isEmpty(records)) {
return;
}
for (MapRecord<String, Object, Object> record : records) {
processRecord(record);
redisService.setString(lastIdKey(), record.getId().getValue());
}
} catch (Exception e) {
log.error("consume lucky gift reward stream failed. streamKey={}", streamKey, e);
} finally {
redisService.unlock(CONSUMER_LOCK_KEY);
}
}
private void processRecord(MapRecord<String, Object, Object> record) {
Map<Object, Object> values = record.getValue();
String payload = getValueAsString(values, "payload");
if (StringUtils.isBlank(payload)) {
throw new IllegalStateException("lucky gift reward payload is blank, recordId="
+ record.getId().getValue());
}
LuckyGiftRewardStreamEvent event = JSON.parseObject(payload, LuckyGiftRewardStreamEvent.class);
if (event == null) {
throw new IllegalStateException("lucky gift reward payload parse failed, recordId="
+ record.getId().getValue());
}
if (StringUtils.isBlank(event.getEventId())) {
event.setEventId(getValueAsString(values, "eventId"));
}
if (StringUtils.isBlank(event.getEventId()) && StringUtils.isNotBlank(event.getBusinessId())) {
event.setEventId("LUCKY_GIFT_REWARD:" + event.getBusinessId());
}
String processedKey = PROCESSED_KEY_PREFIX + event.getEventId();
if (Objects.equals(redisService.getString(processedKey), "1")) {
return;
}
luckyGiftRewardSettlementService.settle(event);
redisService.setString(processedKey, "1", 7, TimeUnit.DAYS);
}
private String getValueAsString(Map<Object, Object> values, String key) {
Object value = values.get(key);
return value == null ? null : String.valueOf(value);
}
private String lastIdKey() {
return LAST_ID_KEY_PREFIX + streamKey;
}
}

View File

@ -0,0 +1,22 @@
package com.red.circle.other.app.service.game;
import lombok.Builder;
import lombok.Value;
/**
* 百顺运行时配置.
*/
@Value
@Builder(toBuilder = true)
public class BaishunRuntimeConfig {
String sysOrigin;
String platformBaseUrl;
String appId;
String appName;
String appChannel;
String appKey;
Integer gsp;
Integer launchCodeTtlSeconds;
Integer ssTokenTtlSeconds;
}

View File

@ -0,0 +1,97 @@
package com.red.circle.other.app.service.game;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.game.baishun.props.GameBaishunProperties;
import com.red.circle.other.infra.config.LuckyGiftApiConfig;
import com.red.circle.other.infra.config.LuckyGiftApiConfig.ApiAccount;
import com.red.circle.other.infra.database.rds.entity.game.BaishunProviderConfig;
import com.red.circle.other.infra.database.rds.service.game.BaishunProviderConfigService;
import com.red.circle.tool.core.parse.DataTypeUtils;
import com.red.circle.tool.core.text.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 百顺运行时配置解析器优先读数据库缺失时回退到旧配置
*/
@Component
@RequiredArgsConstructor
public class BaishunRuntimeConfigResolver {
private static final String DEFAULT_SYS_ORIGIN = SysOriginPlatformEnum.LIKEI.name();
private final BaishunProviderConfigService baishunProviderConfigService;
private final GameBaishunProperties gameBaishunProperties;
private final LuckyGiftApiConfig luckyGiftApiConfig;
public BaishunRuntimeConfig resolveBySysOrigin(String sysOrigin) {
String normalizedSysOrigin = normalizeSysOrigin(sysOrigin);
BaishunRuntimeConfig base = defaultConfig(normalizedSysOrigin);
BaishunProviderConfig row = baishunProviderConfigService.getBySysOrigin(normalizedSysOrigin);
if (row == null) {
return base;
}
return applyRow(base, row);
}
public BaishunRuntimeConfig resolveByApp(String appId, String appChannel) {
Long parsedAppId = DataTypeUtils.toLong(appId);
BaishunRuntimeConfig base = defaultConfig(DEFAULT_SYS_ORIGIN);
BaishunProviderConfig row = baishunProviderConfigService.getByApp(parsedAppId,
trim(appChannel));
if (row == null) {
return base;
}
return applyRow(defaultConfig(normalizeSysOrigin(row.getSysOrigin())), row);
}
public LuckyGiftApiConfig.ApiAccount resolveLuckyGiftApiAccount(String sysOrigin, Long standardId) {
ApiAccount base = luckyGiftApiConfig.getConfigByStandardId(standardId);
BaishunRuntimeConfig config = resolveBySysOrigin(sysOrigin);
ApiAccount resolved = new ApiAccount();
resolved.setStandardId(base.getStandardId());
resolved.setUrl(base.getUrl());
resolved.setAppId(defaultIfBlank(config.getAppId(), base.getAppId()));
resolved.setAppChannel(defaultIfBlank(config.getAppChannel(), base.getAppChannel()));
resolved.setAppKey(defaultIfBlank(config.getAppKey(), base.getAppKey()));
return resolved;
}
private BaishunRuntimeConfig defaultConfig(String sysOrigin) {
return BaishunRuntimeConfig.builder()
.sysOrigin(normalizeSysOrigin(sysOrigin))
.appId(trim(gameBaishunProperties.getAppId()))
.appKey(trim(gameBaishunProperties.getAppKey()))
.appChannel(trim(gameBaishunProperties.getAppChannel()))
.build();
}
private BaishunRuntimeConfig applyRow(BaishunRuntimeConfig base, BaishunProviderConfig row) {
return base.toBuilder()
.sysOrigin(normalizeSysOrigin(defaultIfBlank(row.getSysOrigin(), base.getSysOrigin())))
.platformBaseUrl(defaultIfBlank(row.getPlatformBaseUrl(), base.getPlatformBaseUrl()))
.appId(row.getAppId() != null && row.getAppId() > 0 ? String.valueOf(row.getAppId()) : base.getAppId())
.appName(defaultIfBlank(row.getAppName(), base.getAppName()))
.appChannel(defaultIfBlank(row.getAppChannel(), base.getAppChannel()))
.appKey(defaultIfBlank(row.getAppKey(), base.getAppKey()))
.gsp(row.getGsp() != null && row.getGsp() > 0 ? row.getGsp() : base.getGsp())
.launchCodeTtlSeconds(row.getLaunchCodeTtlSeconds() != null && row.getLaunchCodeTtlSeconds() > 0
? row.getLaunchCodeTtlSeconds() : base.getLaunchCodeTtlSeconds())
.ssTokenTtlSeconds(row.getSsTokenTtlSeconds() != null && row.getSsTokenTtlSeconds() > 0
? row.getSsTokenTtlSeconds() : base.getSsTokenTtlSeconds())
.build();
}
private String normalizeSysOrigin(String sysOrigin) {
return StringUtils.isBlank(sysOrigin) ? DEFAULT_SYS_ORIGIN : sysOrigin.trim();
}
private String defaultIfBlank(String value, String fallback) {
return StringUtils.isBlank(value) ? fallback : trim(value);
}
private String trim(String value) {
return value == null ? null : value.trim();
}
}

View File

@ -1,14 +1,13 @@
package com.red.circle.other.app.service.game; package com.red.circle.other.app.service.game;
import com.red.circle.auth.inner.endpoint.AuthClient; import com.red.circle.auth.inner.endpoint.AuthClient;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.game.baishun.GameBaishunService; import com.red.circle.component.game.baishun.GameBaishunService;
import com.red.circle.component.game.baishun.error.BaishunErrorCode; import com.red.circle.component.game.baishun.error.BaishunErrorCode;
import com.red.circle.component.game.baishun.props.GameBaishunProperties; import com.red.circle.component.game.baishun.request.BaishunChangeCurrencyRequest;
import com.red.circle.component.game.baishun.request.BaishunChangeCurrencyRequest; import com.red.circle.component.game.baishun.request.BaishunTokenRequest;
import com.red.circle.component.game.baishun.request.BaishunTokenRequest; import com.red.circle.component.game.baishun.request.BaishunTokenUpdateRequest;
import com.red.circle.component.game.baishun.request.BaishunTokenUpdateRequest; import com.red.circle.component.game.baishun.request.BaishunUserProfileRequest;
import com.red.circle.component.game.baishun.request.BaishunUserProfileRequest;
import com.red.circle.component.game.baishun.response.BaishunChangeCurrencyResponse; import com.red.circle.component.game.baishun.response.BaishunChangeCurrencyResponse;
import com.red.circle.component.game.baishun.response.BaishunResponse; import com.red.circle.component.game.baishun.response.BaishunResponse;
import com.red.circle.component.game.baishun.response.BaishunTokenResponse; import com.red.circle.component.game.baishun.response.BaishunTokenResponse;
@ -76,12 +75,12 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class GameBaishunServiceImpl implements GameBaishunService { public class GameBaishunServiceImpl implements GameBaishunService {
private final AuthClient authClient; private final AuthClient authClient;
private final RedisService redisService; private final RedisService redisService;
private final WalletGoldClient walletGoldClient; private final WalletGoldClient walletGoldClient;
private final UserProfileGateway userProfileGateway; private final UserProfileGateway userProfileGateway;
private final GameBaishunProperties gameBaishunProperties; private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver;
private final TaskMqMessage taskMqMessage; private final TaskMqMessage taskMqMessage;
private final UserProfileAppConvertor userProfileAppConvertor; private final UserProfileAppConvertor userProfileAppConvertor;
private final EnumConfigCacheService enumConfigCacheService; private final EnumConfigCacheService enumConfigCacheService;
private final GameListConfigService gameListConfigService; private final GameListConfigService gameListConfigService;
@ -96,12 +95,12 @@ public class GameBaishunServiceImpl implements GameBaishunService {
@Value("${red-circle.game-rank.gameid}") @Value("${red-circle.game-rank.gameid}")
private String gameId; private String gameId;
@Override @Override
public BaishunResponse<BaishunTokenResponse> getToken(BaishunTokenRequest request) { public BaishunResponse<BaishunTokenResponse> getToken(BaishunTokenRequest request) {
log.info("getToken: {}", request); log.info("getToken: {}", request);
if (!request.checkSignature(gameBaishunProperties.getAppKey())) { if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
log.info("getToken: {}", "验证签名失败"); log.info("getToken: {}", "验证签名失败");
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
/*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
}*/ }*/
@ -110,12 +109,12 @@ public class GameBaishunServiceImpl implements GameBaishunService {
return getToken(request.getUserId()); return getToken(request.getUserId());
} }
@Override @Override
public BaishunResponse<BaishunUserProfileResponse> getUserProfile(BaishunUserProfileRequest request) { public BaishunResponse<BaishunUserProfileResponse> getUserProfile(BaishunUserProfileRequest request) {
log.info("getUserProfile: {}", request); log.info("getUserProfile: {}", request);
if (!request.checkSignature(gameBaishunProperties.getAppKey())) { if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
/*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
}*/ }*/
log.info("getToken: {}", "验证签名失败"); log.info("getToken: {}", "验证签名失败");
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
@ -140,12 +139,12 @@ public class GameBaishunServiceImpl implements GameBaishunService {
); );
} }
@Override @Override
public BaishunResponse<BaishunTokenResponse> updateToken(BaishunTokenUpdateRequest request) { public BaishunResponse<BaishunTokenResponse> updateToken(BaishunTokenUpdateRequest request) {
log.info("updateToken: {}", request); log.info("updateToken: {}", request);
if (!request.checkSignature(gameBaishunProperties.getAppKey())) { if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
log.info("getToken: {}", "验证签名失败"); log.info("getToken: {}", "验证签名失败");
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
/*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
}*/ }*/
@ -158,12 +157,12 @@ public class GameBaishunServiceImpl implements GameBaishunService {
return getToken(request.getUserId()); return getToken(request.getUserId());
} }
@Override @Override
public BaishunResponse<BaishunChangeCurrencyResponse> changeBalance(BaishunChangeCurrencyRequest request) { public BaishunResponse<BaishunChangeCurrencyResponse> changeBalance(BaishunChangeCurrencyRequest request) {
log.info("changeBalance: {}", request); log.info("changeBalance: {}", request);
if (!request.checkSignature(gameBaishunProperties.getAppKey())) { if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
log.info("getToken: {}", "验证签名失败"); log.info("getToken: {}", "验证签名失败");
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
/*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
}*/ }*/
@ -248,8 +247,12 @@ public class GameBaishunServiceImpl implements GameBaishunService {
return BaishunResponse.fail(BaishunErrorCode.SERVER_ERROR); return BaishunResponse.fail(BaishunErrorCode.SERVER_ERROR);
} }
private BaishunRuntimeConfig resolveRuntimeConfig(String appId) {
return baishunRuntimeConfigResolver.resolveByApp(appId, null);
}
private void updateRoomDailyTask(Long coins, Long userId) { private void updateRoomDailyTask(Long coins, Long userId) {
RoomDailyTaskCode taskType = RoomDailyTaskCode.PERSONAL_GAME_CONSUME; RoomDailyTaskCode taskType = RoomDailyTaskCode.PERSONAL_GAME_CONSUME;

View File

@ -0,0 +1,85 @@
package com.red.circle.other.app.command.team;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.red.circle.framework.core.dto.ReqSysOrigin;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.other.app.dto.cmd.team.DbInviteMessageProcessCmd;
import com.red.circle.other.infra.database.cache.service.team.BdTeamCacheService;
import com.red.circle.other.infra.database.mongo.entity.team.bd.BdInviteAgentMessage;
import com.red.circle.other.infra.database.mongo.entity.team.bd.MessageInviteGroup;
import com.red.circle.other.infra.database.mongo.service.team.bd.BdInviteAgentMessageService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamApplicationProcessApprovalService;
import com.red.circle.other.infra.database.rds.entity.team.RoomBdLead;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorService;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService;
import com.red.circle.other.infra.database.rds.service.team.RoomBdLeadService;
import com.red.circle.other.infra.database.rds.service.team.UserHistoryIdentityService;
import org.junit.jupiter.api.Test;
class BdLeaderInviteMessageProcessExeTest {
@Test
void executeShouldClearLeaderAndBdCachesAfterAcceptingInvite() {
RoomBdLeadService roomBdLeadService = mock(RoomBdLeadService.class);
UserHistoryIdentityService userHistoryIdentityService = mock(UserHistoryIdentityService.class);
BdInviteAgentMessageService bdInviteAgentMessageService = mock(BdInviteAgentMessageService.class);
BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService =
mock(BusinessDevelopmentBaseInfoService.class);
TeamApplicationProcessApprovalService teamApplicationProcessApprovalService =
mock(TeamApplicationProcessApprovalService.class);
UserMqMessageService userMqMessageService = mock(UserMqMessageService.class);
AdministratorService administratorService = mock(AdministratorService.class);
TaskMqMessage taskMqMessage = mock(TaskMqMessage.class);
BdTeamCacheService bdTeamCacheService = mock(BdTeamCacheService.class);
BdLeaderInviteMessageProcessExe exe = new BdLeaderInviteMessageProcessExe(
roomBdLeadService,
userHistoryIdentityService,
bdInviteAgentMessageService,
businessDevelopmentBaseInfoService,
teamApplicationProcessApprovalService,
userMqMessageService,
administratorService,
taskMqMessage,
bdTeamCacheService
);
DbInviteMessageProcessCmd cmd = new DbInviteMessageProcessCmd();
cmd.setId("msg-1");
cmd.setStatus(1);
cmd.setContact("tg");
cmd.setReqUserId(300L);
cmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI", "LIKEI"));
BdInviteAgentMessage message = new BdInviteAgentMessage()
.setId("msg-1")
.setGroup(MessageInviteGroup.BD_LEADER_INVITE_BD.name())
.setUserId(200L)
.setInviteUserId(300L)
.setStatus(0);
when(bdInviteAgentMessageService.getById("msg-1")).thenReturn(message);
when(roomBdLeadService.checkBdLeader(300L)).thenReturn(false);
when(businessDevelopmentBaseInfoService.checkBD(300L)).thenReturn(false);
when(roomBdLeadService.getByUserId(200L)).thenReturn(new RoomBdLead().setRegionId("SA"));
when(bdInviteAgentMessageService.updateStatus(
"msg-1",
0,
1,
MessageInviteGroup.BD_LEADER_INVITE_BD.name())).thenReturn(true);
when(administratorService.existsAdmin(200L)).thenReturn(false);
exe.execute(cmd);
verify(businessDevelopmentBaseInfoService).add(any());
verify(bdTeamCacheService).clearRelationChangeCaches(200L, 200L, 300L);
verify(bdTeamCacheService, never()).clearLeaderRelatedCaches(eq(300L));
}
}

View File

@ -0,0 +1,143 @@
package com.red.circle.other.app.common.gift;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.live.LiveHeartbeatCache;
import com.red.circle.other.infra.database.cache.service.other.LiveHeartbeatCacheService;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomContributionCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomContributionRankCountService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.service.live.RoomContributionBalanceService;
import java.math.BigDecimal;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
class LuckyGiftRewardSettlementServiceTest {
private static final long ROOM_ID = 2001L;
private static final long ROOM_OWNER_ID = 3001L;
private static final long SEND_USER_ID = 4001L;
private static final long ACCEPT_USER_ONE = 5001L;
private static final long ACCEPT_USER_TWO = 5002L;
private LiveHeartbeatCacheService liveHeartbeatCacheService;
private RoomProfileManagerService roomProfileManagerService;
private UserRegionGateway userRegionGateway;
private RoomContributionRankCountService roomContributionRankCountService;
private RoomContributionBalanceService roomContributionBalanceService;
private RoomContributionCountService roomContributionCountService;
private RoomContributionActivityCountService roomContributionActivityCountService;
private LuckyGiftRewardSettlementService service;
@BeforeEach
void setUp() {
liveHeartbeatCacheService = mock(LiveHeartbeatCacheService.class);
roomProfileManagerService = mock(RoomProfileManagerService.class);
userRegionGateway = mock(UserRegionGateway.class);
roomContributionRankCountService = mock(RoomContributionRankCountService.class);
roomContributionBalanceService = mock(RoomContributionBalanceService.class);
roomContributionCountService = mock(RoomContributionCountService.class);
roomContributionActivityCountService = mock(RoomContributionActivityCountService.class);
service = new LuckyGiftRewardSettlementService(
userRegionGateway,
liveHeartbeatCacheService,
roomProfileManagerService,
roomContributionRankCountService,
roomContributionBalanceService,
roomContributionCountService,
roomContributionActivityCountService
);
}
@Test
void settle_shouldApplyHeartbeatAndRoomContributionWithTenPercentReward() {
when(liveHeartbeatCacheService.listLiveHeartbeat(ROOM_ID)).thenReturn(List.of(
new LiveHeartbeatCache().setId(ACCEPT_USER_ONE).setHeartbeatVal(5L)
));
RoomProfile roomProfile = new RoomProfile();
roomProfile.setId(ROOM_ID);
roomProfile.setUserId(ROOM_OWNER_ID);
roomProfile.setSysOrigin("LIKEI");
when(roomProfileManagerService.getProfileById(ROOM_ID)).thenReturn(roomProfile);
when(userRegionGateway.getRegionCode(ROOM_OWNER_ID)).thenReturn("SA");
when(userRegionGateway.getRegionCode(SEND_USER_ID)).thenReturn("SA");
service.settle(buildEvent());
ArgumentCaptor<List<LiveHeartbeatCache>> heartbeatCaptor = ArgumentCaptor.forClass(List.class);
verify(liveHeartbeatCacheService).create(eq(ROOM_ID), heartbeatCaptor.capture());
List<LiveHeartbeatCache> caches = heartbeatCaptor.getValue();
assertEquals(2, caches.size());
assertEquals(13L, findHeartbeat(caches, ACCEPT_USER_ONE));
assertEquals(2L, findHeartbeat(caches, ACCEPT_USER_TWO));
BigDecimal expectedContribution = BigDecimal.TEN;
verify(roomContributionRankCountService).incr("LIKEI", ROOM_ID, SEND_USER_ID,
expectedContribution);
verify(roomContributionBalanceService).incrTotalQuantity(ROOM_ID, ROOM_OWNER_ID,
expectedContribution);
verify(roomContributionCountService).incrQuantity("LIKEI", ROOM_ID, ROOM_OWNER_ID,
expectedContribution);
verify(roomContributionActivityCountService).incrQuantity(ROOM_ID, expectedContribution);
}
@Test
void settle_shouldSkipRoomContributionWhenSenderRegionDiffers() {
when(liveHeartbeatCacheService.listLiveHeartbeat(ROOM_ID)).thenReturn(List.of());
RoomProfile roomProfile = new RoomProfile();
roomProfile.setId(ROOM_ID);
roomProfile.setUserId(ROOM_OWNER_ID);
roomProfile.setSysOrigin("LIKEI");
when(roomProfileManagerService.getProfileById(ROOM_ID)).thenReturn(roomProfile);
when(userRegionGateway.getRegionCode(ROOM_OWNER_ID)).thenReturn("SA");
when(userRegionGateway.getRegionCode(SEND_USER_ID)).thenReturn("PK");
service.settle(buildEvent());
verify(liveHeartbeatCacheService).create(eq(ROOM_ID), any());
verify(roomContributionRankCountService, never()).incr(any(), any(), any(), any());
verify(roomContributionBalanceService, never()).incrTotalQuantity(any(), any(), any());
verify(roomContributionCountService, never()).incrQuantity(any(), any(), any(), any());
verify(roomContributionActivityCountService, never()).incrQuantity(any(), any());
}
private LuckyGiftRewardStreamEvent buildEvent() {
return new LuckyGiftRewardStreamEvent()
.setEventId("LUCKY_GIFT_REWARD:biz-1")
.setBusinessId("biz-1")
.setSysOrigin("LIKEI")
.setRoomId(ROOM_ID)
.setSendUserId(SEND_USER_ID)
.setResults(List.of(
new LuckyGiftRewardStreamEvent.Result()
.setOrderSeed("order-1")
.setAcceptUserId(ACCEPT_USER_ONE)
.setRewardNum(80L),
new LuckyGiftRewardStreamEvent.Result()
.setOrderSeed("order-2")
.setAcceptUserId(ACCEPT_USER_TWO)
.setRewardNum(25L)
));
}
private long findHeartbeat(List<LiveHeartbeatCache> caches, long userId) {
return caches.stream()
.filter(cache -> cache != null && cache.getId() != null && cache.getId() == userId)
.findFirst()
.map(LiveHeartbeatCache::getHeartbeatVal)
.orElse(0L);
}
}

View File

@ -0,0 +1,87 @@
package com.red.circle.other.app.scheduler;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.app.common.gift.LuckyGiftRewardSettlementService;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StreamOperations;
class LuckyGiftRewardStreamTaskTest {
private RedisTemplate<String, Object> redisTemplate;
private RedisService redisService;
private LuckyGiftRewardSettlementService settlementService;
private LuckyGiftRewardStreamTask task;
@BeforeEach
void setUp() throws Exception {
redisTemplate = mock(RedisTemplate.class);
redisService = mock(RedisService.class);
settlementService = mock(LuckyGiftRewardSettlementService.class);
task = new LuckyGiftRewardStreamTask(redisTemplate, redisService, settlementService);
setField(task, "streamKey", "lucky-gift:reward");
setField(task, "batchSize", 20L);
setField(task, "readBlockMillis", 50L);
}
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
void consume_shouldSettleEventAndAdvanceStreamOffset() throws Exception {
StreamOperations streamOperations = mock(StreamOperations.class);
when(redisTemplate.opsForStream()).thenReturn(streamOperations);
when(redisService.lock("lucky_gift:reward:consumer:lock", 30)).thenReturn(true);
when(redisService.getString("lucky_gift:reward:last-id:lucky-gift:reward")).thenReturn(null);
when(redisService.getString("lucky_gift:reward:processed:LUCKY_GIFT_REWARD:biz-1"))
.thenReturn(null);
MapRecord<String, String, String> sourceRecord = StreamRecords.newRecord()
.in("lucky-gift:reward")
.withId(org.springframework.data.redis.connection.stream.RecordId.of("1745244000000-0"))
.ofMap(Map.of(
"eventId", "LUCKY_GIFT_REWARD:biz-1",
"payload", "{\"eventId\":\"LUCKY_GIFT_REWARD:biz-1\",\"businessId\":\"biz-1\","
+ "\"sysOrigin\":\"LIKEI\",\"roomId\":1001,\"sendUserId\":2001,"
+ "\"results\":[{\"orderSeed\":\"order-1\",\"acceptUserId\":3001,\"rewardNum\":80}]}"
));
MapRecord<String, Object, Object> record =
(MapRecord<String, Object, Object>) (MapRecord<?, ?, ?>) sourceRecord;
when(streamOperations.read(any(StreamReadOptions.class), any(StreamOffset.class)))
.thenReturn(List.of(record));
task.consume();
verify(settlementService).settle(any());
verify(redisService).setString(
"lucky_gift:reward:processed:LUCKY_GIFT_REWARD:biz-1",
"1",
7,
TimeUnit.DAYS
);
verify(redisService).setString(
"lucky_gift:reward:last-id:lucky-gift:reward",
"1745244000000-0"
);
verify(redisService).unlock(eq("lucky_gift:reward:consumer:lock"));
}
private void setField(Object target, String name, Object value) throws Exception {
Field field = target.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(target, value);
}
}

View File

@ -0,0 +1,22 @@
package com.red.circle.other.infra.database.cache.service.team;
/**
* BD 团队相关缓存服务.
*/
public interface BdTeamCacheService {
/**
* 清理 BD Leader 维度缓存.
*/
void clearLeaderRelatedCaches(Long leaderUserId);
/**
* 清理 BD 维度缓存.
*/
void clearBdRelatedCaches(Long bdUserId);
/**
* 清理 BD BD Leader 关系变更产生的缓存.
*/
void clearRelationChangeCaches(Long oldLeaderUserId, Long newLeaderUserId, Long bdUserId);
}

View File

@ -0,0 +1,65 @@
package com.red.circle.other.infra.database.cache.service.team.impl;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.infra.database.cache.service.team.BdTeamCacheService;
import java.util.Objects;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* BD 团队相关缓存服务实现.
*/
@Service
@RequiredArgsConstructor
public class BdTeamCacheServiceImpl implements BdTeamCacheService {
private static final String BD_CENTER_LEADER_KEY = "bdcenter:BDLEADER:%d";
private static final String BD_CENTER_BD_KEY = "bdcenter:BD:%d";
private static final String BD_TEAM_LEADER_KEY = "bdteam:BD_LEADER:%d";
private static final String BD_TEAM_BD_KEY = "bdteam:BD:%d";
private static final String BD_TEAM_SUMMARY_KEY = "bdteam:summary:%d";
private final RedisService redisService;
@Override
public void clearLeaderRelatedCaches(Long leaderUserId) {
if (Objects.isNull(leaderUserId)) {
return;
}
delete(
BD_CENTER_LEADER_KEY.formatted(leaderUserId),
BD_TEAM_LEADER_KEY.formatted(leaderUserId),
BD_TEAM_SUMMARY_KEY.formatted(leaderUserId)
);
}
@Override
public void clearBdRelatedCaches(Long bdUserId) {
if (Objects.isNull(bdUserId)) {
return;
}
delete(
BD_CENTER_BD_KEY.formatted(bdUserId),
BD_TEAM_BD_KEY.formatted(bdUserId),
BD_TEAM_SUMMARY_KEY.formatted(bdUserId)
);
}
@Override
public void clearRelationChangeCaches(Long oldLeaderUserId, Long newLeaderUserId, Long bdUserId) {
Stream.of(oldLeaderUserId, newLeaderUserId)
.filter(Objects::nonNull)
.distinct()
.forEach(this::clearLeaderRelatedCaches);
clearBdRelatedCaches(bdUserId);
}
private void delete(String... keys) {
for (String key : keys) {
redisService.delete(key);
}
}
}

View File

@ -0,0 +1,11 @@
package com.red.circle.other.infra.database.rds.dao.game;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.game.BaishunProviderConfig;
/**
* 百顺三方配置 DAO.
*/
public interface BaishunProviderConfigDAO extends BaseDAO<BaishunProviderConfig> {
}

View File

@ -0,0 +1,54 @@
package com.red.circle.other.infra.database.rds.entity.game;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.red.circle.framework.mybatis.entity.TimestampBaseEntity;
import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 百顺三方接入配置.
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("baishun_provider_config")
public class BaishunProviderConfig extends TimestampBaseEntity {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@TableField("sys_origin")
private String sysOrigin;
@TableField("platform_base_url")
private String platformBaseUrl;
@TableField("app_id")
private Long appId;
@TableField("app_name")
private String appName;
@TableField("app_channel")
private String appChannel;
@TableField("app_key")
private String appKey;
@TableField("gsp")
private Integer gsp;
@TableField("launch_code_ttl_seconds")
private Integer launchCodeTtlSeconds;
@TableField("ss_token_ttl_seconds")
private Integer ssTokenTtlSeconds;
}

View File

@ -0,0 +1,16 @@
package com.red.circle.other.infra.database.rds.service.game;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.other.infra.database.rds.entity.game.BaishunProviderConfig;
/**
* 百顺三方配置服务.
*/
public interface BaishunProviderConfigService extends BaseService<BaishunProviderConfig> {
BaishunProviderConfig getBySysOrigin(String sysOrigin);
BaishunProviderConfig getByAppId(Long appId);
BaishunProviderConfig getByApp(Long appId, String appChannel);
}

View File

@ -0,0 +1,57 @@
package com.red.circle.other.infra.database.rds.service.game.impl;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.dao.game.BaishunProviderConfigDAO;
import com.red.circle.other.infra.database.rds.entity.game.BaishunProviderConfig;
import com.red.circle.other.infra.database.rds.service.game.BaishunProviderConfigService;
import com.red.circle.tool.core.text.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 百顺三方配置服务实现.
*/
@Service
@RequiredArgsConstructor
public class BaishunProviderConfigServiceImpl
extends BaseServiceImpl<BaishunProviderConfigDAO, BaishunProviderConfig>
implements BaishunProviderConfigService {
@Override
public BaishunProviderConfig getBySysOrigin(String sysOrigin) {
if (StringUtils.isBlank(sysOrigin)) {
return null;
}
return query()
.eq(BaishunProviderConfig::getSysOrigin, sysOrigin)
.last(PageConstant.LIMIT_ONE)
.getOne();
}
@Override
public BaishunProviderConfig getByAppId(Long appId) {
if (appId == null || appId <= 0) {
return null;
}
return query()
.eq(BaishunProviderConfig::getAppId, appId)
.last(PageConstant.LIMIT_ONE)
.getOne();
}
@Override
public BaishunProviderConfig getByApp(Long appId, String appChannel) {
if (appId == null || appId <= 0) {
return null;
}
if (StringUtils.isBlank(appChannel)) {
return getByAppId(appId);
}
return query()
.eq(BaishunProviderConfig::getAppId, appId)
.eq(BaishunProviderConfig::getAppChannel, appChannel)
.last(PageConstant.LIMIT_ONE)
.getOne();
}
}

View File

@ -5,7 +5,6 @@ import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.mybatis.constant.PageConstant; import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl; import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.inner.asserts.ImErrorCode;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils; import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.infra.convertor.user.UserProfileInfraConvertor; import com.red.circle.other.infra.convertor.user.UserProfileInfraConvertor;
@ -68,13 +67,12 @@ public class UserBeautifulNumberServiceImpl extends
.execute(); .execute();
} }
@Override @Override
public void addBeautifulNumber(UserBeautifulNumberCmd dto) { public void addBeautifulNumber(UserBeautifulNumberCmd dto) {
ResponseAssert.failure(ImErrorCode.USER_ALREADY_EXISTS); ResponseAssert.isNull(CommonErrorCode.INFORMATION_EXISTS,
ResponseAssert.isNull(CommonErrorCode.INFORMATION_EXISTS, query().eq(UserBeautifulNumber::getUserAccount, dto.getUserAccount()).getOne());
query().eq(UserBeautifulNumber::getUserAccount, dto.getUserAccount()).getOne()); UserBeautifulNumber userBeautifulNumber = userProfileInfraConvertor.toUserBeautifulNumberCmd(
UserBeautifulNumber userBeautifulNumber = userProfileInfraConvertor.toUserBeautifulNumberCmd( dto);
dto);
if (CollectionUtils.isNotEmpty(dto.getTabs())) { if (CollectionUtils.isNotEmpty(dto.getTabs())) {
userBeautifulNumber.setTabName(dto.getTabs().get(0).getTabName()); userBeautifulNumber.setTabName(dto.getTabs().get(0).getTabName());
userBeautifulNumber.setTypeName(dto.getTabs().get(0).getTypeName()); userBeautifulNumber.setTypeName(dto.getTabs().get(0).getTypeName());

View File

@ -7,17 +7,23 @@
<description>内部通讯服务端点</description> <description>内部通讯服务端点</description>
<packaging>jar</packaging> <packaging>jar</packaging>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.red.circle</groupId> <groupId>com.red.circle</groupId>
<artifactId>other-application</artifactId> <artifactId>other-application</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.red.circle</groupId> <groupId>com.red.circle</groupId>
<artifactId>other-inner-api</artifactId> <artifactId>other-inner-api</artifactId>
</dependency> </dependency>
</dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<parent> <parent>
<artifactId>rc-service-other</artifactId> <artifactId>rc-service-other</artifactId>

View File

@ -1,11 +1,12 @@
package com.red.circle.other.app.inner.service.team.impl; package com.red.circle.other.app.inner.service.team.impl;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentBaseInfo; import com.red.circle.other.infra.database.cache.service.team.BdTeamCacheService;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService; import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentBaseInfo;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService;
import com.red.circle.other.app.inner.convertor.team.BdInnerConvertor; import com.red.circle.other.app.inner.convertor.team.BdInnerConvertor;
import com.red.circle.other.app.inner.service.team.BdTeamInfoClientService; import com.red.circle.other.app.inner.service.team.BdTeamInfoClientService;
import com.red.circle.other.inner.model.cmd.team.BdTeamInfoCmd; import com.red.circle.other.inner.model.cmd.team.BdTeamInfoCmd;
@ -31,9 +32,10 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService { public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService {
private final BdInnerConvertor bdAppConvertor; private final BdInnerConvertor bdAppConvertor;
private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService;
private final UserProfileGateway userProfileGateway; private final UserProfileGateway userProfileGateway;
private final BdTeamCacheService bdTeamCacheService;
@Override @Override
public Boolean check(Long userId) { public Boolean check(Long userId) {
@ -50,10 +52,15 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService {
return bdAppConvertor.toBdTeamInfoDTO(businessDevelopmentBaseInfoService.getById(id)); return bdAppConvertor.toBdTeamInfoDTO(businessDevelopmentBaseInfoService.getById(id));
} }
@Override @Override
public void addBdInfo(BdTeamInfoCmd cmd) { public void addBdInfo(BdTeamInfoCmd cmd) {
businessDevelopmentBaseInfoService.save(bdAppConvertor.toRoomBusinessDevelopmentBaseInfo(cmd)); BusinessDevelopmentBaseInfo bdInfo = bdAppConvertor.toRoomBusinessDevelopmentBaseInfo(cmd);
} businessDevelopmentBaseInfoService.save(bdInfo);
bdTeamCacheService.clearRelationChangeCaches(
bdInfo.getBdLeadUserId(),
bdInfo.getBdLeadUserId(),
bdInfo.getUserId());
}
@Override @Override
public void incrMemberCount(Set<Long> userIds) { public void incrMemberCount(Set<Long> userIds) {
@ -65,12 +72,22 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService {
return businessDevelopmentBaseInfoService.mapCheckBd(userIds); return businessDevelopmentBaseInfoService.mapCheckBd(userIds);
} }
@Override @Override
public void updateSelectiveById(BdTeamInfoDTO dto) { public void updateSelectiveById(BdTeamInfoDTO dto) {
businessDevelopmentBaseInfoService.updateSelectiveById( BusinessDevelopmentBaseInfo oldBdInfo = businessDevelopmentBaseInfoService.getById(dto.getId());
bdAppConvertor.toBusinessDevelopmentBaseInfo(dto) businessDevelopmentBaseInfoService.updateSelectiveById(
); bdAppConvertor.toBusinessDevelopmentBaseInfo(dto)
} );
if (Objects.nonNull(oldBdInfo)) {
Long newLeaderUserId = Objects.nonNull(dto.getBdLeadUserId())
? dto.getBdLeadUserId()
: oldBdInfo.getBdLeadUserId();
bdTeamCacheService.clearRelationChangeCaches(
oldBdInfo.getBdLeadUserId(),
newLeaderUserId,
oldBdInfo.getUserId());
}
}
@Override @Override
public void updateOpsById(BdTeamUpdateOpsCmd cmd) { public void updateOpsById(BdTeamUpdateOpsCmd cmd) {
@ -133,10 +150,17 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService {
qryCmd.getSysOrigin()).list()); qryCmd.getSysOrigin()).list());
} }
@Override @Override
public void deleteById(Long id) { public void deleteById(Long id) {
businessDevelopmentBaseInfoService.removeById(id); BusinessDevelopmentBaseInfo bdInfo = businessDevelopmentBaseInfoService.getById(id);
} businessDevelopmentBaseInfoService.removeById(id);
if (Objects.nonNull(bdInfo)) {
bdTeamCacheService.clearRelationChangeCaches(
bdInfo.getBdLeadUserId(),
null,
bdInfo.getUserId());
}
}
@Override @Override
public void changeBdLeader(ChangeBdLeaderCmd cmd) { public void changeBdLeader(ChangeBdLeaderCmd cmd) {
@ -153,12 +177,17 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService {
} }
// 修改BD Leader // 修改BD Leader
businessDevelopmentBaseInfoService.update() businessDevelopmentBaseInfoService.update()
.set(BusinessDevelopmentBaseInfo::getBdLeadUserId, userProfile.getId()) .set(BusinessDevelopmentBaseInfo::getBdLeadUserId, userProfile.getId())
.set(BusinessDevelopmentBaseInfo::getUpdateUser, cmd.getUpdateUser()) .set(BusinessDevelopmentBaseInfo::getUpdateUser, cmd.getUpdateUser())
.eq(BusinessDevelopmentBaseInfo::getId, cmd.getBdId()) .eq(BusinessDevelopmentBaseInfo::getId, cmd.getBdId())
.execute(); .execute();
}
bdTeamCacheService.clearRelationChangeCaches(
bdInfo.getBdLeadUserId(),
userProfile.getId(),
bdInfo.getUserId());
}
} }

View File

@ -1,7 +1,8 @@
package com.red.circle.other.app.inner.service.team.impl; package com.red.circle.other.app.inner.service.team.impl;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.infra.database.rds.entity.team.RoomBdLead; import com.red.circle.other.infra.database.cache.service.team.BdTeamCacheService;
import com.red.circle.other.infra.database.rds.entity.team.RoomBdLead;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService; import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService; import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService;
import com.red.circle.other.infra.database.rds.service.team.RoomBdLeadService; import com.red.circle.other.infra.database.rds.service.team.RoomBdLeadService;
@ -28,9 +29,10 @@ import org.springframework.stereotype.Service;
public class BdTeamLeaderClientServiceImpl implements BdTeamLeaderClientService { public class BdTeamLeaderClientServiceImpl implements BdTeamLeaderClientService {
private final BdInnerConvertor bdInnerConvertor; private final BdInnerConvertor bdInnerConvertor;
private final RoomBdLeadService roomBdLeadService; private final RoomBdLeadService roomBdLeadService;
private final BusinessDevelopmentTeamService businessDevelopmentTeamService; private final BusinessDevelopmentTeamService businessDevelopmentTeamService;
private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService;
private final BdTeamCacheService bdTeamCacheService;
@Override @Override
public BdTeamLeaderDTO getById(Long id) { public BdTeamLeaderDTO getById(Long id) {
@ -91,11 +93,14 @@ public class BdTeamLeaderClientServiceImpl implements BdTeamLeaderClientService
return businessDevelopmentTeamService.mapTeamBdUserId(ids); return businessDevelopmentTeamService.mapTeamBdUserId(ids);
} }
@Override @Override
public void removeLeader(Long teamId, Long bdLeadUserId) { public void removeLeader(Long teamId, Long bdLeadUserId) {
roomBdLeadService.deleteById(teamId); List<Long> bdUserIds = businessDevelopmentBaseInfoService.listBdUserIdsByLeaderId(bdLeadUserId);
businessDevelopmentBaseInfoService.removeBdLeader(bdLeadUserId); roomBdLeadService.deleteById(teamId);
} businessDevelopmentBaseInfoService.removeBdLeader(bdLeadUserId);
bdTeamCacheService.clearLeaderRelatedCaches(bdLeadUserId);
bdUserIds.forEach(bdTeamCacheService::clearBdRelatedCaches);
}
} }

View File

@ -0,0 +1,88 @@
package com.red.circle.other.app.inner.service.team.impl;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.RETURNS_SELF;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.red.circle.framework.mybatis.mybatisplus.LambdaUpdateWrapperChain;
import com.red.circle.other.app.inner.convertor.team.BdInnerConvertor;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.cache.service.team.BdTeamCacheService;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentBaseInfo;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService;
import com.red.circle.other.inner.model.cmd.team.bd.ChangeBdLeaderCmd;
import org.junit.jupiter.api.Test;
class BdTeamInfoClientServiceImplTest {
@Test
@SuppressWarnings("unchecked")
void changeBdLeaderShouldClearOldAndNewLeaderCaches() {
BdInnerConvertor convertor = mock(BdInnerConvertor.class);
BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService =
mock(BusinessDevelopmentBaseInfoService.class);
UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
BdTeamCacheService bdTeamCacheService = mock(BdTeamCacheService.class);
LambdaUpdateWrapperChain<BusinessDevelopmentBaseInfo> updateChain =
mock(LambdaUpdateWrapperChain.class, RETURNS_SELF);
BdTeamInfoClientServiceImpl service = new BdTeamInfoClientServiceImpl(
convertor,
businessDevelopmentBaseInfoService,
userProfileGateway,
bdTeamCacheService
);
ChangeBdLeaderCmd cmd = new ChangeBdLeaderCmd();
cmd.setBdId(1L);
cmd.setNewAccount(888L);
cmd.setUpdateUser(999L);
BusinessDevelopmentBaseInfo oldBdInfo = new BusinessDevelopmentBaseInfo()
.setId(1L)
.setUserId(300L)
.setBdLeadUserId(200L);
UserProfile newLeader = mock(UserProfile.class);
when(businessDevelopmentBaseInfoService.getById(1L)).thenReturn(oldBdInfo);
when(businessDevelopmentBaseInfoService.update()).thenReturn(updateChain);
when(userProfileGateway.getByAccount("LIKEI", "888")).thenReturn(newLeader);
when(newLeader.getId()).thenReturn(400L);
doReturn(updateChain).when(updateChain).eq(any(), any());
when(updateChain.execute()).thenReturn(true);
service.changeBdLeader(cmd);
verify(updateChain).execute();
verify(bdTeamCacheService).clearRelationChangeCaches(200L, 400L, 300L);
}
@Test
void changeBdLeaderShouldThrowWhenBdNotFound() {
BdInnerConvertor convertor = mock(BdInnerConvertor.class);
BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService =
mock(BusinessDevelopmentBaseInfoService.class);
UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
BdTeamCacheService bdTeamCacheService = mock(BdTeamCacheService.class);
BdTeamInfoClientServiceImpl service = new BdTeamInfoClientServiceImpl(
convertor,
businessDevelopmentBaseInfoService,
userProfileGateway,
bdTeamCacheService
);
ChangeBdLeaderCmd cmd = new ChangeBdLeaderCmd();
cmd.setBdId(1L);
cmd.setNewAccount(888L);
when(businessDevelopmentBaseInfoService.getById(1L)).thenReturn(null);
assertThrows(RuntimeException.class, () -> service.changeBdLeader(cmd));
}
}