cp功能完善3

This commit is contained in:
tianfeng 2025-12-10 16:01:17 +08:00
parent b957576aa2
commit d3a05b1cf3
11 changed files with 312 additions and 58 deletions

View File

@ -57,6 +57,11 @@ public enum UserRelationErrorCode implements IResponseErrorCode {
*/
UNAVAILABLE_SAME_GENDER(4310, "Couples of the same gender cannot be created."),
/**
* CP数量已达上限.
*/
CP_LIMIT_EXCEEDED(4311, "CP limit exceeded, maximum 4 CPs allowed"),
;

View File

@ -41,22 +41,19 @@ public class DismissCpApplyCmdExe {
return;
}
// 更新申请状态为解散
cpApplyService.dismiss(cmd.requiredReqUserId());
cpRelationshipService.deleteCpRelationship(cmd.requiredReqUserId());
// 更新CP关系为分手中不删除数据
cpRelationshipService.updateToDismissing(cmd.requiredReqUserId(), cmd.getCpUserId());
weekCpValueCountService.removeThisWeek(cpRelationship.getUserId(),
cpRelationship.getCpUserId());
cpValueService.deleteById(cpRelationship.getCpValId());
userGiftBackpackCommon.removeUserCpGift(
Sets.newHashSet(cmd.requiredReqUserId(), cpRelationship.getCpUserId()));
userCpValueCacheService.remove(cpRelationship.getUserId(), cpRelationship.getCpUserId());
cpCabinAchieveService.deleteByCpValId(cpRelationship.getCpValId());
cpBlessRecordService.deleteByCpValId(cpRelationship.getCpValId());
// 以下数据不删除7天后由定时任务清理
// weekCpValueCountService.removeThisWeek(...)
// cpValueService.deleteById(...)
// userGiftBackpackCommon.removeUserCpGift(...)
// userCpValueCacheService.remove(...)
// cpCabinAchieveService.deleteByCpValId(...)
// cpBlessRecordService.deleteByCpValId(...)
}

View File

@ -10,6 +10,7 @@ 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.dto.cmd.user.relation.cp.ProcessApplyCmd;
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;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
@ -56,10 +57,16 @@ public class ProcessCpApplyCmd {
ResponseAssert.isTrue(CommonErrorCode.STATE_ERROR,
Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name()));
// CP已存在
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
cpRelationshipService.existsCp(
Lists.newArrayList(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId())));
// 检查是否是复合申请
CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId());
boolean isReconcile = Objects.nonNull(dismissingCp);
if (!isReconcile) {
// 非复合申请CP已存在
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
cpRelationshipService.existsCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()));
}
// 操作失败
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
@ -67,22 +74,20 @@ public class ProcessCpApplyCmd {
if (!isAgree(cmd)) {
returnGoldCoins(cmd, cpApply);
// imMessageClient.sendMessageText(cpApply.getAcceptApplyUserId().toString(),
// cpApply.getSendApplyUserId().toString(),
// cmd.getApplyText());
return;
}
// GiftConfigDTO cpGift = ResponseAssert.requiredSuccess(
// giftConfigClient.getPairCpGive(SysOriginPlatformEnum.valueOf(cmd.requireReqSysOrigin()))
// );
//
// ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, cpGift);
refuseStatusWait(cmd, cpApply);
cpRelationshipService
.addCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), createCpValue(),
cmd.requireReqSysOrigin());
if (isReconcile) {
// 复合更新状态为NORMAL
cpRelationshipService.reconcile(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId());
} else {
// 新建关系
cpRelationshipService
.addCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), createCpValue(),
cmd.requireReqSysOrigin());
}
// giftBackpackClient.incrGiftAndLimit(new GiftBackpackLimitCmd()
// .setUserId(cpApply.getSendApplyUserId())

View File

