Merge branch 'test'
This commit is contained in:
commit
c2bd1554f9
@ -4,6 +4,7 @@ import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||
import com.red.circle.framework.web.controller.BaseController;
|
||||
import com.red.circle.other.app.command.user.LoveLetterQueryCmdExe;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpDismissApplyCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpGiftRelationCheckCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
|
||||
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO;
|
||||
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO;
|
||||
@ -11,6 +12,7 @@ import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyStatusQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpGiftRelationCheckCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd;
|
||||
@ -124,7 +126,20 @@ public class CpRelationshipRestController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送CP告白信封.
|
||||
* CP礼物关系名额校验.
|
||||
*
|
||||
* @eo.name CP礼物关系名额校验.
|
||||
* @eo.url /gift-relation/check
|
||||
* @eo.method get
|
||||
* @eo.request-type formdata
|
||||
*/
|
||||
@GetMapping("/gift-relation/check")
|
||||
public CpGiftRelationCheckCO checkGiftRelation(@Validated CpGiftRelationCheckCmd cmd) {
|
||||
return userCpRelationService.checkGiftRelation(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送CP告白信封.
|
||||
*
|
||||
* @eo.name 发送CP告白信封
|
||||
* @eo.url /send-love-letter
|
||||
|
||||
@ -19,6 +19,7 @@ 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 java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -40,6 +41,7 @@ public class ProcessCpApplyCmd {
|
||||
|
||||
private static final int CP_MAX_COUNT = 1;
|
||||
private static final int FRIEND_MAX_COUNT = 3;
|
||||
private static final long APPLY_WAIT_EXPIRE_HOURS = 24L;
|
||||
|
||||
private final CpApplyService cpApplyService;
|
||||
private final CpValueService cpValueService;
|
||||
@ -60,8 +62,9 @@ public class ProcessCpApplyCmd {
|
||||
String relationType = CpRelationshipType.normalize(cpApply.getRelationType());
|
||||
|
||||
// 状态错误
|
||||
ResponseAssert.isTrue(CommonErrorCode.STATE_ERROR,
|
||||
Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name()));
|
||||
ResponseAssert.isTrue(CommonErrorCode.STATE_ERROR,
|
||||
Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name()));
|
||||
ResponseAssert.isFalse(CommonErrorCode.STATE_ERROR, isOverdueWait(cpApply));
|
||||
|
||||
boolean agree = isAgree(cmd);
|
||||
boolean isReconcile = false;
|
||||
@ -114,6 +117,13 @@ public class ProcessCpApplyCmd {
|
||||
sendProcessNotice(cpApply, true);
|
||||
}
|
||||
|
||||
private boolean isOverdueWait(CpApply cpApply) {
|
||||
return Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name())
|
||||
&& Objects.nonNull(cpApply.getCreateTime())
|
||||
&& cpApply.getCreateTime().toLocalDateTime()
|
||||
.isBefore(LocalDateTime.now().minusHours(APPLY_WAIT_EXPIRE_HOURS));
|
||||
}
|
||||
|
||||
private void sendProcessNotice(CpApply cpApply, boolean agree) {
|
||||
try {
|
||||
UserProfile senderProfile = userProfileGateway.getByUserId(cpApply.getSendApplyUserId());
|
||||
@ -134,9 +144,15 @@ public class ProcessCpApplyCmd {
|
||||
|
||||
private List<CpApply> autoRefuseWaitAppliesAfterAgree(CpApply cpApply, String relationType) {
|
||||
Map<Long, CpApply> refusedApplyMap = new LinkedHashMap<>();
|
||||
addRefusedApplies(refusedApplyMap,
|
||||
cpApplyService.refuseOtherWaitAppliesBetweenUsers(cpApply.getId(),
|
||||
cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()));
|
||||
List<CpApply> samePairRefusedApplies = cpApplyService.refuseOtherWaitAppliesBetweenUsers(
|
||||
cpApply.getId(), cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId());
|
||||
addRefusedApplies(refusedApplyMap, samePairRefusedApplies);
|
||||
if (!samePairRefusedApplies.isEmpty()) {
|
||||
log.info("[CP] auto refused other wait applies between same users after agree, "
|
||||
+ "agreeApplyId={}, sendUserId={}, acceptUserId={}, applyIds={}",
|
||||
cpApply.getId(), cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(),
|
||||
samePairRefusedApplies.stream().map(CpApply::getId).toList());
|
||||
}
|
||||
addRefusedApplies(refusedApplyMap,
|
||||
refuseWaitAppliesIfLimitReached(cpApply, cpApply.getSendApplyUserId(), relationType));
|
||||
addRefusedApplies(refusedApplyMap,
|
||||
|
||||
@ -42,9 +42,6 @@ import org.springframework.stereotype.Component;
|
||||
@Slf4j
|
||||
public class SendCpApplyCmdExe {
|
||||
|
||||
private static final int CP_MAX_COUNT = 1;
|
||||
private static final int FRIEND_MAX_COUNT = 3;
|
||||
|
||||
private final CpApplyService cpApplyService;
|
||||
private final ImMessageClient imMessageClient;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
@ -108,6 +105,9 @@ public class SendCpApplyCmdExe {
|
||||
ResponseAssert.isFalse(UserRelationErrorCode.UNAVAILABLE_OPS_YOURSELF,
|
||||
Objects.equals(sendUserId, acceptUserId));
|
||||
|
||||
cpApplyService.expireOverdueWaitApplies(sendUserId);
|
||||
cpApplyService.expireOverdueWaitApplies(acceptUserId);
|
||||
|
||||
boolean eqRegion = userRegionGateway.checkEqRegion(sendUserId, acceptUserId);
|
||||
ResponseAssert.isTrue(UserRelationErrorCode.UNAVAILABLE_NOT_REGION, eqRegion);
|
||||
|
||||
@ -204,7 +204,7 @@ public class SendCpApplyCmdExe {
|
||||
}
|
||||
|
||||
private int relationMaxCount(CpRelationshipType relationType) {
|
||||
return Objects.equals(relationType, CpRelationshipType.CP) ? CP_MAX_COUNT : FRIEND_MAX_COUNT;
|
||||
return relationType.maxCount();
|
||||
}
|
||||
|
||||
private CpRelationshipType parseRelationType(String relationTypeValue) {
|
||||
|
||||
@ -3,7 +3,8 @@ package com.red.circle.other.app.command.user.query;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
|
||||
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
|
||||
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
|
||||
import java.util.Optional;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -16,13 +17,27 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class CpApplyIdQryExe {
|
||||
|
||||
private final CpApplyService cpApplyService;
|
||||
|
||||
private final CpApplyService cpApplyService;
|
||||
|
||||
public String execute(CpApplyIdQueryCmd cmd) {
|
||||
return Optional.ofNullable(cpApplyService.getId(cmd.requiredReqUserId(), cmd.getUserId(),
|
||||
CpRelationshipType.normalize(cmd.getRelationType())))
|
||||
Long currentUserId = cmd.requiredReqUserId();
|
||||
Long targetUserId = cmd.getUserId();
|
||||
String relationType = CpRelationshipType.normalize(cmd.getRelationType());
|
||||
cpApplyService.expireOverdueWaitApplies(currentUserId);
|
||||
cpApplyService.expireOverdueWaitApplies(targetUserId);
|
||||
|
||||
Long applyId = cpApplyService.getId(targetUserId, currentUserId, relationType);
|
||||
if (isEmptyApplyId(applyId)) {
|
||||
applyId = cpApplyService.getId(currentUserId, targetUserId, relationType);
|
||||
}
|
||||
return Optional.ofNullable(applyId)
|
||||
.filter(id -> id > 0)
|
||||
.map(String::valueOf)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean isEmptyApplyId(Long applyId) {
|
||||
return Objects.isNull(applyId) || applyId <= 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,6 +3,8 @@ package com.red.circle.other.app.command.user.query;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyStatusQueryCmd;
|
||||
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 java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -14,10 +16,25 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class CpApplyStatusQryExe {
|
||||
|
||||
private static final long APPLY_WAIT_EXPIRE_HOURS = 24L;
|
||||
|
||||
private final CpApplyService cpApplyService;
|
||||
|
||||
public String execute(CpApplyStatusQueryCmd cmd) {
|
||||
CpApply cpApply = cpApplyService.getById(cmd.getApplyId());
|
||||
return Objects.nonNull(cpApply) ? cpApply.getStatus() : null;
|
||||
if (Objects.isNull(cpApply)) {
|
||||
return null;
|
||||
}
|
||||
if (isOverdueWait(cpApply)) {
|
||||
return CpApplyStatus.EXPIRE.name();
|
||||
}
|
||||
return cpApply.getStatus();
|
||||
}
|
||||
|
||||
private boolean isOverdueWait(CpApply cpApply) {
|
||||
return Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name())
|
||||
&& Objects.nonNull(cpApply.getCreateTime())
|
||||
&& cpApply.getCreateTime().toLocalDateTime()
|
||||
.isBefore(LocalDateTime.now().minusHours(APPLY_WAIT_EXPIRE_HOURS));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
package com.red.circle.other.app.command.user.query;
|
||||
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpGiftRelationCheckCO;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpGiftRelationCheckCmd;
|
||||
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
|
||||
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Check whether a relationship gift can trigger a relationship apply.
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CpGiftRelationCheckQryExe {
|
||||
|
||||
private static final String OK_MSG = "OK";
|
||||
private static final String USER_FULL_MSG = "Your relationships are full.";
|
||||
private static final String ACCEPT_USER_FULL_MSG = "The other party's relationships are full.";
|
||||
private static final String RELATION_TYPE_NOT_SUPPORTED_MSG = "relationType is not supported.";
|
||||
|
||||
private final CpRelationshipService cpRelationshipService;
|
||||
|
||||
public CpGiftRelationCheckCO execute(CpGiftRelationCheckCmd cmd) {
|
||||
Long userId = cmd.requiredReqUserId();
|
||||
Long acceptUserId = cmd.getAcceptUserId();
|
||||
CpRelationshipType relationType = parseRelationType(cmd.getRelationType());
|
||||
if (Objects.isNull(relationType)) {
|
||||
return new CpGiftRelationCheckCO()
|
||||
.setStatus(false)
|
||||
.setMsg(RELATION_TYPE_NOT_SUPPORTED_MSG)
|
||||
.setRelationType(cmd.getRelationType())
|
||||
.setUserId(userId)
|
||||
.setAcceptUserId(acceptUserId);
|
||||
}
|
||||
int maxCount = relationType.maxCount();
|
||||
int userCount = cpRelationshipService.countCp(userId, relationType.name());
|
||||
int acceptUserCount = cpRelationshipService.countCp(acceptUserId, relationType.name());
|
||||
boolean userFull = userCount >= maxCount;
|
||||
boolean acceptUserFull = acceptUserCount >= maxCount;
|
||||
boolean skipApplyCheck = Objects.equals(userId, acceptUserId)
|
||||
|| cpRelationshipService.existsRelationship(userId, acceptUserId)
|
||||
|| Objects.nonNull(cpRelationshipService.getDismissingCp(userId, acceptUserId,
|
||||
relationType.name()));
|
||||
boolean canContinue = skipApplyCheck || (!userFull && !acceptUserFull);
|
||||
|
||||
return new CpGiftRelationCheckCO()
|
||||
.setStatus(canContinue)
|
||||
.setMsg(message(canContinue, userFull, acceptUserFull))
|
||||
.setRelationType(relationType.name())
|
||||
.setUserId(userId)
|
||||
.setAcceptUserId(acceptUserId)
|
||||
.setUserRelationCount(userCount)
|
||||
.setAcceptUserRelationCount(acceptUserCount)
|
||||
.setMaxRelationCount(maxCount)
|
||||
.setUserRelationFull(userFull)
|
||||
.setAcceptUserRelationFull(acceptUserFull);
|
||||
}
|
||||
|
||||
private String message(boolean canContinue, boolean userFull, boolean acceptUserFull) {
|
||||
if (canContinue) {
|
||||
return OK_MSG;
|
||||
}
|
||||
if (userFull) {
|
||||
return USER_FULL_MSG;
|
||||
}
|
||||
if (acceptUserFull) {
|
||||
return ACCEPT_USER_FULL_MSG;
|
||||
}
|
||||
return OK_MSG;
|
||||
}
|
||||
|
||||
private CpRelationshipType parseRelationType(String relationTypeValue) {
|
||||
try {
|
||||
return CpRelationshipType.of(relationTypeValue);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public final class CpRelationC2cNoticeUtils {
|
||||
@ -36,9 +37,17 @@ public final class CpRelationC2cNoticeUtils {
|
||||
content.put("applyType", isReconcile ? "RECONCILE" : "APPLY");
|
||||
content.put("relationType", relationType.name());
|
||||
putAcceptProfile(content, acceptProfile);
|
||||
long createTimeSeconds = System.currentTimeMillis() / 1000;
|
||||
long expireTimeSeconds = createTimeSeconds + TimeUnit.DAYS.toSeconds(1);
|
||||
content.put("createTime", createTimeSeconds);
|
||||
content.put("sendTime", createTimeSeconds);
|
||||
content.put("dismissEndTime", expireTimeSeconds);
|
||||
|
||||
Map<String, Object> payload = basePayload(applyId, relationType.name(), title,
|
||||
ACTION_INVITE_PENDING, "PENDING", content);
|
||||
payload.put("createTime", createTimeSeconds);
|
||||
payload.put("sendTime", createTimeSeconds);
|
||||
payload.put("dismissEndTime", expireTimeSeconds);
|
||||
putSenderProfile(payload, senderProfile);
|
||||
putAcceptProfile(payload, acceptProfile);
|
||||
return buildMessage(fromUserId, toUserId, payload);
|
||||
|
||||
@ -56,12 +56,15 @@ public class CpApplyListener implements MessageListener {
|
||||
// 等待超时
|
||||
if (Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name())) {
|
||||
expire(cpApply);
|
||||
} else {
|
||||
log.info("[CP] skip expire cp apply, applyId={}, status={}", cpApply.getId(),
|
||||
cpApply.getStatus());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void expire(CpApply cpApply) {
|
||||
boolean changed = cpApplyService.changeStatusById(cpApply.getId(), CpApplyStatus.EXPIRE);
|
||||
boolean changed = cpApplyService.expireWaitById(cpApply.getId());
|
||||
if (changed) {
|
||||
sendExpiredNotice(cpApply);
|
||||
}
|
||||
|
||||
@ -9,17 +9,20 @@ import com.red.circle.other.app.command.user.SendCpApplyCmdExe;
|
||||
import com.red.circle.other.app.command.user.SendLoveLetterCmdExe;
|
||||
import com.red.circle.other.app.command.user.query.CpApplyIdQryExe;
|
||||
import com.red.circle.other.app.command.user.query.CpApplyStatusQryExe;
|
||||
import com.red.circle.other.app.command.user.query.CpGiftRelationCheckQryExe;
|
||||
import com.red.circle.other.app.command.user.query.UserCpPairUserProfileQryExe;
|
||||
import com.red.circle.other.app.command.user.query.UserCpRelationQryExe;
|
||||
import com.red.circle.other.app.command.user.query.UserCpRelationQueryV4Exe;
|
||||
import com.red.circle.other.app.command.user.query.UserCpRelationQryExe;
|
||||
import com.red.circle.other.app.command.user.query.UserCpRelationQueryV4Exe;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpDismissApplyCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpGiftRelationCheckCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyStatusQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpGiftRelationCheckCmd;
|
||||
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO;
|
||||
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO;
|
||||
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
|
||||
@ -43,6 +46,7 @@ public class UserCpRelationServiceImpl implements UserCpRelationService {
|
||||
|
||||
private final CpApplyIdQryExe cpApplyIdQryExe;
|
||||
private final CpApplyStatusQryExe cpApplyStatusQryExe;
|
||||
private final CpGiftRelationCheckQryExe cpGiftRelationCheckQryExe;
|
||||
private final SendCpApplyCmdExe sendCpApplyCmdExe;
|
||||
private final ProcessCpApplyCmd processCpApplyCmd;
|
||||
private final DismissCpApplyCmdExe dismissCpApplyCmdExe;
|
||||
@ -96,7 +100,12 @@ public class UserCpRelationServiceImpl implements UserCpRelationService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CpCabinUserProfileCO getCpUserV4(UserIdCommonCmd cmd) {
|
||||
public CpGiftRelationCheckCO checkGiftRelation(CpGiftRelationCheckCmd cmd) {
|
||||
return cpGiftRelationCheckQryExe.execute(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CpCabinUserProfileCO getCpUserV4(UserIdCommonCmd cmd) {
|
||||
return userCpRelationQueryV4Exe.execute(cmd);
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,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.infra.database.rds.service.user.user.CpValueService;
|
||||
import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
@ -84,6 +86,22 @@ class ProcessCpApplyCmdTest {
|
||||
verify(fixture.imMessageClient, never()).sendCustomMessage(any(CustomC2cMsgBodyCmd.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void processOverdueWaitApplyShouldFailStateError() {
|
||||
Fixture fixture = new Fixture("CP");
|
||||
CpApply overdueApply = apply("CP");
|
||||
overdueApply.setCreateTime(Timestamp.valueOf(LocalDateTime.now().minusHours(25)));
|
||||
when(fixture.cpApplyService.getById(3001L)).thenReturn(overdueApply);
|
||||
|
||||
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.imMessageClient, never()).sendCustomMessage(any(CustomC2cMsgBodyCmd.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void agreeApplyShouldAutoRefuseOtherWaitAppliesBetweenSameUsersWithoutRejectedIm() {
|
||||
Fixture fixture = new Fixture("CP");
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
package com.red.circle.other.app.command.user.query;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
|
||||
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class CpApplyIdQryExeTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnApplyIdSentByCurrentUser() {
|
||||
CpApplyService cpApplyService = mock(CpApplyService.class);
|
||||
when(cpApplyService.getId(2001L, 1001L, "BROTHER")).thenReturn(3001L);
|
||||
|
||||
CpApplyIdQryExe exe = new CpApplyIdQryExe(cpApplyService);
|
||||
|
||||
assertEquals("3001", exe.execute(cmd(1001L, 2001L, "BROTHER")));
|
||||
verify(cpApplyService).expireOverdueWaitApplies(1001L);
|
||||
verify(cpApplyService).expireOverdueWaitApplies(2001L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFallbackToApplyIdReceivedByCurrentUser() {
|
||||
CpApplyService cpApplyService = mock(CpApplyService.class);
|
||||
when(cpApplyService.getId(1001L, 2001L, "CP")).thenReturn(3002L);
|
||||
|
||||
CpApplyIdQryExe exe = new CpApplyIdQryExe(cpApplyService);
|
||||
|
||||
assertEquals("3002", exe.execute(cmd(1001L, 2001L, "CP")));
|
||||
}
|
||||
|
||||
private static CpApplyIdQueryCmd cmd(Long reqUserId, Long targetUserId, String relationType) {
|
||||
CpApplyIdQueryCmd cmd = new CpApplyIdQueryCmd();
|
||||
cmd.setReqUserId(reqUserId);
|
||||
cmd.setUserId(targetUserId);
|
||||
cmd.setRelationType(relationType);
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
@ -3,11 +3,15 @@ package com.red.circle.other.app.command.user.query;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
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.other.app.dto.cmd.user.relation.cp.CpApplyStatusQueryCmd;
|
||||
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 java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class CpApplyStatusQryExeTest {
|
||||
@ -30,6 +34,21 @@ class CpApplyStatusQryExeTest {
|
||||
assertNull(exe.execute(cmd(3001L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnExpireForOverdueWaitApplyWithoutPersistingStatus() {
|
||||
CpApplyService cpApplyService = mock(CpApplyService.class);
|
||||
CpApply cpApply = new CpApply()
|
||||
.setId(3001L)
|
||||
.setStatus("WAIT");
|
||||
cpApply.setCreateTime(Timestamp.valueOf(LocalDateTime.now().minusHours(25)));
|
||||
when(cpApplyService.getById(3001L)).thenReturn(cpApply);
|
||||
|
||||
CpApplyStatusQryExe exe = new CpApplyStatusQryExe(cpApplyService);
|
||||
|
||||
assertEquals("EXPIRE", exe.execute(cmd(3001L)));
|
||||
verify(cpApplyService, never()).expireWaitById(3001L);
|
||||
}
|
||||
|
||||
private static CpApplyStatusQueryCmd cmd(Long applyId) {
|
||||
CpApplyStatusQueryCmd cmd = new CpApplyStatusQueryCmd();
|
||||
cmd.setApplyId(applyId);
|
||||
|
||||
@ -0,0 +1,119 @@
|
||||
package com.red.circle.other.app.command.user.query;
|
||||
|
||||
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.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpGiftRelationCheckCO;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpGiftRelationCheckCmd;
|
||||
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
|
||||
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class CpGiftRelationCheckQryExeTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnOkWhenBothUsersHaveAvailableCpSlot() {
|
||||
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
CpGiftRelationCheckQryExe exe = new CpGiftRelationCheckQryExe(cpRelationshipService);
|
||||
|
||||
CpGiftRelationCheckCO result = exe.execute(cmd(1001L, 2001L, "CP"));
|
||||
|
||||
assertTrue(result.getStatus());
|
||||
assertEquals("OK", result.getMsg());
|
||||
assertEquals("CP", result.getRelationType());
|
||||
assertEquals(0, result.getUserRelationCount());
|
||||
assertEquals(0, result.getAcceptUserRelationCount());
|
||||
assertEquals(1, result.getMaxRelationCount());
|
||||
assertFalse(result.getUserRelationFull());
|
||||
assertFalse(result.getAcceptUserRelationFull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenSenderRelationshipIsFull() {
|
||||
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
when(cpRelationshipService.countCp(1001L, "CP")).thenReturn(1);
|
||||
CpGiftRelationCheckQryExe exe = new CpGiftRelationCheckQryExe(cpRelationshipService);
|
||||
|
||||
CpGiftRelationCheckCO result = exe.execute(cmd(1001L, 2001L, "CP"));
|
||||
|
||||
assertFalse(result.getStatus());
|
||||
assertEquals("Your relationships are full.", result.getMsg());
|
||||
assertEquals(1, result.getUserRelationCount());
|
||||
assertEquals(0, result.getAcceptUserRelationCount());
|
||||
assertTrue(result.getUserRelationFull());
|
||||
assertFalse(result.getAcceptUserRelationFull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenReceiverRelationshipIsFull() {
|
||||
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
when(cpRelationshipService.countCp(1001L, "BROTHER")).thenReturn(2);
|
||||
when(cpRelationshipService.countCp(2001L, "BROTHER")).thenReturn(3);
|
||||
CpGiftRelationCheckQryExe exe = new CpGiftRelationCheckQryExe(cpRelationshipService);
|
||||
|
||||
CpGiftRelationCheckCO result = exe.execute(cmd(1001L, 2001L, "BROTHER"));
|
||||
|
||||
assertFalse(result.getStatus());
|
||||
assertEquals("The other party's relationships are full.", result.getMsg());
|
||||
assertEquals(2, result.getUserRelationCount());
|
||||
assertEquals(3, result.getAcceptUserRelationCount());
|
||||
assertEquals(3, result.getMaxRelationCount());
|
||||
assertFalse(result.getUserRelationFull());
|
||||
assertTrue(result.getAcceptUserRelationFull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotBlockGiftWhenUsersAlreadyHaveRelationship() {
|
||||
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
when(cpRelationshipService.countCp(1001L, "CP")).thenReturn(1);
|
||||
when(cpRelationshipService.countCp(2001L, "CP")).thenReturn(1);
|
||||
when(cpRelationshipService.existsRelationship(1001L, 2001L)).thenReturn(true);
|
||||
CpGiftRelationCheckQryExe exe = new CpGiftRelationCheckQryExe(cpRelationshipService);
|
||||
|
||||
CpGiftRelationCheckCO result = exe.execute(cmd(1001L, 2001L, "CP"));
|
||||
|
||||
assertTrue(result.getStatus());
|
||||
assertEquals("OK", result.getMsg());
|
||||
assertTrue(result.getUserRelationFull());
|
||||
assertTrue(result.getAcceptUserRelationFull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotBlockGiftWhenRelationIsDismissing() {
|
||||
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
when(cpRelationshipService.countCp(1001L, "SISTERS")).thenReturn(3);
|
||||
when(cpRelationshipService.countCp(2001L, "SISTERS")).thenReturn(3);
|
||||
when(cpRelationshipService.getDismissingCp(1001L, 2001L, "SISTERS"))
|
||||
.thenReturn(new CpRelationship());
|
||||
CpGiftRelationCheckQryExe exe = new CpGiftRelationCheckQryExe(cpRelationshipService);
|
||||
|
||||
CpGiftRelationCheckCO result = exe.execute(cmd(1001L, 2001L, "SISTERS"));
|
||||
|
||||
assertTrue(result.getStatus());
|
||||
assertEquals("OK", result.getMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseForUnsupportedRelationTypeWithoutThrowing() {
|
||||
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
|
||||
CpGiftRelationCheckQryExe exe = new CpGiftRelationCheckQryExe(cpRelationshipService);
|
||||
|
||||
CpGiftRelationCheckCO result = exe.execute(cmd(1001L, 2001L, "FRIEND"));
|
||||
|
||||
assertFalse(result.getStatus());
|
||||
assertEquals("relationType is not supported.", result.getMsg());
|
||||
assertEquals("FRIEND", result.getRelationType());
|
||||
}
|
||||
|
||||
private static CpGiftRelationCheckCmd cmd(Long reqUserId, Long acceptUserId,
|
||||
String relationType) {
|
||||
CpGiftRelationCheckCmd cmd = new CpGiftRelationCheckCmd();
|
||||
cmd.setReqUserId(reqUserId);
|
||||
cmd.setAcceptUserId(acceptUserId);
|
||||
cmd.setRelationType(relationType);
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,8 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
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.common.business.core.enums.SysOriginPlatformEnum;
|
||||
@ -45,7 +47,7 @@ class CpApplyListenerTest {
|
||||
.setStatus(CpApplyStatus.WAIT.name());
|
||||
|
||||
when(cpApplyService.getById(3001L)).thenReturn(apply);
|
||||
when(cpApplyService.changeStatusById(3001L, CpApplyStatus.EXPIRE)).thenReturn(true);
|
||||
when(cpApplyService.expireWaitById(3001L)).thenReturn(true);
|
||||
when(userProfileGateway.getByUserId(1001L)).thenReturn(profile(1001L));
|
||||
when(userProfileGateway.getByUserId(2001L)).thenReturn(profile(2001L));
|
||||
when(imMessageClient.sendCustomMessage(any(CustomC2cMsgBodyCmd.class)))
|
||||
@ -68,12 +70,41 @@ class CpApplyListenerTest {
|
||||
ArgumentCaptor<CustomC2cMsgBodyCmd> captor = ArgumentCaptor.forClass(
|
||||
CustomC2cMsgBodyCmd.class);
|
||||
InOrder inOrder = inOrder(cpApplyService, imMessageClient);
|
||||
inOrder.verify(cpApplyService).changeStatusById(3001L, CpApplyStatus.EXPIRE);
|
||||
inOrder.verify(cpApplyService).expireWaitById(3001L);
|
||||
inOrder.verify(imMessageClient).sendCustomMessage(captor.capture());
|
||||
List<CustomC2cMsgBodyCmd> messages = captor.getAllValues();
|
||||
assertExpiredMessage(messages.get(0), "1001", "2001");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonWaitApplyShouldNotExpireAgain() {
|
||||
CpApplyService cpApplyService = mock(CpApplyService.class);
|
||||
ImMessageClient imMessageClient = mock(ImMessageClient.class);
|
||||
UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
|
||||
MessageEventProcess messageEventProcess = mock(MessageEventProcess.class);
|
||||
CpApply apply = new CpApply()
|
||||
.setId(3001L)
|
||||
.setStatus(CpApplyStatus.EXPIRE.name());
|
||||
|
||||
when(cpApplyService.getById(3001L)).thenReturn(apply);
|
||||
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, messageEventProcess);
|
||||
|
||||
assertEquals(Action.SUCCESS, listener.consume(new ConsumerMessage()));
|
||||
verify(cpApplyService, never()).expireWaitById(3001L);
|
||||
verify(imMessageClient, never()).sendCustomMessage(any(CustomC2cMsgBodyCmd.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void assertExpiredMessage(CustomC2cMsgBodyCmd message, String fromAccount,
|
||||
String toAccount) {
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
package com.red.circle.other.app.dto.clientobject.user.relation.cp;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.red.circle.framework.dto.ClientObject;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* CP gift relationship limit check result.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CpGiftRelationCheckCO extends ClientObject {
|
||||
|
||||
/**
|
||||
* Whether app can continue sending the relationship gift.
|
||||
*/
|
||||
private Boolean status;
|
||||
|
||||
/**
|
||||
* App display message.
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* Relationship type: CP/BROTHER/SISTERS.
|
||||
*/
|
||||
private String relationType;
|
||||
|
||||
/**
|
||||
* Gift sender user id.
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* Gift receiver user id.
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long acceptUserId;
|
||||
|
||||
/**
|
||||
* Sender current normal relationship count for relationType.
|
||||
*/
|
||||
private Integer userRelationCount;
|
||||
|
||||
/**
|
||||
* Receiver current normal relationship count for relationType.
|
||||
*/
|
||||
private Integer acceptUserRelationCount;
|
||||
|
||||
/**
|
||||
* Max count for relationType: CP=1, BROTHER/SISTERS=3.
|
||||
*/
|
||||
private Integer maxRelationCount;
|
||||
|
||||
/**
|
||||
* Whether sender reached maxRelationCount.
|
||||
*/
|
||||
private Boolean userRelationFull;
|
||||
|
||||
/**
|
||||
* Whether receiver reached maxRelationCount.
|
||||
*/
|
||||
private Boolean acceptUserRelationFull;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.red.circle.other.app.dto.cmd.user.relation.cp;
|
||||
|
||||
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* CP gift relationship limit check.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CpGiftRelationCheckCmd extends AppExtCommand {
|
||||
|
||||
/**
|
||||
* Gift receiver user id.
|
||||
*
|
||||
* @eo.required
|
||||
*/
|
||||
@NotNull(message = "acceptUserId required.")
|
||||
private Long acceptUserId;
|
||||
|
||||
/**
|
||||
* Relationship type carried by the CP gift: CP/BROTHER/SISTERS, default CP.
|
||||
*/
|
||||
private String relationType;
|
||||
}
|
||||
@ -4,15 +4,17 @@ import com.red.circle.common.business.dto.cmd.UserIdCmd;
|
||||
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpDismissApplyCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpGiftRelationCheckCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
|
||||
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyStatusQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpGiftRelationCheckCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd;
|
||||
import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -37,6 +39,8 @@ public interface UserCpRelationService {
|
||||
|
||||
String getCpApplyStatus(CpApplyStatusQueryCmd cmd);
|
||||
|
||||
CpGiftRelationCheckCO checkGiftRelation(CpGiftRelationCheckCmd cmd);
|
||||
|
||||
CpCabinUserProfileCO getCpUserV4(UserIdCommonCmd cmd);
|
||||
|
||||
void sendLoveLetter(SendLoveLetterCmd cmd);
|
||||
|
||||
@ -76,7 +76,17 @@ public interface CpApplyService extends BaseService<CpApply> {
|
||||
* @param status 状态
|
||||
* @return true 成功,false 失败
|
||||
*/
|
||||
boolean changeStatusById(Long id, CpApplyStatus status);
|
||||
boolean changeStatusById(Long id, CpApplyStatus status);
|
||||
|
||||
/**
|
||||
* 只把等待中的申请置为超时.
|
||||
*/
|
||||
boolean expireWaitById(Long id);
|
||||
|
||||
/**
|
||||
* 清理用户相关超过 24 小时仍处于等待状态的申请.
|
||||
*/
|
||||
void expireOverdueWaitApplies(Long userId);
|
||||
|
||||
/**
|
||||
* 拒绝所有等待状态在用户.
|
||||
|
||||
@ -10,9 +10,10 @@ 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.other.infra.enums.user.user.CpRelationshipType;
|
||||
import com.red.circle.other.inner.model.cmd.user.CpApplyQryCmd;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import com.red.circle.other.inner.model.cmd.user.CpApplyQryCmd;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -26,9 +27,11 @@ import org.springframework.stereotype.Service;
|
||||
* @since 2021-07-13
|
||||
*/
|
||||
@Service
|
||||
public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> implements
|
||||
CpApplyService {
|
||||
|
||||
public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> implements
|
||||
CpApplyService {
|
||||
|
||||
private static final long APPLY_WAIT_EXPIRE_HOURS = 24L;
|
||||
|
||||
@Override
|
||||
public void dismiss(Long requestUserId) {
|
||||
dismiss(requestUserId, CpRelationshipType.CP.name());
|
||||
@ -37,14 +40,14 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
@Override
|
||||
public void dismiss(Long requestUserId, String relationType) {
|
||||
update()
|
||||
.set(CpApply::getStatus, CpApplyStatus.DISMISS)
|
||||
.set(CpApply::getStatus, CpApplyStatus.DISMISS.name())
|
||||
.set(CpApply::getUpdateTime, LocalDateTime.now())
|
||||
.set(CpApply::getUpdateUser, requestUserId)
|
||||
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
|
||||
.eq(CpApply::getStatus, CpApplyStatus.AGREE)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.AGREE.name())
|
||||
.and(where -> where.eq(CpApply::getSendApplyUserId, requestUserId)
|
||||
.or().eq(CpApply::getAcceptApplyUserId, requestUserId))
|
||||
.last(PageConstant.LIMIT_ONE)
|
||||
.or().eq(CpApply::getAcceptApplyUserId, requestUserId))
|
||||
.last(PageConstant.LIMIT_ONE)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@ -58,14 +61,16 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
return Optional.ofNullable(
|
||||
query()
|
||||
.select(CpApply::getId)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT.name())
|
||||
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
|
||||
.eq(CpApply::getAcceptApplyUserId, acceptApplyUserId)
|
||||
.eq(CpApply::getSendApplyUserId, sendApplyUserId)
|
||||
.last(PageConstant.LIMIT_ONE)
|
||||
.getOne()
|
||||
).map(CpApply::getId).orElse(null);
|
||||
}
|
||||
.gt(CpApply::getCreateTime, waitExpireBefore())
|
||||
.orderByDesc(CpApply::getCreateTime)
|
||||
.last(PageConstant.LIMIT_ONE)
|
||||
.getOne()
|
||||
).map(CpApply::getId).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(Long acceptApplyUserId, Long sendApplyUserId) {
|
||||
@ -88,19 +93,44 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
.select(CpApply::getId)
|
||||
.eq(CpApply::getSendApplyUserId, sendApplyUserId)
|
||||
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT.name())
|
||||
.gt(CpApply::getCreateTime, waitExpireBefore())
|
||||
.getOne())
|
||||
.map(cpApply -> Objects.nonNull(cpApply.getId()))
|
||||
.orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeStatusById(Long id, CpApplyStatus status) {
|
||||
return update()
|
||||
.set(CpApply::getStatus, status)
|
||||
.eq(CpApply::getId, id)
|
||||
.execute();
|
||||
}
|
||||
.map(cpApply -> Objects.nonNull(cpApply.getId()))
|
||||
.orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeStatusById(Long id, CpApplyStatus status) {
|
||||
return update()
|
||||
.set(CpApply::getStatus, status.name())
|
||||
.set(CpApply::getUpdateTime, LocalDateTime.now())
|
||||
.eq(CpApply::getId, id)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expireWaitById(Long id) {
|
||||
return Optional.ofNullable(query()
|
||||
.select(CpApply::getId)
|
||||
.eq(CpApply::getId, id)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT.name())
|
||||
.lt(CpApply::getCreateTime, waitExpireBefore())
|
||||
.last(PageConstant.LIMIT_ONE)
|
||||
.getOne())
|
||||
.map(cpApply -> Objects.nonNull(cpApply.getId()))
|
||||
.orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireOverdueWaitApplies(Long userId) {
|
||||
// The production status column does not accept a persisted EXPIRE value.
|
||||
// Expiration is derived from create_time so old WAIT rows do not block new applies.
|
||||
}
|
||||
|
||||
private Timestamp waitExpireBefore() {
|
||||
return Timestamp.valueOf(LocalDateTime.now().minusHours(APPLY_WAIT_EXPIRE_HOURS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refuseStatusWait(Long acceptApplyUserId) {
|
||||
@ -110,8 +140,8 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
@Override
|
||||
public void refuseStatusWait(Long acceptApplyUserId, String relationType) {
|
||||
update()
|
||||
.set(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT)
|
||||
.set(CpApply::getStatus, CpApplyStatus.REFUSE.name())
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT.name())
|
||||
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
|
||||
.eq(CpApply::getAcceptApplyUserId, acceptApplyUserId)
|
||||
.execute();
|
||||
@ -130,7 +160,7 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
public List<CpApply> refuseOtherWaitAppliesBetweenUsers(Long excludeApplyId, Long userId,
|
||||
Long cpUserId) {
|
||||
List<CpApply> applies = query()
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT.name())
|
||||
.ne(Objects.nonNull(excludeApplyId), CpApply::getId, excludeApplyId)
|
||||
.and(where -> where
|
||||
.nested(w -> w.eq(CpApply::getSendApplyUserId, userId)
|
||||
@ -147,7 +177,7 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
return applies;
|
||||
}
|
||||
update()
|
||||
.set(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.set(CpApply::getStatus, CpApplyStatus.REFUSE.name())
|
||||
.set(CpApply::getUpdateTime, LocalDateTime.now())
|
||||
.in(CpApply::getId, applyIds)
|
||||
.execute();
|
||||
@ -159,7 +189,7 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
public List<CpApply> refuseWaitAppliesByUserAndRelation(Long excludeApplyId, Long userId,
|
||||
String relationType) {
|
||||
List<CpApply> applies = query()
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.WAIT.name())
|
||||
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
|
||||
.ne(Objects.nonNull(excludeApplyId), CpApply::getId, excludeApplyId)
|
||||
.and(where -> where.eq(CpApply::getSendApplyUserId, userId)
|
||||
@ -174,7 +204,7 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
return applies;
|
||||
}
|
||||
update()
|
||||
.set(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.set(CpApply::getStatus, CpApplyStatus.REFUSE.name())
|
||||
.set(CpApply::getUpdateTime, LocalDateTime.now())
|
||||
.in(CpApply::getId, applyIds)
|
||||
.execute();
|
||||
@ -185,8 +215,8 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
@Override
|
||||
public List<CpApply> listRefuse(Long userId) {
|
||||
return query()
|
||||
.eq(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.eq(CpApply::getAcceptApplyUserId, userId)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.REFUSE.name())
|
||||
.eq(CpApply::getAcceptApplyUserId, userId)
|
||||
.last(PageConstant.MAX_LIMIT)
|
||||
.list();
|
||||
}
|
||||
@ -194,7 +224,7 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
|
||||
@Override
|
||||
public List<CpApply> listRefuseBetweenUsers(Long userId, Long cpUserId) {
|
||||
return query()
|
||||
.eq(CpApply::getStatus, CpApplyStatus.REFUSE)
|
||||
.eq(CpApply::getStatus, CpApplyStatus.REFUSE.name())
|
||||
.and(where -> where
|
||||
.nested(w -> w.eq(CpApply::getSendApplyUserId, userId)
|
||||
.eq(CpApply::getAcceptApplyUserId, cpUserId))
|
||||
|
||||
@ -56,4 +56,11 @@ public enum CpRelationshipType {
|
||||
case SISTERS -> "sisters";
|
||||
};
|
||||
}
|
||||
|
||||
public int maxCount() {
|
||||
return switch (this) {
|
||||
case CP -> 1;
|
||||
case BROTHER, SISTERS -> 3;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,45 @@
|
||||
package com.red.circle.other.app.inner.service.team.impl;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anySet;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
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.component.redis.service.RedisService;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillMemberTarget;
|
||||
import com.red.circle.other.infra.database.mongo.dto.team.TeamPolicy;
|
||||
import com.red.circle.other.infra.database.mongo.dto.team.TeamSalaryPaymentDetails;
|
||||
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
|
||||
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
|
||||
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager;
|
||||
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile;
|
||||
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
|
||||
import com.red.circle.other.infra.database.mongo.service.team.team.TeamSalaryPaymentService;
|
||||
import com.red.circle.other.inner.enums.team.TeamMemberTargetIndex;
|
||||
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
|
||||
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
|
||||
import com.red.circle.wallet.inner.endpoint.bank.UserBankBalanceClient;
|
||||
import com.red.circle.wallet.inner.endpoint.bank.UserBankRunningWaterClient;
|
||||
import com.red.circle.wallet.inner.model.dto.BankBalanceDTO;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
class TeamManualSalaryPaymentClientServiceImplTest {
|
||||
@ -79,6 +110,111 @@ class TeamManualSalaryPaymentClientServiceImplTest {
|
||||
"salary"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void payShouldReturnTopUpDetailsWhenCurrentLevelRecordExistsButSalaryIsInsufficient() {
|
||||
RedisService redisService = mock(RedisService.class);
|
||||
TeamMemberService teamMemberService = mock(TeamMemberService.class);
|
||||
TeamProfileService teamProfileService = mock(TeamProfileService.class);
|
||||
UserBankBalanceClient userBankBalanceClient = mock(UserBankBalanceClient.class);
|
||||
TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class);
|
||||
TeamSalaryPaymentService teamSalaryPaymentService = mock(TeamSalaryPaymentService.class);
|
||||
TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class);
|
||||
UserBankRunningWaterClient userBankRunningWaterClient = mock(UserBankRunningWaterClient.class);
|
||||
TeamManualSalaryPaymentClientServiceImpl service = new TeamManualSalaryPaymentClientServiceImpl(
|
||||
redisService, teamMemberService, teamProfileService, userBankBalanceClient,
|
||||
teamMemberTargetService, teamSalaryPaymentService, teamPolicyManagerService,
|
||||
userBankRunningWaterClient);
|
||||
|
||||
String sysOrigin = "LIKEI";
|
||||
String countryCode = "PH";
|
||||
String region = "2046067717605224449";
|
||||
Long userId = 2054591808909930498L;
|
||||
Long teamId = 2054614353419759617L;
|
||||
Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
|
||||
String paymentId = userId + "_" + billBelong;
|
||||
TeamMemberTarget target = new TeamMemberTarget()
|
||||
.setId("target-id")
|
||||
.setTimeId(2056024761217118210L)
|
||||
.setSysOrigin(sysOrigin)
|
||||
.setRegion(region)
|
||||
.setCountryCode(countryCode)
|
||||
.setTeamId(teamId)
|
||||
.setUserId(userId)
|
||||
.setBillBelong(billBelong)
|
||||
.setDailyTargets(List.of(new TeamMemberTargetIndex()
|
||||
.setOwnOnlineTime(717L)
|
||||
.setAcceptGiftValue(15151819L)));
|
||||
TeamSalaryPayment existingPayment = new TeamSalaryPayment()
|
||||
.setId(paymentId)
|
||||
.setUserId(userId)
|
||||
.setDateNumber(billBelong)
|
||||
.setSalarySendDetails(List.of(
|
||||
new TeamSalaryPaymentDetails()
|
||||
.setTeamMemberId(userId)
|
||||
.setTeamOwnId(userId)
|
||||
.setTeamRegionId(region)
|
||||
.setPolicyLevel(6)
|
||||
.setAnchor(Boolean.TRUE)
|
||||
.setSalary(new BigDecimal("22.00"))
|
||||
.setRecycled(Boolean.FALSE),
|
||||
new TeamSalaryPaymentDetails()
|
||||
.setTeamMemberId(userId)
|
||||
.setTeamOwnId(userId)
|
||||
.setTeamRegionId(region)
|
||||
.setPolicyLevel(6)
|
||||
.setAnchor(Boolean.FALSE)
|
||||
.setSalary(new BigDecimal("9.50"))
|
||||
.setRecycled(Boolean.FALSE)));
|
||||
TeamPolicyManager policyManager = new TeamPolicyManager()
|
||||
.setSysOrigin(sysOrigin)
|
||||
.setRegion(region)
|
||||
.setCountryCode(countryCode)
|
||||
.setPolicy(List.of(new TeamPolicy()
|
||||
.setLevel(6)
|
||||
.setTarget(8810000L)
|
||||
.setOnlineTime(10L)
|
||||
.setEffectiveDay(0)
|
||||
.setMemberSalary(new BigDecimal("35.00"))
|
||||
.setOwnSalary(new BigDecimal("8.75"))
|
||||
.setTotalSalary(new BigDecimal("43.75"))));
|
||||
|
||||
when(redisService.lock(anyString(), anyLong())).thenReturn(true);
|
||||
when(teamMemberTargetService.listManualSalaryTargets(eq(sysOrigin), eq(countryCode), anySet(),
|
||||
eq(billBelong))).thenReturn(List.of(target));
|
||||
when(teamMemberService.mapByMemberIds(anySet())).thenReturn(Map.of(userId,
|
||||
new TeamMember().setMemberId(userId).setTeamId(teamId)));
|
||||
when(teamProfileService.mapProfileByIds(anySet())).thenReturn(Map.of(teamId,
|
||||
new TeamProfile().setId(teamId).setOwnUserId(userId).setRegion(region)));
|
||||
when(teamSalaryPaymentService.listByPaymentOrRecipientUserIdsAndDateNumbers(eq(sysOrigin),
|
||||
anySet(), anySet())).thenReturn(List.of(existingPayment));
|
||||
when(teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin, region, null, countryCode))
|
||||
.thenReturn(policyManager);
|
||||
when(teamSalaryPaymentService.createIfAbsent(userId, billBelong, sysOrigin))
|
||||
.thenReturn(new TeamSalaryPayment().setId(paymentId));
|
||||
when(userBankBalanceClient.getBalance(userId))
|
||||
.thenReturn(ResultResponse.success(BankBalanceDTO.of(0L)))
|
||||
.thenReturn(ResultResponse.success(BankBalanceDTO.of(1225L)));
|
||||
when(userBankBalanceClient.incr(any())).thenReturn(ResultResponse.success(Boolean.TRUE));
|
||||
when(userBankRunningWaterClient.add(any())).thenReturn(ResultResponse.success());
|
||||
when(teamSalaryPaymentService.mapPolicy(paymentId)).thenReturn(new HashMap<>());
|
||||
|
||||
List<TeamManualSalaryPaymentResultDTO> results = service.pay(new TeamManualSalaryPaymentCmd()
|
||||
.setSysOrigin(sysOrigin)
|
||||
.setCountryCode(countryCode)
|
||||
.setUserIds(List.of(userId)));
|
||||
|
||||
assertEquals(1, results.size());
|
||||
assertEquals(userId, results.get(0).getUserId());
|
||||
assertEquals(new BigDecimal("12.25"), results.get(0).getIssuedSalary());
|
||||
ArgumentCaptor<TeamSalaryPaymentDetails> detailsCaptor =
|
||||
ArgumentCaptor.forClass(TeamSalaryPaymentDetails.class);
|
||||
verify(teamSalaryPaymentService).paymentSalary(eq(paymentId), detailsCaptor.capture());
|
||||
assertEquals(new BigDecimal("12.25"), detailsCaptor.getValue().getSalary());
|
||||
assertEquals(Boolean.TRUE, detailsCaptor.getValue().getAnchor());
|
||||
verify(teamSalaryPaymentService, never()).checkRepeatPayment(anyString(), anyLong(),
|
||||
anyString(), any(), any());
|
||||
}
|
||||
|
||||
private Object paymentSummary(BigDecimal totalIssuedSalary, BigDecimal memberIssuedSalary,
|
||||
BigDecimal agentIssuedSalary) throws Exception {
|
||||
Class<?> summaryClass = Class.forName(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user