已有关系拒绝其他关系
This commit is contained in:
parent
dca9f12cb5
commit
3168637944
@ -40,7 +40,9 @@ public class ImMessageClientServiceImpl implements ImMessageClientService {
|
||||
.msgRandom(DataTypeUtils.toInteger(NumUtils.getRandomNumberString(9)))
|
||||
.build()
|
||||
.addMsgBody(MessageBody.customBody(cmd.getDesc(), cmd.getData()));
|
||||
imMessageManagerService.sendMessage(param).subscribe();
|
||||
imMessageManagerService.sendMessage(param)
|
||||
.subscribe(res -> log.warn("sendCustomMessage response result:{}", res),
|
||||
res -> log.error("sendCustomMessage error cmd={}, result={}", cmd, res));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,10 +6,12 @@ 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;
|
||||
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
|
||||
import com.red.circle.other.app.common.cp.CpRelationC2cNoticeUtils;
|
||||
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.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
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.entity.user.user.CpRelationship;
|
||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpValue;
|
||||
@ -22,10 +24,11 @@ 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.wallet.inner.model.cmd.GoldReceiptCmd;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -67,17 +70,22 @@ public class ProcessCpApplyCmd {
|
||||
ResponseAssert.isTrue(CommonErrorCode.STATE_ERROR,
|
||||
Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name()));
|
||||
|
||||
// 检查是否是复合申请
|
||||
CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
|
||||
cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), relationType);
|
||||
boolean isReconcile = Objects.nonNull(dismissingCp);
|
||||
|
||||
if (!isReconcile) {
|
||||
// 非复合申请:CP已存在
|
||||
boolean agree = isAgree(cmd);
|
||||
boolean isReconcile = false;
|
||||
|
||||
if (agree) {
|
||||
// 检查是否是复合申请
|
||||
CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
|
||||
cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), relationType);
|
||||
isReconcile = Objects.nonNull(dismissingCp);
|
||||
}
|
||||
|
||||
if (agree && !isReconcile) {
|
||||
// 非复合申请:两人之间只能存在一种正常关系
|
||||
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
|
||||
cpRelationshipService.existsCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(),
|
||||
relationType));
|
||||
|
||||
cpRelationshipService.existsRelationship(cpApply.getSendApplyUserId(),
|
||||
cpApply.getAcceptApplyUserId()));
|
||||
|
||||
int maxCount = relationMaxCount(relationType);
|
||||
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
|
||||
cpRelationshipService.countCp(cpApply.getSendApplyUserId(), relationType) >= maxCount);
|
||||
@ -86,13 +94,14 @@ public class ProcessCpApplyCmd {
|
||||
}
|
||||
|
||||
// 操作失败
|
||||
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
|
||||
cpApplyService.changeStatusById(cmd.getApplyId(), getStatus(cmd)));
|
||||
|
||||
if (!isAgree(cmd)) {
|
||||
returnGoldCoins(cmd, cpApply);
|
||||
return;
|
||||
}
|
||||
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
|
||||
cpApplyService.changeStatusById(cmd.getApplyId(), getStatus(cmd)));
|
||||
|
||||
if (!agree) {
|
||||
returnGoldCoins(cmd, cpApply);
|
||||
sendProcessNotice(cpApply, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReconcile) {
|
||||
// 复合:更新状态为NORMAL
|
||||
@ -105,24 +114,52 @@ public class ProcessCpApplyCmd {
|
||||
cmd.requireReqSysOrigin(), relationType);
|
||||
}
|
||||
|
||||
refuseStatusWaitIfLimitReached(cmd, cpApply, relationType);
|
||||
List<Long> refusedApplyIds = cpApplyService.refuseOtherWaitBetweenUsers(cpApply.getId(),
|
||||
cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId());
|
||||
boolean refusedByLimit = refuseStatusWaitIfLimitReached(cmd, cpApply, relationType);
|
||||
if (!refusedApplyIds.isEmpty() || refusedByLimit) {
|
||||
scheduleReimburse(cmd, cpApply.getId());
|
||||
}
|
||||
|
||||
cpRelationshipCacheService.remove(Arrays.asList(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()));
|
||||
cpRelationBroadcastMqClient.publish(cmd.requireReqSysOrigin(), cpApply.getId(),
|
||||
cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), relationType);
|
||||
sendProcessNotice(cpApply, true);
|
||||
}
|
||||
|
||||
private void refuseStatusWaitIfLimitReached(ProcessApplyCmd cmd, CpApply cpApply,
|
||||
private void sendProcessNotice(CpApply cpApply, boolean agree) {
|
||||
try {
|
||||
UserProfile senderProfile = userProfileGateway.getByUserId(cpApply.getSendApplyUserId());
|
||||
UserProfile acceptProfile = userProfileGateway.getByUserId(cpApply.getAcceptApplyUserId());
|
||||
if (Objects.isNull(senderProfile) || Objects.isNull(acceptProfile)) {
|
||||
log.info("[CP] skip process c2c notice, applyId={}, senderProfile={}, acceptProfile={}",
|
||||
cpApply.getId(), Objects.nonNull(senderProfile), Objects.nonNull(acceptProfile));
|
||||
return;
|
||||
}
|
||||
imMessageClient.sendCustomMessage(agree
|
||||
? CpRelationC2cNoticeUtils.buildAccepted(cpApply, senderProfile, acceptProfile)
|
||||
: CpRelationC2cNoticeUtils.buildRejected(cpApply, senderProfile, acceptProfile));
|
||||
} catch (Exception ex) {
|
||||
log.warn("[CP] send process c2c notice failed, applyId={}, agree={}, reason={}",
|
||||
cpApply.getId(), agree, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean refuseStatusWaitIfLimitReached(ProcessApplyCmd cmd, CpApply cpApply,
|
||||
String relationType) {
|
||||
if (cpRelationshipService.countCp(cpApply.getAcceptApplyUserId(), relationType)
|
||||
< relationMaxCount(relationType)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
cpApplyService.refuseStatusWait(cmd.requiredReqUserId(), relationType);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void scheduleReimburse(ProcessApplyCmd cmd, Long applyId) {
|
||||
userMqMessageService.cpApplyDelayed(
|
||||
new CpApplyEvent()
|
||||
.setId(cpApply.getId())
|
||||
.setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.requireReqSysOrigin())),
|
||||
.setId(applyId)
|
||||
.setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.requireReqSysOrigin())),
|
||||
TimestampUtils.nowPlusMinutes(1));
|
||||
}
|
||||
|
||||
|
||||
@ -2,16 +2,15 @@ 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.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;
|
||||
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
|
||||
import com.red.circle.other.app.common.cp.CpRelationC2cNoticeUtils;
|
||||
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
|
||||
import com.red.circle.other.app.util.OfficialNoticeUtils;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
|
||||
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
|
||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
|
||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
|
||||
@ -23,17 +22,12 @@ 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.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;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -48,7 +42,6 @@ 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;
|
||||
|
||||
@ -124,10 +117,9 @@ public class SendCpApplyCmdExe {
|
||||
boolean isReconcile = Objects.nonNull(dismissingCp);
|
||||
|
||||
if (!isReconcile) {
|
||||
// 非复合申请:检查CP是否已存在
|
||||
// 非复合申请:两人之间只能存在一种正常关系
|
||||
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
|
||||
cpRelationshipService
|
||||
.existsCp(sendUserId, acceptUserId, relationType.name()));
|
||||
cpRelationshipService.existsRelationship(sendUserId, acceptUserId));
|
||||
|
||||
validateRelationLimit(sendUserId, acceptUserId, relationType);
|
||||
}
|
||||
@ -172,8 +164,8 @@ public class SendCpApplyCmdExe {
|
||||
|
||||
cpApplyService.save(cpApply);
|
||||
|
||||
sendCpBuildIm(sendUserId, acceptUserId, cpApply.getId(), userProfile, isReconcile,
|
||||
relationType, applyText);
|
||||
sendCpBuildIm(sendUserId, acceptUserId, cpApply.getId(), userProfile, acceptUserProfile,
|
||||
isReconcile, relationType, applyText);
|
||||
|
||||
userMqMessageService.cpApplyDelayed(
|
||||
new CpApplyEvent()
|
||||
@ -183,29 +175,10 @@ public class SendCpApplyCmdExe {
|
||||
}
|
||||
|
||||
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());
|
||||
UserProfileDTO userProfile, UserProfileDTO acceptUserProfile, boolean isReconcile,
|
||||
CpRelationshipType relationType, String title) {
|
||||
imMessageClient.sendCustomMessage(CpRelationC2cNoticeUtils.buildInvitePending(sendUserId,
|
||||
acceptUserId, applyId, userProfile, acceptUserProfile, isReconcile, relationType, title));
|
||||
}
|
||||
|
||||
private BigDecimal normalizeApplyConsumeGold(BigDecimal applyConsumeGold) {
|
||||
|
||||
@ -0,0 +1,205 @@
|
||||
package com.red.circle.other.app.common.cp;
|
||||
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
|
||||
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public final class CpRelationC2cNoticeUtils {
|
||||
|
||||
public static final String NOTICE_TYPE = "CP_BUILD";
|
||||
public static final String ACTION_INVITE_PENDING = "INVITE_PENDING";
|
||||
public static final String ACTION_RELATION_ESTABLISHED = "RELATION_ESTABLISHED";
|
||||
public static final String ACTION_RELATION_REJECTED = "RELATION_REJECTED";
|
||||
public static final String ACTION_RELATION_EXPIRED = "RELATION_EXPIRED";
|
||||
|
||||
private CpRelationC2cNoticeUtils() {
|
||||
}
|
||||
|
||||
public static CustomC2cMsgBodyCmd buildInvitePending(Long fromUserId, Long toUserId,
|
||||
Long applyId, UserProfileDTO senderProfile, UserProfileDTO acceptProfile,
|
||||
boolean isReconcile, CpRelationshipType relationType, String title) {
|
||||
Map<String, Object> content = senderContent(senderProfile);
|
||||
content.put("applyType", isReconcile ? "RECONCILE" : "APPLY");
|
||||
content.put("relationType", relationType.name());
|
||||
putAcceptProfile(content, acceptProfile);
|
||||
|
||||
Map<String, Object> payload = basePayload(applyId, relationType.name(), title,
|
||||
ACTION_INVITE_PENDING, "PENDING", content);
|
||||
putSenderProfile(payload, senderProfile);
|
||||
putAcceptProfile(payload, acceptProfile);
|
||||
return buildMessage(fromUserId, toUserId, payload);
|
||||
}
|
||||
|
||||
public static CustomC2cMsgBodyCmd buildAccepted(CpApply cpApply, UserProfile senderProfile,
|
||||
UserProfile acceptProfile) {
|
||||
String relationType = CpRelationshipType.normalize(cpApply.getRelationType());
|
||||
String title = "User " + displayName(acceptProfile) + " accepted your "
|
||||
+ relationType.toLowerCase() + " invitation.";
|
||||
Map<String, Object> content = senderContent(senderProfile);
|
||||
content.put("applyType", "APPLY");
|
||||
content.put("relationType", relationType);
|
||||
putAcceptProfile(content, acceptProfile);
|
||||
|
||||
Map<String, Object> payload = basePayload(cpApply.getId(), relationType, title,
|
||||
ACTION_RELATION_ESTABLISHED, "ACCEPTED", content);
|
||||
payload.put("agree", true);
|
||||
putSenderProfile(payload, senderProfile);
|
||||
putAcceptProfile(payload, acceptProfile);
|
||||
return buildMessage(cpApply.getAcceptApplyUserId(), cpApply.getSendApplyUserId(), payload);
|
||||
}
|
||||
|
||||
public static CustomC2cMsgBodyCmd buildRejected(CpApply cpApply, UserProfile senderProfile,
|
||||
UserProfile acceptProfile) {
|
||||
String relationType = CpRelationshipType.normalize(cpApply.getRelationType());
|
||||
String title = "User " + displayName(acceptProfile) + " declined your "
|
||||
+ relationType.toLowerCase() + " invitation.";
|
||||
Map<String, Object> content = senderContent(senderProfile);
|
||||
content.put("applyType", "APPLY");
|
||||
content.put("relationType", relationType);
|
||||
putAcceptProfile(content, acceptProfile);
|
||||
|
||||
Map<String, Object> payload = basePayload(cpApply.getId(), relationType, title,
|
||||
ACTION_RELATION_REJECTED, "REJECTED", content);
|
||||
payload.put("agree", false);
|
||||
putSenderProfile(payload, senderProfile);
|
||||
putAcceptProfile(payload, acceptProfile);
|
||||
return buildMessage(cpApply.getAcceptApplyUserId(), cpApply.getSendApplyUserId(), payload);
|
||||
}
|
||||
|
||||
public static CustomC2cMsgBodyCmd buildExpiredToAcceptUser(CpApply cpApply,
|
||||
UserProfile senderProfile, UserProfile acceptProfile) {
|
||||
return buildExpired(cpApply, senderProfile, acceptProfile, cpApply.getSendApplyUserId(),
|
||||
cpApply.getAcceptApplyUserId());
|
||||
}
|
||||
|
||||
public static CustomC2cMsgBodyCmd buildExpiredToSendUser(CpApply cpApply,
|
||||
UserProfile senderProfile, UserProfile acceptProfile) {
|
||||
return buildExpired(cpApply, senderProfile, acceptProfile, cpApply.getAcceptApplyUserId(),
|
||||
cpApply.getSendApplyUserId());
|
||||
}
|
||||
|
||||
private static CustomC2cMsgBodyCmd buildExpired(CpApply cpApply, UserProfile senderProfile,
|
||||
UserProfile acceptProfile, Long fromUserId, Long toUserId) {
|
||||
String relationType = CpRelationshipType.normalize(cpApply.getRelationType());
|
||||
String title = "Your " + relationType.toLowerCase() + " invitation expired.";
|
||||
Map<String, Object> content = senderContent(senderProfile);
|
||||
content.put("applyType", "APPLY");
|
||||
content.put("relationType", relationType);
|
||||
putAcceptProfile(content, acceptProfile);
|
||||
|
||||
Map<String, Object> payload = basePayload(cpApply.getId(), relationType, title,
|
||||
ACTION_RELATION_EXPIRED, "EXPIRED", content);
|
||||
payload.put("expired", true);
|
||||
putSenderProfile(payload, senderProfile);
|
||||
putAcceptProfile(payload, acceptProfile);
|
||||
return buildMessage(fromUserId, toUserId, payload);
|
||||
}
|
||||
|
||||
private static Map<String, Object> basePayload(Long applyId, String relationType, String title,
|
||||
String action, String status, Map<String, Object> content) {
|
||||
Map<String, Object> payload = new LinkedHashMap<>();
|
||||
payload.put("noticeType", NOTICE_TYPE);
|
||||
payload.put("type", NOTICE_TYPE);
|
||||
payload.put("noticeAction", action);
|
||||
payload.put("action", action);
|
||||
payload.put("status", status);
|
||||
payload.put("result", status);
|
||||
payload.put("state", status);
|
||||
payload.put("title", title);
|
||||
payload.put("content", JacksonUtils.toJson(content));
|
||||
payload.put("expand", applyId);
|
||||
payload.put("applyId", applyId);
|
||||
payload.put("relationType", relationType);
|
||||
return payload;
|
||||
}
|
||||
|
||||
private static CustomC2cMsgBodyCmd buildMessage(Long fromUserId, Long toUserId,
|
||||
Map<String, Object> payload) {
|
||||
return CustomC2cMsgBodyCmd.builder()
|
||||
.fromAccount(fromUserId)
|
||||
.toAccount(toUserId)
|
||||
.desc(NOTICE_TYPE)
|
||||
.data(payload)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Map<String, Object> senderContent(UserProfileDTO userProfile) {
|
||||
Map<String, Object> content = new LinkedHashMap<>();
|
||||
content.put("userNickname", userProfile.getUserNickname());
|
||||
content.put("account", userProfile.getAccount());
|
||||
content.put("actualAccount", userProfile.actualAccount());
|
||||
content.put("userAvatar", userProfile.getUserAvatar());
|
||||
content.put("countryCode", userProfile.getCountryCode());
|
||||
content.put("countryName", userProfile.getCountryName());
|
||||
return content;
|
||||
}
|
||||
|
||||
private static Map<String, Object> senderContent(UserProfile userProfile) {
|
||||
Map<String, Object> content = new LinkedHashMap<>();
|
||||
content.put("userNickname", Objects.nonNull(userProfile) ? userProfile.getUserNickname() : null);
|
||||
content.put("account", Objects.nonNull(userProfile) ? userProfile.getAccount() : null);
|
||||
content.put("actualAccount", Objects.nonNull(userProfile) ? userProfile.getActualAccount() : null);
|
||||
content.put("userAvatar", Objects.nonNull(userProfile) ? userProfile.getUserAvatar() : null);
|
||||
content.put("countryCode", Objects.nonNull(userProfile) ? userProfile.getCountryCode() : null);
|
||||
content.put("countryName", Objects.nonNull(userProfile) ? userProfile.getCountryName() : null);
|
||||
return content;
|
||||
}
|
||||
|
||||
private static void putSenderProfile(Map<String, Object> payload, UserProfileDTO userProfile) {
|
||||
payload.put("account", userProfile.getAccount());
|
||||
payload.put("actualAccount", userProfile.actualAccount());
|
||||
payload.put("userNickname", userProfile.getUserNickname());
|
||||
payload.put("userAvatar", userProfile.getUserAvatar());
|
||||
}
|
||||
|
||||
private static void putSenderProfile(Map<String, Object> payload, UserProfile userProfile) {
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return;
|
||||
}
|
||||
payload.put("account", userProfile.getAccount());
|
||||
payload.put("actualAccount", userProfile.getActualAccount());
|
||||
payload.put("userNickname", userProfile.getUserNickname());
|
||||
payload.put("userAvatar", userProfile.getUserAvatar());
|
||||
}
|
||||
|
||||
private static void putAcceptProfile(Map<String, Object> payload, UserProfileDTO userProfile) {
|
||||
payload.put("acceptUserId", userProfile.getId());
|
||||
payload.put("acceptAccount", userProfile.getAccount());
|
||||
payload.put("acceptActualAccount", userProfile.actualAccount());
|
||||
payload.put("acceptNickname", userProfile.getUserNickname());
|
||||
payload.put("acceptUserNickname", userProfile.getUserNickname());
|
||||
payload.put("acceptUserAvatar", userProfile.getUserAvatar());
|
||||
}
|
||||
|
||||
private static void putAcceptProfile(Map<String, Object> payload, UserProfile userProfile) {
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return;
|
||||
}
|
||||
payload.put("acceptUserId", userProfile.getId());
|
||||
payload.put("acceptAccount", userProfile.getAccount());
|
||||
payload.put("acceptActualAccount", userProfile.getActualAccount());
|
||||
payload.put("acceptNickname", userProfile.getUserNickname());
|
||||
payload.put("acceptUserNickname", userProfile.getUserNickname());
|
||||
payload.put("acceptUserAvatar", userProfile.getUserAvatar());
|
||||
}
|
||||
|
||||
private static String displayName(UserProfile userProfile) {
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return "";
|
||||
}
|
||||
if (StringUtils.isNotBlank(userProfile.getUserNickname())) {
|
||||
return userProfile.getUserNickname();
|
||||
}
|
||||
if (StringUtils.isNotBlank(userProfile.getAccount())) {
|
||||
return userProfile.getAccount();
|
||||
}
|
||||
return Objects.toString(userProfile.getId(), "");
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,29 @@
|
||||
package com.red.circle.other.app.listener.user;
|
||||
|
||||
import com.red.circle.component.mq.MessageEventProcess;
|
||||
import com.red.circle.component.mq.MessageEventProcessDescribe;
|
||||
import com.red.circle.component.mq.config.RocketMqMessageListener;
|
||||
import com.red.circle.component.mq.service.Action;
|
||||
import com.red.circle.component.mq.service.ConsumerMessage;
|
||||
import com.red.circle.component.mq.service.MessageListener;
|
||||
import com.red.circle.mq.business.model.event.user.CpApplyEvent;
|
||||
import com.red.circle.mq.rocket.business.streams.CpApplySink;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
|
||||
import com.red.circle.component.mq.MessageEventProcess;
|
||||
import com.red.circle.component.mq.MessageEventProcessDescribe;
|
||||
import com.red.circle.component.mq.config.RocketMqMessageListener;
|
||||
import com.red.circle.component.mq.service.Action;
|
||||
import com.red.circle.component.mq.service.ConsumerMessage;
|
||||
import com.red.circle.component.mq.service.MessageListener;
|
||||
import com.red.circle.external.inner.endpoint.message.ImMessageClient;
|
||||
import com.red.circle.mq.business.model.event.user.CpApplyEvent;
|
||||
import com.red.circle.mq.rocket.business.streams.CpApplySink;
|
||||
import com.red.circle.other.app.common.cp.CpRelationC2cNoticeUtils;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
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.enums.user.user.CpApplyStatus;
|
||||
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.enums.GoldOrigin;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -29,11 +35,13 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RocketMqMessageListener(topic = "${rocketmq.topics.RC_DEFAULT_APP_DELAYED}", groupId = CpApplySink.INPUT, tag = CpApplySink.TAG)
|
||||
public class CpApplyListener implements MessageListener {
|
||||
|
||||
private final CpApplyService cpApplyService;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
private final MessageEventProcess messageEventProcess;
|
||||
public class CpApplyListener implements MessageListener {
|
||||
|
||||
private final CpApplyService cpApplyService;
|
||||
private final ImMessageClient imMessageClient;
|
||||
private final UserProfileGateway userProfileGateway;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
private final MessageEventProcess messageEventProcess;
|
||||
|
||||
@Override
|
||||
public Action consume(ConsumerMessage message) {
|
||||
@ -59,22 +67,38 @@ public class CpApplyListener implements MessageListener {
|
||||
return;
|
||||
}
|
||||
|
||||
// 等待超时
|
||||
if (Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name())) {
|
||||
reimburse(eventBody, cpApply);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 等待超时
|
||||
if (Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name())) {
|
||||
reimburse(eventBody, cpApply, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void checkReimburse(CpApplyEvent param, CpApply cpApply) {
|
||||
List<CpApply> cpApplies = cpApplyService.listRefuse(cpApply.getAcceptApplyUserId());
|
||||
if (CollectionUtils.isEmpty(cpApplies)) {
|
||||
return;
|
||||
}
|
||||
cpApplies.forEach(apply -> reimburse(param, apply));
|
||||
}
|
||||
|
||||
private void reimburse(CpApplyEvent param, CpApply cpApply) {
|
||||
private void checkReimburse(CpApplyEvent param, CpApply cpApply) {
|
||||
Map<Long, CpApply> applyMap = new LinkedHashMap<>();
|
||||
addReimburseApplies(applyMap, cpApplyService.listRefuse(cpApply.getAcceptApplyUserId()));
|
||||
addReimburseApplies(applyMap, cpApplyService.listRefuseBetweenUsers(cpApply.getSendApplyUserId(),
|
||||
cpApply.getAcceptApplyUserId()));
|
||||
if (applyMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
applyMap.values().forEach(apply -> reimburse(param, apply, false));
|
||||
}
|
||||
|
||||
private void addReimburseApplies(Map<Long, CpApply> applyMap, List<CpApply> cpApplies) {
|
||||
if (CollectionUtils.isEmpty(cpApplies)) {
|
||||
return;
|
||||
}
|
||||
cpApplies.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(apply -> Objects.nonNull(apply.getId()))
|
||||
.forEach(apply -> applyMap.putIfAbsent(apply.getId(), apply));
|
||||
}
|
||||
|
||||
private void reimburse(CpApplyEvent param, CpApply cpApply, boolean expired) {
|
||||
if (expired) {
|
||||
sendExpiredNotice(cpApply);
|
||||
}
|
||||
if (Objects.isNull(cpApply.getApplyConsumeGold())
|
||||
|| cpApply.getApplyConsumeGold().signum() <= 0) {
|
||||
cpApplyService.changeStatusById(cpApply.getId(), CpApplyStatus.REIMBURSE);
|
||||
@ -88,7 +112,26 @@ public class CpApplyListener implements MessageListener {
|
||||
.origin(GoldOrigin.CP_BUILD)
|
||||
.amount(cpApply.getApplyConsumeGold())
|
||||
.build());
|
||||
cpApplyService.changeStatusById(cpApply.getId(), CpApplyStatus.REIMBURSE);
|
||||
}
|
||||
|
||||
}
|
||||
cpApplyService.changeStatusById(cpApply.getId(), CpApplyStatus.REIMBURSE);
|
||||
}
|
||||
|
||||
private void sendExpiredNotice(CpApply cpApply) {
|
||||
try {
|
||||
UserProfile senderProfile = userProfileGateway.getByUserId(cpApply.getSendApplyUserId());
|
||||
UserProfile acceptProfile = userProfileGateway.getByUserId(cpApply.getAcceptApplyUserId());
|
||||
if (Objects.isNull(senderProfile) || Objects.isNull(acceptProfile)) {
|
||||
log.info("[CP] skip expired c2c notice, applyId={}, senderProfile={}, acceptProfile={}",
|
||||
cpApply.getId(), Objects.nonNull(senderProfile), Objects.nonNull(acceptProfile));
|
||||
return;
|
||||
}
|
||||
imMessageClient.sendCustomMessage(
|
||||
CpRelationC2cNoticeUtils.buildExpiredToAcceptUser(cpApply, senderProfile, acceptProfile));
|
||||
imMessageClient.sendCustomMessage(
|
||||
CpRelationC2cNoticeUtils.buildExpiredToSendUser(cpApply, senderProfile, acceptProfile));
|
||||
} catch (Exception ex) {
|
||||
log.warn("[CP] send expired c2c notice failed, applyId={}, reason={}", cpApply.getId(),
|
||||
ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,17 +1,23 @@
|
||||
package com.red.circle.other.app.command.user;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
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.ImMessageClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.mq.business.model.event.user.CpApplyEvent;
|
||||
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.domain.model.user.UserProfile;
|
||||
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;
|
||||
@ -19,7 +25,12 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipS
|
||||
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 java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
class ProcessCpApplyCmdTest {
|
||||
|
||||
@ -27,30 +38,75 @@ class ProcessCpApplyCmdTest {
|
||||
void agreeBrotherApplyShouldCreateBrotherRelationship() {
|
||||
Fixture fixture = new Fixture("BROTHER");
|
||||
|
||||
fixture.exe.execute(processCmd());
|
||||
fixture.exe.execute(processCmd(true));
|
||||
|
||||
verify(fixture.cpRelationshipService).addCp(eq(1001L), eq(2001L),
|
||||
nullable(Long.class), eq("LIKEI"), eq("BROTHER"));
|
||||
verify(fixture.cpRelationBroadcastMqClient).publish("LIKEI", 3001L, 1001L, 2001L,
|
||||
"BROTHER");
|
||||
assertProcessMessage(fixture.sentCustomMessage(), "RELATION_ESTABLISHED", "ACCEPTED",
|
||||
true, "BROTHER");
|
||||
}
|
||||
|
||||
@Test
|
||||
void agreeSistersApplyShouldCreateSistersRelationship() {
|
||||
Fixture fixture = new Fixture("SISTERS");
|
||||
|
||||
fixture.exe.execute(processCmd());
|
||||
fixture.exe.execute(processCmd(true));
|
||||
|
||||
verify(fixture.cpRelationshipService).addCp(eq(1001L), eq(2001L),
|
||||
nullable(Long.class), eq("LIKEI"), eq("SISTERS"));
|
||||
verify(fixture.cpRelationBroadcastMqClient).publish("LIKEI", 3001L, 1001L, 2001L,
|
||||
"SISTERS");
|
||||
assertProcessMessage(fixture.sentCustomMessage(), "RELATION_ESTABLISHED", "ACCEPTED",
|
||||
true, "SISTERS");
|
||||
}
|
||||
|
||||
private static ProcessApplyCmd processCmd() {
|
||||
@Test
|
||||
void rejectApplyShouldSendRejectedC2cMessageToSender() {
|
||||
Fixture fixture = new Fixture("CP");
|
||||
|
||||
fixture.exe.execute(processCmd(false));
|
||||
|
||||
assertProcessMessage(fixture.sentCustomMessage(), "RELATION_REJECTED", "REJECTED",
|
||||
false, "CP");
|
||||
}
|
||||
|
||||
@Test
|
||||
void agreeApplyShouldFailWhenPairAlreadyHasAnyRelation() {
|
||||
Fixture fixture = new Fixture("BROTHER");
|
||||
when(fixture.cpRelationshipService.existsRelationship(1001L, 2001L)).thenReturn(true);
|
||||
|
||||
Assertions.assertThrows(RuntimeException.class, () -> fixture.exe.execute(processCmd(true)));
|
||||
|
||||
verify(fixture.cpApplyService, never()).changeStatusById(eq(3001L),
|
||||
any(CpApplyStatus.class));
|
||||
verify(fixture.cpRelationshipService, never()).addCp(any(Long.class), any(Long.class),
|
||||
nullable(Long.class), any(String.class), any(String.class));
|
||||
verify(fixture.cpRelationBroadcastMqClient, never()).publish(any(String.class),
|
||||
any(Long.class), any(Long.class), any(Long.class), any(String.class));
|
||||
verify(fixture.imMessageClient, never()).sendCustomMessage(any(CustomC2cMsgBodyCmd.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void agreeApplyShouldRejectOtherWaitAppliesBetweenSameUsers() {
|
||||
Fixture fixture = new Fixture("CP");
|
||||
when(fixture.cpApplyService.refuseOtherWaitBetweenUsers(3001L, 1001L, 2001L))
|
||||
.thenReturn(List.of(3002L, 3003L));
|
||||
|
||||
fixture.exe.execute(processCmd(true));
|
||||
|
||||
verify(fixture.cpApplyService).refuseOtherWaitBetweenUsers(3001L, 1001L, 2001L);
|
||||
ArgumentCaptor<CpApplyEvent> eventCaptor = ArgumentCaptor.forClass(CpApplyEvent.class);
|
||||
verify(fixture.userMqMessageService).cpApplyDelayed(eventCaptor.capture(),
|
||||
any(Timestamp.class));
|
||||
Assertions.assertEquals(3001L, eventCaptor.getValue().getId());
|
||||
}
|
||||
|
||||
private static ProcessApplyCmd processCmd(boolean agree) {
|
||||
ProcessApplyCmd cmd = new ProcessApplyCmd()
|
||||
.setApplyId(3001L)
|
||||
.setAgree(Boolean.TRUE);
|
||||
.setAgree(agree);
|
||||
cmd.setReqUserId(2001L);
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI", "LIKEI"));
|
||||
return cmd;
|
||||
@ -65,25 +121,53 @@ class ProcessCpApplyCmdTest {
|
||||
.setStatus(CpApplyStatus.WAIT.name());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void assertProcessMessage(CustomC2cMsgBodyCmd message, String action,
|
||||
String status, Boolean agree, String relationType) {
|
||||
Assertions.assertEquals("2001", message.getFromAccount());
|
||||
Assertions.assertEquals("1001", message.getToAccount());
|
||||
Assertions.assertEquals("CP_BUILD", message.getDesc());
|
||||
Map<String, Object> data = (Map<String, Object>) message.getData();
|
||||
Assertions.assertEquals("CP_BUILD", data.get("noticeType"));
|
||||
Assertions.assertEquals(action, data.get("noticeAction"));
|
||||
Assertions.assertEquals(action, data.get("action"));
|
||||
Assertions.assertEquals(status, data.get("status"));
|
||||
Assertions.assertEquals(status, data.get("result"));
|
||||
Assertions.assertEquals(status, data.get("state"));
|
||||
Assertions.assertEquals(agree, data.get("agree"));
|
||||
Assertions.assertEquals(3001L, data.get("applyId"));
|
||||
Assertions.assertEquals(relationType, data.get("relationType"));
|
||||
Assertions.assertTrue(data.get("content").toString()
|
||||
.contains("\"relationType\":\"" + relationType + "\""));
|
||||
}
|
||||
|
||||
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 ImMessageClient imMessageClient = mock(ImMessageClient.class);
|
||||
private final UserMqMessageService userMqMessageService = mock(UserMqMessageService.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);
|
||||
when(cpApplyService.changeStatusById(3001L, CpApplyStatus.REFUSE)).thenReturn(true);
|
||||
when(cpApplyService.changeStatusById(3001L, CpApplyStatus.REIMBURSE)).thenReturn(true);
|
||||
when(cpApplyService.refuseOtherWaitBetweenUsers(3001L, 1001L, 2001L))
|
||||
.thenReturn(List.of());
|
||||
when(userProfileGateway.getByUserId(1001L)).thenReturn(profile(1001L));
|
||||
when(userProfileGateway.getByUserId(2001L)).thenReturn(profile(2001L));
|
||||
when(imMessageClient.sendCustomMessage(any(CustomC2cMsgBodyCmd.class)))
|
||||
.thenReturn(ResultResponse.success());
|
||||
|
||||
exe = new ProcessCpApplyCmd(
|
||||
cpApplyService,
|
||||
@ -96,5 +180,22 @@ class ProcessCpApplyCmdTest {
|
||||
cpRelationshipCacheService,
|
||||
cpRelationBroadcastMqClient);
|
||||
}
|
||||
|
||||
private CustomC2cMsgBodyCmd sentCustomMessage() {
|
||||
ArgumentCaptor<CustomC2cMsgBodyCmd> captor = ArgumentCaptor.forClass(
|
||||
CustomC2cMsgBodyCmd.class);
|
||||
verify(imMessageClient).sendCustomMessage(captor.capture());
|
||||
return captor.getValue();
|
||||
}
|
||||
|
||||
private static UserProfile profile(Long userId) {
|
||||
UserProfile userProfile = new UserProfile();
|
||||
userProfile.setId(userId);
|
||||
userProfile.setAccount(userId.toString());
|
||||
userProfile.setUserNickname("user-" + userId);
|
||||
userProfile.setCountryCode("SA");
|
||||
userProfile.setCountryName("Saudi Arabia");
|
||||
return userProfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import static org.mockito.Mockito.when;
|
||||
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.business.model.event.user.CpApplyEvent;
|
||||
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
|
||||
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
@ -24,6 +25,7 @@ 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.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -52,6 +54,27 @@ class SendCpApplyCmdExeTest {
|
||||
verify(fixture.cpApplyService, never()).save(any(CpApply.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoCreateFromCpGiftShouldSkipWhenAnyRelationAlreadyExistsInEitherDirection() {
|
||||
Fixture fixture = new Fixture(1, 0);
|
||||
when(fixture.cpRelationshipService.existsRelationship(1001L, 2001L)).thenReturn(true);
|
||||
|
||||
assertFalse(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "BROTHER"));
|
||||
verify(fixture.cpApplyService, never()).save(any(CpApply.class));
|
||||
verify(fixture.imMessageClient, never()).sendCustomMessage(any(CustomC2cMsgBodyCmd.class));
|
||||
verify(fixture.userMqMessageService, never()).cpApplyDelayed(any(CpApplyEvent.class),
|
||||
any(Timestamp.class));
|
||||
|
||||
Fixture reverseFixture = new Fixture(1, 0);
|
||||
when(reverseFixture.cpRelationshipService.existsRelationship(2001L, 1001L)).thenReturn(true);
|
||||
|
||||
assertFalse(reverseFixture.exe.autoCreateFromCpGift(2001L, 1001L, "LIKEI", "SISTERS"));
|
||||
verify(reverseFixture.cpApplyService, never()).save(any(CpApply.class));
|
||||
verify(reverseFixture.imMessageClient, never()).sendCustomMessage(any(CustomC2cMsgBodyCmd.class));
|
||||
verify(reverseFixture.userMqMessageService, never()).cpApplyDelayed(any(CpApplyEvent.class),
|
||||
any(Timestamp.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoCreateFromCpGiftShouldCreateIndependentAppliesForDifferentRelationTypes() {
|
||||
Fixture fixture = new Fixture(1, 0);
|
||||
@ -90,13 +113,13 @@ class SendCpApplyCmdExeTest {
|
||||
|
||||
private final CpApplyService cpApplyService = mock(CpApplyService.class);
|
||||
private final ImMessageClient imMessageClient = mock(ImMessageClient.class);
|
||||
private final UserMqMessageService userMqMessageService = mock(UserMqMessageService.class);
|
||||
private final CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
private final SendCpApplyCmdExe exe;
|
||||
private long nextApplyId = 3001L;
|
||||
|
||||
private Fixture(Integer sendSex, Integer acceptSex) {
|
||||
WalletGoldClient walletGoldClient = mock(WalletGoldClient.class);
|
||||
UserMqMessageService userMqMessageService = mock(UserMqMessageService.class);
|
||||
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
EnumConfigCacheService enumConfigCacheService = mock(EnumConfigCacheService.class);
|
||||
UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
|
||||
UserProfileAppConvertor userProfileAppConvertor = mock(UserProfileAppConvertor.class);
|
||||
@ -110,6 +133,7 @@ class SendCpApplyCmdExeTest {
|
||||
UserProfileDTO acceptProfileDTO = profile(2001L, acceptSex);
|
||||
|
||||
when(userRegionGateway.checkEqRegion(1001L, 2001L)).thenReturn(true);
|
||||
when(userRegionGateway.checkEqRegion(2001L, 1001L)).thenReturn(true);
|
||||
when(userProfileGateway.getByUserId(1001L)).thenReturn(sendProfile);
|
||||
when(userProfileGateway.getByUserId(2001L)).thenReturn(acceptProfile);
|
||||
when(userProfileAppConvertor.toUserProfileDTO(sendProfile)).thenReturn(sendProfileDTO);
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
package com.red.circle.other.app.listener.user;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.component.mq.MessageEventProcess;
|
||||
import com.red.circle.component.mq.MessageEventProcessDescribe;
|
||||
import com.red.circle.component.mq.service.Action;
|
||||
import com.red.circle.component.mq.service.ConsumerMessage;
|
||||
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.business.model.event.user.CpApplyEvent;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
|
||||
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
|
||||
import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
|
||||
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
class CpApplyListenerTest {
|
||||
|
||||
@Test
|
||||
void waitApplyExpiredShouldSendC2cMessageToBothUsers() {
|
||||
CpApplyService cpApplyService = mock(CpApplyService.class);
|
||||
ImMessageClient imMessageClient = mock(ImMessageClient.class);
|
||||
UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
|
||||
WalletGoldClient walletGoldClient = mock(WalletGoldClient.class);
|
||||
MessageEventProcess messageEventProcess = mock(MessageEventProcess.class);
|
||||
CpApply apply = new CpApply()
|
||||
.setId(3001L)
|
||||
.setSendApplyUserId(1001L)
|
||||
.setAcceptApplyUserId(2001L)
|
||||
.setRelationType("SISTERS")
|
||||
.setStatus(CpApplyStatus.WAIT.name());
|
||||
|
||||
when(cpApplyService.getById(3001L)).thenReturn(apply);
|
||||
when(cpApplyService.changeStatusById(3001L, CpApplyStatus.REIMBURSE)).thenReturn(true);
|
||||
when(userProfileGateway.getByUserId(1001L)).thenReturn(profile(1001L));
|
||||
when(userProfileGateway.getByUserId(2001L)).thenReturn(profile(2001L));
|
||||
when(imMessageClient.sendCustomMessage(any(CustomC2cMsgBodyCmd.class)))
|
||||
.thenReturn(ResultResponse.success());
|
||||
when(messageEventProcess.consume(any(MessageEventProcessDescribe.class),
|
||||
eq(CpApplyEvent.class), any())).thenAnswer(invocation -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
Consumer<CpApplyEvent> consumer = invocation.getArgument(2, Consumer.class);
|
||||
consumer.accept(new CpApplyEvent()
|
||||
.setId(3001L)
|
||||
.setSysOrigin(SysOriginPlatformEnum.LIKEI));
|
||||
return Action.SUCCESS;
|
||||
});
|
||||
|
||||
CpApplyListener listener = new CpApplyListener(cpApplyService, imMessageClient,
|
||||
userProfileGateway, walletGoldClient, messageEventProcess);
|
||||
|
||||
assertEquals(Action.SUCCESS, listener.consume(new ConsumerMessage()));
|
||||
|
||||
ArgumentCaptor<CustomC2cMsgBodyCmd> captor = ArgumentCaptor.forClass(
|
||||
CustomC2cMsgBodyCmd.class);
|
||||
verify(imMessageClient, org.mockito.Mockito.times(2)).sendCustomMessage(captor.capture());
|
||||
List<CustomC2cMsgBodyCmd> messages = captor.getAllValues();
|
||||
assertExpiredMessage(messages.get(0), "1001", "2001");
|
||||
assertExpiredMessage(messages.get(1), "2001", "1001");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void assertExpiredMessage(CustomC2cMsgBodyCmd message, String fromAccount,
|
||||
String toAccount) {
|
||||
assertEquals(fromAccount, message.getFromAccount());
|
||||
assertEquals(toAccount, message.getToAccount());
|
||||
assertEquals("CP_BUILD", message.getDesc());
|
||||
Map<String, Object> data = (Map<String, Object>) message.getData();
|
||||
assertEquals("RELATION_EXPIRED", data.get("noticeAction"));
|
||||
assertEquals("RELATION_EXPIRED", data.get("action"));
|
||||
assertEquals("EXPIRED", data.get("status"));
|
||||
assertEquals("EXPIRED", data.get("result"));
|
||||
assertEquals("EXPIRED", data.get("state"));
|
||||
assertEquals(Boolean.TRUE, data.get("expired"));
|
||||
assertEquals("SISTERS", data.get("relationType"));
|
||||
assertTrue(data.get("content").toString().contains("\"relationType\":\"SISTERS\""));
|
||||
}
|
||||
|
||||
private static UserProfile profile(Long userId) {
|
||||
UserProfile userProfile = new UserProfile();
|
||||
userProfile.setId(userId);
|
||||
userProfile.setAccount(userId.toString());
|
||||
userProfile.setUserNickname("user-" + userId);
|
||||
userProfile.setCountryCode("SA");
|
||||
userProfile.setCountryName("Saudi Arabia");
|
||||
return userProfile;
|
||||
}
|
||||
}
|
||||
@ -89,9 +89,19 @@ public interface CpApplyService extends BaseService<CpApply> {
|
||||
* 拒绝指定类型所有等待状态.
|
||||
*/
|
||||
void refuseStatusWait(Long acceptApplyUserId, String relationType);
|
||||
|
||||
/**
|
||||
* 获取我拒绝都信息.
|
||||
|
||||
/**
|
||||
* 拒绝两个用户之间除指定申请外的其他等待申请.
|
||||
*/
|
||||
List<Long> refuseOtherWaitBetweenUsers(Long excludeApplyId, Long userId, Long cpUserId);
|
||||
|
||||
/**
|
||||
* 获取两个用户之间已拒绝的申请.
|
||||
*/
|
||||
List<CpApply> listRefuseBetweenUsers(Long userId, Long cpUserId);
|
||||
|
||||
/**
|
||||
* 获取我拒绝都信息.
|
||||
*/
|
||||
List<CpApply> listRefuse(Long userId);
|
||||
|
||||
|
||||
@ -36,9 +36,14 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
|
||||
*/
|
||||
boolean existsCp(Long userId, Long cpUserId, String relationType);
|
||||
|
||||
/**
|
||||
* 获取分手中的cp信息集合
|
||||
*/
|
||||
/**
|
||||
* 两个用户之间是否存在任意正常关系.
|
||||
*/
|
||||
boolean existsRelationship(Long userId, Long cpUserId);
|
||||
|
||||
/**
|
||||
* 获取分手中的cp信息集合
|
||||
*/
|
||||
List<CpRelationship> getDismissingByUserId(Long userId);
|
||||
|
||||
/**
|
||||
|
||||
@ -115,19 +115,61 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
|
||||
.eq(CpApply::getAcceptApplyUserId, acceptApplyUserId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CpApply> listRefuse(Long userId) {
|
||||
return query()
|
||||
.eq(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> refuseOtherWaitBetweenUsers(Long excludeApplyId, Long userId, Long cpUserId) {
|
||||
List<Long> applyIds = query()
|
||||
.select(CpApply::getId)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT)
|
||||
.ne(Objects.nonNull(excludeApplyId), CpApply::getId, excludeApplyId)
|
||||
.and(where -> where
|
||||
.nested(w -> w.eq(CpApply::getSendApplyUserId, userId)
|
||||
.eq(CpApply::getAcceptApplyUserId, cpUserId))
|
||||
.or()
|
||||
.nested(w -> w.eq(CpApply::getSendApplyUserId, cpUserId)
|
||||
.eq(CpApply::getAcceptApplyUserId, userId)))
|
||||
.list()
|
||||
.stream()
|
||||
.map(CpApply::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (applyIds.isEmpty()) {
|
||||
return applyIds;
|
||||
}
|
||||
update()
|
||||
.set(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.set(CpApply::getUpdateTime, LocalDateTime.now())
|
||||
.in(CpApply::getId, applyIds)
|
||||
.execute();
|
||||
return applyIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CpApply> listRefuse(Long userId) {
|
||||
return query()
|
||||
.eq(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.eq(CpApply::getAcceptApplyUserId, userId)
|
||||
.last(PageConstant.MAX_LIMIT)
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CpApply> pageCpApply(CpApplyQryCmd query) {
|
||||
.last(PageConstant.MAX_LIMIT)
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CpApply> listRefuseBetweenUsers(Long userId, Long cpUserId) {
|
||||
return query()
|
||||
.eq(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.and(where -> where
|
||||
.nested(w -> w.eq(CpApply::getSendApplyUserId, userId)
|
||||
.eq(CpApply::getAcceptApplyUserId, cpUserId))
|
||||
.or()
|
||||
.nested(w -> w.eq(CpApply::getSendApplyUserId, cpUserId)
|
||||
.eq(CpApply::getAcceptApplyUserId, userId)))
|
||||
.last(PageConstant.MAX_LIMIT)
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CpApply> pageCpApply(CpApplyQryCmd query) {
|
||||
return query()
|
||||
.eq(Objects.nonNull(query.getSendApplyUserId()), CpApply::getSendApplyUserId,
|
||||
query.getSendApplyUserId())
|
||||
|
||||
@ -66,10 +66,25 @@ public class CpRelationshipServiceImpl extends
|
||||
.nested(w -> w.eq(CpRelationship::getUserId, cpUserId).eq(CpRelationship::getCpUserId, userId))
|
||||
)
|
||||
.last(PageConstant.LIMIT_ONE)
|
||||
.getOne()
|
||||
).map(cpRelationship -> Objects.nonNull(cpRelationship.getId())).orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
.getOne()
|
||||
).map(cpRelationship -> Objects.nonNull(cpRelationship.getId())).orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsRelationship(Long userId, Long cpUserId) {
|
||||
return Optional.ofNullable(
|
||||
query().select(CpRelationship::getId)
|
||||
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
|
||||
.and(where -> where
|
||||
.nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId))
|
||||
.or()
|
||||
.nested(w -> w.eq(CpRelationship::getUserId, cpUserId).eq(CpRelationship::getCpUserId, userId))
|
||||
)
|
||||
.last(PageConstant.LIMIT_ONE)
|
||||
.getOne()
|
||||
).map(cpRelationship -> Objects.nonNull(cpRelationship.getId())).orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CpRelationship> getDismissingByUserId(Long userId) {
|
||||
return getDismissingByUserId(userId, CpRelationshipType.CP.name());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user