@ -18,6 +18,7 @@ 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.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;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
@ -58,10 +59,23 @@ public class SendCpApplyCmdExe {
ResponseAssert.isFalse(UserRelationErrorCode.UNAVAILABLE_OPS_YOURSELF,
Objects.equals(cmd.requiredReqUserId(), cmd.getAcceptApplyUserId()));
// CP已存在
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
cpRelationshipService
.existsCp(Lists.newArrayList(cmd.getAcceptApplyUserId(), cmd.requiredReqUserId())));
// 检查是否是复合申请
CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
cmd.requiredReqUserId(), cmd.getAcceptApplyUserId());
boolean isReconcile = Objects.nonNull(dismissingCp);
if (!isReconcile) {
// 非复合申请检查CP是否已存在
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
cpRelationshipService
.existsCp(cmd.requiredReqUserId(), cmd.getAcceptApplyUserId()));
// 检查CP数量限制最多4个
int senderCpCount = cpRelationshipService.countNormalCp(cmd.requiredReqUserId());
int receiverCpCount = cpRelationshipService.countNormalCp(cmd.getAcceptApplyUserId());
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, senderCpCount >= 4);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, receiverCpCount >= 4);
}
// 您已发送申请
ResponseAssert.isFalse(UserRelationErrorCode.YOU_HAVE_SENT_AN_APPLICATION,
@ -87,7 +101,9 @@ public class SendCpApplyCmdExe {
.build())
);
String applyText = "User " + userProfile.getUserNickname() + " confessed the feeling to you; if you accept, you will become a couple.";
String applyText = isReconcile
? "User " + userProfile.getUserNickname() + " wants to reconcile with you; if you accept, you will be a couple again."
: "User " + userProfile.getUserNickname() + " confessed the feeling to you; if you accept, you will become a couple.";
CpApply cpApply = new CpApply()
.setSendApplyUserId(cmd.requiredReqUserId())

View File

@ -0,0 +1,89 @@
package com.red.circle.other.app.scheduler;
import com.google.common.collect.Sets;
import com.red.circle.other.infra.common.user.UserGiftBackpackCommon;
import com.red.circle.other.infra.database.cache.service.user.UserCpValueCacheService;
import com.red.circle.other.infra.database.mongo.service.user.count.WeekCpValueCountService;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService;
import com.red.circle.other.infra.database.rds.service.user.user.CpCabinAchieveService;
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.tool.core.collection.CollectionUtils;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 清理7天前分手中的CP数据.
*
* @author AI Assistant
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CleanExpiredDismissingCpTask {
private final CpRelationshipService cpRelationshipService;
private final CpValueService cpValueService;
private final WeekCpValueCountService weekCpValueCountService;
private final UserGiftBackpackCommon userGiftBackpackCommon;
private final UserCpValueCacheService userCpValueCacheService;
private final CpCabinAchieveService cpCabinAchieveService;
private final CpBlessRecordService cpBlessRecordService;
/**
* 每天凌晨3点执行清理任务.
*/
@Scheduled(cron = "0 0 3 * * ?")
public void execute() {
try {
List<CpRelationship> expiredList = cpRelationshipService.listExpiredDismissingCp();
if (CollectionUtils.isEmpty(expiredList)) {
return;
}
for (CpRelationship cpRelationship : expiredList) {
cleanCpData(cpRelationship);
}
} catch (Exception e) {
log.error("CleanExpiredDismissingCpTask execute error", e);
}
}
private void cleanCpData(CpRelationship cpRelationship) {
try {
// 删除CP关系
cpRelationshipService.deleteCpRelationship(cpRelationship.getUserId());
// 删除周统计
weekCpValueCountService.removeThisWeek(cpRelationship.getUserId(),
cpRelationship.getCpUserId());
// 删除CP值
cpValueService.deleteById(cpRelationship.getCpValId());
// 移除CP礼物
userGiftBackpackCommon.removeUserCpGift(
Sets.newHashSet(cpRelationship.getUserId(), cpRelationship.getCpUserId()));
// 清除缓存
userCpValueCacheService.remove(cpRelationship.getUserId(), cpRelationship.getCpUserId());
// 删除成就
cpCabinAchieveService.deleteByCpValId(cpRelationship.getCpValId());
// 删除祝福记录
cpBlessRecordService.deleteByCpValId(cpRelationship.getCpValId());
} catch (Exception e) {
log.error("CleanExpiredDismissingCpTask cleanCpData error, userId={}, cpUserId={}",
cpRelationship.getUserId(), cpRelationship.getCpUserId(), e);
}
}
}

View File

@ -2,6 +2,7 @@ package com.red.circle.other.app.dto.cmd.user.relation.cp;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -17,13 +18,11 @@ import org.hibernate.validator.constraints.Length;
@Accessors(chain = true)
public class CpApplyDismissCmd extends AppExtCommand {
/**
* 申请文案长度1100.
*
* @eo.required
*/
// @Length(min = 1, max = 100, message = "applyText range 1~100")
// @NotBlank(message = "applyText {not.null}")
// private String applyText;
/**
* cp用户id
*/
@NotNull
private Long cpUserId;
}

View File

@ -70,5 +70,16 @@ public class CpRelationship extends TimestampBaseEntity {
@TableField("amount")
private BigDecimal amount;
/**
* CP关系状态NORMAL-正常, DISMISSING-分手中
*/
@TableField("status")
private String status;
/**
* 分手时间分手中状态时记录
*/
@TableField("dismiss_time")
private Long dismissTime;
}

View File

