feat(other): add private chat gifting

This commit is contained in:
zhx 2026-07-06 15:26:55 +08:00
parent 9b4972f8e5
commit 0901496c7f
21 changed files with 512 additions and 4 deletions

View File

@ -0,0 +1,9 @@
package com.red.circle.mq.business.model.event.gift;
/**
* 礼物发送场景.
*/
public enum GiftSendSceneEnum {
ROOM,
PRIVATE_CHAT
}

View File

@ -83,6 +83,11 @@ public class GiveAwayGiftBatchEvent implements Serializable {
*/ */
private Long roomId; private Long roomId;
/**
* 发送场景.
*/
private GiftSendSceneEnum sendScene;
/** /**
* 动态id * 动态id
*/ */
@ -270,6 +275,14 @@ public class GiveAwayGiftBatchEvent implements Serializable {
return Objects.nonNull(giftConfig) && Objects.equals(giftConfig.getGiftTab(), "MAGIC"); 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);
}
/** /**
* 是否是专属礼物. * 是否是专属礼物.

View File

@ -1,7 +1,11 @@
package com.red.circle.external.inner.endpoint.message.api; 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 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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
/** /**
@ -29,4 +33,10 @@ public interface ImMessageClientApi {
@RequestParam("toAccount") Long toAccount, @RequestParam("toAccount") Long toAccount,
@RequestParam("text") String text); @RequestParam("text") String text);
/**
* 发送 C2C 自定义消息.
*/
@PostMapping("/sendCustomMessage")
ResultResponse<Void> sendCustomMessage(@RequestBody @Validated CustomC2CMsgBodyCmd cmd);
} }

View File

@ -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;
}
}
}

View File

@ -14,11 +14,15 @@ public enum BannerDisplayPositionEnum {
ROOM("房间内"), ROOM("房间内"),
ROOM_LIST("房间列表页"),
EXPLORE_PAGE("发现页"), EXPLORE_PAGE("发现页"),
WALLET("钱包"), WALLET("钱包"),
HOME_ALERT("首页弹出层"); HOME_ALERT("首页弹出层"),
GAME("游戏");
private final String desc; private final String desc;

View File

@ -1,6 +1,7 @@
package com.red.circle.external.inner.endpoint.message; package com.red.circle.external.inner.endpoint.message;
import com.red.circle.external.inner.endpoint.message.api.ImMessageClientApi; 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.external.inner.service.message.ImMessageClientService;
import com.red.circle.framework.dto.ResultResponse; import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.tool.core.parse.DataTypeUtils; import com.red.circle.tool.core.parse.DataTypeUtils;
@ -36,4 +37,10 @@ public class ImMessageClientEndpoint implements ImMessageClientApi {
return ResultResponse.success(); return ResultResponse.success();
} }
@Override
public ResultResponse<Void> sendCustomMessage(CustomC2CMsgBodyCmd cmd) {
imMessageClientService.sendCustomMessage(cmd);
return ResultResponse.success();
}
} }

View File

@ -1,5 +1,7 @@
package com.red.circle.external.inner.service.message; package com.red.circle.external.inner.service.message;
import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd;
/** /**
* im 消息服务. * im 消息服务.
* *
@ -12,4 +14,9 @@ public interface ImMessageClientService {
*/ */
void sendMessageText(String fromAccount, String toAccount, String text); void sendMessageText(String fromAccount, String toAccount, String text);
/**
* 发送 C2C 自定义消息.
*/
void sendCustomMessage(CustomC2CMsgBodyCmd cmd);
} }

View File

@ -1,7 +1,12 @@
package com.red.circle.external.inner.service.message.impl; 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.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 com.red.circle.external.inner.service.message.ImMessageClientService;
import java.util.concurrent.ThreadLocalRandom;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -23,4 +28,20 @@ public class ImMessageClientServiceImpl implements ImMessageClientService {
imMessageManagerService.sendMessageText(fromAccount, toAccount, text).subscribe(); 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));
}
} }

View File

@ -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.clientobject.gift.UserGiftBackpackCO;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; 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.GiveAwayGiftBatchCmd;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
import com.red.circle.other.app.service.gift.GiftService; import com.red.circle.other.app.service.gift.GiftService;
import com.red.circle.tool.core.json.JacksonUtils; import com.red.circle.tool.core.json.JacksonUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -131,6 +132,20 @@ public class GiftRestController extends BaseController {
return giftService.giveAwayGiftBatch(cmd); 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 {
} }
} }

