家族宝箱功能完善

This commit is contained in:
tianfeng 2025-12-24 16:05:39 +08:00
parent 5f9b076a16
commit 6883d89b99
10 changed files with 145 additions and 64 deletions

View File

@ -83,6 +83,16 @@ public enum FamilyErrorCode implements IResponseErrorCode {
*/
USER_NOT_FOUND(2514, "There are no member of this family"),
/**
* 宝箱未解锁
*/
BOX_NOT_UNLOCKED(2515, "The treasure box has not been unlocked yet"),
/**
* 宝箱已领取
*/
BOX_ALREADY_CLAIMED(2516, "You have already claimed this treasure box"),
;

View File

@ -4,17 +4,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.app.dto.clientobject.family.FamilyBaseInfoCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyCreateRulesCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyLeaderboardCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyLevelParentCacheCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyListCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyBoxStatusCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyMemberCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyMsgListCO;
import com.red.circle.other.app.dto.clientobject.family.MyFamilyApplyCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyRewardCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyUserInfoCO;
import com.red.circle.other.app.dto.clientobject.family.*;
import com.red.circle.other.app.dto.clientobject.room.RoomVoiceProfileCO;
import com.red.circle.other.app.dto.cmd.family.ApplyJoinFamilyCmd;
import com.red.circle.other.app.dto.cmd.family.CancelFamilyApplyCmd;
@ -234,18 +224,10 @@ public class FamilyRestController extends BaseController {
* 投入宝箱金币.
*/
@PostMapping("/box/contribute")
public void boxContribute(@RequestBody AppExtCommand cmd) {
public void boxContribute(@RequestBody FamilyBaseInfoQueryCmd cmd) {
familyService.boxContribute(cmd);
}
/**
* 查询宝箱状态.
*/
@GetMapping("/box/status")
public FamilyBoxStatusCO boxStatus(AppExtCommand cmd) {
return familyService.boxStatus(cmd);
}
/**
* 领取宝箱奖励.
*/

View File

@ -3,6 +3,7 @@ package com.red.circle.other.app.command.family;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.clientobject.family.FamilyBaseInfoCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyBoxChestCO;
import com.red.circle.other.app.dto.cmd.family.FamilyBaseInfoQueryCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.OwnSpecialId;
@ -10,20 +11,31 @@ import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.common.family.FamilyCommon;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBaseInfo;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBoxDaily;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBoxUserReward;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberInfo;
import com.red.circle.other.infra.database.rds.enums.OtherConfigEnum;
import com.red.circle.other.infra.database.rds.service.family.FamilyBaseInfoService;
import com.red.circle.other.infra.database.rds.service.family.FamilyBoxDailyService;
import com.red.circle.other.infra.database.rds.service.family.FamilyBoxUserContributeService;
import com.red.circle.other.infra.database.rds.service.family.FamilyBoxUserRewardService;
import com.red.circle.other.infra.database.rds.service.family.FamilyLevelExpService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService;
import com.red.circle.other.infra.enums.family.FamilyRoleEnum;
import com.red.circle.other.infra.enums.family.FamilyStatusEnum;
import com.red.circle.other.infra.enums.family.FamilyBoxConfigEnum;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.model.dto.famliy.FamilyDetailsDTO;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@ -43,6 +55,9 @@ public class FamilyBaseInfoExe {
private final FamilyLevelExpService familyLevelExpService;
private final FamilyBaseInfoService familyBaseInfoService;
private final FamilyMemberInfoService familyMemberInfoService;
private final FamilyBoxDailyService familyBoxDailyService;
private final FamilyBoxUserContributeService familyBoxUserContributeService;
private final FamilyBoxUserRewardService familyBoxUserRewardService;
private final EnumConfigCacheService enumConfigCacheService;
private final UserProfileGateway userProfileGateway;
@ -66,12 +81,15 @@ public class FamilyBaseInfoExe {
}
}
return getFamilyInfoCO(baseInfo, familyLevel, adminInfo);
FamilyBaseInfoCO.FamilyBoxInfo boxInfo = buildBoxInfo(cmd.getFamilyId(), cmd.getReqUserId());
return getFamilyInfoCO(baseInfo, familyLevel, adminInfo, boxInfo);
}
private FamilyBaseInfoCO getFamilyInfoCO(FamilyBaseInfo baseInfo,
FamilyDetailsDTO familyLevel,
FamilyBaseInfoCO.FamilyAdminInfo adminInfo) {
FamilyBaseInfoCO.FamilyAdminInfo adminInfo,
FamilyBaseInfoCO.FamilyBoxInfo boxInfo) {
return new FamilyBaseInfoCO()
.setFamilyId(baseInfo.getId())
@ -89,7 +107,8 @@ public class FamilyBaseInfoExe {
.orElse(0))
.setLevelExp(familyLevel.getLevelExp())
.setMaxMember(familyLevel.getMaxMember())
.setLevelBackgroundPicture(familyLevel.getLevelBackgroundPicture());
.setLevelBackgroundPicture(familyLevel.getLevelBackgroundPicture())
.setBoxInfo(boxInfo);
}
private FamilyDetailsDTO getCacheFamilyLevel(FamilyBaseInfoQueryCmd cmd, Long familyId) {
@ -108,5 +127,33 @@ public class FamilyBaseInfoExe {
return familyBaseInfo;
}
private FamilyBaseInfoCO.FamilyBoxInfo buildBoxInfo(Long familyId, Long userId) {
LocalDate today = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
FamilyBoxDaily dailyRecord = familyBoxDailyService.getByFamilyAndDate(familyId, today);
Integer contributeCount = Objects.nonNull(dailyRecord) ? dailyRecord.getContributeCount() : 0;
Boolean userContributed = familyBoxUserContributeService.hasContributed(familyId, userId, today);
List<FamilyBoxUserReward> claimedList = familyBoxUserRewardService.listByUserAndDate(familyId, userId, today);
Set<Integer> claimedLevels = claimedList.stream()
.map(FamilyBoxUserReward::getLevel)
.collect(Collectors.toSet());
List<FamilyBoxChestCO> chests = java.util.stream.Stream.of(FamilyBoxConfigEnum.values())
.map(config -> new FamilyBoxChestCO()
.setLevel(config.getLevel())
.setRequiredCount(config.getRequiredCount())
// .setRewardGold(config.getRewardGold())
.setUnlocked(contributeCount >= config.getRequiredCount())
.setClaimed(claimedLevels.contains(config.getLevel())))
.collect(Collectors.toList());
FamilyBaseInfoCO.FamilyBoxInfo boxInfo = new FamilyBaseInfoCO.FamilyBoxInfo();
boxInfo.setContributeCount(contributeCount);
boxInfo.setUserContributed(userContributed);
boxInfo.setChests(chests);
return boxInfo;
}
}

View File

@ -3,6 +3,7 @@ package com.red.circle.other.app.command.family;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.app.dto.cmd.family.FamilyBoxClaimCmd;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBoxDaily;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBoxUserReward;
@ -13,9 +14,11 @@ import com.red.circle.other.infra.database.rds.service.family.FamilyBoxUserRewar
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService;
import com.red.circle.other.infra.enums.family.FamilyBoxConfigEnum;
import com.red.circle.other.inner.asserts.FamilyErrorCode;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.time.LocalDate;
import java.util.Objects;
@ -47,7 +50,7 @@ public class FamilyBoxClaimExe {
FamilyMemberInfo memberInfo = familyMemberInfoService.getFamilyMemberByUserId(cmd.getReqUserId());
ResponseAssert.notNull(FamilyErrorCode.NOT_EXIST_FAMILY_INFO_DATA, memberInfo);
LocalDate today = LocalDate.now();
LocalDate today = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
ResponseAssert.isTrue(FamilyErrorCode.NOT_EXIST_FAMILY_INFO_DATA,
familyBoxUserContributeService.hasContributed(memberInfo.getFamilyId(), cmd.getReqUserId(), today));
@ -55,12 +58,12 @@ public class FamilyBoxClaimExe {
FamilyBoxDaily dailyRecord = familyBoxDailyService.getByFamilyAndDate(memberInfo.getFamilyId(), today);
Integer contributeCount = Objects.nonNull(dailyRecord) ? dailyRecord.getContributeCount() : 0;
ResponseAssert.isTrue(CommonErrorCode.DATA_ERROR, contributeCount >= config.getRequiredCount());
ResponseAssert.isTrue(FamilyErrorCode.BOX_NOT_UNLOCKED, contributeCount >= config.getRequiredCount());
ResponseAssert.isFalse(FamilyErrorCode.REPEAT_APPLICATION,
ResponseAssert.isFalse(FamilyErrorCode.BOX_ALREADY_CLAIMED,
familyBoxUserRewardService.hasClaimed(memberInfo.getFamilyId(), cmd.getReqUserId(), today, cmd.getLevel()));
String recordId = memberInfo.getFamilyId() + "_" + cmd.getReqUserId() + "_" + today + "_" + cmd.getLevel();
String recordId = cmd.getReqUserId() + "_" + today + "_" + cmd.getLevel();
addUserGold(cmd.getReqUserId(), cmd.getReqSysOrigin().getOrigin(), config.getRewardGold(), recordId);
familyBoxUserRewardService.save(new FamilyBoxUserReward()
@ -82,6 +85,9 @@ public class FamilyBoxClaimExe {
.origin(GoldOrigin.FAMILY_BOX_REWARD)
.build();
walletGoldClient.changeBalance(build);
ResultResponse<WalletReceiptResDTO> changed = walletGoldClient.changeBalance(build);
if (!changed.checkSuccess()) {
throw new RuntimeException("Family box claim failed to change balance");
}
}
}

View File

@ -1,19 +1,26 @@
package com.red.circle.other.app.command.family;
import com.red.circle.common.business.core.enums.CommonErrorEnum;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.app.dto.cmd.family.FamilyBaseInfoQueryCmd;
import com.red.circle.other.infra.common.family.FamilyCommon;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBaseInfo;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBoxUserContribute;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberInfo;
import com.red.circle.other.infra.database.rds.service.family.FamilyBaseInfoService;
import com.red.circle.other.infra.database.rds.service.family.FamilyBoxDailyService;
import com.red.circle.other.infra.database.rds.service.family.FamilyBoxUserContributeService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService;
import com.red.circle.other.infra.enums.family.FamilyBoxConfigEnum;
import com.red.circle.other.inner.asserts.FamilyErrorCode;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.time.LocalDate;
import java.util.Objects;
@ -35,19 +42,24 @@ public class FamilyBoxContributeExe {
private final FamilyBoxDailyService familyBoxDailyService;
private final FamilyBoxUserContributeService familyBoxUserContributeService;
private final WalletGoldClient walletGoldClient;
private final FamilyBaseInfoService familyBaseInfoService;
@Transactional(rollbackFor = Exception.class)
public void execute(AppExtCommand cmd) {
public void execute(FamilyBaseInfoQueryCmd cmd) {
FamilyMemberInfo memberInfo = familyMemberInfoService.getFamilyMemberByUserId(cmd.getReqUserId());
ResponseAssert.notNull(FamilyErrorCode.NOT_EXIST_FAMILY_INFO_DATA, memberInfo);
LocalDate today = LocalDate.now();
FamilyBaseInfo info = familyBaseInfoService.getBaseInfoById(cmd.getFamilyId());
ResponseAssert.notNull(FamilyErrorCode.NOT_EXIST_FAMILY_INFO_DATA, info);
ResponseAssert.isTrue(CommonErrorEnum.INSUFFICIENT_PERMISSION, Objects.equals(info.getId(), memberInfo.getId()));
LocalDate today = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
ResponseAssert.isFalse(FamilyErrorCode.REPEAT_APPLICATION,
familyBoxUserContributeService.hasContributed(memberInfo.getFamilyId(), cmd.getReqUserId(), today));
String recordId = memberInfo.getFamilyId() + "_" + cmd.getReqUserId() + "_" + today;
String recordId = cmd.getReqUserId() + "_" + today;
consumeGold(cmd.getReqUserId(), cmd.getReqSysOrigin().getOrigin(), recordId);
familyBoxUserContributeService.save(new FamilyBoxUserContribute()
@ -70,6 +82,10 @@ public class FamilyBoxContributeExe {
.sysOrigin(SysOriginPlatformEnum.valueOf(sysOrigin))
.origin(GoldOrigin.FAMILY_BOX_CONTRIBUTE)
.build();
walletGoldClient.changeBalance(build);
ResultResponse<WalletReceiptResDTO> changed = walletGoldClient.changeBalance(build);
if (!changed.checkSuccess()) {
throw new RuntimeException("Family box contribute failed to change balance");
}
}
}

View File

@ -58,8 +58,8 @@ public class FamilyBoxStatusExe {
for (FamilyBoxConfigEnum config : FamilyBoxConfigEnum.values()) {
chests.add(new FamilyBoxChestCO()
.setLevel(config.getLevel())
.setRequiredCount(config.getRequiredCount())
.setRewardGold(config.getRewardGold())
// .setRequiredCount(config.getRequiredCount())
// .setRewardGold(config.getRewardGold())
.setUnlocked(contributeCount >= config.getRequiredCount())
.setClaimed(claimedLevels.contains(config.getLevel())));
}

View File

@ -30,14 +30,12 @@ import com.red.circle.other.app.command.family.CancelFamilyApplyExe;
import com.red.circle.other.app.command.family.ReapplyFamilyExe;
import com.red.circle.other.app.command.family.FamilyListExe;
import com.red.circle.other.app.command.family.FamilyBoxContributeExe;
import com.red.circle.other.app.command.family.FamilyBoxStatusExe;
import com.red.circle.other.app.command.family.FamilyBoxClaimExe;
import com.red.circle.other.app.dto.clientobject.family.FamilyBaseInfoCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyCreateRulesCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyLeaderboardCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyLevelParentCacheCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyListCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyBoxStatusCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyMemberCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyMsgListCO;
import com.red.circle.other.app.dto.clientobject.family.MyFamilyApplyCO;
@ -116,7 +114,6 @@ public class FamilyServiceImpl implements FamilyService {
private final ReapplyFamilyExe reapplyFamilyExe;
private final FamilyListExe familyListExe;
private final FamilyBoxContributeExe familyBoxContributeExe;
private final FamilyBoxStatusExe familyBoxStatusExe;
private final FamilyBoxClaimExe familyBoxClaimExe;
private final DistributedLockUtil distributedLockUtil;
@ -278,18 +275,13 @@ public class FamilyServiceImpl implements FamilyService {
}
@Override
public void boxContribute(AppExtCommand cmd) {
public void boxContribute(FamilyBaseInfoQueryCmd cmd) {
String key = "FAMILY_BOX_CONTRIBUTE" + ":" + cmd.getReqUserId();
distributedLockUtil.executeWithLock(key, () -> {
familyBoxContributeExe.execute(cmd);
});
}
@Override
public FamilyBoxStatusCO boxStatus(AppExtCommand cmd) {
return familyBoxStatusExe.execute(cmd);
}
@Override
public void boxClaim(FamilyBoxClaimCmd cmd) {
String key = "FAMILY_BOX_CLAIM" + ":" + cmd.getReqUserId();

View File

@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.framework.dto.ClientObject;
import java.io.Serial;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -114,6 +115,10 @@ public class FamilyBaseInfoCO extends ClientObject {
*/
private String levelBackgroundPicture;
/**
* 家族宝箱信息.
*/
private FamilyBoxInfo boxInfo;
/**
* 家族群聊付费解锁金额.
@ -136,4 +141,24 @@ public class FamilyBaseInfoCO extends ClientObject {
}
@Data
public static class FamilyBoxInfo {
/**
* 当日投入人数.
*/
private Integer contributeCount;
/**
* 当前用户是否已投入.
*/
private Boolean userContributed;
/**
* 宝箱列表.
*/
private List<FamilyBoxChestCO> chests;
}
}

View File

@ -159,12 +159,7 @@ public interface FamilyService {
/**
* 投入家族宝箱金币.
*/
void boxContribute(AppExtCommand cmd);
/**
* 查询家族宝箱状态.
*/
FamilyBoxStatusCO boxStatus(AppExtCommand cmd);
void boxContribute(FamilyBaseInfoQueryCmd cmd);
/**
* 领取家族宝箱奖励.

View File

@ -6,6 +6,8 @@ import com.red.circle.other.infra.database.rds.entity.family.FamilyBoxDaily;
import com.red.circle.other.infra.database.rds.service.family.FamilyBoxDailyService;
import java.time.LocalDate;
import java.util.Objects;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
/**
@ -25,21 +27,27 @@ public class FamilyBoxDailyServiceImpl extends BaseServiceImpl<FamilyBoxDailyDAO
@Override
public void incrementContributeCount(Long familyId, LocalDate boxDate, String sysOrigin) {
FamilyBoxDaily record = getByFamilyAndDate(familyId, boxDate);
if (Objects.isNull(record)) {
record = new FamilyBoxDaily()
.setFamilyId(familyId)
.setBoxDate(boxDate)
.setSysOrigin(sysOrigin)
.setContributeCount(1);
save(record);
} else {
update()
.setSql("contribute_count = contribute_count + 1")
.eq(FamilyBoxDaily::getFamilyId, familyId)
.eq(FamilyBoxDaily::getBoxDate, boxDate)
.execute();
// 先尝试更新
if (tryUpdate(familyId, boxDate)) {
try {
FamilyBoxDaily record = new FamilyBoxDaily()
.setFamilyId(familyId)
.setBoxDate(boxDate)
.setSysOrigin(sysOrigin)
.setContributeCount(1);
save(record);
} catch (DuplicateKeyException ignored) {
tryUpdate(familyId, boxDate);
}
}
}
private boolean tryUpdate(Long familyId, LocalDate boxDate) {
return update()
.setSql("contribute_count = contribute_count + 1")
.eq(FamilyBoxDaily::getFamilyId, familyId)
.eq(FamilyBoxDaily::getBoxDate, boxDate)
.execute();
}
}