@ -35,12 +35,9 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
boolean existsCp(Long userIds);
/**
* 是否存在cp.
*
* @param userIds 用户id
* @return true 存在false 不存在
* 是否互为cp.
*/
boolean existsCp(List<Long> userIds);
boolean existsCp(Long userId, Long cpUserId);
/**
* 获取cp用户id.
@ -106,4 +103,43 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/
CpRelationship getByUserIdOrCpUserId(Long userId);
/**
* 获取用户正常CP数量.
*
* @param userId 用户id
* @return CP数量
*/
int countNormalCp(Long userId);
/**
* 获取分手中的CP关系.
*
* @param userId 用户id
* @param cpUserId cp用户id
* @return CP关系
*/
CpRelationship getDismissingCp(Long userId, Long cpUserId);
/**
* 更新CP关系为分手中.
*
* @param userId 用户id
*/
void updateToDismissing(Long userId, Long cpUserId);
/**
* 复合CP更新为正常状态.
*
* @param userId 用户id
* @param cpUserId cp用户id
*/
void reconcile(Long userId, Long cpUserId);
/**
* 获取7天前分手中的CP关系列表.
*
* @return CP关系列表
*/
List<CpRelationship> listExpiredDismissingCp();
}

View File

@ -8,6 +8,7 @@ import com.red.circle.tool.core.date.LocalDateUtils;
import com.red.circle.other.infra.database.rds.dao.user.user.CpRelationshipDAO;
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 com.red.circle.other.infra.enums.user.user.CpRelationshipStatus;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
@ -46,20 +47,26 @@ public class CpRelationshipServiceImpl extends
@Override
public boolean existsCp(Long userId) {
return Optional.ofNullable(
query().select(CpRelationship::getId).eq(CpRelationship::getUserId, userId)
query().select(CpRelationship::getId)
.eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.last(PageConstant.LIMIT_ONE).getOne()
).map(cpRelationship -> Objects.nonNull(cpRelationship.getId())).orElse(Boolean.FALSE);
}
@Override
public boolean existsCp(List<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Boolean.FALSE;
}
return CollectionUtils
.isNotEmpty(query().select(CpRelationship::getId).in(CpRelationship::getUserId, userIds)
.last(PageConstant.formatLimit(userIds.size())).list());
public boolean existsCp(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
@ -74,6 +81,7 @@ public class CpRelationshipServiceImpl extends
public CpRelationship getByUserId(Long userId) {
return Optional.ofNullable(query()
.eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.last(PageConstant.LIMIT_ONE)
.getOne()
).orElse(null);
@ -140,11 +148,74 @@ public class CpRelationshipServiceImpl extends
.setCpValId(bindCpValId)
.setInitiate(initiate)
.setSysOrigin(sysOrigin)
.setAmount(BigDecimal.ZERO);
.setAmount(BigDecimal.ZERO)
.setStatus(CpRelationshipStatus.NORMAL.name())
.setDismissTime(null);
cpUser.setCreateTime(LocalDateUtils.nowTimestamp());
cpUser.setUpdateTime(LocalDateUtils.nowTimestamp());
return cpUser;
}
@Override
public int countNormalCp(Long userId) {
return Math.toIntExact(query()
.eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.count());
}
@Override
public CpRelationship getDismissingCp(Long userId, Long cpUserId) {
return query()
.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))
)
.eq(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name())
.last(PageConstant.LIMIT_ONE)
.getOne();
}
@Override
public void updateToDismissing(Long userId, Long cpUserId) {
Long now = LocalDateUtils.nowTimestamp().getTime();
update()
.set(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name())
.set(CpRelationship::getDismissTime, now)
.set(CpRelationship::getUpdateTime, now)
.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))
)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.execute();
}
@Override
public void reconcile(Long userId, Long cpUserId) {
Long now = LocalDateUtils.nowTimestamp().getTime();
update()
.set(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.set(CpRelationship::getDismissTime, null)
.set(CpRelationship::getUpdateTime, now)
.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))
)
.eq(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name())
.execute();
}
@Override
public List<CpRelationship> listExpiredDismissingCp() {
Long sevenDaysAgo = LocalDateUtils.nowTimestamp().getTime() - (7 * 24 * 60 * 60 * 1000L) ;
return query()
.eq(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name())
.le(CpRelationship::getDismissTime, sevenDaysAgo)
.list();
}
}

View File

@ -30,6 +30,11 @@ public enum CpApplyStatus {
/**
* 退款.
*/
REIMBURSE
REIMBURSE,
/**
* 分手中.
*/
DISMISSING
}

View File

@ -0,0 +1,20 @@
package com.red.circle.other.infra.enums.user.user;
/**
* CP关系状态.
*
* @author AI Assistant
*/
public enum CpRelationshipStatus {
/**
* 正常.
*/
NORMAL,
/**
* 分手中.
*/
DISMISSING
}