View File

@ -6,6 +6,7 @@ import com.red.circle.common.business.core.enums.*;
import com.red.circle.framework.core.asserts.ResponseAssert; 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.GiveAwayGiftBatchEvent;
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts; 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.gift.GiveGiftConfig;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.GiftMqMessage; import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
@ -128,6 +129,7 @@ public class BlessingsGiftGiveCmdExe {
giveAwayGiftBatchEvent.setQuantity(cmd.getQuantity()); giveAwayGiftBatchEvent.setQuantity(cmd.getQuantity());
giveAwayGiftBatchEvent.setGiftConfig(toGiveGiftConfig(giftConfig)); giveAwayGiftBatchEvent.setGiftConfig(toGiveGiftConfig(giftConfig));
giveAwayGiftBatchEvent.setRoomId(cmd.getRoomId()); giveAwayGiftBatchEvent.setRoomId(cmd.getRoomId());
giveAwayGiftBatchEvent.setSendScene(GiftSendSceneEnum.ROOM);
giveAwayGiftBatchEvent.setDynamicContentId(cmd.getDynamicContentId()); giveAwayGiftBatchEvent.setDynamicContentId(cmd.getDynamicContentId());
giveAwayGiftBatchEvent.setTrackId(trackId); giveAwayGiftBatchEvent.setTrackId(trackId);
giveAwayGiftBatchEvent.setSongId(cmd.getSongId()); giveAwayGiftBatchEvent.setSongId(cmd.getSongId());

View File

@ -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.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.other.app.convertor.material.GiftAppConvertor; 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.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.app.service.task.TaskService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; 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.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.infra.database.cache.service.other.GiftCacheService;
import com.red.circle.other.inner.asserts.GiftErrorCode; 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.GiftCurrencyType;
import com.red.circle.other.inner.enums.material.GiftTabEnum; import com.red.circle.other.inner.enums.material.GiftTabEnum;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
@ -100,6 +103,49 @@ public class GiftGiveAwayBatchCmdExe {
return receipt.getBalance(); 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) { private void checkDynamicRegion(GiveAwayGiftBatchCmd cmd) {
if (cmd.getDynamicContentId() == null) { if (cmd.getDynamicContentId() == null) {
return; return;
@ -163,6 +209,60 @@ public class GiftGiveAwayBatchCmdExe {
throw new IllegalArgumentException("param error."); 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) { private boolean isGiftTypeEqDiamond(GiftConfigDTO giftConfigInfo) {
return Objects.equals(giftConfigInfo.getType(), GiftCurrencyType.DIAMOND.name()); return Objects.equals(giftConfigInfo.getType(), GiftCurrencyType.DIAMOND.name());
} }

View File

@ -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.GameLuckyGiftUser;
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent; 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.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.producer.GiftMqMessage;
import com.red.circle.mq.rocket.business.service.MessageSenderService; import com.red.circle.mq.rocket.business.service.MessageSenderService;
import com.red.circle.mq.rocket.business.streams.GameLuckyGiftBusinessSink; import com.red.circle.mq.rocket.business.streams.GameLuckyGiftBusinessSink;
@ -318,6 +319,7 @@ public class GameLuckyGiftCommon {
.setQuantity(cmd.getQuantity()) .setQuantity(cmd.getQuantity())
.setGiftConfig(giftAppConvertor.toGiveGiftConfig(giftConfigDTO)) .setGiftConfig(giftAppConvertor.toGiveGiftConfig(giftConfigDTO))
.setRoomId(cmd.getRoomId()) .setRoomId(cmd.getRoomId())
.setSendScene(GiftSendSceneEnum.ROOM)
.setTrackId(Long.parseLong(timeId)) .setTrackId(Long.parseLong(timeId))
.setBagGift(Boolean.FALSE) .setBagGift(Boolean.FALSE)
.setCreateTime(TimestampUtils.now()); .setCreateTime(TimestampUtils.now());

View File

@ -4,11 +4,13 @@ import com.google.common.collect.Lists;
import com.red.circle.framework.core.convertor.ConvertorModel; 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.GiveAwayGiftBatchEvent;
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts; 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.gift.GiveGiftConfig;
import com.red.circle.other.app.dto.clientobject.gift.GiftBackpackLimitCO; 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.clientobject.gift.GiftConfigCO;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; 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.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.GiftBackpackLimit;
import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
@ -49,6 +51,7 @@ public interface GiftAppConvertor {
.setQuantity(cmd.getQuantity()) .setQuantity(cmd.getQuantity())
.setGiftConfig(toGiveGiftConfig(giftConfig)) .setGiftConfig(toGiveGiftConfig(giftConfig))
.setRoomId(cmd.getRoomId()) .setRoomId(cmd.getRoomId())
.setSendScene(GiftSendSceneEnum.ROOM)
.setDynamicContentId(cmd.getDynamicContentId()) .setDynamicContentId(cmd.getDynamicContentId())
.setTrackId(trackId) .setTrackId(trackId)
.setSongId(cmd.getSongId()) .setSongId(cmd.getSongId())
@ -103,10 +106,32 @@ public interface GiftAppConvertor {
.setQuantity(cmd.getQuantity()) .setQuantity(cmd.getQuantity())
.setGiftConfig(toGiveGiftConfig(giftConfig)) .setGiftConfig(toGiveGiftConfig(giftConfig))
.setRoomId(cmd.getRoomId()) .setRoomId(cmd.getRoomId())
.setSendScene(GiftSendSceneEnum.ROOM)
.setTrackId(trackId) .setTrackId(trackId)
.setBagGift(Boolean.TRUE) .setBagGift(Boolean.TRUE)
.setCreateTime(Objects.isNull(cmd.getReqTime()) ? TimestampUtils.now() .setCreateTime(Objects.isNull(cmd.getReqTime()) ? TimestampUtils.now()
: new Timestamp(cmd.getReqTime().getTime())); : 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()));
}
} }

View File

@ -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.ConsumerMessage;
import com.red.circle.component.mq.service.MessageListener; 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.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.cmd.message.CustomGroupMsgBodyCmd;
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum; import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
import com.red.circle.framework.core.dto.ReqSysOrigin; 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.command.game.teampk.GameTeamPkBaseInfoCmdExe;
import com.red.circle.other.app.common.gift.GameLuckyGiftCommon; 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.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.clientobject.gift.SendGiftNotifyCO;
import com.red.circle.other.app.dto.cmd.game.barrage.GameUserEffectsCmd; import com.red.circle.other.app.dto.cmd.game.barrage.GameUserEffectsCmd;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway; import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.live.LiveHeartbeatCache; import com.red.circle.other.domain.live.LiveHeartbeatCache;
import com.red.circle.other.domain.model.user.ability.RegionConfig; import com.red.circle.other.domain.model.user.ability.RegionConfig;
import com.red.circle.other.domain.ranking.RankingActivityType; 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.EnumConfigCacheService;
import com.red.circle.other.infra.database.cache.service.other.LiveHeartbeatCacheService; import com.red.circle.other.infra.database.cache.service.other.LiveHeartbeatCacheService;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftAcceptUser; 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.material.GiftSpecialEnum;
import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum; 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.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.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.date.TimestampUtils;
@ -86,6 +91,7 @@ import org.jetbrains.annotations.NotNull;
public class GiveGiftsListener implements MessageListener { public class GiveGiftsListener implements MessageListener {
private static final String TAG = "礼物消息"; private static final String TAG = "礼物消息";
private static final String PRIVATE_GIFT_IM_DESC = "PRIVATE_GIFT";
private final GameMqMessage gameMqMessage; private final GameMqMessage gameMqMessage;
private final GiftMqMessage giftMqMessage; private final GiftMqMessage giftMqMessage;
private final TeamMemberService teamMemberService; private final TeamMemberService teamMemberService;
@ -98,6 +104,8 @@ public class GiveGiftsListener implements MessageListener {
private final LiveHeartbeatCacheService liveHeartbeatCacheService; private final LiveHeartbeatCacheService liveHeartbeatCacheService;
private final GiftGiveRunningWaterService giftGiveRunningWaterService; private final GiftGiveRunningWaterService giftGiveRunningWaterService;
private final ImGroupClient imGroupClient; private final ImGroupClient imGroupClient;
private final ImMessageClient imMessageClient;
private final GiftCacheService giftCacheService;
private final RoomProfileManagerService roomProfileManagerService; private final RoomProfileManagerService roomProfileManagerService;
private final GameTeamPkBaseInfoCmdExe gameTeamPkBaseInfoCmdExe; private final GameTeamPkBaseInfoCmdExe gameTeamPkBaseInfoCmdExe;
private final GameRoomPkUserCmdExe gameRoomPkUserCmdExe; private final GameRoomPkUserCmdExe gameRoomPkUserCmdExe;
@ -370,6 +378,10 @@ public class GiveGiftsListener implements MessageListener {
return; return;
} }
if (event.privateChatScene()) {
sendPrivateGiftIM(event);
}
if (!SysOriginPlatformEnum.isVoiceSystem(runningWater.getSysOrigin())) { if (!SysOriginPlatformEnum.isVoiceSystem(runningWater.getSysOrigin())) {
return; return;
} }
@ -393,8 +405,47 @@ public class GiveGiftsListener implements MessageListener {
// 排行榜相关 // 排行榜相关
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.RANK_COUNT, runningWater); offlineProcessGiftEvent(OfflineStatisticsGiftEventType.RANK_COUNT, runningWater);
// 火箭统计相关 if (!event.privateChatScene()) {
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.ROCKET_COUNT, runningWater); // 火箭统计相关
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<GiftGiveRunningWater,Boolean> saveRunningWater(GiveAwayGiftBatchEvent event) { private Pair<GiftGiveRunningWater,Boolean> saveRunningWater(GiveAwayGiftBatchEvent event) {
@ -444,6 +495,7 @@ public class GiveGiftsListener implements MessageListener {
.processed(Boolean.FALSE) .processed(Boolean.FALSE)
.trackId(event.getTrackId().toString()) .trackId(event.getTrackId().toString())
.originId(Objects.nonNull(event.getRoomId()) ? event.getRoomId().toString() : "OTHER") .originId(Objects.nonNull(event.getRoomId()) ? event.getRoomId().toString() : "OTHER")
.sendScene(event.sendSceneOrRoom().name())
.dynamicContentId(Objects.nonNull(event.getDynamicContentId()) ? event.getDynamicContentId().toString() : null) .dynamicContentId(Objects.nonNull(event.getDynamicContentId()) ? event.getDynamicContentId().toString() : null)
.sysOrigin(event.getSysOrigin().name()) .sysOrigin(event.getSysOrigin().name())
.requestPlatform(event.getRequestPlatform()) .requestPlatform(event.getRequestPlatform())

View File

@ -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.clientobject.gift.UserGiftBackpackCO;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; 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.GiveAwayGiftBatchCmd;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -60,6 +61,11 @@ public class GiftServiceImpl implements GiftService {
return giftGiveAwayBatchCmdExe.execute(cmd); return giftGiveAwayBatchCmdExe.execute(cmd);
} }
@Override
public BigDecimal giveAwayPrivateGift(GiveAwayPrivateGiftCmd cmd) {
return giftGiveAwayBatchCmdExe.executePrivate(cmd);
}
@Override @Override
public BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd) { public BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd) {
return luckyGiftGiveCmdExe.execute(cmd); return luckyGiftGiveCmdExe.execute(cmd);

View File

@ -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");
}
}

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -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.clientobject.gift.UserGiftBackpackCO;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd; 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.GiveAwayGiftBatchCmd;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
@ -27,6 +28,8 @@ public interface GiftService {
BigDecimal giveAwayGiftBatch(GiveAwayGiftBatchCmd cmd); BigDecimal giveAwayGiftBatch(GiveAwayGiftBatchCmd cmd);
BigDecimal giveAwayPrivateGift(GiveAwayPrivateGiftCmd cmd);
BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd); BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd);
List<GiftWallCO> listUserGiftWall(AppUserIdCmd cmd); List<GiftWallCO> listUserGiftWall(AppUserIdCmd cmd);

View File

@ -47,6 +47,11 @@ public class GiftGiveRunningWater implements Serializable {
*/ */
private String originId; private String originId;
/**
* 发送场景.
*/
private String sendScene;
/** /**
* 动态id * 动态id
*/ */

View File

@ -89,6 +89,7 @@ public class GiftGiveRunningWaterServiceImpl implements GiftGiveRunningWaterServ
.songId(document.getLong("songId")) .songId(document.getLong("songId"))
.trackId(document.getString("trackId")) .trackId(document.getString("trackId"))
.originId(document.getString("originId")) .originId(document.getString("originId"))
.sendScene(document.getString("sendScene"))
.dynamicContentId(document.getString("dynamicContentId")) .dynamicContentId(document.getString("dynamicContentId"))
.sysOrigin(document.getString("sysOrigin")) .sysOrigin(document.getString("sysOrigin"))
.requestPlatform(document.getString("requestPlatform")) .requestPlatform(document.getString("requestPlatform"))