From dca9f12cb536fa7874f92a16736090fc33ab6e78 Mon Sep 17 00:00:00 2001 From: hy001 Date: Fri, 22 May 2026 20:11:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8F=91IM=20C2c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../message/api/ImMessageClientApi.java | 28 +++-- .../cmd/message/CustomC2cMsgBodyCmd.java | 82 ++++++++++++++ .../message/ImMessageClientEndpoint.java | 27 +++-- .../message/ImMessageClientService.java | 23 ++-- .../impl/ImMessageClientServiceImpl.java | 40 +++++-- .../command/gift/BlessingsGiftGiveCmdExe.java | 15 +-- .../app/command/user/SendCpApplyCmdExe.java | 84 +++++++++++---- .../gift/strategy/GiftCountStrategy.java | 44 +++++++- .../command/user/ProcessCpApplyCmdTest.java | 100 ++++++++++++++++++ .../command/user/SendCpApplyCmdExeTest.java | 69 ++++++++++-- .../src/test/java/CpLocalFlowSmokeTest.java | 18 +++- 11 files changed, 452 insertions(+), 78 deletions(-) create mode 100644 rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/cmd/message/CustomC2cMsgBodyCmd.java create mode 100644 rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/ProcessCpApplyCmdTest.java 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 8a035257..902bfdb1 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,8 +1,12 @@ package com.red.circle.external.inner.endpoint.message.api; -import com.red.circle.framework.dto.ResultResponse; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; +import com.red.circle.framework.dto.ResultResponse; +import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd; +import jakarta.validation.Valid; +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; /** * im 消息服务. @@ -24,9 +28,15 @@ public interface ImMessageClientApi { /** * 发送文本消息. */ - @GetMapping("/sendMessageText/long") - ResultResponse sendMessageText(@RequestParam("fromAccount") Long fromAccount, - @RequestParam("toAccount") Long toAccount, - @RequestParam("text") String text); - -} + @GetMapping("/sendMessageText/long") + ResultResponse sendMessageText(@RequestParam("fromAccount") Long fromAccount, + @RequestParam("toAccount") Long toAccount, + @RequestParam("text") String text); + + /** + * 发送 C2C 自定义消息. + */ + @PostMapping("/sendCustomMessage") + ResultResponse sendCustomMessage(@RequestBody @Valid 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..ae2c28d7 --- /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,82 @@ +package com.red.circle.external.inner.model.cmd.message; + +import com.red.circle.framework.dto.Command; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.io.Serial; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * C2C custom IM message. + */ +@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; + + @NotNull(message = "data required.") + 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 fromAccount(Long fromAccount) { + this.fromAccount = String.valueOf(fromAccount); + return this; + } + + public CustomC2cMsgBodyBuilder toAccount(String toAccount) { + this.toAccount = toAccount; + return this; + } + + public CustomC2cMsgBodyBuilder toAccount(Long toAccount) { + this.toAccount = String.valueOf(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(this.fromAccount); + cmd.setToAccount(this.toAccount); + cmd.setDesc(this.desc); + cmd.setData(this.data); + return cmd; + } + } +} 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 c1be8772..6b9dcf2e 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,8 +1,9 @@ package com.red.circle.external.inner.endpoint.message; -import com.red.circle.external.inner.endpoint.message.api.ImMessageClientApi; -import com.red.circle.external.inner.service.message.ImMessageClientService; -import com.red.circle.framework.dto.ResultResponse; +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; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -30,10 +31,16 @@ public class ImMessageClientEndpoint implements ImMessageClientApi { } @Override - public ResultResponse sendMessageText(Long fromAccount, Long toAccount, String text) { - imMessageClientService.sendMessageText(DataTypeUtils.toString(fromAccount), - DataTypeUtils.toString(toAccount), text); - return ResultResponse.success(); - } - -} + public ResultResponse sendMessageText(Long fromAccount, Long toAccount, String text) { + imMessageClientService.sendMessageText(DataTypeUtils.toString(fromAccount), + DataTypeUtils.toString(toAccount), text); + 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 c9dc464d..1283f897 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,7 +1,9 @@ -package com.red.circle.external.inner.service.message; - -/** - * im 消息服务. +package com.red.circle.external.inner.service.message; + +import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd; + +/** + * im 消息服务. * * @author pengliang on 2023/7/12 */ @@ -9,7 +11,12 @@ public interface ImMessageClientService { /** * 发送文本消息. - */ - void sendMessageText(String fromAccount, String toAccount, String text); - -} + */ + 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 7751319e..707089bc 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,10 +1,16 @@ 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.external.inner.service.message.ImMessageClientService; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Service; +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.tool.core.num.NumUtils; +import com.red.circle.tool.core.parse.DataTypeUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; /** * im 消息服务. @@ -19,8 +25,22 @@ public class ImMessageClientServiceImpl implements ImMessageClientService { private final ImMessageManagerService imMessageManagerService; @Override - public void sendMessageText(String fromAccount, String toAccount, String text) { - imMessageManagerService.sendMessageText(fromAccount, toAccount, text).subscribe(); - } - -} + public void sendMessageText(String fromAccount, String toAccount, String text) { + imMessageManagerService.sendMessageText(fromAccount, toAccount, text).subscribe(); + } + + @Override + public void sendCustomMessage(CustomC2cMsgBodyCmd cmd) { + ImMessageParam param = ImMessageParam.builder() + .fromAccount(cmd.getFromAccount()) + .toAccount(cmd.getToAccount()) + .offlinePushInfo(OfflinePushInfo.builder() + .pushFlag(0) + .build()) + .msgRandom(DataTypeUtils.toInteger(NumUtils.getRandomNumberString(9))) + .build() + .addMsgBody(MessageBody.customBody(cmd.getDesc(), cmd.getData())); + imMessageManagerService.sendMessage(param).subscribe(); + } + +} 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 4e17f044..ca65417f 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 @@ -186,10 +186,11 @@ public class BlessingsGiftGiveCmdExe { giftConfig.setId(giftConfigCache.getId()); giftConfig.setGiftCandy(giftConfigCache.getGiftCandy()); giftConfig.setGiftIntegral(giftConfigCache.getGiftIntegral()); - giftConfig.setSpecial(giftConfigCache.getSpecial()); - giftConfig.setType(giftConfigCache.getType()); - giftConfig.setGiftTab(giftConfigCache.getGiftTab()); - return giftConfig; - } - -} + giftConfig.setSpecial(giftConfigCache.getSpecial()); + giftConfig.setType(giftConfigCache.getType()); + giftConfig.setGiftTab(giftConfigCache.getGiftTab()); + giftConfig.setCpRelationType(giftConfigCache.getCpRelationType()); + return giftConfig; + } + +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/SendCpApplyCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/SendCpApplyCmdExe.java index ecb456df..e4ff1362 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/SendCpApplyCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/SendCpApplyCmdExe.java @@ -1,9 +1,8 @@ package com.red.circle.other.app.command.user; -import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; -import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; -import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd; -import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum; +import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.external.inner.endpoint.message.ImMessageClient; +import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd; import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.response.CommonErrorCode; import com.red.circle.mq.business.model.event.user.CpApplyEvent; @@ -20,16 +19,18 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.enums.user.user.CpApplyStatus; import com.red.circle.other.infra.enums.user.user.CpRelationshipType; +import com.red.circle.other.inner.asserts.user.UserRelationErrorCode; import com.red.circle.other.inner.enums.config.EnumConfigKey; import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.tool.core.date.TimestampUtils; -import com.red.circle.other.inner.asserts.user.UserRelationErrorCode; -import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; +import com.red.circle.tool.core.json.JacksonUtils; +import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO; import com.red.circle.wallet.inner.model.enums.GoldOrigin; import java.math.BigDecimal; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -47,12 +48,13 @@ import org.springframework.stereotype.Component; @Slf4j public class SendCpApplyCmdExe { + private static final String CP_BUILD_NOTICE_TYPE = "CP_BUILD"; private static final int CP_MAX_COUNT = 1; private static final int FRIEND_MAX_COUNT = 3; private final CpApplyService cpApplyService; - private final OfficialNoticeClient officialNoticeClient; - private final WalletGoldClient walletGoldClient; + private final ImMessageClient imMessageClient; + private final WalletGoldClient walletGoldClient; private final UserMqMessageService userMqMessageService; private final CpRelationshipService cpRelationshipService; private final EnumConfigCacheService enumConfigCacheService; @@ -69,10 +71,22 @@ public class SendCpApplyCmdExe { return autoCreateFromCpGift(sendUserId, acceptUserId, sysOrigin, CpRelationshipType.CP.name()); } + public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin, + BigDecimal applyConsumeGold) { + return autoCreateFromCpGift(sendUserId, acceptUserId, sysOrigin, CpRelationshipType.CP.name(), + applyConsumeGold); + } + public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin, String relationTypeValue) { + return autoCreateFromCpGift(sendUserId, acceptUserId, sysOrigin, relationTypeValue, + BigDecimal.ZERO); + } + + public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin, + String relationTypeValue, BigDecimal applyConsumeGold) { try { - process(sendUserId, acceptUserId, sysOrigin, relationTypeValue, false); + process(sendUserId, acceptUserId, sysOrigin, relationTypeValue, false, applyConsumeGold); return true; } catch (Exception ex) { log.info( @@ -84,6 +98,11 @@ public class SendCpApplyCmdExe { private void process(Long sendUserId, Long acceptUserId, String sysOrigin, String relationTypeValue, boolean chargeGold) { + process(sendUserId, acceptUserId, sysOrigin, relationTypeValue, chargeGold, BigDecimal.ZERO); + } + + private void process(Long sendUserId, Long acceptUserId, String sysOrigin, String relationTypeValue, + boolean chargeGold, BigDecimal giftApplyConsumeGold) { CpRelationshipType relationType = parseRelationType(relationTypeValue); // 不可用操作自己 @@ -121,7 +140,7 @@ public class SendCpApplyCmdExe { ResponseAssert.isFalse(UserRelationErrorCode.OTHER_SIDE_ALREADY_APPLIED, cpApplyService.exists(sendUserId, acceptUserId, relationType.name())); - BigDecimal applyConsumeGold = BigDecimal.ZERO; + BigDecimal applyConsumeGold = normalizeApplyConsumeGold(giftApplyConsumeGold); if (chargeGold) { WalletReceiptResDTO receipt = ResponseAssert.requiredSuccess( walletGoldClient.changeBalance(GoldReceiptCmd.builder() @@ -153,16 +172,8 @@ public class SendCpApplyCmdExe { cpApplyService.save(cpApply); - Map map = OfficialNoticeUtils.buildUserProfile(userProfile); - map.put("applyType", isReconcile ? "RECONCILE" : "APPLY"); - map.put("relationType", relationType.name()); - officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder() - .toAccount(acceptUserId) - .noticeType(OfficialNoticeTypeEnum.CP_BUILD) - .templateParam(map) - .expand(cpApply.getId()) - .build() - ); + sendCpBuildIm(sendUserId, acceptUserId, cpApply.getId(), userProfile, isReconcile, + relationType, applyText); userMqMessageService.cpApplyDelayed( new CpApplyEvent() @@ -171,6 +182,39 @@ public class SendCpApplyCmdExe { TimestampUtils.nowPlusDays(1)); } + private void sendCpBuildIm(Long sendUserId, Long acceptUserId, Long applyId, + UserProfileDTO userProfile, boolean isReconcile, CpRelationshipType relationType, + String title) { + Map content = new LinkedHashMap<>(); + OfficialNoticeUtils.buildUserProfile(userProfile) + .forEach((key, value) -> content.put(String.valueOf(key), value)); + content.put("applyType", isReconcile ? "RECONCILE" : "APPLY"); + content.put("relationType", relationType.name()); + + Map payload = new LinkedHashMap<>(); + payload.put("noticeType", CP_BUILD_NOTICE_TYPE); + payload.put("type", CP_BUILD_NOTICE_TYPE); + payload.put("title", title); + payload.put("content", JacksonUtils.toJson(content)); + payload.put("expand", applyId); + payload.put("applyId", applyId); + payload.put("relationType", relationType.name()); + + imMessageClient.sendCustomMessage(CustomC2cMsgBodyCmd.builder() + .fromAccount(sendUserId) + .toAccount(acceptUserId) + .desc(CP_BUILD_NOTICE_TYPE) + .data(payload) + .build()); + } + + private BigDecimal normalizeApplyConsumeGold(BigDecimal applyConsumeGold) { + if (Objects.isNull(applyConsumeGold) || applyConsumeGold.compareTo(BigDecimal.ZERO) <= 0) { + return BigDecimal.ZERO; + } + return applyConsumeGold; + } + private void validateRelationLimit(Long sendUserId, Long acceptUserId, CpRelationshipType relationType) { int maxCount = relationMaxCount(relationType); diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/strategy/GiftCountStrategy.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/strategy/GiftCountStrategy.java index 7ee7fbd1..ac113ed2 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/strategy/GiftCountStrategy.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/gift/strategy/GiftCountStrategy.java @@ -379,15 +379,15 @@ public class GiftCountStrategy implements GiftStrategy { if (CollectionUtils.isEmpty(runningWater.getAcceptUsers())) { return; } - String relationType = CpRelationshipType.normalize( - runningWater.getGiftValue().getCpRelationType()); + String relationType = resolveCpGiftRelationType(runningWater); + BigDecimal applyConsumeGold = cpGiftApplyConsumeGold(runningWater.getGiftValue()); runningWater.getAcceptUsers().forEach(accept -> { if (Objects.equals(runningWater.getUserId(), accept.getAcceptUserId())) { return; } sendCpApplyCmdExe.autoCreateFromCpGift( runningWater.getUserId(), accept.getAcceptUserId(), runningWater.getSysOrigin(), - relationType); + relationType, applyConsumeGold); }); } catch (Exception ex) { log.warn("[CP] skip auto create cp apply from gift, runningWaterId={}, giftId={}, sendUserId={}, reason={}", @@ -398,6 +398,44 @@ public class GiftCountStrategy implements GiftStrategy { } } + private String resolveCpGiftRelationType(GiftGiveRunningWater runningWater) { + String relationType = Objects.nonNull(runningWater.getGiftValue()) + ? runningWater.getGiftValue().getCpRelationType() + : null; + if (StringUtils.isNotBlank(relationType)) { + return CpRelationshipType.normalize(relationType); + } + String configuredRelationType = cpRelationTypeFromGiftConfig(runningWater); + return CpRelationshipType.normalize(configuredRelationType); + } + + private String cpRelationTypeFromGiftConfig(GiftGiveRunningWater runningWater) { + if (Objects.isNull(runningWater) || Objects.isNull(runningWater.getGiftId())) { + log.warn("[CP] cp gift relation type missing and giftId missing, fallback CP. runningWaterId={}", + Objects.nonNull(runningWater) ? runningWater.getId() : null); + return CpRelationshipType.CP.name(); + } + GiftConfigDTO giftConfig = cacheGiftConfigManagerService.getById(runningWater.getGiftId()); + if (Objects.nonNull(giftConfig) && StringUtils.isNotBlank(giftConfig.getCpRelationType())) { + return giftConfig.getCpRelationType(); + } + log.warn("[CP] cp gift relation type missing from running water and config, fallback CP. runningWaterId={}, giftId={}", + runningWater.getId(), runningWater.getGiftId()); + return CpRelationshipType.CP.name(); + } + + private BigDecimal cpGiftApplyConsumeGold(GiftValue giftValue) { + if (Objects.isNull(giftValue) || !GiftCurrencyType.GOLD.eq(giftValue.getCurrencyType()) + || Objects.equals(giftValue.getBag(), Boolean.TRUE)) { + return BigDecimal.ZERO; + } + BigDecimal giftGold = giftValue.getGiftValue(); + if (Objects.isNull(giftGold) || giftGold.compareTo(BigDecimal.ZERO) <= 0) { + return BigDecimal.ZERO; + } + return giftGold; + } + private void sendCpBlessNotice(GiftGiveRunningWater runningWater, CpRelationship cpRelationship, GiftConfigDTO giftConfig) { UserProfile userProfile = userProfileRepository.getByUserId(runningWater.getUserId()); diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/ProcessCpApplyCmdTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/ProcessCpApplyCmdTest.java new file mode 100644 index 00000000..b09e9366 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/ProcessCpApplyCmdTest.java @@ -0,0 +1,100 @@ +package com.red.circle.other.app.command.user; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.red.circle.external.inner.endpoint.message.ImMessageClient; +import com.red.circle.framework.core.dto.ReqSysOrigin; +import com.red.circle.mq.rocket.business.producer.UserMqMessageService; +import com.red.circle.other.app.common.cp.CpRelationBroadcastMqClient; +import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService; +import com.red.circle.other.infra.database.rds.entity.user.user.CpApply; +import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService; +import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; +import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; +import com.red.circle.other.infra.enums.user.user.CpApplyStatus; +import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; +import org.junit.jupiter.api.Test; + +class ProcessCpApplyCmdTest { + + @Test + void agreeBrotherApplyShouldCreateBrotherRelationship() { + Fixture fixture = new Fixture("BROTHER"); + + fixture.exe.execute(processCmd()); + + verify(fixture.cpRelationshipService).addCp(eq(1001L), eq(2001L), + nullable(Long.class), eq("LIKEI"), eq("BROTHER")); + verify(fixture.cpRelationBroadcastMqClient).publish("LIKEI", 3001L, 1001L, 2001L, + "BROTHER"); + } + + @Test + void agreeSistersApplyShouldCreateSistersRelationship() { + Fixture fixture = new Fixture("SISTERS"); + + fixture.exe.execute(processCmd()); + + verify(fixture.cpRelationshipService).addCp(eq(1001L), eq(2001L), + nullable(Long.class), eq("LIKEI"), eq("SISTERS")); + verify(fixture.cpRelationBroadcastMqClient).publish("LIKEI", 3001L, 1001L, 2001L, + "SISTERS"); + } + + private static ProcessApplyCmd processCmd() { + ProcessApplyCmd cmd = new ProcessApplyCmd() + .setApplyId(3001L) + .setAgree(Boolean.TRUE); + cmd.setReqUserId(2001L); + cmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI", "LIKEI")); + return cmd; + } + + private static CpApply apply(String relationType) { + return new CpApply() + .setId(3001L) + .setSendApplyUserId(1001L) + .setAcceptApplyUserId(2001L) + .setRelationType(relationType) + .setStatus(CpApplyStatus.WAIT.name()); + } + + private static class Fixture { + + private final CpApplyService cpApplyService = mock(CpApplyService.class); + private final CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class); + private final CpRelationBroadcastMqClient cpRelationBroadcastMqClient = mock( + CpRelationBroadcastMqClient.class); + private final ProcessCpApplyCmd exe; + + private Fixture(String relationType) { + CpValueService cpValueService = mock(CpValueService.class); + ImMessageClient imMessageClient = mock(ImMessageClient.class); + WalletGoldClient walletGoldClient = mock(WalletGoldClient.class); + UserMqMessageService userMqMessageService = mock(UserMqMessageService.class); + UserProfileGateway userProfileGateway = mock(UserProfileGateway.class); + CpRelationshipCacheService cpRelationshipCacheService = mock( + CpRelationshipCacheService.class); + + when(cpApplyService.getById(3001L)).thenReturn(apply(relationType)); + when(cpApplyService.changeStatusById(3001L, CpApplyStatus.AGREE)).thenReturn(true); + + exe = new ProcessCpApplyCmd( + cpApplyService, + cpValueService, + imMessageClient, + walletGoldClient, + userMqMessageService, + cpRelationshipService, + userProfileGateway, + cpRelationshipCacheService, + cpRelationBroadcastMqClient); + } + } +} diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/SendCpApplyCmdExeTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/SendCpApplyCmdExeTest.java index d2b1f809..05e7b11c 100644 --- a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/SendCpApplyCmdExeTest.java +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/user/SendCpApplyCmdExeTest.java @@ -4,13 +4,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; 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.external.inner.endpoint.message.OfficialNoticeClient; -import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd; +import com.red.circle.external.inner.endpoint.message.ImMessageClient; +import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd; import com.red.circle.framework.dto.ResultResponse; import com.red.circle.mq.rocket.business.producer.UserMqMessageService; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; @@ -23,6 +24,8 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -49,13 +52,48 @@ class SendCpApplyCmdExeTest { verify(fixture.cpApplyService, never()).save(any(CpApply.class)); } + @Test + void autoCreateFromCpGiftShouldCreateIndependentAppliesForDifferentRelationTypes() { + Fixture fixture = new Fixture(1, 0); + + assertTrue(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "CP")); + assertTrue(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "BROTHER")); + assertTrue(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "SISTERS")); + + List applies = fixture.savedApplies(3); + assertEquals("CP", applies.get(0).getRelationType()); + assertEquals("BROTHER", applies.get(1).getRelationType()); + assertEquals("SISTERS", applies.get(2).getRelationType()); + + List messages = fixture.sentCustomMessages(3); + assertEquals("1001", messages.get(0).getFromAccount()); + assertEquals("2001", messages.get(0).getToAccount()); + assertCpBuildMessage(messages.get(0), "CP", 3001L); + assertCpBuildMessage(messages.get(1), "BROTHER", 3002L); + assertCpBuildMessage(messages.get(2), "SISTERS", 3003L); + } + + @SuppressWarnings("unchecked") + private static void assertCpBuildMessage(CustomC2cMsgBodyCmd message, String relationType, + Long applyId) { + assertEquals("CP_BUILD", message.getDesc()); + Map data = (Map) message.getData(); + assertEquals("CP_BUILD", data.get("noticeType")); + assertEquals("CP_BUILD", data.get("type")); + assertEquals(applyId, data.get("expand")); + assertEquals(applyId, data.get("applyId")); + assertEquals(relationType, data.get("relationType")); + assertTrue(data.get("content").toString().contains("\"relationType\":\"" + relationType + "\"")); + } + private static class Fixture { private final CpApplyService cpApplyService = mock(CpApplyService.class); + private final ImMessageClient imMessageClient = mock(ImMessageClient.class); private final SendCpApplyCmdExe exe; + private long nextApplyId = 3001L; private Fixture(Integer sendSex, Integer acceptSex) { - OfficialNoticeClient officialNoticeClient = mock(OfficialNoticeClient.class); WalletGoldClient walletGoldClient = mock(WalletGoldClient.class); UserMqMessageService userMqMessageService = mock(UserMqMessageService.class); CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class); @@ -65,7 +103,9 @@ class SendCpApplyCmdExeTest { UserRegionGateway userRegionGateway = mock(UserRegionGateway.class); UserProfile sendProfile = new UserProfile(); + sendProfile.setId(1001L); UserProfile acceptProfile = new UserProfile(); + acceptProfile.setId(2001L); UserProfileDTO sendProfileDTO = profile(1001L, sendSex); UserProfileDTO acceptProfileDTO = profile(2001L, acceptSex); @@ -74,12 +114,17 @@ class SendCpApplyCmdExeTest { when(userProfileGateway.getByUserId(2001L)).thenReturn(acceptProfile); when(userProfileAppConvertor.toUserProfileDTO(sendProfile)).thenReturn(sendProfileDTO); when(userProfileAppConvertor.toUserProfileDTO(acceptProfile)).thenReturn(acceptProfileDTO); - when(officialNoticeClient.send(any(NoticeExtTemplateTypeCmd.class))) + when(imMessageClient.sendCustomMessage(any(CustomC2cMsgBodyCmd.class))) .thenReturn(ResultResponse.success()); + when(cpApplyService.save(any(CpApply.class))).thenAnswer(invocation -> { + CpApply apply = invocation.getArgument(0); + apply.setId(nextApplyId++); + return true; + }); exe = new SendCpApplyCmdExe( cpApplyService, - officialNoticeClient, + imMessageClient, walletGoldClient, userMqMessageService, cpRelationshipService, @@ -91,9 +136,19 @@ class SendCpApplyCmdExeTest { } private CpApply savedApply() { + return savedApplies(1).get(0); + } + + private List savedApplies(int count) { ArgumentCaptor captor = ArgumentCaptor.forClass(CpApply.class); - verify(cpApplyService).save(captor.capture()); - return captor.getValue(); + verify(cpApplyService, times(count)).save(captor.capture()); + return captor.getAllValues(); + } + + private List sentCustomMessages(int count) { + ArgumentCaptor captor = ArgumentCaptor.forClass(CustomC2cMsgBodyCmd.class); + verify(imMessageClient, times(count)).sendCustomMessage(captor.capture()); + return captor.getAllValues(); } private static UserProfileDTO profile(Long userId, Integer sex) { diff --git a/rc-service/rc-service-other/other-start/src/test/java/CpLocalFlowSmokeTest.java b/rc-service/rc-service-other/other-start/src/test/java/CpLocalFlowSmokeTest.java index b0138a5e..718c6aa0 100644 --- a/rc-service/rc-service-other/other-start/src/test/java/CpLocalFlowSmokeTest.java +++ b/rc-service/rc-service-other/other-start/src/test/java/CpLocalFlowSmokeTest.java @@ -215,8 +215,9 @@ public class CpLocalFlowSmokeTest { } private void assertGiftAutoApplyRelationType(UserPair pair, String relationType) { + BigDecimal giftConsumeGold = BigDecimal.valueOf(66); boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(pair.sendUserId, - pair.acceptUserId, SYS_ORIGIN, relationType); + pair.acceptUserId, SYS_ORIGIN, relationType, giftConsumeGold); assertTrue(relationType + " gift should auto create apply", created); Long applyId = cpApplyService.getId(pair.acceptUserId, pair.sendUserId, relationType); @@ -225,12 +226,21 @@ public class CpLocalFlowSmokeTest { CpApply apply = cpApplyService.getById(applyId); assertEquals(relationType, apply.getRelationType()); - assertEquals(0, BigDecimal.ZERO.compareTo(apply.getApplyConsumeGold())); + assertEquals(0, giftConsumeGold.compareTo(apply.getApplyConsumeGold())); + + agree(applyId, pair.acceptUserId); + CpRelationship relationship = cpRelationshipService.getByUserId(pair.sendUserId, + pair.acceptUserId, relationType); + assertNotNull(relationType + " relationship after gift auto apply agree", relationship); + assertEquals(relationType, relationship.getRelationType()); + createdPairs.add(new RelationPair(pair.sendUserId, pair.acceptUserId, relationType)); + createdCpValIds.add(relationship.getCpValId()); } private void runCpGiftApplyBlessDismissFlow(UserPair pair) throws Exception { + BigDecimal giftConsumeGold = BigDecimal.valueOf(88); boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(pair.sendUserId, pair.acceptUserId, - SYS_ORIGIN); + SYS_ORIGIN, giftConsumeGold); Long pendingApplyId = cpApplyService.getId(pair.acceptUserId, pair.sendUserId, "CP"); if (pendingApplyId != null && !createdApplyIds.contains(pendingApplyId)) { createdApplyIds.add(pendingApplyId); @@ -243,7 +253,7 @@ public class CpLocalFlowSmokeTest { CpApply apply = cpApplyService.getById(applyId); assertEquals("CP", apply.getRelationType()); - assertEquals(0, BigDecimal.ZERO.compareTo(apply.getApplyConsumeGold())); + assertEquals(0, giftConsumeGold.compareTo(apply.getApplyConsumeGold())); agree(applyId, pair.acceptUserId); CpRelationship relationship = cpRelationshipService.getByUserId(pair.sendUserId,