diff --git a/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/gift/GiftSendSceneEnum.java b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/gift/GiftSendSceneEnum.java new file mode 100644 index 00000000..0be979bb --- /dev/null +++ b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/gift/GiftSendSceneEnum.java @@ -0,0 +1,9 @@ +package com.red.circle.mq.business.model.event.gift; + +/** + * 礼物发送场景. + */ +public enum GiftSendSceneEnum { + ROOM, + PRIVATE_CHAT +} diff --git a/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/gift/GiveAwayGiftBatchEvent.java b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/gift/GiveAwayGiftBatchEvent.java index bbda3086..db3da66b 100644 --- a/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/gift/GiveAwayGiftBatchEvent.java +++ b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/gift/GiveAwayGiftBatchEvent.java @@ -83,6 +83,11 @@ public class GiveAwayGiftBatchEvent implements Serializable { */ private Long roomId; + /** + * 发送场景. + */ + private GiftSendSceneEnum sendScene; + /** * 动态id */ @@ -270,6 +275,14 @@ public class GiveAwayGiftBatchEvent implements Serializable { return Objects.nonNull(giftConfig) && Objects.equals(giftConfig.getGiftTab(), "MAGIC"); } + public GiftSendSceneEnum sendSceneOrRoom() { + return Objects.isNull(sendScene) ? GiftSendSceneEnum.ROOM : sendScene; + } + + public boolean privateChatScene() { + return Objects.equals(sendScene, GiftSendSceneEnum.PRIVATE_CHAT); + } + /** * 是否是专属礼物. diff --git a/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/ImMessageClientApi.java b/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/ImMessageClientApi.java index 8d55d0d1..6936fc5f 100644 --- a/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/ImMessageClientApi.java +++ b/rc-service/rc-inner-api/external-inner/external-inner-api/src/main/java/com/red/circle/external/inner/endpoint/message/api/ImMessageClientApi.java @@ -1,7 +1,11 @@ package com.red.circle.external.inner.endpoint.message.api; +import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd; import com.red.circle.framework.dto.ResultResponse; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; /** @@ -29,4 +33,10 @@ public interface ImMessageClientApi { @RequestParam("toAccount") Long toAccount, @RequestParam("text") String text); + /** + * 发送 C2C 自定义消息. + */ + @PostMapping("/sendCustomMessage") + ResultResponse sendCustomMessage(@RequestBody @Validated CustomC2CMsgBodyCmd cmd); + } diff --git a/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/cmd/message/CustomC2CMsgBodyCmd.java b/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/cmd/message/CustomC2CMsgBodyCmd.java new file mode 100644 index 00000000..87f319f7 --- /dev/null +++ b/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/cmd/message/CustomC2CMsgBodyCmd.java @@ -0,0 +1,70 @@ +package com.red.circle.external.inner.model.cmd.message; + +import com.red.circle.framework.dto.Command; +import jakarta.validation.constraints.NotBlank; +import java.io.Serial; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * C2C 自定义消息. + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CustomC2CMsgBodyCmd extends Command { + + @Serial + private static final long serialVersionUID = 1L; + + @NotBlank(message = "fromAccount required.") + private String fromAccount; + + @NotBlank(message = "toAccount required.") + private String toAccount; + + @NotBlank(message = "desc required.") + private String desc; + + private Object data; + + public static CustomC2CMsgBodyBuilder builder() { + return new CustomC2CMsgBodyBuilder(); + } + + public static class CustomC2CMsgBodyBuilder { + + private String fromAccount; + private String toAccount; + private String desc; + private Object data; + + public CustomC2CMsgBodyBuilder fromAccount(String fromAccount) { + this.fromAccount = fromAccount; + return this; + } + + public CustomC2CMsgBodyBuilder toAccount(String toAccount) { + this.toAccount = toAccount; + return this; + } + + public CustomC2CMsgBodyBuilder desc(String desc) { + this.desc = desc; + return this; + } + + public CustomC2CMsgBodyBuilder data(Object data) { + this.data = data; + return this; + } + + public CustomC2CMsgBodyCmd build() { + CustomC2CMsgBodyCmd cmd = new CustomC2CMsgBodyCmd(); + cmd.setFromAccount(fromAccount); + cmd.setToAccount(toAccount); + cmd.setDesc(desc); + cmd.setData(data); + return cmd; + } + } +} diff --git a/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/enums/sys/BannerDisplayPositionEnum.java b/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/enums/sys/BannerDisplayPositionEnum.java index 73357bee..d5bb373f 100644 --- a/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/enums/sys/BannerDisplayPositionEnum.java +++ b/rc-service/rc-service-console/console-infrastructure/src/main/java/com/red/circle/console/infra/enums/sys/BannerDisplayPositionEnum.java @@ -14,11 +14,15 @@ public enum BannerDisplayPositionEnum { ROOM("房间内"), + ROOM_LIST("房间列表页"), + EXPLORE_PAGE("发现页"), WALLET("钱包"), - HOME_ALERT("首页弹出层"); + HOME_ALERT("首页弹出层"), + + GAME("游戏"); private final String desc; diff --git a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/ImMessageClientEndpoint.java b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/ImMessageClientEndpoint.java index 956948b9..64ec0aba 100644 --- a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/ImMessageClientEndpoint.java +++ b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/endpoint/message/ImMessageClientEndpoint.java @@ -1,6 +1,7 @@ package com.red.circle.external.inner.endpoint.message; import com.red.circle.external.inner.endpoint.message.api.ImMessageClientApi; +import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd; import com.red.circle.external.inner.service.message.ImMessageClientService; import com.red.circle.framework.dto.ResultResponse; import com.red.circle.tool.core.parse.DataTypeUtils; @@ -36,4 +37,10 @@ public class ImMessageClientEndpoint implements ImMessageClientApi { return ResultResponse.success(); } + @Override + public ResultResponse sendCustomMessage(CustomC2CMsgBodyCmd cmd) { + imMessageClientService.sendCustomMessage(cmd); + return ResultResponse.success(); + } + } diff --git a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/ImMessageClientService.java b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/ImMessageClientService.java index d5873810..1acbfb81 100644 --- a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/ImMessageClientService.java +++ b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/ImMessageClientService.java @@ -1,5 +1,7 @@ package com.red.circle.external.inner.service.message; +import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd; + /** * im 消息服务. * @@ -12,4 +14,9 @@ public interface ImMessageClientService { */ void sendMessageText(String fromAccount, String toAccount, String text); + /** + * 发送 C2C 自定义消息. + */ + void sendCustomMessage(CustomC2CMsgBodyCmd cmd); + } diff --git a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/ImMessageClientServiceImpl.java b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/ImMessageClientServiceImpl.java index 6acf063c..f8c1acd5 100644 --- a/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/ImMessageClientServiceImpl.java +++ b/rc-service/rc-service-external/external-inner-endpoint/src/main/java/com/red/circle/external/inner/service/message/impl/ImMessageClientServiceImpl.java @@ -1,7 +1,12 @@ package com.red.circle.external.inner.service.message.impl; import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.account.ImMessageManagerService; +import com.red.circle.component.instant.msg.tencet.service.param.ImMessageParam; +import com.red.circle.component.instant.msg.tencet.service.param.MessageBody; +import com.red.circle.component.instant.msg.tencet.service.param.OfflinePushInfo; +import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd; import com.red.circle.external.inner.service.message.ImMessageClientService; +import java.util.concurrent.ThreadLocalRandom; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; @@ -23,4 +28,20 @@ public class ImMessageClientServiceImpl implements ImMessageClientService { imMessageManagerService.sendMessageText(fromAccount, toAccount, text).subscribe(); } + @Override + public void sendCustomMessage(CustomC2CMsgBodyCmd cmd) { + ImMessageParam param = ImMessageParam.builder() + .fromAccount(cmd.getFromAccount()) + .toAccount(cmd.getToAccount()) + .msgRandom(ThreadLocalRandom.current().nextInt(100_000_000, 1_000_000_000)) + .offlinePushInfo(OfflinePushInfo.builder().pushFlag(0).build()) + .build() + .addMsgBody(MessageBody.customBody(cmd.getDesc(), cmd.getData())); + imMessageManagerService.sendMessage(param) + .subscribe(resp -> log.info("send C2C custom message success: fromAccount={}, toAccount={}, desc={}, resp={}", + cmd.getFromAccount(), cmd.getToAccount(), cmd.getDesc(), resp), + throwable -> log.warn("send C2C custom message failed: fromAccount={}, toAccount={}, desc={}", + cmd.getFromAccount(), cmd.getToAccount(), cmd.getDesc(), throwable)); + } + } diff --git a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/gift/GiftRestController.java b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/gift/GiftRestController.java index d8e016fc..d53fb732 100644 --- a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/gift/GiftRestController.java +++ b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/gift/GiftRestController.java @@ -12,6 +12,7 @@ import com.red.circle.other.app.dto.clientobject.gift.PremiumGiftWallCO; import com.red.circle.other.app.dto.clientobject.gift.UserGiftBackpackCO; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; +import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd; import com.red.circle.other.app.service.gift.GiftService; import com.red.circle.tool.core.json.JacksonUtils; import java.math.BigDecimal; @@ -131,6 +132,20 @@ public class GiftRestController extends BaseController { return giftService.giveAwayGiftBatch(cmd); } + /** + * 私聊赠送-礼物. + * + * @eo.name 私聊赠送-礼物. + * @eo.url /private + * @eo.method post + * @eo.request-type json + */ + @PostMapping("/private") + public BigDecimal giveAwayPrivateGift(@RequestBody @Validated GiveAwayPrivateGiftCmd cmd) { + log.warn("giveAwayPrivateGift:{}", JacksonUtils.toJson(cmd)); + return giftService.giveAwayPrivateGift(cmd); + } + /** * 赠送-幸运礼物. * @@ -181,4 +196,3 @@ public class GiftRestController extends BaseController { } } - diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/BlessingsGiftGiveCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/BlessingsGiftGiveCmdExe.java index 465f6d73..620ce6e6 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/BlessingsGiftGiveCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/BlessingsGiftGiveCmdExe.java @@ -6,6 +6,7 @@ import com.red.circle.common.business.core.enums.*; import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent; import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts; +import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum; import com.red.circle.mq.business.model.event.gift.GiveGiftConfig; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.rocket.business.producer.GiftMqMessage; @@ -128,6 +129,7 @@ public class BlessingsGiftGiveCmdExe { giveAwayGiftBatchEvent.setQuantity(cmd.getQuantity()); giveAwayGiftBatchEvent.setGiftConfig(toGiveGiftConfig(giftConfig)); giveAwayGiftBatchEvent.setRoomId(cmd.getRoomId()); + giveAwayGiftBatchEvent.setSendScene(GiftSendSceneEnum.ROOM); giveAwayGiftBatchEvent.setDynamicContentId(cmd.getDynamicContentId()); giveAwayGiftBatchEvent.setTrackId(trackId); giveAwayGiftBatchEvent.setSongId(cmd.getSongId()); diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/GiftGiveAwayBatchCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/GiftGiveAwayBatchCmdExe.java index 8bf4dec6..d8b31c05 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/GiftGiveAwayBatchCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/gift/GiftGiveAwayBatchCmdExe.java @@ -10,11 +10,14 @@ import com.red.circle.mq.rocket.business.producer.GiftMqMessage; import com.red.circle.mq.rocket.business.producer.TaskMqMessage; import com.red.circle.other.app.convertor.material.GiftAppConvertor; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; +import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd; import com.red.circle.other.app.service.task.TaskService; import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; +import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.infra.database.cache.service.other.GiftCacheService; import com.red.circle.other.inner.asserts.GiftErrorCode; +import com.red.circle.other.inner.asserts.user.UserErrorCode; import com.red.circle.other.inner.enums.material.GiftCurrencyType; import com.red.circle.other.inner.enums.material.GiftTabEnum; import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; @@ -100,6 +103,49 @@ public class GiftGiveAwayBatchCmdExe { return receipt.getBalance(); } + public BigDecimal executePrivate(GiveAwayPrivateGiftCmd cmd) { + GiftConfigDTO giftConfig = giftCacheService.getById(cmd.getGiftId()); + log.warn("GiftGiveAwayPrivateCmdExe{}", giftConfig); + + ResponseAssert.notNull(GiftErrorCode.GIFT_NOT_FOUND, giftConfig); + ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, + Objects.equals(cmd.requireReqSysOrigin(), giftConfig.getSysOrigin())); + ResponseAssert.isFalse(GiftErrorCode.NOT_SUPPORTED_GIFT_TYPE, + Objects.equals(giftConfig.getGiftTab(), GiftTabEnum.LUCKY_GIFT.name())); + checkPrivateAcceptUser(cmd); + + if (isZeroGiftCandy(giftConfig)) { + BigDecimal dollarAmount = walletGoldClient.getBalance(cmd.requiredReqUserId()).getBody().getDollarAmount(); + log.info("私聊赠送普通礼物金额为0,余额为 {}", dollarAmount); + return dollarAmount; + } + + WalletReceiptDTO receipt = consumePrivateCandy(cmd, giftConfig); + log.warn("GiftGiveAwayPrivateCmdExe{}", receipt); + + giftMqMessage.sendGift(receipt.getConsumeId().toString(), + giftAppConvertor.toGiveAwayPrivateGiftEvent(cmd, giftConfig, receipt.getConsumeId())); + userProfileGateway.removeCache(cmd.requiredReqUserId()); + + Boolean taskStatus = taskService.checkTaskStatus(cmd.getReqUserId(), 2L, + LocalDateTimeUtils.nowFormat("yyyy-MM-dd")); + if (taskStatus) { + taskMqMessage.sendTask(TaskApprovalEvent.builder() + .taskId(2) + .userId(cmd.getReqUserId()) + .day(LocalDateTimeUtils.nowFormat("yyyy-MM-dd")) + .build()); + } + return receipt.getBalance(); + } + + private void checkPrivateAcceptUser(GiveAwayPrivateGiftCmd cmd) { + UserProfile acceptUser = userProfileGateway.getByUserId(cmd.getAcceptUserId()); + ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, acceptUser); + ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR, + Objects.equals(cmd.requireReqSysOrigin(), acceptUser.getOriginSys())); + } + private void checkDynamicRegion(GiveAwayGiftBatchCmd cmd) { if (cmd.getDynamicContentId() == null) { return; @@ -163,6 +209,60 @@ public class GiftGiveAwayBatchCmdExe { throw new IllegalArgumentException("param error."); } + /** + * 私聊付钱. + */ + private WalletReceiptDTO consumePrivateCandy(GiveAwayPrivateGiftCmd cmd, GiftConfigDTO giftConfig) { + + if (isGiftTypeEqFree(giftConfig)) { + return new WalletReceiptDTO() + .setConsumeId(IdWorkerUtils.getId()) + .setProcessAmount(BigDecimal.ZERO) + .setBalance( + ResponseAssert.requiredSuccess(walletGoldClient.getBalance(cmd.requiredReqUserId())) + .getDollarAmount()); + + } + + if (isGiftTypeEqGold(giftConfig)) { + BigDecimal amount = cmd.calculateConsumeGolds(giftConfig.getGiftCandy()); + WalletReceiptResDTO receiptRes = walletGoldClient.changeBalance(GoldReceiptCmd.builder() + .appExpenditure() + .userId(cmd.requiredReqUserId()) + .eventId(giftConfig.getId()) + .sysOrigin(cmd.requireReqSysOrigin()) + .origin(GoldOrigin.GIVE_GIFT + "_" + giftConfig.getGiftTab()) + .amount(amount) + .closeDelayAsset() + .build()).getBody(); + return new WalletReceiptDTO() + .setConsumeId(receiptRes.getAssetRecordId()) + .setBalance(receiptRes.getBalance().getDollarAmount()) + .setProcessAmount(amount); + } + + if (isGiftTypeEqDiamond(giftConfig)) { + return walletDiamondClient.changeBalance(new DiamondReceiptCmd() + .setConsumeId(Objects.toString(giftConfig.getId())) + .setType(ReceiptType.EXPENDITURE) + .setTrackId(giftConfig.getId()) + .setUserId(cmd.requiredReqUserId()) + .setSysOrigin(cmd.requireReqSysOriginEnum()) + .setOrigin(DiamondOrigin.GIVE_GIFT) + .setAmount(cmd.calculateConsumeGolds(giftConfig.getGiftCandy())) + .setCreateTime(TimestampUtils.now()) + ).getBody(); + } + + ResponseAssert.failure(CommonErrorCode.TYPE_IS_NOT_IN_SCOPE); + throw new IllegalArgumentException("param error."); + } + + private boolean isZeroGiftCandy(GiftConfigDTO giftConfig) { + return Objects.nonNull(giftConfig.getGiftCandy()) + && BigDecimal.ZERO.compareTo(giftConfig.getGiftCandy()) == 0; + } + private boolean isGiftTypeEqDiamond(GiftConfigDTO giftConfigInfo) { return Objects.equals(giftConfigInfo.getType(), GiftCurrencyType.DIAMOND.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 b4a831ad..903dcff7 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 @@ -23,6 +23,7 @@ import com.red.circle.mq.business.model.event.gift.GameLuckyGiftBusinessEvent; import com.red.circle.mq.business.model.event.gift.GameLuckyGiftUser; import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent; import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts; +import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum; import com.red.circle.mq.rocket.business.producer.GiftMqMessage; import com.red.circle.mq.rocket.business.service.MessageSenderService; import com.red.circle.mq.rocket.business.streams.GameLuckyGiftBusinessSink; @@ -318,6 +319,7 @@ public class GameLuckyGiftCommon { .setQuantity(cmd.getQuantity()) .setGiftConfig(giftAppConvertor.toGiveGiftConfig(giftConfigDTO)) .setRoomId(cmd.getRoomId()) + .setSendScene(GiftSendSceneEnum.ROOM) .setTrackId(Long.parseLong(timeId)) .setBagGift(Boolean.FALSE) .setCreateTime(TimestampUtils.now()); diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/material/GiftAppConvertor.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/material/GiftAppConvertor.java index 6738e9e8..abd64903 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/material/GiftAppConvertor.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/material/GiftAppConvertor.java @@ -4,11 +4,13 @@ import com.google.common.collect.Lists; import com.red.circle.framework.core.convertor.ConvertorModel; import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent; import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts; +import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum; import com.red.circle.mq.business.model.event.gift.GiveGiftConfig; import com.red.circle.other.app.dto.clientobject.gift.GiftBackpackLimitCO; import com.red.circle.other.app.dto.clientobject.gift.GiftConfigCO; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; +import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd; import com.red.circle.other.infra.database.rds.entity.gift.GiftBackpackLimit; import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; @@ -49,6 +51,7 @@ public interface GiftAppConvertor { .setQuantity(cmd.getQuantity()) .setGiftConfig(toGiveGiftConfig(giftConfig)) .setRoomId(cmd.getRoomId()) + .setSendScene(GiftSendSceneEnum.ROOM) .setDynamicContentId(cmd.getDynamicContentId()) .setTrackId(trackId) .setSongId(cmd.getSongId()) @@ -103,10 +106,32 @@ public interface GiftAppConvertor { .setQuantity(cmd.getQuantity()) .setGiftConfig(toGiveGiftConfig(giftConfig)) .setRoomId(cmd.getRoomId()) + .setSendScene(GiftSendSceneEnum.ROOM) .setTrackId(trackId) .setBagGift(Boolean.TRUE) .setCreateTime(Objects.isNull(cmd.getReqTime()) ? TimestampUtils.now() : new Timestamp(cmd.getReqTime().getTime())); } + default GiveAwayGiftBatchEvent toGiveAwayPrivateGiftEvent(GiveAwayPrivateGiftCmd cmd, + GiftConfigDTO giftConfig, Long trackId) { + return new GiveAwayGiftBatchEvent() + .setSysOrigin(cmd.requireReqSysOriginEnum()) + .setRequestPlatform(Objects.toString(cmd.getReqClient())) + .setSendUserId(cmd.requiredReqUserId()) + .setAccepts(List.of( + new GiveAwayGiftRoomAccepts() + .setSeatIndex(null) + .setAcceptUserId(cmd.getAcceptUserId()) + )) + .setQuantity(cmd.getQuantity()) + .setGiftConfig(toGiveGiftConfig(giftConfig)) + .setRoomId(null) + .setSendScene(GiftSendSceneEnum.PRIVATE_CHAT) + .setTrackId(trackId) + .setBagGift(Boolean.FALSE) + .setCreateTime(Objects.isNull(cmd.getReqTime()) ? TimestampUtils.now() + : new Timestamp(cmd.getReqTime().getTime())); + } + } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/GiveGiftsListener.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/GiveGiftsListener.java index 58263160..13d22ffd 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/GiveGiftsListener.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/GiveGiftsListener.java @@ -11,6 +11,8 @@ import com.red.circle.component.mq.service.Action; import com.red.circle.component.mq.service.ConsumerMessage; import com.red.circle.component.mq.service.MessageListener; import com.red.circle.external.inner.endpoint.message.ImGroupClient; +import com.red.circle.external.inner.endpoint.message.ImMessageClient; +import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd; import com.red.circle.external.inner.model.cmd.message.CustomGroupMsgBodyCmd; import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum; import com.red.circle.framework.core.dto.ReqSysOrigin; @@ -33,12 +35,14 @@ import com.red.circle.other.app.command.game.roompk.GameRoomPkUserCmdExe; import com.red.circle.other.app.command.game.teampk.GameTeamPkBaseInfoCmdExe; import com.red.circle.other.app.common.gift.GameLuckyGiftCommon; import com.red.circle.other.app.dto.clientobject.game.*; +import com.red.circle.other.app.dto.clientobject.gift.PrivateGiftImPayloadCO; import com.red.circle.other.app.dto.clientobject.gift.SendGiftNotifyCO; import com.red.circle.other.app.dto.cmd.game.barrage.GameUserEffectsCmd; import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; import com.red.circle.other.domain.live.LiveHeartbeatCache; import com.red.circle.other.domain.model.user.ability.RegionConfig; import com.red.circle.other.domain.ranking.RankingActivityType; +import com.red.circle.other.infra.database.cache.service.other.GiftCacheService; import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService; import com.red.circle.other.infra.database.cache.service.other.LiveHeartbeatCacheService; import com.red.circle.other.infra.database.mongo.entity.gift.GiftAcceptUser; @@ -59,6 +63,7 @@ import com.red.circle.other.inner.enums.config.EnumConfigKey; import com.red.circle.other.inner.enums.material.GiftSpecialEnum; import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum; import com.red.circle.other.inner.model.dto.activity.props.ActivityRewardPropsDTO; +import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.date.TimestampUtils; @@ -86,6 +91,7 @@ import org.jetbrains.annotations.NotNull; public class GiveGiftsListener implements MessageListener { private static final String TAG = "礼物消息"; + private static final String PRIVATE_GIFT_IM_DESC = "PRIVATE_GIFT"; private final GameMqMessage gameMqMessage; private final GiftMqMessage giftMqMessage; private final TeamMemberService teamMemberService; @@ -98,6 +104,8 @@ public class GiveGiftsListener implements MessageListener { private final LiveHeartbeatCacheService liveHeartbeatCacheService; private final GiftGiveRunningWaterService giftGiveRunningWaterService; private final ImGroupClient imGroupClient; + private final ImMessageClient imMessageClient; + private final GiftCacheService giftCacheService; private final RoomProfileManagerService roomProfileManagerService; private final GameTeamPkBaseInfoCmdExe gameTeamPkBaseInfoCmdExe; private final GameRoomPkUserCmdExe gameRoomPkUserCmdExe; @@ -370,6 +378,10 @@ public class GiveGiftsListener implements MessageListener { return; } + if (event.privateChatScene()) { + sendPrivateGiftIM(event); + } + if (!SysOriginPlatformEnum.isVoiceSystem(runningWater.getSysOrigin())) { return; } @@ -393,8 +405,47 @@ public class GiveGiftsListener implements MessageListener { // 排行榜相关 offlineProcessGiftEvent(OfflineStatisticsGiftEventType.RANK_COUNT, runningWater); - // 火箭统计相关 - offlineProcessGiftEvent(OfflineStatisticsGiftEventType.ROCKET_COUNT, runningWater); + if (!event.privateChatScene()) { + // 火箭统计相关 + offlineProcessGiftEvent(OfflineStatisticsGiftEventType.ROCKET_COUNT, runningWater); + } + } + + private void sendPrivateGiftIM(GiveAwayGiftBatchEvent event) { + if (CollectionUtils.isEmpty(event.getAccepts()) || event.getAccepts().get(0).getAcceptUserId() == null) { + log.warn("私聊礼物IM发送失败,接收人为空:{}", JacksonUtils.toJson(event)); + return; + } + Long acceptUserId = event.getAccepts().get(0).getAcceptUserId(); + try { + GiftConfigDTO giftConfig = giftCacheService.getById(event.giftId()); + PrivateGiftImPayloadCO payload = new PrivateGiftImPayloadCO() + .setType(PRIVATE_GIFT_IM_DESC) + .setScene(event.sendSceneOrRoom().name()) + .setTrackId(event.getTrackId()) + .setSendUserId(event.getSendUserId()) + .setAcceptUserId(acceptUserId) + .setGiftId(event.giftId()) + .setGiftName(Objects.nonNull(giftConfig) ? giftConfig.getGiftName() : null) + .setGiftPhoto(Objects.nonNull(giftConfig) ? giftConfig.getGiftPhoto() : null) + .setGiftSourceUrl(Objects.nonNull(giftConfig) ? giftConfig.getGiftSourceUrl() : null) + .setGiftType(event.giftType()) + .setGiftTab(event.getGiftConfig().getGiftTab()) + .setGiftCandy(event.getGiftConfig().getGiftCandy()) + .setQuantity(event.getQuantity()) + .setCreateTime(getCreateTime(event).getTime()); + imMessageClient.sendCustomMessage(CustomC2CMsgBodyCmd.builder() + .fromAccount(Objects.toString(event.getSendUserId())) + .toAccount(Objects.toString(acceptUserId)) + .desc(PRIVATE_GIFT_IM_DESC) + .data(payload) + .build()); + log.info("私聊礼物IM发送成功,trackId={},sendUserId={},acceptUserId={}", + event.getTrackId(), event.getSendUserId(), acceptUserId); + } catch (Exception e) { + log.warn("私聊礼物IM发送失败,trackId={},sendUserId={},acceptUserId={}", + event.getTrackId(), event.getSendUserId(), acceptUserId, e); + } } private Pair saveRunningWater(GiveAwayGiftBatchEvent event) { @@ -444,6 +495,7 @@ public class GiveGiftsListener implements MessageListener { .processed(Boolean.FALSE) .trackId(event.getTrackId().toString()) .originId(Objects.nonNull(event.getRoomId()) ? event.getRoomId().toString() : "OTHER") + .sendScene(event.sendSceneOrRoom().name()) .dynamicContentId(Objects.nonNull(event.getDynamicContentId()) ? event.getDynamicContentId().toString() : null) .sysOrigin(event.getSysOrigin().name()) .requestPlatform(event.getRequestPlatform()) diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/gift/GiftServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/gift/GiftServiceImpl.java index f1644c9b..76552da1 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/gift/GiftServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/gift/GiftServiceImpl.java @@ -18,6 +18,7 @@ import com.red.circle.other.app.dto.clientobject.gift.PremiumGiftWallCO; import com.red.circle.other.app.dto.clientobject.gift.UserGiftBackpackCO; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; +import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd; import java.math.BigDecimal; import java.util.List; import lombok.RequiredArgsConstructor; @@ -60,6 +61,11 @@ public class GiftServiceImpl implements GiftService { return giftGiveAwayBatchCmdExe.execute(cmd); } + @Override + public BigDecimal giveAwayPrivateGift(GiveAwayPrivateGiftCmd cmd) { + return giftGiveAwayBatchCmdExe.executePrivate(cmd); + } + @Override public BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd) { return luckyGiftGiveCmdExe.execute(cmd); diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/convertor/material/GiftAppConvertorTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/convertor/material/GiftAppConvertorTest.java new file mode 100644 index 00000000..fab89688 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/convertor/material/GiftAppConvertorTest.java @@ -0,0 +1,74 @@ +package com.red.circle.other.app.convertor.material; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.framework.core.dto.ReqSysOrigin; +import com.red.circle.framework.core.request.RequestClientEnum; +import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum; +import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; +import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd; +import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; +import java.math.BigDecimal; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class GiftAppConvertorTest { + + private final GiftAppConvertor convertor = Mappers.getMapper(GiftAppConvertor.class); + + @Test + void shouldBuildPrivateGiftEventWithOneAcceptAndNoRoom() { + GiveAwayPrivateGiftCmd cmd = new GiveAwayPrivateGiftCmd(); + cmd.setReqUserId(1L); + cmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name())); + cmd.setReqClient(RequestClientEnum.Android); + cmd.setAcceptUserId(2L); + cmd.setGiftId(3L); + cmd.setQuantity(4); + + var event = convertor.toGiveAwayPrivateGiftEvent(cmd, giftConfig(), 99L); + + assertEquals(GiftSendSceneEnum.PRIVATE_CHAT, event.getSendScene()); + assertTrue(event.privateChatScene()); + assertNull(event.getRoomId()); + assertEquals(1L, event.getSendUserId()); + assertEquals(1, event.getAccepts().size()); + assertEquals(2L, event.getAccepts().get(0).getAcceptUserId()); + assertEquals(4, event.getQuantity()); + assertEquals(99L, event.getTrackId()); + } + + @Test + void shouldKeepBatchGiftEventAsRoomScene() { + GiveAwayGiftBatchCmd cmd = new GiveAwayGiftBatchCmd(); + cmd.setReqUserId(1L); + cmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name())); + cmd.setReqClient(RequestClientEnum.Android); + cmd.setAcceptUserIds(List.of(2L, 2L)); + cmd.setGiftId(3L); + cmd.setQuantity(4); + cmd.setRoomId(5L); + + var event = convertor.toGiveAwayGiftBatchEvent(cmd, giftConfig(), 99L); + + assertEquals(GiftSendSceneEnum.ROOM, event.getSendScene()); + assertFalse(event.privateChatScene()); + assertEquals(5L, event.getRoomId()); + assertEquals(1, event.getAccepts().size()); + assertEquals(2L, event.getAccepts().get(0).getAcceptUserId()); + } + + private GiftConfigDTO giftConfig() { + return new GiftConfigDTO() + .setId(3L) + .setGiftCandy(new BigDecimal("10")) + .setGiftIntegral(BigDecimal.ZERO) + .setType("GOLD") + .setGiftTab("NORMAL"); + } +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/gift/PrivateGiftImPayloadCO.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/gift/PrivateGiftImPayloadCO.java new file mode 100644 index 00000000..a3bae69c --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/clientobject/gift/PrivateGiftImPayloadCO.java @@ -0,0 +1,44 @@ +package com.red.circle.other.app.dto.clientobject.gift; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 私聊礼物 IM 自定义消息内容. + */ +@Data +@Accessors(chain = true) +public class PrivateGiftImPayloadCO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String type; + private String scene; + + @JsonSerialize(using = ToStringSerializer.class) + private Long trackId; + + @JsonSerialize(using = ToStringSerializer.class) + private Long sendUserId; + + @JsonSerialize(using = ToStringSerializer.class) + private Long acceptUserId; + + @JsonSerialize(using = ToStringSerializer.class) + private Long giftId; + + private String giftName; + private String giftPhoto; + private String giftSourceUrl; + private String giftType; + private String giftTab; + private BigDecimal giftCandy; + private Integer quantity; + private Long createTime; +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/gift/GiveAwayPrivateGiftCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/gift/GiveAwayPrivateGiftCmd.java new file mode 100644 index 00000000..20b59278 --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/gift/GiveAwayPrivateGiftCmd.java @@ -0,0 +1,39 @@ +package com.red.circle.other.app.dto.cmd.gift; + +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Range; + +/** + * 私聊赠送礼物. + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class GiveAwayPrivateGiftCmd extends AppExtCommand { + + /** + * 接收人. + */ + @NotNull(message = "acceptUserId required.") + private Long acceptUserId; + + /** + * 礼物编号. + */ + @NotNull(message = "giftId required.") + private Long giftId; + + /** + * 礼物数量. + */ + @Range(min = 1, max = 7777, message = "quantity range 1~7777") + @NotNull(message = "quantity required.") + private Integer quantity; + + public BigDecimal calculateConsumeGolds(BigDecimal giftGolds) { + return giftGolds.multiply(BigDecimal.valueOf(quantity)); + } +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/gift/GiftService.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/gift/GiftService.java index 41ac0666..80ec1808 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/gift/GiftService.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/gift/GiftService.java @@ -9,6 +9,7 @@ import com.red.circle.other.app.dto.clientobject.gift.PremiumGiftWallCO; import com.red.circle.other.app.dto.clientobject.gift.UserGiftBackpackCO; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd; +import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd; import java.math.BigDecimal; import java.util.List; @@ -27,6 +28,8 @@ public interface GiftService { BigDecimal giveAwayGiftBatch(GiveAwayGiftBatchCmd cmd); + BigDecimal giveAwayPrivateGift(GiveAwayPrivateGiftCmd cmd); + BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd); List listUserGiftWall(AppUserIdCmd cmd); diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/gift/GiftGiveRunningWater.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/gift/GiftGiveRunningWater.java index 54acdb52..526eba66 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/gift/GiftGiveRunningWater.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/gift/GiftGiveRunningWater.java @@ -47,6 +47,11 @@ public class GiftGiveRunningWater implements Serializable { */ private String originId; + /** + * 发送场景. + */ + private String sendScene; + /** * 动态id */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/gift/impl/GiftGiveRunningWaterServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/gift/impl/GiftGiveRunningWaterServiceImpl.java index 12732443..62e22498 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/gift/impl/GiftGiveRunningWaterServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/gift/impl/GiftGiveRunningWaterServiceImpl.java @@ -89,6 +89,7 @@ public class GiftGiveRunningWaterServiceImpl implements GiftGiveRunningWaterServ .songId(document.getLong("songId")) .trackId(document.getString("trackId")) .originId(document.getString("originId")) + .sendScene(document.getString("sendScene")) .dynamicContentId(document.getString("dynamicContentId")) .sysOrigin(document.getString("sysOrigin")) .requestPlatform(document.getString("requestPlatform"))