This commit is contained in:
hy001 2026-05-21 15:43:38 +08:00
parent c02050274b
commit 3f59e5b653
21 changed files with 448 additions and 167 deletions

View File

@ -51,4 +51,9 @@ public class GiveGiftConfig implements Serializable {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
} }

View File

@ -96,6 +96,11 @@ public class GiftConfigDTO implements Serializable {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 0.未删除 1.已删除. * 0.未删除 1.已删除.
*/ */

View File

@ -91,6 +91,11 @@ public class SysGiftConfigCO implements Serializable {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 0.未删除 1.已删除. * 0.未删除 1.已删除.
*/ */

View File

@ -39,6 +39,9 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ProcessCpApplyCmd { public class ProcessCpApplyCmd {
private static final int CP_MAX_COUNT = 1;
private static final int FRIEND_MAX_COUNT = 3;
private final CpApplyService cpApplyService; private final CpApplyService cpApplyService;
private final CpValueService cpValueService; private final CpValueService cpValueService;
private final ImMessageClient imMessageClient; private final ImMessageClient imMessageClient;
@ -73,11 +76,11 @@ public class ProcessCpApplyCmd {
cpRelationshipService.existsCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), cpRelationshipService.existsCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(),
relationType)); relationType));
// 检查CP数量限制最多4个 int maxCount = relationMaxCount(relationType);
int senderCpCount = cpRelationshipService.countCp(cpApply.getSendApplyUserId()); ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
int receiverCpCount = cpRelationshipService.countCp(cpApply.getAcceptApplyUserId()); cpRelationshipService.countCp(cpApply.getSendApplyUserId(), relationType) >= maxCount);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, senderCpCount >= 4); ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, receiverCpCount >= 4); cpRelationshipService.countCp(cpApply.getAcceptApplyUserId(), relationType) >= maxCount);
} }
// 操作失败 // 操作失败
@ -89,8 +92,6 @@ public class ProcessCpApplyCmd {
return; return;
} }
refuseStatusWait(cmd, cpApply, relationType);
if (isReconcile) { if (isReconcile) {
// 复合更新状态为NORMAL // 复合更新状态为NORMAL
cpRelationshipService.reconcile(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), cpRelationshipService.reconcile(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(),
@ -102,10 +103,17 @@ public class ProcessCpApplyCmd {
cmd.requireReqSysOrigin(), relationType); cmd.requireReqSysOrigin(), relationType);
} }
refuseStatusWaitIfLimitReached(cmd, cpApply, relationType);
cpRelationshipCacheService.remove(Arrays.asList(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId())); cpRelationshipCacheService.remove(Arrays.asList(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()));
} }
private void refuseStatusWait(ProcessApplyCmd cmd, CpApply cpApply, String relationType) { private void refuseStatusWaitIfLimitReached(ProcessApplyCmd cmd, CpApply cpApply,
String relationType) {
if (cpRelationshipService.countCp(cpApply.getAcceptApplyUserId(), relationType)
< relationMaxCount(relationType)) {
return;
}
cpApplyService.refuseStatusWait(cmd.requiredReqUserId(), relationType); cpApplyService.refuseStatusWait(cmd.requiredReqUserId(), relationType);
userMqMessageService.cpApplyDelayed( userMqMessageService.cpApplyDelayed(
new CpApplyEvent() new CpApplyEvent()
@ -114,6 +122,12 @@ public class ProcessCpApplyCmd {
TimestampUtils.nowPlusMinutes(1)); TimestampUtils.nowPlusMinutes(1));
} }
private int relationMaxCount(String relationType) {
return Objects.equals(CpRelationshipType.CP, CpRelationshipType.of(relationType))
? CP_MAX_COUNT
: FRIEND_MAX_COUNT;
}
private void returnGoldCoins(ProcessApplyCmd cmd, CpApply cpApply) { private void returnGoldCoins(ProcessApplyCmd cmd, CpApply cpApply) {
if (Objects.isNull(cpApply.getApplyConsumeGold()) if (Objects.isNull(cpApply.getApplyConsumeGold())
|| cpApply.getApplyConsumeGold().compareTo(BigDecimal.ZERO) <= 0) { || cpApply.getApplyConsumeGold().compareTo(BigDecimal.ZERO) <= 0) {

View File

@ -47,6 +47,9 @@ import org.springframework.stereotype.Component;
@Slf4j @Slf4j
public class SendCpApplyCmdExe { 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 CpApplyService cpApplyService;
private final OfficialNoticeClient officialNoticeClient; private final OfficialNoticeClient officialNoticeClient;
private final WalletGoldClient walletGoldClient; private final WalletGoldClient walletGoldClient;
@ -63,12 +66,18 @@ public class SendCpApplyCmdExe {
} }
public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin) { public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin) {
return autoCreateFromCpGift(sendUserId, acceptUserId, sysOrigin, CpRelationshipType.CP.name());
}
public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin,
String relationTypeValue) {
try { try {
process(sendUserId, acceptUserId, sysOrigin, CpRelationshipType.CP.name(), false); process(sendUserId, acceptUserId, sysOrigin, relationTypeValue, false);
return true; return true;
} catch (Exception ex) { } catch (Exception ex) {
log.info("[CP] skip auto create cp apply from gift, sendUserId={}, acceptUserId={}, reason={}", log.info(
sendUserId, acceptUserId, ex.getMessage()); "[CP] skip auto create cp apply from gift, sendUserId={}, acceptUserId={}, relationType={}, reason={}",
sendUserId, acceptUserId, relationTypeValue, ex.getMessage());
return false; return false;
} }
} }
@ -101,16 +110,12 @@ public class SendCpApplyCmdExe {
cpRelationshipService cpRelationshipService
.existsCp(sendUserId, acceptUserId, relationType.name())); .existsCp(sendUserId, acceptUserId, relationType.name()));
// 检查CP数量限制最多4个 validateRelationLimit(sendUserId, acceptUserId, relationType);
int senderCpCount = cpRelationshipService.countCp(sendUserId);
int receiverCpCount = cpRelationshipService.countCp(acceptUserId);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, senderCpCount >= 4);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, receiverCpCount >= 4);
} }
// 您已发送申请 // 您已发送申请
ResponseAssert.isFalse(UserRelationErrorCode.YOU_HAVE_SENT_AN_APPLICATION, ResponseAssert.isFalse(UserRelationErrorCode.YOU_HAVE_SENT_AN_APPLICATION,
cpApplyService.existsNotProcessSendApply(sendUserId, relationType.name())); cpApplyService.exists(acceptUserId, sendUserId, relationType.name()));
// 对方已向你发送申请 // 对方已向你发送申请
ResponseAssert.isFalse(UserRelationErrorCode.OTHER_SIDE_ALREADY_APPLIED, ResponseAssert.isFalse(UserRelationErrorCode.OTHER_SIDE_ALREADY_APPLIED,
@ -166,6 +171,19 @@ public class SendCpApplyCmdExe {
TimestampUtils.nowPlusDays(1)); TimestampUtils.nowPlusDays(1));
} }
private void validateRelationLimit(Long sendUserId, Long acceptUserId,
CpRelationshipType relationType) {
int maxCount = relationMaxCount(relationType);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
cpRelationshipService.countCp(sendUserId, relationType.name()) >= maxCount);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
cpRelationshipService.countCp(acceptUserId, relationType.name()) >= maxCount);
}
private int relationMaxCount(CpRelationshipType relationType) {
return Objects.equals(relationType, CpRelationshipType.CP) ? CP_MAX_COUNT : FRIEND_MAX_COUNT;
}
private CpRelationshipType parseRelationType(String relationTypeValue) { private CpRelationshipType parseRelationType(String relationTypeValue) {
try { try {
return CpRelationshipType.of(relationTypeValue); return CpRelationshipType.of(relationTypeValue);
@ -177,12 +195,10 @@ public class SendCpApplyCmdExe {
private void validateRelationTypeGender(CpRelationshipType relationType, UserProfileDTO userProfile, private void validateRelationTypeGender(CpRelationshipType relationType, UserProfileDTO userProfile,
UserProfileDTO acceptUserProfile) { UserProfileDTO acceptUserProfile) {
if (Objects.equals(relationType, CpRelationshipType.CP)) { if (!Objects.equals(relationType, CpRelationshipType.CP)) {
ResponseAssert.isFalse(UserRelationErrorCode.UNAVAILABLE_SAME_GENDER,
Objects.equals(userProfile.getUserSex(), acceptUserProfile.getUserSex()));
return; return;
} }
ResponseAssert.isTrue(UserRelationErrorCode.RELATION_TYPE_GENDER_NOT_MATCH, ResponseAssert.isTrue(UserRelationErrorCode.UNAVAILABLE_SAME_GENDER,
relationType.genderMatched(userProfile.getUserSex(), acceptUserProfile.getUserSex())); relationType.genderMatched(userProfile.getUserSex(), acceptUserProfile.getUserSex()));
} }

View File

@ -84,7 +84,8 @@ public interface GiftAppConvertor {
.setGiftIntegral(giftConfig.getGiftIntegral()) .setGiftIntegral(giftConfig.getGiftIntegral())
.setSpecial(giftConfig.getSpecial()) .setSpecial(giftConfig.getSpecial())
.setType(giftConfig.getType()) .setType(giftConfig.getType())
.setGiftTab(giftConfig.getGiftTab()); .setGiftTab(giftConfig.getGiftTab())
.setCpRelationType(giftConfig.getCpRelationType());
} }
default GiveAwayGiftBatchEvent toBackpackGift(GiveAwayGiftBackpackCmd cmd, default GiveAwayGiftBatchEvent toBackpackGift(GiveAwayGiftBackpackCmd cmd,

View File

@ -810,6 +810,7 @@ public class GiveGiftsListener implements MessageListener {
return new GiftValue() return new GiftValue()
.setCurrencyType(giftConfig.getType()) .setCurrencyType(giftConfig.getType())
.setGiftType(getGiftType(event, giftConfig)) .setGiftType(getGiftType(event, giftConfig))
.setCpRelationType(giftConfig.getCpRelationType())
.setUnitPrice(giftConfig.getGiftCandy()) .setUnitPrice(giftConfig.getGiftCandy())
.setQuantity(event.getQuantity()) .setQuantity(event.getQuantity())
.setUserSize(event.acceptUserSize()) .setUserSize(event.acceptUserSize())

View File

@ -72,6 +72,7 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordSe
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; 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.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.database.rds.service.user.user.FriendshipCardService; import com.red.circle.other.infra.database.rds.service.user.user.FriendshipCardService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.config.EnumConfigKey; import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.enums.game.GamePkTypeEnum; import com.red.circle.other.inner.enums.game.GamePkTypeEnum;
import com.red.circle.other.inner.enums.material.GiftCurrencyType; import com.red.circle.other.inner.enums.material.GiftCurrencyType;
@ -378,12 +379,15 @@ public class GiftCountStrategy implements GiftStrategy {
if (CollectionUtils.isEmpty(runningWater.getAcceptUsers())) { if (CollectionUtils.isEmpty(runningWater.getAcceptUsers())) {
return; return;
} }
String relationType = CpRelationshipType.normalize(
runningWater.getGiftValue().getCpRelationType());
runningWater.getAcceptUsers().forEach(accept -> { runningWater.getAcceptUsers().forEach(accept -> {
if (Objects.equals(runningWater.getUserId(), accept.getAcceptUserId())) { if (Objects.equals(runningWater.getUserId(), accept.getAcceptUserId())) {
return; return;
} }
sendCpApplyCmdExe.autoCreateFromCpGift( sendCpApplyCmdExe.autoCreateFromCpGift(
runningWater.getUserId(), accept.getAcceptUserId(), runningWater.getSysOrigin()); runningWater.getUserId(), accept.getAcceptUserId(), runningWater.getSysOrigin(),
relationType);
}); });
} catch (Exception ex) { } catch (Exception ex) {
log.warn("[CP] skip auto create cp apply from gift, runningWaterId={}, giftId={}, sendUserId={}, reason={}", log.warn("[CP] skip auto create cp apply from gift, runningWaterId={}, giftId={}, sendUserId={}, reason={}",

View File

@ -0,0 +1,110 @@
package com.red.circle.other.app.command.user;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd;
import com.red.circle.framework.dto.ResultResponse;
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;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
class SendCpApplyCmdExeTest {
@Test
void autoCreateFromCpGiftShouldNotRestrictBrotherAndSistersGender() {
Fixture fixture = new Fixture(1, 0);
assertTrue(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "BROTHER"));
assertEquals("BROTHER", fixture.savedApply().getRelationType());
fixture = new Fixture(0, 1);
assertTrue(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "SISTERS"));
assertEquals("SISTERS", fixture.savedApply().getRelationType());
}
@Test
void autoCreateFromCpGiftShouldSkipSameGenderCp() {
Fixture fixture = new Fixture(1, 1);
assertFalse(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "CP"));
verify(fixture.cpApplyService, never()).save(any(CpApply.class));
}
private static class Fixture {
private final CpApplyService cpApplyService = mock(CpApplyService.class);
private final SendCpApplyCmdExe exe;
private Fixture(Integer sendSex, Integer acceptSex) {
OfficialNoticeClient officialNoticeClient = mock(OfficialNoticeClient.class);
WalletGoldClient walletGoldClient = mock(WalletGoldClient.class);
UserMqMessageService userMqMessageService = mock(UserMqMessageService.class);
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
EnumConfigCacheService enumConfigCacheService = mock(EnumConfigCacheService.class);
UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
UserProfileAppConvertor userProfileAppConvertor = mock(UserProfileAppConvertor.class);
UserRegionGateway userRegionGateway = mock(UserRegionGateway.class);
UserProfile sendProfile = new UserProfile();
UserProfile acceptProfile = new UserProfile();
UserProfileDTO sendProfileDTO = profile(1001L, sendSex);
UserProfileDTO acceptProfileDTO = profile(2001L, acceptSex);
when(userRegionGateway.checkEqRegion(1001L, 2001L)).thenReturn(true);
when(userProfileGateway.getByUserId(1001L)).thenReturn(sendProfile);
when(userProfileGateway.getByUserId(2001L)).thenReturn(acceptProfile);
when(userProfileAppConvertor.toUserProfileDTO(sendProfile)).thenReturn(sendProfileDTO);
when(userProfileAppConvertor.toUserProfileDTO(acceptProfile)).thenReturn(acceptProfileDTO);
when(officialNoticeClient.send(any(NoticeExtTemplateTypeCmd.class)))
.thenReturn(ResultResponse.success());
exe = new SendCpApplyCmdExe(
cpApplyService,
officialNoticeClient,
walletGoldClient,
userMqMessageService,
cpRelationshipService,
enumConfigCacheService,
userProfileGateway,
userProfileAppConvertor,
userRegionGateway
);
}
private CpApply savedApply() {
ArgumentCaptor<CpApply> captor = ArgumentCaptor.forClass(CpApply.class);
verify(cpApplyService).save(captor.capture());
return captor.getValue();
}
private static UserProfileDTO profile(Long userId, Integer sex) {
UserProfileDTO userProfile = new UserProfileDTO();
userProfile.setId(userId);
userProfile.setUserSex(sex);
userProfile.setAccount(userId.toString());
userProfile.setUserNickname("user-" + userId);
userProfile.setCountryCode("SA");
userProfile.setCountryName("Saudi Arabia");
return userProfile;
}
}
}

View File

@ -68,6 +68,11 @@ public class GiftConfigCO extends ClientObject {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 规格ID. * 规格ID.
*/ */

View File

@ -32,6 +32,11 @@ public class GiftValue implements Serializable {
*/ */
private String giftType; private String giftType;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 礼物单价. * 礼物单价.
*/ */

View File

@ -112,6 +112,12 @@ public class GiftConfig extends TimestampBaseEntity implements Serializable {
@TableField("gift_tab") @TableField("gift_tab")
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
@TableField("cp_relation_type")
private String cpRelationType;
/** /**
* 0.未删除 1.已删除. * 0.未删除 1.已删除.
*/ */

View File

@ -8,6 +8,7 @@ import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.dao.gift.GiftConfigDAO; import com.red.circle.other.infra.database.rds.dao.gift.GiftConfigDAO;
import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig;
import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService; import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.material.GiftCurrencyType; import com.red.circle.other.inner.enums.material.GiftCurrencyType;
import com.red.circle.other.inner.enums.material.GiftSpecialEnum; import com.red.circle.other.inner.enums.material.GiftSpecialEnum;
import com.red.circle.other.inner.enums.material.GiftTabEnum; import com.red.circle.other.inner.enums.material.GiftTabEnum;
@ -225,6 +226,7 @@ public class GiftConfigServiceImpl extends BaseServiceImpl<GiftConfigDAO, GiftCo
.set(GiftConfig::getType, giftConfig.getType()) .set(GiftConfig::getType, giftConfig.getType())
.set(GiftConfig::getSort, giftConfig.getSort()) .set(GiftConfig::getSort, giftConfig.getSort())
.set(GiftConfig::getGiftTab, giftConfig.getGiftTab()) .set(GiftConfig::getGiftTab, giftConfig.getGiftTab())
.set(GiftConfig::getCpRelationType, normalizeCpRelationType(giftConfig))
.set(GiftConfig::getStandardId, giftConfig.getStandardId()) .set(GiftConfig::getStandardId, giftConfig.getStandardId())
.set(GiftConfig::getExpiredTime, giftConfig.getExpiredTime()) .set(GiftConfig::getExpiredTime, giftConfig.getExpiredTime())
.set(GiftConfig::getExplanationGift, .set(GiftConfig::getExplanationGift,
@ -236,6 +238,14 @@ public class GiftConfigServiceImpl extends BaseServiceImpl<GiftConfigDAO, GiftCo
.execute(); .execute();
} }
private String normalizeCpRelationType(GiftConfig giftConfig) {
if (Objects.isNull(giftConfig)
|| !Objects.equals(giftConfig.getGiftTab(), GiftTabEnum.CP.name())) {
return null;
}
return CpRelationshipType.normalize(giftConfig.getCpRelationType());
}
@Override @Override
public Boolean switchDelStatus(Long id, Boolean status) { public Boolean switchDelStatus(Long id, Boolean status) {
return update().set(GiftConfig::getDel, status) return update().set(GiftConfig::getDel, status)

View File

@ -131,6 +131,11 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
int countCp(Long userId); int countCp(Long userId);
/**
* 获取用户指定类型正常关系数量.
*/
int countCp(Long userId, String relationType);
/** /**
* 获取分手中的CP关系. * 获取分手中的CP关系.
* *

View File

@ -205,6 +205,16 @@ public class CpRelationshipServiceImpl extends
public int countCp(Long userId) { public int countCp(Long userId) {
return Math.toIntExact(query() return Math.toIntExact(query()
.eq(CpRelationship::getUserId, userId) .eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.count());
}
@Override
public int countCp(Long userId, String relationType) {
return Math.toIntExact(query()
.eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.count()); .count());
} }

View File

@ -15,18 +15,15 @@ public enum CpRelationshipType {
CP, CP,
/** /**
* Brothers, requires both users to be male. * Brothers.
*/ */
BROTHER, BROTHER,
/** /**
* Sisters, requires both users to be female. * Sisters.
*/ */
SISTERS; SISTERS;
private static final int FEMALE = 0;
private static final int MALE = 1;
public static CpRelationshipType of(String value) { public static CpRelationshipType of(String value) {
if (StringUtils.isBlank(value)) { if (StringUtils.isBlank(value)) {
return CP; return CP;
@ -48,8 +45,7 @@ public enum CpRelationshipType {
public boolean genderMatched(Integer sendUserSex, Integer acceptUserSex) { public boolean genderMatched(Integer sendUserSex, Integer acceptUserSex) {
return switch (this) { return switch (this) {
case CP -> !Objects.equals(sendUserSex, acceptUserSex); case CP -> !Objects.equals(sendUserSex, acceptUserSex);
case BROTHER -> Objects.equals(sendUserSex, MALE) && Objects.equals(acceptUserSex, MALE); case BROTHER, SISTERS -> true;
case SISTERS -> Objects.equals(sendUserSex, FEMALE) && Objects.equals(acceptUserSex, FEMALE);
}; };
} }

View File

@ -0,0 +1,30 @@
package com.red.circle.other.infra.enums.user.user;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
class CpRelationshipTypeTest {
@Test
void cpRequiresDifferentGender() {
assertTrue(CpRelationshipType.CP.genderMatched(1, 0));
assertTrue(CpRelationshipType.CP.genderMatched(0, 1));
assertFalse(CpRelationshipType.CP.genderMatched(1, 1));
assertFalse(CpRelationshipType.CP.genderMatched(0, 0));
}
@Test
void brotherAndSistersDoNotRestrictGender() {
assertTrue(CpRelationshipType.BROTHER.genderMatched(1, 1));
assertTrue(CpRelationshipType.BROTHER.genderMatched(1, 0));
assertTrue(CpRelationshipType.BROTHER.genderMatched(0, 1));
assertTrue(CpRelationshipType.BROTHER.genderMatched(0, 0));
assertTrue(CpRelationshipType.SISTERS.genderMatched(1, 1));
assertTrue(CpRelationshipType.SISTERS.genderMatched(1, 0));
assertTrue(CpRelationshipType.SISTERS.genderMatched(0, 1));
assertTrue(CpRelationshipType.SISTERS.genderMatched(0, 0));
}
}

View File

@ -9,6 +9,8 @@ import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig;
import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService; import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService;
import com.red.circle.other.infra.database.rds.service.gift.GiftWallService; import com.red.circle.other.infra.database.rds.service.gift.GiftWallService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.material.GiftTabEnum;
import com.red.circle.other.inner.model.cmd.sys.GiftQryCmd; import com.red.circle.other.inner.model.cmd.sys.GiftQryCmd;
import com.red.circle.other.inner.model.dto.gift.UserGiftWallDTO; import com.red.circle.other.inner.model.dto.gift.UserGiftWallDTO;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
@ -67,7 +69,8 @@ public class GiftConfigClientServiceImpl implements GiftConfigClientService {
@Override @Override
public Boolean updateGift(GiftConfigDTO giftConfigDTO) { public Boolean updateGift(GiftConfigDTO giftConfigDTO) {
return giftConfigService.updateGift(giftInnerConvertor.toGiftConfig(giftConfigDTO)); return giftConfigService.updateGift(
normalizeCpRelationType(giftInnerConvertor.toGiftConfig(giftConfigDTO)));
} }
@Override @Override
@ -82,7 +85,17 @@ public class GiftConfigClientServiceImpl implements GiftConfigClientService {
@Override @Override
public Boolean saveGift(GiftConfigDTO giftConfig) { public Boolean saveGift(GiftConfigDTO giftConfig) {
return giftConfigService.save(giftInnerConvertor.toGiftConfig(giftConfig)); return giftConfigService.save(normalizeCpRelationType(giftInnerConvertor.toGiftConfig(giftConfig)));
}
private GiftConfig normalizeCpRelationType(GiftConfig giftConfig) {
if (Objects.isNull(giftConfig)) {
return null;
}
if (!Objects.equals(giftConfig.getGiftTab(), GiftTabEnum.CP.name())) {
return giftConfig.setCpRelationType(null);
}
return giftConfig.setCpRelationType(CpRelationshipType.normalize(giftConfig.getCpRelationType()));
} }
@Override @Override

View File

@ -367,24 +367,23 @@ public class TeamManualSalaryPaymentClientServiceImpl implements
List<TeamSalaryPayment> cyclePayments = listCyclePayments(salaryPaymentMap, List<TeamSalaryPayment> cyclePayments = listCyclePayments(salaryPaymentMap,
target.getUserId(), billBelong); target.getUserId(), billBelong);
PaymentSummary paymentSummary = summarizePayments(cyclePayments);
PaymentSummary receivedPaymentSummary = summarizeReceivedPayments( PaymentSummary receivedPaymentSummary = summarizeReceivedPayments(
new ArrayList<>(salaryPaymentMap.values()), target.getUserId()); new ArrayList<>(salaryPaymentMap.values()), target.getUserId());
BigDecimal expectedMemberSalary = salaryValue(billTarget.getMemberSalary()); BigDecimal expectedMemberSalary = salaryValue(billTarget.getMemberSalary());
BigDecimal expectedAgentSalary = salaryValue(billTarget.getOwnSalary()); BigDecimal expectedAgentSalary = salaryValue(billTarget.getOwnSalary());
BigDecimal expectedSalary = expectedTotalSalary(billTarget); BigDecimal expectedSalary = expectedTotalSalary(billTarget);
BigDecimal payableSalary = expectedSalary.subtract(paymentSummary.getTotalIssuedSalary()) BigDecimal payableSalary = expectedSalary.subtract(receivedPaymentSummary.getTotalIssuedSalary())
.setScale(2, RoundingMode.DOWN); .setScale(2, RoundingMode.DOWN);
BigDecimal payableMemberSalary = expectedMemberSalary BigDecimal payableMemberSalary = expectedMemberSalary
.subtract(paymentSummary.getMemberIssuedSalary()) .subtract(receivedPaymentSummary.getMemberIssuedSalary())
.setScale(2, RoundingMode.DOWN); .setScale(2, RoundingMode.DOWN);
BigDecimal payableAgentSalary = expectedAgentSalary BigDecimal payableAgentSalary = expectedAgentSalary
.subtract(paymentSummary.getAgentIssuedSalary()) .subtract(receivedPaymentSummary.getAgentIssuedSalary())
.setScale(2, RoundingMode.DOWN); .setScale(2, RoundingMode.DOWN);
boolean currentLevelSettled = areCurrentRolesSettled(cyclePayments, target.getUserId(), region, boolean currentLevelSettled = areCurrentRolesSettled(cyclePayments, target.getUserId(), region,
billTarget, payableSalary); billTarget, payableSalary);
List<PolicyPayment> policyPayments = buildSettlementPayments(policyManager, billTarget, List<PolicyPayment> policyPayments = buildSettlementPayments(policyManager, billTarget,
cyclePayments, target.getUserId(), region, paymentSummary, payableSalary, cyclePayments, target.getUserId(), region, receivedPaymentSummary, payableSalary,
currentLevelSettled); currentLevelSettled);
TeamManualSalaryPaymentDTO dto = new TeamManualSalaryPaymentDTO() TeamManualSalaryPaymentDTO dto = new TeamManualSalaryPaymentDTO()

View File

@ -151,10 +151,10 @@ public class CpLocalFlowSmokeTest {
UserPair cpPair = findPair(candidates, 1, 0, "CP"); UserPair cpPair = findPair(candidates, 1, 0, "CP");
runCpGiftApplyBlessDismissFlow(cpPair); runCpGiftApplyBlessDismissFlow(cpPair);
UserPair brotherPair = findPair(candidates, 1, 1, "BROTHER"); UserPair brotherPair = findPair(candidates, 1, 0, "BROTHER");
runNoChargeApplyAndAgreeFlow(brotherPair, "BROTHER"); runNoChargeApplyAndAgreeFlow(brotherPair, "BROTHER");
UserPair sistersPair = findPair(candidates, 0, 0, "SISTERS"); UserPair sistersPair = findPair(candidates, 0, 1, "SISTERS");
runNoChargeApplyAndAgreeFlow(sistersPair, "SISTERS"); runNoChargeApplyAndAgreeFlow(sistersPair, "SISTERS");
} }
@ -197,6 +197,36 @@ public class CpLocalFlowSmokeTest {
cpApplyService.getId(sameGenderPair.acceptUserId, sameGenderPair.sendUserId, "CP")); cpApplyService.getId(sameGenderPair.acceptUserId, sameGenderPair.sendUserId, "CP"));
} }
@Test
public void cpGiftAutoApplyUsesGiftRelationType() {
List<BaseInfo> candidates = baseInfoService.query()
.select(BaseInfo::getId, BaseInfo::getUserSex, BaseInfo::getOriginSys,
BaseInfo::getCountryCode)
.eq(BaseInfo::getOriginSys, SYS_ORIGIN)
.in(BaseInfo::getUserSex, Arrays.asList(0, 1))
.orderByDesc(BaseInfo::getId)
.last("LIMIT 500")
.list();
assertFalse("local LIKEI candidates should not be empty", candidates.isEmpty());
assertGiftAutoApplyRelationType(findPair(candidates, 1, 0, "BROTHER"), "BROTHER");
assertGiftAutoApplyRelationType(findPair(candidates, 0, 1, "SISTERS"), "SISTERS");
}
private void assertGiftAutoApplyRelationType(UserPair pair, String relationType) {
boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(pair.sendUserId,
pair.acceptUserId, SYS_ORIGIN, relationType);
assertTrue(relationType + " gift should auto create apply", created);
Long applyId = cpApplyService.getId(pair.acceptUserId, pair.sendUserId, relationType);
assertNotNull(relationType + " auto apply id", applyId);
createdApplyIds.add(applyId);
CpApply apply = cpApplyService.getById(applyId);
assertEquals(relationType, apply.getRelationType());
assertEquals(0, BigDecimal.ZERO.compareTo(apply.getApplyConsumeGold()));
}
private void runCpGiftApplyBlessDismissFlow(UserPair pair) throws Exception { private void runCpGiftApplyBlessDismissFlow(UserPair pair) throws Exception {
boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(pair.sendUserId, pair.acceptUserId, boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(pair.sendUserId, pair.acceptUserId,
SYS_ORIGIN); SYS_ORIGIN);
@ -394,14 +424,14 @@ public class CpLocalFlowSmokeTest {
String relationType) { String relationType) {
for (BaseInfo send : candidates) { for (BaseInfo send : candidates) {
if (!Objects.equals(send.getUserSex(), sendSex) || hasWaitApply(send.getId(), relationType) if (!Objects.equals(send.getUserSex(), sendSex) || hasWaitApply(send.getId(), relationType)
|| cpRelationshipService.countCp(send.getId()) >= 4) { || cpRelationshipService.countCp(send.getId(), relationType) >= relationMaxCount(relationType)) {
continue; continue;
} }
for (BaseInfo accept : candidates) { for (BaseInfo accept : candidates) {
if (Objects.equals(send.getId(), accept.getId()) if (Objects.equals(send.getId(), accept.getId())
|| !Objects.equals(accept.getUserSex(), acceptSex) || !Objects.equals(accept.getUserSex(), acceptSex)
|| hasReceivedWaitApply(accept.getId(), relationType) || hasReceivedWaitApply(accept.getId(), relationType)
|| cpRelationshipService.countCp(accept.getId()) >= 4 || cpRelationshipService.countCp(accept.getId(), relationType) >= relationMaxCount(relationType)
|| cpRelationshipService.existsCp(send.getId(), accept.getId(), relationType)) { || cpRelationshipService.existsCp(send.getId(), accept.getId(), relationType)) {
continue; continue;
} }
@ -417,6 +447,10 @@ public class CpLocalFlowSmokeTest {
return cpApplyService.existsNotProcessSendApply(sendUserId, relationType); return cpApplyService.existsNotProcessSendApply(sendUserId, relationType);
} }
private int relationMaxCount(String relationType) {
return Objects.equals("CP", relationType) ? 1 : 3;
}
private boolean hasReceivedWaitApply(Long acceptUserId, String relationType) { private boolean hasReceivedWaitApply(Long acceptUserId, String relationType) {
return cpApplyService.query() return cpApplyService.query()
.eq(CpApply::getAcceptApplyUserId, acceptUserId) .eq(CpApply::getAcceptApplyUserId, acceptUserId)

View File

@ -0,0 +1,7 @@
ALTER TABLE `sys_gift_config`
ADD COLUMN `cp_relation_type` varchar(32) DEFAULT NULL COMMENT 'CP礼物关系类型CP/BROTHER/SISTERS' AFTER `gift_tab`;
UPDATE `sys_gift_config`
SET `cp_relation_type` = 'CP'
WHERE `gift_tab` = 'CP'
AND (`cp_relation_type` IS NULL OR `cp_relation_type` = '');