新增发IM C2c

This commit is contained in:
hy001 2026-05-22 20:11:20 +08:00
parent a0461ce68c
commit dca9f12cb5
11 changed files with 452 additions and 78 deletions

View File

@ -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<Void> sendMessageText(@RequestParam("fromAccount") Long fromAccount,
@RequestParam("toAccount") Long toAccount,
@RequestParam("text") String text);
}
@GetMapping("/sendMessageText/long")
ResultResponse<Void> sendMessageText(@RequestParam("fromAccount") Long fromAccount,
@RequestParam("toAccount") Long toAccount,
@RequestParam("text") String text);
/**
* 发送 C2C 自定义消息.
*/
@PostMapping("/sendCustomMessage")
ResultResponse<Void> sendCustomMessage(@RequestBody @Valid CustomC2cMsgBodyCmd cmd);
}

View File

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

View File

@ -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<Void> sendMessageText(Long fromAccount, Long toAccount, String text) {
imMessageClientService.sendMessageText(DataTypeUtils.toString(fromAccount),
DataTypeUtils.toString(toAccount), text);
return ResultResponse.success();
}
}
public ResultResponse<Void> sendMessageText(Long fromAccount, Long toAccount, String text) {
imMessageClientService.sendMessageText(DataTypeUtils.toString(fromAccount),
DataTypeUtils.toString(toAccount), text);
return ResultResponse.success();
}
@Override
public ResultResponse<Void> sendCustomMessage(CustomC2cMsgBodyCmd cmd) {
imMessageClientService.sendCustomMessage(cmd);
return ResultResponse.success();
}
}

View File

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

View File

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

View File

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

View File

@ -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<Object, Object> 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<String, Object> 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<String, Object> 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);

View File

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

View File

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

View File

@ -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<CpApply> applies = fixture.savedApplies(3);
assertEquals("CP", applies.get(0).getRelationType());
assertEquals("BROTHER", applies.get(1).getRelationType());
assertEquals("SISTERS", applies.get(2).getRelationType());
List<CustomC2cMsgBodyCmd> 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<String, Object> data = (Map<String, Object>) 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<CpApply> savedApplies(int count) {
ArgumentCaptor<CpApply> 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<CustomC2cMsgBodyCmd> sentCustomMessages(int count) {
ArgumentCaptor<CustomC2cMsgBodyCmd> captor = ArgumentCaptor.forClass(CustomC2cMsgBodyCmd.class);
verify(imMessageClient, times(count)).sendCustomMessage(captor.capture());
return captor.getAllValues();
}
private static UserProfileDTO profile(Long userId, Integer sex) {

View File

@ -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,