diff --git a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBaseInfoRestController.java b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBaseInfoRestController.java index 45b70f48..7a96ed25 100644 --- a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBaseInfoRestController.java +++ b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBaseInfoRestController.java @@ -62,10 +62,19 @@ public class UserBaseInfoRestController extends BaseController { /** * 获取用户详情. */ - @GetMapping("/account/{account}") - public List getAccount(@PathVariable String account) { - return userBaseInfoService.getByAccount(account); - } + @GetMapping("/account/{account}") + public List getAccount(@PathVariable String 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用户信息") @PutMapping diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoServiceImpl.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoServiceImpl.java index 5076a7c1..4a111a2e 100644 --- a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoServiceImpl.java +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoServiceImpl.java @@ -190,9 +190,14 @@ public class UserBaseInfoServiceImpl implements } @Override - public List getByAccount(String account) { - return ResponseAssert.requiredSuccess(userProfileClient.getByAccount(account)); - } + public List getByAccount(String account) { + return ResponseAssert.requiredSuccess(userProfileClient.getByAccount(account)); + } + + @Override + public UserProfileDTO getByAccount(String sysOrigin, String account) { + return ResponseAssert.requiredSuccess(userProfileClient.getByAccount(sysOrigin, account)); + } @Override public void updateUserProfile(UserProfileUpdateCmd param) { diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBeautifulNumberServiceImpl.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBeautifulNumberServiceImpl.java index 6e6bfb6b..6de11bb5 100644 --- a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBeautifulNumberServiceImpl.java +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBeautifulNumberServiceImpl.java @@ -35,10 +35,10 @@ public class UserBeautifulNumberServiceImpl implements ResponseAssert.requiredSuccess(userBeautifulNumberApplyClient.updateBeautifulNumber(cmd)); } - @Override - public void addBeautifulNumber(UserBeautifulNumberCmd cmd) { - ResponseAssert.requiredSuccess(userBeautifulNumberApplyClient.updateBeautifulNumber(cmd)); - } + @Override + public void addBeautifulNumber(UserBeautifulNumberCmd cmd) { + ResponseAssert.requiredSuccess(userBeautifulNumberApplyClient.addBeautifulNumber(cmd)); + } @Override public void deleteBeautifulNumber(Long id) { diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserSpecialIdManagerServiceImpl.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserSpecialIdManagerServiceImpl.java index 14dfaba8..906b2e37 100644 --- a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserSpecialIdManagerServiceImpl.java +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserSpecialIdManagerServiceImpl.java @@ -51,15 +51,20 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ private final UserService userService; private final UserAppConvertor userAppConvertor; private final UserProfileClient userProfileClient; - private final UserSpecialIdClient userSpecialIdClient; - private final SysUserSpecialIdLogClient userSpecialIdLogClient; + private final UserSpecialIdClient userSpecialIdClient; + private final SysUserSpecialIdLogClient userSpecialIdLogClient; - @Override - public void addOrUpdate(UserSpecialIdCmd param) { - ResponseAssert.isTrue(UserErrorCode.USER_INFO_NOT_FOUND, - Objects.nonNull(param.getUserId()) && param.getUserId() > 0); - - userProfileClient.removeCache(param.getUserId()); + @Override + public void addOrUpdate(UserSpecialIdCmd param) { + ResponseAssert.isTrue(UserErrorCode.USER_INFO_NOT_FOUND, + Objects.nonNull(param.getUserId()) && param.getUserId() > 0); + UserProfileDTO userProfile = ResponseAssert.requiredSuccess( + 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())) { add(param); return; @@ -78,14 +83,11 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ ResponseAssert.isNull(ConsoleErrorCode.RESOURCES_ALREADY_EXISTS); } - userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() - .setAccount(param.getAccount()) - .setUserId(param.getUserId()) - .setSysOrigin(param.getSysOrigin()) - .setEventDesc(getUpdateEventDesc(param)) - ); - userSpecialIdClient.updateSelective(toUserSpecialId(param)); - } + userSpecialIdLogClient.addSpecialIdLog( + buildSpecialIdLogCmd(param.getAccount(), param.getUserId(), param.getSysOrigin(), + getUpdateEventDesc(param), param.getOptUserId())); + userSpecialIdClient.updateSelective(toUserSpecialId(param)); + } private String getUpdateEventDesc(UserSpecialIdCmd param) { String desc = "【修改】靓号"; @@ -110,16 +112,13 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ UserErrorCode.USER_RESOURCES_ALREADY_EXISTS, ResponseAssert.requiredSuccess(userSpecialIdClient.existsUserId(param.getUserId()))); - userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() - .setAccount(param.getAccount()) - .setUserId(param.getUserId()) - .setSysOrigin(param.getSysOrigin()) - .setEventDesc( - "【添加】靓号" + (StringUtils.isNotBlank(param.getRemark()) ? "," + param.getRemark() - : "")) - ); - userSpecialIdClient.add(toUserSpecialId(param)); - } + userSpecialIdLogClient.addSpecialIdLog( + buildSpecialIdLogCmd(param.getAccount(), param.getUserId(), param.getSysOrigin(), + "【添加】靓号" + (StringUtils.isNotBlank(param.getRemark()) ? "," + param.getRemark() + : ""), + param.getOptUserId())); + userSpecialIdClient.add(toUserSpecialId(param)); + } @Override public void updateExpiredTime(UserSpecialIdExpiredTimeCmd param) { @@ -129,15 +128,12 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ CommonErrorCode.NOT_FOUND_RECORD_INFO, userSpecialId); userProfileClient.removeCache(userSpecialId.getUserId()); if (Objects.equals(param.getExpiredDays(), 0)) { - userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() - .setAccount(userSpecialId.getAccount()) - .setUserId(userSpecialId.getUserId()) - .setSysOrigin(userSpecialId.getSysOrigin()) - .setEventDesc("【修改】账号为永久") - ); - userSpecialIdClient.removeExpiredTime(userSpecialId.getSysOrigin(), - userSpecialId.getAccount()); - return; + userSpecialIdLogClient.addSpecialIdLog( + buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(), + userSpecialId.getSysOrigin(), "【修改】账号为永久", param.getOptUserId())); + userSpecialIdClient.removeExpiredTime(userSpecialId.getSysOrigin(), + userSpecialId.getAccount()); + return; } if (param.getExpiredDays() > 0) { @@ -151,14 +147,12 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ } else if (Objects.isNull(userSpecialId.getExpiredTime())) { userSpecialId.setExpiredTime(TimestampUtils.now()); } - userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() - .setAccount(userSpecialId.getAccount()) - .setUserId(userSpecialId.getUserId()) - .setSysOrigin(userSpecialId.getSysOrigin()) - .setEventDesc("【修改】设置账号过期时间: " + userSpecialId.getExpiredTime()) - ); - userSpecialIdClient.updateSelective(userAppConvertor.toUserSpecialIdCmd(userSpecialId)); - } + userSpecialIdLogClient.addSpecialIdLog( + buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(), + userSpecialId.getSysOrigin(), "【修改】设置账号过期时间: " + userSpecialId.getExpiredTime(), + param.getOptUserId())); + userSpecialIdClient.updateSelective(userAppConvertor.toUserSpecialIdCmd(userSpecialId)); + } @Override public void remove(String id, Long opeUserId) { @@ -167,15 +161,12 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ if (Objects.isNull(userSpecialId)) { return; } - userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() - .setAccount(userSpecialId.getAccount()) - .setUserId(userSpecialId.getUserId()) - .setSysOrigin(userSpecialId.getSysOrigin()) - .setEventDesc("【移除】靓号") - ); - userSpecialIdClient.remove(id); - userProfileClient.removeCache(userSpecialId.getUserId()); - userProfileClient.removeRunProfileById(userSpecialId.getUserId()); + userSpecialIdLogClient.addSpecialIdLog( + buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(), + userSpecialId.getSysOrigin(), "【移除】靓号", opeUserId)); + userSpecialIdClient.remove(id); + userProfileClient.removeCache(userSpecialId.getUserId()); + userProfileClient.removeRunProfileById(userSpecialId.getUserId()); } @Override @@ -208,33 +199,30 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ } @Override - public List listLatestLog(Long userId) { - List sysUserSpecialIdLogs = ResponseAssert.requiredSuccess( - userSpecialIdLogClient.listLatest(userId, - 20)); + public List listLatestLog(Long userId) { + List sysUserSpecialIdLogs = ResponseAssert.requiredSuccess( + userSpecialIdLogClient.listLatest(userId, + 20)); if (CollectionUtils.isEmpty(sysUserSpecialIdLogs)) { return Lists.newArrayList(); } - Map userBaseInfoMap = ResponseAssert.requiredSuccess( - userProfileClient.mapByUserIds( - sysUserSpecialIdLogs.stream().map(SysUserSpecialIdLogDTO::getUserId) - .collect(Collectors.toSet())) - ); - - Map backUserInfoMap = userService.mapBackUser(sysUserSpecialIdLogs.stream() - .filter(log -> Objects.nonNull(log.getAccount())) - .map(SysUserSpecialIdLogDTO::getCreateUser) - .collect(Collectors.toSet())); - - return sysUserSpecialIdLogs.stream().map(log -> { - SpecialIdLogCO logVo = userAppConvertor.toSpecialIdLogCO(log); - logVo.setUserBaseInfo(userBaseInfoMap.get(logVo.getUserId())); - User user = backUserInfoMap.get(log.getCreateUser()); - logVo.setOptUserNickname(Objects.nonNull(user) ? user.getNickname() : StringPool.UNKNOWN); - return logVo; - }).collect(Collectors.toList()); - } + Map userBaseInfoMap = getUserBaseInfoMap( + sysUserSpecialIdLogs.stream().map(SysUserSpecialIdLogDTO::getUserId) + .collect(Collectors.toSet())); + Map backUserInfoMap = userService.mapBackUser(sysUserSpecialIdLogs.stream() + .map(this::getOperateUserId) + .filter(Objects::nonNull) + .collect(Collectors.toSet())); + + return sysUserSpecialIdLogs.stream() + .map(log -> userAppConvertor.toSpecialIdLogCO(log) + .setUserBaseInfo(userBaseInfoMap.get(log.getUserId())) + .setOptUserNickname(Optional.ofNullable(backUserInfoMap.get(getOperateUserId(log))) + .map(User::getNickname) + .orElse(StringPool.UNKNOWN))) + .collect(Collectors.toList()); + } @Override public void updateAccount(UpdateUserSpecialIdCmd param) { @@ -256,16 +244,14 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ userSpecialIdClient.remove(userSpecialId.getId()); - userSpecialIdLogClient.addSpecialIdLog(new SysUserSpecialIdLogCmd() - .setAccount(param.getNewAccount()) - .setUserId(userSpecialId.getUserId()) - .setSysOrigin(userSpecialId.getSysOrigin()) - .setEventDesc( - "【修改】靓号" + ",由" + userSpecialId.getAccount() + "修改成: " + param.getNewAccount()) - ); - userSpecialId.setAccount(param.getNewAccount()); - userSpecialId.setTimeId(IdWorkerUtils.getId()); - userSpecialId.setExpiredTime(TimestampUtils.nowPlusDays(param.getExpiredDays())); + userSpecialIdLogClient.addSpecialIdLog( + buildSpecialIdLogCmd(param.getNewAccount(), userSpecialId.getUserId(), + userSpecialId.getSysOrigin(), + "【修改】靓号" + ",由" + userSpecialId.getAccount() + "修改成: " + param.getNewAccount(), + param.getOptUserId())); + userSpecialId.setAccount(param.getNewAccount()); + userSpecialId.setTimeId(IdWorkerUtils.getId()); + userSpecialId.setExpiredTime(TimestampUtils.nowPlusDays(param.getExpiredDays())); userSpecialId.setCreateTime(TimestampUtils.now()); userSpecialId.setUpdateTime(TimestampUtils.now()); userSpecialIdClient.add(userAppConvertor.toUserSpecialIdCmd(userSpecialId)); @@ -273,42 +259,58 @@ public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerServ } @Override - public PageResult pageSpecialIdLog(PageUserSpecialIdCmd param) { - - PageResult pageResult = ResponseAssert.requiredSuccess( - userSpecialIdLogClient.pageSpecialIdLog( - param)); - - PageResult result = pageResult.convert(userAppConvertor::toSpecialIdLogCO); - if (CollectionUtils.isEmpty(result.getRecords())) { - return result; - } - - Set userIds = Stream - .of(result.getRecords().stream().map(SpecialIdLogCO::getUserId) - .collect(Collectors.toSet())).flatMap(Collection::stream) - .collect(Collectors.toSet()); - - Map userBaseInfoMap = ResponseAssert.requiredSuccess( - userProfileClient.mapByUserIds(userIds)); - - Map backUserInfoMap = userService.mapBackUser(result.getRecords().stream() - .filter(log -> Objects.nonNull(log.getAccount())) - .map(SpecialIdLogCO::getCreateUser) - .collect(Collectors.toSet())); - - return result.convert(specialLog -> { - UserProfileDTO UserProfileDTO = userBaseInfoMap.get(specialLog.getUserId()); - User user = backUserInfoMap.get(specialLog.getCreateUser()); - specialLog.setUserBaseInfo(UserProfileDTO); - specialLog.setOptUserNickname( - Objects.nonNull(user) ? user.getNickname() : StringPool.UNKNOWN); - return specialLog; - }); - } - - private UserSpecialIdCmd toUserSpecialId(UserSpecialIdCmd param) { - return new UserSpecialIdCmd() + public PageResult pageSpecialIdLog(PageUserSpecialIdCmd param) { + + PageResult pageResult = ResponseAssert.requiredSuccess( + userSpecialIdLogClient.pageSpecialIdLog( + param)); + if (CollectionUtils.isEmpty(pageResult.getRecords())) { + return pageResult.convert(userAppConvertor::toSpecialIdLogCO); + } + + Set userIds = pageResult.getRecords().stream() + .map(SysUserSpecialIdLogDTO::getUserId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + Map userBaseInfoMap = getUserBaseInfoMap(userIds); + + Map backUserInfoMap = userService.mapBackUser(pageResult.getRecords().stream() + .map(this::getOperateUserId) + .filter(Objects::nonNull) + .collect(Collectors.toSet())); + + return pageResult.convert(log -> userAppConvertor.toSpecialIdLogCO(log) + .setUserBaseInfo(userBaseInfoMap.get(log.getUserId())) + .setOptUserNickname(Optional.ofNullable(backUserInfoMap.get(getOperateUserId(log))) + .map(User::getNickname) + .orElse(StringPool.UNKNOWN))); + } + + private SysUserSpecialIdLogCmd buildSpecialIdLogCmd(String account, Long userId, String sysOrigin, + String eventDesc, Long operateUserId) { + return new SysUserSpecialIdLogCmd() + .setAccount(account) + .setUserId(userId) + .setSysOrigin(sysOrigin) + .setEventDesc(eventDesc) + .setCreateUser(operateUserId) + .setUpdateUser(operateUserId); + } + + private Map getUserBaseInfoMap(Set 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()) .setTimeId(IdWorkerUtils.getId()) .setSysOrigin(param.getSysOrigin()) diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoService.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoService.java index 45a1c208..f8e6f8e9 100644 --- a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoService.java +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBaseInfoService.java @@ -43,10 +43,15 @@ public interface UserBaseInfoService { */ UserProfileDetailCO getByUserId(Long userId); - /** - * 根据账号查询用户信息 - */ - List getByAccount(String account); + /** + * 根据账号查询用户信息 + */ + List getByAccount(String account); + + /** + * 根据系统和账号查询单个用户信息 + */ + UserProfileDTO getByAccount(String sysOrigin, String account); /** * 修改APP用户信息 diff --git a/rc-service/rc-service-live/live-adapter/src/main/java/com/red/circle/live/adapter/app/LiveMicRestController.java b/rc-service/rc-service-live/live-adapter/src/main/java/com/red/circle/live/adapter/app/LiveMicRestController.java index 6435de61..6b590463 100644 --- a/rc-service/rc-service-live/live-adapter/src/main/java/com/red/circle/live/adapter/app/LiveMicRestController.java +++ b/rc-service/rc-service-live/live-adapter/src/main/java/com/red/circle/live/adapter/app/LiveMicRestController.java @@ -88,18 +88,19 @@ public class LiveMicRestController extends BaseController { liveMicrophoneService.bannedLiveUser(cmd); } - /** - * 下麦克风. - * - * @eo.name 下麦克风. - * @eo.url /go-down - * @eo.method post - * @eo.request-type json - */ - @PostMapping("/go-down") - public void goDown(@Validated @RequestBody MicUpDownCmd cmd) { - liveMicrophoneService.goDown(cmd); - } + /** + * 下麦克风. + * + * @eo.name 下麦克风. + * @eo.url /go-down + * @eo.method post + * @eo.request-type json + */ + @PostMapping("/go-down") + public Boolean goDown(@Validated @RequestBody MicUpDownCmd cmd) { + liveMicrophoneService.goDown(cmd); + return Boolean.TRUE; + } /** * T麦克风(房主&管理员). diff --git a/rc-service/rc-service-live/live-adapter/src/test/java/com/red/circle/live/adapter/app/LiveMicRestControllerTest.java b/rc-service/rc-service-live/live-adapter/src/test/java/com/red/circle/live/adapter/app/LiveMicRestControllerTest.java new file mode 100644 index 00000000..c504def0 --- /dev/null +++ b/rc-service/rc-service-live/live-adapter/src/test/java/com/red/circle/live/adapter/app/LiveMicRestControllerTest.java @@ -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 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) { + } + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/GameBaishunReportCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/GameBaishunReportCmdExe.java index cb5bbd85..95a55749 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/GameBaishunReportCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/GameBaishunReportCmdExe.java @@ -1,12 +1,11 @@ package com.red.circle.other.app.command.game; -import com.alibaba.fastjson.JSONObject; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.red.circle.auth.inner.endpoint.AuthClient; -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.response.BaishunResponse; +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.red.circle.auth.inner.endpoint.AuthClient; +import com.red.circle.component.game.baishun.error.BaishunErrorCode; +import com.red.circle.component.game.baishun.request.BaishunUserProfileRequest; +import com.red.circle.component.game.baishun.response.BaishunResponse; import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.security.UserCredential; import com.red.circle.other.app.dto.cmd.game.GameBaishunReportCmd; @@ -35,12 +34,11 @@ import org.springframework.stereotype.Component; public class GameBaishunReportCmdExe { private static final String REPORT_TYPE_START = "game_start"; - private static final String REPORT_TYPE_SETTLE = "game_settle"; - - private final ObjectMapper objectMapper; - private final GameBaishunProperties gameBaishunProperties; - private final WalletGoldClient walletGoldClient; - private final AuthClient authClient; + private static final String REPORT_TYPE_SETTLE = "game_settle"; + + private final ObjectMapper objectMapper; + private final WalletGoldClient walletGoldClient; + private final AuthClient authClient; public BaishunResponse execute(GameBaishunReportCmd cmd) { // if (!checkTokenStrict(cmd)) { diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/barrage/BarrageGameBaishunExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/barrage/BarrageGameBaishunExe.java index 088d809c..6c723d83 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/barrage/BarrageGameBaishunExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/game/barrage/BarrageGameBaishunExe.java @@ -1,19 +1,21 @@ -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.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.UserEffectsCmd; -import com.red.circle.framework.core.asserts.ResponseAssert; -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.GameUserEffectsCmd; -import com.red.circle.other.infra.database.rds.service.live.RoomMemberService; -import com.red.circle.other.inner.enums.live.RoomUserRolesEnum; -import com.red.circle.tool.core.text.StringUtils; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +package com.red.circle.other.app.command.game.barrage; + +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.CloseRoomCmd; +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.response.CommonErrorCode; +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.service.game.BaishunRuntimeConfig; +import com.red.circle.other.app.service.game.BaishunRuntimeConfigResolver; +import com.red.circle.other.infra.database.rds.service.live.RoomMemberService; +import com.red.circle.other.inner.enums.live.RoomUserRolesEnum; +import com.red.circle.tool.core.parse.DataTypeUtils; +import com.red.circle.tool.core.text.StringUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; @@ -33,55 +35,59 @@ import java.util.Random; @Slf4j @Component @RequiredArgsConstructor -public class BarrageGameBaishunExe { - - private final BarrageGameClient barrageGameClient; - private final RoomMemberService roomMemberService; - private final GameBaishunProperties gameBaishunProperties; - - public void userEffects(GameUserEffectsCmd cmd) { - UserEffectsCmd userEffectsCmd = getUserEffectsCmd(cmd); - barrageGameClient.userEffects(userEffectsCmd); - } +public class BarrageGameBaishunExe { + + private static final String DEFAULT_SYS_ORIGIN = "LIKEI"; + + private final BarrageGameClient barrageGameClient; + private final RoomMemberService roomMemberService; + private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver; + + public void userEffects(GameUserEffectsCmd cmd) { + UserEffectsCmd userEffectsCmd = getUserEffectsCmd(cmd, + baishunRuntimeConfigResolver.resolveBySysOrigin(DEFAULT_SYS_ORIGIN)); + barrageGameClient.userEffects(userEffectsCmd); + } public void closeRoom(GameCloseRoomCmd cmd) { log.info("关闭游戏接⼝ closeRoom: {}", cmd); // 判断是否是房主或房间管理员 RoomUserRolesEnum roomUserRoles = roomMemberService.getRoomUserRoles(cmd.requiredReqUserId(), cmd.getRoomId()); - // 权限不足: 只有管理员和房主能往下走 - ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION, - Objects.nonNull(roomUserRoles) && roomUserRoles.checkHomeownerOrAdmin()); - CloseRoomCmd closeRoomCmd = getCloseRoomCmd(cmd); - barrageGameClient.closeRoom(closeRoomCmd); - } - - @NotNull - private UserEffectsCmd getUserEffectsCmd(GameUserEffectsCmd cmd) { - UserEffectsCmd userEffectsCmd = new UserEffectsCmd(); - userEffectsCmd.setGameId(cmd.getGameId()); - userEffectsCmd.setUserId(String.valueOf(cmd.getUserId())); - userEffectsCmd.setEffectsId(cmd.getEffectsId()); - userEffectsCmd.setGiftPrice(cmd.getGiftPrice()); - userEffectsCmd.setGiftNum(cmd.getGiftNum()); - userEffectsCmd.setAppId(Long.parseLong(gameBaishunProperties.getAppId())); - BarrageGameCmd barrageGameCmd = getBarrageGameCmd(gameBaishunProperties.getAppKey()); - userEffectsCmd.setTimestamp(barrageGameCmd.getTimestamp()); - userEffectsCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce()); - userEffectsCmd.setSignature(barrageGameCmd.getSignature()); - return userEffectsCmd; - } - - @NotNull - private CloseRoomCmd getCloseRoomCmd(GameCloseRoomCmd cmd) { - CloseRoomCmd closeRoomCmd = new CloseRoomCmd(); - closeRoomCmd.setRoomId(String.valueOf(cmd.getRoomId())); - closeRoomCmd.setGameId(cmd.getGameId()); - closeRoomCmd.setAppId(Long.parseLong(gameBaishunProperties.getAppId())); - closeRoomCmd.setAppChannel(gameBaishunProperties.getAppChannel()); - BarrageGameCmd barrageGameCmd = getBarrageGameCmd(gameBaishunProperties.getAppKey()); - closeRoomCmd.setTimestamp(barrageGameCmd.getTimestamp()); - closeRoomCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce()); - closeRoomCmd.setSignature(barrageGameCmd.getSignature()); + // 权限不足: 只有管理员和房主能往下走 + ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION, + Objects.nonNull(roomUserRoles) && roomUserRoles.checkHomeownerOrAdmin()); + CloseRoomCmd closeRoomCmd = getCloseRoomCmd(cmd, + baishunRuntimeConfigResolver.resolveBySysOrigin(cmd.requireReqSysOrigin())); + barrageGameClient.closeRoom(closeRoomCmd); + } + + @NotNull + private UserEffectsCmd getUserEffectsCmd(GameUserEffectsCmd cmd, BaishunRuntimeConfig runtimeConfig) { + UserEffectsCmd userEffectsCmd = new UserEffectsCmd(); + userEffectsCmd.setGameId(cmd.getGameId()); + userEffectsCmd.setUserId(String.valueOf(cmd.getUserId())); + userEffectsCmd.setEffectsId(cmd.getEffectsId()); + userEffectsCmd.setGiftPrice(cmd.getGiftPrice()); + userEffectsCmd.setGiftNum(cmd.getGiftNum()); + userEffectsCmd.setAppId(DataTypeUtils.toLong(runtimeConfig.getAppId())); + BarrageGameCmd barrageGameCmd = getBarrageGameCmd(runtimeConfig.getAppKey()); + userEffectsCmd.setTimestamp(barrageGameCmd.getTimestamp()); + userEffectsCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce()); + userEffectsCmd.setSignature(barrageGameCmd.getSignature()); + return userEffectsCmd; + } + + @NotNull + private CloseRoomCmd getCloseRoomCmd(GameCloseRoomCmd cmd, BaishunRuntimeConfig runtimeConfig) { + CloseRoomCmd closeRoomCmd = new CloseRoomCmd(); + closeRoomCmd.setRoomId(String.valueOf(cmd.getRoomId())); + closeRoomCmd.setGameId(cmd.getGameId()); + closeRoomCmd.setAppId(DataTypeUtils.toLong(runtimeConfig.getAppId())); + closeRoomCmd.setAppChannel(runtimeConfig.getAppChannel()); + BarrageGameCmd barrageGameCmd = getBarrageGameCmd(runtimeConfig.getAppKey()); + closeRoomCmd.setTimestamp(barrageGameCmd.getTimestamp()); + closeRoomCmd.setSignatureNonce(barrageGameCmd.getSignatureNonce()); + closeRoomCmd.setSignature(barrageGameCmd.getSignature()); return closeRoomCmd; } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/BdLeaderInviteMessageProcessExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/BdLeaderInviteMessageProcessExe.java index 7826d377..9848a323 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/BdLeaderInviteMessageProcessExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/BdLeaderInviteMessageProcessExe.java @@ -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.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.mongo.entity.team.bd.BdInviteAgentMessage; -import com.red.circle.other.infra.database.mongo.entity.team.bd.MessageInviteGroup; +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.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.team.TeamApplicationProcessApprovalService; @@ -46,10 +47,11 @@ public class BdLeaderInviteMessageProcessExe { private final UserHistoryIdentityService userHistoryIdentityService; private final BdInviteAgentMessageService bdInviteAgentMessageService; private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; - private final TeamApplicationProcessApprovalService teamApplicationProcessApprovalService; - private final UserMqMessageService userMqMessageService; - private final AdministratorService administratorService; - private final TaskMqMessage taskMqMessage; + private final TeamApplicationProcessApprovalService teamApplicationProcessApprovalService; + private final UserMqMessageService userMqMessageService; + private final AdministratorService administratorService; + private final TaskMqMessage taskMqMessage; + private final BdTeamCacheService bdTeamCacheService; public void execute(DbInviteMessageProcessCmd cmd) { ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getStatus() > 0); @@ -99,10 +101,14 @@ public class BdLeaderInviteMessageProcessExe { .setCreateUserOrigin(0) ); - userHistoryIdentityService.saveBd(cmd.requiredReqUserId()); - - // 发送管理员任务消息 - sendAdminTaskMessage(message.getUserId(), 14); + userHistoryIdentityService.saveBd(cmd.requiredReqUserId()); + bdTeamCacheService.clearRelationChangeCaches( + message.getUserId(), + message.getUserId(), + cmd.requiredReqUserId()); + + // 发送管理员任务消息 + sendAdminTaskMessage(message.getUserId(), 14); 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()) ); - teamApplicationProcessApprovalService.add(new TeamApplicationProcessApproval() - .setSysOrigin(cmd.requireReqSysOrigin()) - .setType(TeamApplicationType.BD) + teamApplicationProcessApprovalService.add(new TeamApplicationProcessApproval() + .setSysOrigin(cmd.requireReqSysOrigin()) + .setType(TeamApplicationType.BD) .setReason(TeamAppProcessApprovalReason.ACCEPT_BDLEADER_INVITE) .setAssociateId(message.getUserId()) .setBeProcessUserId(message.getInviteUserId()) .setCreateTime(TimestampUtils.now()) .setCreateUser(cmd.requiredReqUserId()) - .setCreateUserOrigin(0) - ); - - // 发送管理员任务消息 - sendAdminTaskMessage(message.getUserId(), 13); + .setCreateUserOrigin(0) + ); + + bdTeamCacheService.clearRelationChangeCaches( + 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_LEADER.name(), BadgeOperateEvent.OperationType.WEAR.name()); diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/GameLuckyGiftCommon.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/GameLuckyGiftCommon.java index dc3ae117..236de5dd 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/GameLuckyGiftCommon.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/GameLuckyGiftCommon.java @@ -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.other.app.convertor.material.GiftAppConvertor; 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.cmd.activity.RankingDataUpdateCmd; -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.service.activity.ActivityRechargeTicketService; -import com.red.circle.other.app.service.activity.RankingActivityService; +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.gift.GiveAwayGiftBatchCmd; +import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd; +import com.red.circle.other.app.service.game.BaishunRuntimeConfigResolver; +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.task.RoomDailyTaskProgressService; import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils; @@ -117,11 +118,12 @@ public class GameLuckyGiftCommon { private final UserProfileAppConvertor userProfileAppConvertor; private final GameLuckyGiftCountService gameLuckyGiftCountService; private final GameLuckyGiftCacheService gameLuckyGiftCacheService; - private final LuckyGiftProbabilityService luckyGiftProbabilityService; - private final GameLuckyGiftRuleConfigService gameLuckyGiftRuleConfigService; - private final LuckyGiftProbabilityDetailsService luckyGiftProbabilityDetailsService; - private final LuckyGiftApiConfig luckyGiftApiConfig; - private final RankingActivityService rankingActivityService; + private final LuckyGiftProbabilityService luckyGiftProbabilityService; + private final GameLuckyGiftRuleConfigService gameLuckyGiftRuleConfigService; + private final LuckyGiftProbabilityDetailsService luckyGiftProbabilityDetailsService; + private final LuckyGiftApiConfig luckyGiftApiConfig; + private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver; + private final RankingActivityService rankingActivityService; private final GiftMqMessage giftMqMessage; private final GiftAppConvertor giftAppConvertor; private final ActivityRechargeTicketService activityRechargeTicketService; @@ -820,10 +822,11 @@ public class GameLuckyGiftCommon { * 调用第三方幸运礼物API. */ private ThirdPartyLotteryResult callThirdPartyLotteryApi(GameLuckyGiftParam param, List orderIds) { - try { - // 根据 standardId 获取对应的API配置 - ApiAccount apiConfig = luckyGiftApiConfig.getConfigByStandardId(param.getStandardId()); - log.info("使用API配置, standardId: {}, config: {}", param.getStandardId(), apiConfig); + try { + // 根据 standardId 获取对应的API配置 + ApiAccount apiConfig = baishunRuntimeConfigResolver.resolveLuckyGiftApiAccount( + param.getSysOrigin(), param.getStandardId()); + log.info("使用API配置, standardId: {}, config: {}", param.getStandardId(), apiConfig); Map requestData = new HashMap<>(); requestData.put("room_id", param.getRoomId().toString()); diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/LuckyGiftRewardSettlementService.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/LuckyGiftRewardSettlementService.java new file mode 100644 index 00000000..ef3cb05d --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/LuckyGiftRewardSettlementService.java @@ -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 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 liveHeartbeatCaches = liveHeartbeatCacheService.listLiveHeartbeat( + event.getRoomId()); + Map 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 mapHeartbeatCaches(List list) { + if (CollectionUtils.isEmpty(list)) { + return new HashMap<>(); + } + Map result = new HashMap<>(list.size()); + for (LiveHeartbeatCache cache : list) { + if (cache != null && cache.getId() != null) { + result.put(cache.getId(), cache); + } + } + return result; + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/LuckyGiftRewardStreamEvent.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/LuckyGiftRewardStreamEvent.java new file mode 100644 index 00000000..a31839a3 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/common/gift/LuckyGiftRewardStreamEvent.java @@ -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 results; + + @Data + @Accessors(chain = true) + public static class Result implements Serializable { + + private String orderSeed; + private Long acceptUserId; + private Long rewardNum; + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTask.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTask.java new file mode 100644 index 00000000..7d3756b6 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTask.java @@ -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 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> records = redisTemplate.opsForStream().read( + readOptions, + StreamOffset.create(streamKey, ReadOffset.from(lastId)) + ); + if (CollectionUtils.isEmpty(records)) { + return; + } + + for (MapRecord 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 record) { + Map 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 values, String key) { + Object value = values.get(key); + return value == null ? null : String.valueOf(value); + } + + private String lastIdKey() { + return LAST_ID_KEY_PREFIX + streamKey; + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/BaishunRuntimeConfig.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/BaishunRuntimeConfig.java new file mode 100644 index 00000000..1d858af9 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/BaishunRuntimeConfig.java @@ -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; +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/BaishunRuntimeConfigResolver.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/BaishunRuntimeConfigResolver.java new file mode 100644 index 00000000..fe5b4605 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/BaishunRuntimeConfigResolver.java @@ -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(); + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/GameBaishunServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/GameBaishunServiceImpl.java index 08c945d0..cb6f612e 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/GameBaishunServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/game/GameBaishunServiceImpl.java @@ -1,14 +1,13 @@ package com.red.circle.other.app.service.game; -import com.red.circle.auth.inner.endpoint.AuthClient; -import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; -import com.red.circle.component.game.baishun.GameBaishunService; -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.BaishunTokenRequest; -import com.red.circle.component.game.baishun.request.BaishunTokenUpdateRequest; -import com.red.circle.component.game.baishun.request.BaishunUserProfileRequest; +import com.red.circle.auth.inner.endpoint.AuthClient; +import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.component.game.baishun.GameBaishunService; +import com.red.circle.component.game.baishun.error.BaishunErrorCode; +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.BaishunTokenUpdateRequest; +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.BaishunResponse; import com.red.circle.component.game.baishun.response.BaishunTokenResponse; @@ -76,12 +75,12 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class GameBaishunServiceImpl implements GameBaishunService { - private final AuthClient authClient; - private final RedisService redisService; - private final WalletGoldClient walletGoldClient; - private final UserProfileGateway userProfileGateway; - private final GameBaishunProperties gameBaishunProperties; - private final TaskMqMessage taskMqMessage; + private final AuthClient authClient; + private final RedisService redisService; + private final WalletGoldClient walletGoldClient; + private final UserProfileGateway userProfileGateway; + private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver; + private final TaskMqMessage taskMqMessage; private final UserProfileAppConvertor userProfileAppConvertor; private final EnumConfigCacheService enumConfigCacheService; private final GameListConfigService gameListConfigService; @@ -96,12 +95,12 @@ public class GameBaishunServiceImpl implements GameBaishunService { @Value("${red-circle.game-rank.gameid}") private String gameId; - @Override - public BaishunResponse getToken(BaishunTokenRequest request) { - log.info("getToken: {}", request); - if (!request.checkSignature(gameBaishunProperties.getAppKey())) { - log.info("getToken: {}", "验证签名失败"); - return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); + @Override + public BaishunResponse getToken(BaishunTokenRequest request) { + log.info("getToken: {}", request); + if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) { + log.info("getToken: {}", "验证签名失败"); + return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); }*/ @@ -110,12 +109,12 @@ public class GameBaishunServiceImpl implements GameBaishunService { return getToken(request.getUserId()); } - @Override - public BaishunResponse getUserProfile(BaishunUserProfileRequest request) { - log.info("getUserProfile: {}", request); - if (!request.checkSignature(gameBaishunProperties.getAppKey())) { - /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { - return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); + @Override + public BaishunResponse getUserProfile(BaishunUserProfileRequest request) { + log.info("getUserProfile: {}", request); + if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) { + /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { + return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); }*/ log.info("getToken: {}", "验证签名失败"); return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); @@ -140,12 +139,12 @@ public class GameBaishunServiceImpl implements GameBaishunService { ); } - @Override - public BaishunResponse updateToken(BaishunTokenUpdateRequest request) { - log.info("updateToken: {}", request); - if (!request.checkSignature(gameBaishunProperties.getAppKey())) { - log.info("getToken: {}", "验证签名失败"); - return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); + @Override + public BaishunResponse updateToken(BaishunTokenUpdateRequest request) { + log.info("updateToken: {}", request); + if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) { + log.info("getToken: {}", "验证签名失败"); + return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); }*/ @@ -158,12 +157,12 @@ public class GameBaishunServiceImpl implements GameBaishunService { return getToken(request.getUserId()); } - @Override - public BaishunResponse changeBalance(BaishunChangeCurrencyRequest request) { - log.info("changeBalance: {}", request); - if (!request.checkSignature(gameBaishunProperties.getAppKey())) { - log.info("getToken: {}", "验证签名失败"); - return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); + @Override + public BaishunResponse changeBalance(BaishunChangeCurrencyRequest request) { + log.info("changeBalance: {}", request); + if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) { + log.info("getToken: {}", "验证签名失败"); + return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR); /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) { 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) { RoomDailyTaskCode taskType = RoomDailyTaskCode.PERSONAL_GAME_CONSUME; diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/team/BdLeaderInviteMessageProcessExeTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/team/BdLeaderInviteMessageProcessExeTest.java new file mode 100644 index 00000000..4d21ee12 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/team/BdLeaderInviteMessageProcessExeTest.java @@ -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)); + } +} diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/common/gift/LuckyGiftRewardSettlementServiceTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/common/gift/LuckyGiftRewardSettlementServiceTest.java new file mode 100644 index 00000000..c03d84d5 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/common/gift/LuckyGiftRewardSettlementServiceTest.java @@ -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> heartbeatCaptor = ArgumentCaptor.forClass(List.class); + verify(liveHeartbeatCacheService).create(eq(ROOM_ID), heartbeatCaptor.capture()); + List 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 caches, long userId) { + return caches.stream() + .filter(cache -> cache != null && cache.getId() != null && cache.getId() == userId) + .findFirst() + .map(LiveHeartbeatCache::getHeartbeatVal) + .orElse(0L); + } +} diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTaskTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTaskTest.java new file mode 100644 index 00000000..f2963b0c --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/scheduler/LuckyGiftRewardStreamTaskTest.java @@ -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 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 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 record = + (MapRecord) (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); + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/service/team/BdTeamCacheService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/service/team/BdTeamCacheService.java new file mode 100644 index 00000000..d9e8ec43 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/service/team/BdTeamCacheService.java @@ -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); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/service/team/impl/BdTeamCacheServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/service/team/impl/BdTeamCacheServiceImpl.java new file mode 100644 index 00000000..b0049c97 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/cache/service/team/impl/BdTeamCacheServiceImpl.java @@ -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); + } + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/game/BaishunProviderConfigDAO.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/game/BaishunProviderConfigDAO.java new file mode 100644 index 00000000..4226bb70 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/game/BaishunProviderConfigDAO.java @@ -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 { + +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/game/BaishunProviderConfig.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/game/BaishunProviderConfig.java new file mode 100644 index 00000000..e88dd233 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/game/BaishunProviderConfig.java @@ -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; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/game/BaishunProviderConfigService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/game/BaishunProviderConfigService.java new file mode 100644 index 00000000..24a60e04 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/game/BaishunProviderConfigService.java @@ -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 getBySysOrigin(String sysOrigin); + + BaishunProviderConfig getByAppId(Long appId); + + BaishunProviderConfig getByApp(Long appId, String appChannel); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/game/impl/BaishunProviderConfigServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/game/impl/BaishunProviderConfigServiceImpl.java new file mode 100644 index 00000000..b3de5155 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/game/impl/BaishunProviderConfigServiceImpl.java @@ -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 + 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(); + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/user/impl/UserBeautifulNumberServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/user/impl/UserBeautifulNumberServiceImpl.java index 1b260bcf..d857c6df 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/user/impl/UserBeautifulNumberServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/user/user/impl/UserBeautifulNumberServiceImpl.java @@ -5,7 +5,6 @@ import com.red.circle.framework.core.response.CommonErrorCode; import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.mybatis.constant.PageConstant; 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.text.StringUtils; import com.red.circle.other.infra.convertor.user.UserProfileInfraConvertor; @@ -68,13 +67,12 @@ public class UserBeautifulNumberServiceImpl extends .execute(); } - @Override - public void addBeautifulNumber(UserBeautifulNumberCmd dto) { - ResponseAssert.failure(ImErrorCode.USER_ALREADY_EXISTS); - ResponseAssert.isNull(CommonErrorCode.INFORMATION_EXISTS, - query().eq(UserBeautifulNumber::getUserAccount, dto.getUserAccount()).getOne()); - UserBeautifulNumber userBeautifulNumber = userProfileInfraConvertor.toUserBeautifulNumberCmd( - dto); + @Override + public void addBeautifulNumber(UserBeautifulNumberCmd dto) { + ResponseAssert.isNull(CommonErrorCode.INFORMATION_EXISTS, + query().eq(UserBeautifulNumber::getUserAccount, dto.getUserAccount()).getOne()); + UserBeautifulNumber userBeautifulNumber = userProfileInfraConvertor.toUserBeautifulNumberCmd( + dto); if (CollectionUtils.isNotEmpty(dto.getTabs())) { userBeautifulNumber.setTabName(dto.getTabs().get(0).getTabName()); userBeautifulNumber.setTypeName(dto.getTabs().get(0).getTypeName()); diff --git a/rc-service/rc-service-other/other-inner-endpoint/pom.xml b/rc-service/rc-service-other/other-inner-endpoint/pom.xml index 2de931f7..57c67f98 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/pom.xml +++ b/rc-service/rc-service-other/other-inner-endpoint/pom.xml @@ -7,17 +7,23 @@ 内部通讯服务端点 jar - - - com.red.circle - other-application - - - - com.red.circle - other-inner-api - - + + + com.red.circle + other-application + + + + com.red.circle + other-inner-api + + + + org.springframework.boot + spring-boot-starter-test + test + + rc-service-other diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamInfoClientServiceImpl.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamInfoClientServiceImpl.java index 418da41c..c6f1e474 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamInfoClientServiceImpl.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamInfoClientServiceImpl.java @@ -1,11 +1,12 @@ package com.red.circle.other.app.inner.service.team.impl; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; -import com.red.circle.framework.dto.PageResult; -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.rds.entity.team.BusinessDevelopmentBaseInfo; -import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService; +import com.red.circle.framework.dto.PageResult; +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.app.inner.convertor.team.BdInnerConvertor; import com.red.circle.other.app.inner.service.team.BdTeamInfoClientService; import com.red.circle.other.inner.model.cmd.team.BdTeamInfoCmd; @@ -31,9 +32,10 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService { - private final BdInnerConvertor bdAppConvertor; - private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; - private final UserProfileGateway userProfileGateway; + private final BdInnerConvertor bdAppConvertor; + private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; + private final UserProfileGateway userProfileGateway; + private final BdTeamCacheService bdTeamCacheService; @Override public Boolean check(Long userId) { @@ -50,10 +52,15 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService { return bdAppConvertor.toBdTeamInfoDTO(businessDevelopmentBaseInfoService.getById(id)); } - @Override - public void addBdInfo(BdTeamInfoCmd cmd) { - businessDevelopmentBaseInfoService.save(bdAppConvertor.toRoomBusinessDevelopmentBaseInfo(cmd)); - } + @Override + public void addBdInfo(BdTeamInfoCmd cmd) { + BusinessDevelopmentBaseInfo bdInfo = bdAppConvertor.toRoomBusinessDevelopmentBaseInfo(cmd); + businessDevelopmentBaseInfoService.save(bdInfo); + bdTeamCacheService.clearRelationChangeCaches( + bdInfo.getBdLeadUserId(), + bdInfo.getBdLeadUserId(), + bdInfo.getUserId()); + } @Override public void incrMemberCount(Set userIds) { @@ -65,12 +72,22 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService { return businessDevelopmentBaseInfoService.mapCheckBd(userIds); } - @Override - public void updateSelectiveById(BdTeamInfoDTO dto) { - businessDevelopmentBaseInfoService.updateSelectiveById( - bdAppConvertor.toBusinessDevelopmentBaseInfo(dto) - ); - } + @Override + public void updateSelectiveById(BdTeamInfoDTO dto) { + BusinessDevelopmentBaseInfo oldBdInfo = businessDevelopmentBaseInfoService.getById(dto.getId()); + 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 public void updateOpsById(BdTeamUpdateOpsCmd cmd) { @@ -133,10 +150,17 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService { qryCmd.getSysOrigin()).list()); } - @Override - public void deleteById(Long id) { - businessDevelopmentBaseInfoService.removeById(id); - } + @Override + public void deleteById(Long id) { + BusinessDevelopmentBaseInfo bdInfo = businessDevelopmentBaseInfoService.getById(id); + businessDevelopmentBaseInfoService.removeById(id); + if (Objects.nonNull(bdInfo)) { + bdTeamCacheService.clearRelationChangeCaches( + bdInfo.getBdLeadUserId(), + null, + bdInfo.getUserId()); + } + } @Override public void changeBdLeader(ChangeBdLeaderCmd cmd) { @@ -153,12 +177,17 @@ public class BdTeamInfoClientServiceImpl implements BdTeamInfoClientService { } // 修改BD Leader - businessDevelopmentBaseInfoService.update() - .set(BusinessDevelopmentBaseInfo::getBdLeadUserId, userProfile.getId()) - .set(BusinessDevelopmentBaseInfo::getUpdateUser, cmd.getUpdateUser()) - .eq(BusinessDevelopmentBaseInfo::getId, cmd.getBdId()) - .execute(); - } + businessDevelopmentBaseInfoService.update() + .set(BusinessDevelopmentBaseInfo::getBdLeadUserId, userProfile.getId()) + .set(BusinessDevelopmentBaseInfo::getUpdateUser, cmd.getUpdateUser()) + .eq(BusinessDevelopmentBaseInfo::getId, cmd.getBdId()) + .execute(); + + bdTeamCacheService.clearRelationChangeCaches( + bdInfo.getBdLeadUserId(), + userProfile.getId(), + bdInfo.getUserId()); + } } diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamLeaderClientServiceImpl.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamLeaderClientServiceImpl.java index d8a2f173..ccc3cf73 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamLeaderClientServiceImpl.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/team/impl/BdTeamLeaderClientServiceImpl.java @@ -1,7 +1,8 @@ package com.red.circle.other.app.inner.service.team.impl; -import com.red.circle.framework.dto.PageResult; -import com.red.circle.other.infra.database.rds.entity.team.RoomBdLead; +import com.red.circle.framework.dto.PageResult; +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.BusinessDevelopmentTeamService; 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 { private final BdInnerConvertor bdInnerConvertor; - private final RoomBdLeadService roomBdLeadService; - private final BusinessDevelopmentTeamService businessDevelopmentTeamService; - private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; + private final RoomBdLeadService roomBdLeadService; + private final BusinessDevelopmentTeamService businessDevelopmentTeamService; + private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; + private final BdTeamCacheService bdTeamCacheService; @Override public BdTeamLeaderDTO getById(Long id) { @@ -91,11 +93,14 @@ public class BdTeamLeaderClientServiceImpl implements BdTeamLeaderClientService return businessDevelopmentTeamService.mapTeamBdUserId(ids); } - @Override - public void removeLeader(Long teamId, Long bdLeadUserId) { - roomBdLeadService.deleteById(teamId); - businessDevelopmentBaseInfoService.removeBdLeader(bdLeadUserId); - } + @Override + public void removeLeader(Long teamId, Long bdLeadUserId) { + List bdUserIds = businessDevelopmentBaseInfoService.listBdUserIdsByLeaderId(bdLeadUserId); + roomBdLeadService.deleteById(teamId); + businessDevelopmentBaseInfoService.removeBdLeader(bdLeadUserId); + bdTeamCacheService.clearLeaderRelatedCaches(bdLeadUserId); + bdUserIds.forEach(bdTeamCacheService::clearBdRelatedCaches); + } } diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/test/java/com/red/circle/other/app/inner/service/team/impl/BdTeamInfoClientServiceImplTest.java b/rc-service/rc-service-other/other-inner-endpoint/src/test/java/com/red/circle/other/app/inner/service/team/impl/BdTeamInfoClientServiceImplTest.java new file mode 100644 index 00000000..8977b6bb --- /dev/null +++ b/rc-service/rc-service-other/other-inner-endpoint/src/test/java/com/red/circle/other/app/inner/service/team/impl/BdTeamInfoClientServiceImplTest.java @@ -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 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)); + } +}