From a9320d58ec659196da4a6771aec05a2f29783289 Mon Sep 17 00:00:00 2001 From: hy001 Date: Tue, 19 May 2026 21:00:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0VIP=20=E8=B5=A0=E9=80=81?= =?UTF-8?q?=EF=BC=8C=E4=B8=BB=E6=92=AD=E5=B7=A5=E8=B5=84=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bank/api/UserBankBalanceClientApi.java | 24 +- .../model/cmd/UserBankBalanceQryCmd.java | 25 +- .../dto/UserBankBalanceStatisticsDTO.java | 29 ++ .../user/UserBankBalanceRestController.java | 29 +- .../user/UserBankBalanceBackServiceImpl.java | 21 +- .../app/user/UserBankBalanceBackService.java | 28 +- .../activity/PropsActivitySendCommon.java | 109 ++++-- .../activity/VipActivityRewardCommon.java | 346 ++++++++++++++++++ .../database/rds/dao/vip/UserVipStateDAO.java | 7 + .../rds/dao/vip/VipLevelConfigDAO.java | 7 + .../rds/dao/vip/VipOrderRecordDAO.java | 7 + .../database/rds/entity/vip/UserVipState.java | 130 +++++++ .../rds/entity/vip/VipLevelConfig.java | 127 +++++++ .../rds/entity/vip/VipOrderRecord.java | 73 ++++ .../rds/service/vip/UserVipStateService.java | 7 + .../service/vip/VipLevelConfigService.java | 15 + .../service/vip/VipOrderRecordService.java | 7 + .../vip/impl/UserVipStateServiceImpl.java | 12 + .../vip/impl/VipLevelConfigServiceImpl.java | 59 +++ .../vip/impl/VipOrderRecordServiceImpl.java | 12 + .../props/ActivitySourceGroupGatewayImpl.java | 238 +++++++++--- ...sActivityRewardGroupClientServiceImpl.java | 202 +++++++--- .../service/bank/UserBankBalanceService.java | 22 +- .../bank/impl/UserBankBalanceServiceImpl.java | 152 +++++--- .../wallet/UserBankBalanceClientEndpoint.java | 21 +- .../wallet/UserBankBalanceClientService.java | 20 +- .../UserBankBalanceClientServiceImpl.java | 20 +- 27 files changed, 1507 insertions(+), 242 deletions(-) create mode 100644 rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/dto/UserBankBalanceStatisticsDTO.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/VipActivityRewardCommon.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/UserVipStateDAO.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipLevelConfigDAO.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipOrderRecordDAO.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/UserVipState.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipLevelConfig.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipOrderRecord.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/UserVipStateService.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipLevelConfigService.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipOrderRecordService.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/UserVipStateServiceImpl.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipLevelConfigServiceImpl.java create mode 100644 rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipOrderRecordServiceImpl.java diff --git a/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/bank/api/UserBankBalanceClientApi.java b/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/bank/api/UserBankBalanceClientApi.java index 837e3643..ac42a8c0 100644 --- a/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/bank/api/UserBankBalanceClientApi.java +++ b/rc-service/rc-inner-api/wallet-inner/wallet-inner-api/src/main/java/com/red/circle/wallet/inner/endpoint/bank/api/UserBankBalanceClientApi.java @@ -4,8 +4,9 @@ import com.red.circle.framework.dto.ResultResponse; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceDecrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceIncrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; -import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; -import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; import java.util.List; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -56,9 +57,16 @@ public interface UserBankBalanceClientApi { @GetMapping("/getTotalBalance") ResultResponse getTotalBalance(@RequestParam("userId") Long userId); - /** - * 查看用户银行卡账户. - */ - @PostMapping("/listBalance") - ResultResponse> listBalance(@RequestBody UserBankBalanceQryCmd query); -} + /** + * 查看用户银行卡账户. + */ + @PostMapping("/listBalance") + ResultResponse> listBalance(@RequestBody UserBankBalanceQryCmd query); + + /** + * 用户银行卡账户统计. + */ + @PostMapping("/calculateStatistics") + ResultResponse calculateStatistics( + @RequestBody UserBankBalanceQryCmd query); +} diff --git a/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/cmd/UserBankBalanceQryCmd.java b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/cmd/UserBankBalanceQryCmd.java index f2674df3..cddd5301 100644 --- a/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/cmd/UserBankBalanceQryCmd.java +++ b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/cmd/UserBankBalanceQryCmd.java @@ -37,8 +37,23 @@ public class UserBankBalanceQryCmd extends CommonCommand { */ private Long lastId; - /** - * 每页大小. - */ - private Integer limit; -} + /** + * 每页大小. + */ + private Integer limit; + + /** + * 页码. + */ + private Integer pageNo; + + /** + * 排序字段. + */ + private String sortField; + + /** + * 排序方向. + */ + private String sortOrder; +} diff --git a/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/dto/UserBankBalanceStatisticsDTO.java b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/dto/UserBankBalanceStatisticsDTO.java new file mode 100644 index 00000000..6988e356 --- /dev/null +++ b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/dto/UserBankBalanceStatisticsDTO.java @@ -0,0 +1,29 @@ +package com.red.circle.wallet.inner.model.dto; + +import java.io.Serializable; +import java.math.BigDecimal; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 用户银行账户统计. + */ +@Data +@Accessors(chain = true) +public class UserBankBalanceStatisticsDTO implements Serializable { + + /** + * 获得总额. + */ + private BigDecimal totalEarnPoints; + + /** + * 消费总额. + */ + private BigDecimal totalConsumptionPoints; + + /** + * 余额总额. + */ + private BigDecimal totalBalance; +} diff --git a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBankBalanceRestController.java b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBankBalanceRestController.java index e2a54c9f..067ef694 100644 --- a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBankBalanceRestController.java +++ b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/user/UserBankBalanceRestController.java @@ -15,10 +15,11 @@ import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; import com.red.circle.wallet.inner.model.cmd.UserBankMoneyCmd; import com.red.circle.wallet.inner.model.cmd.UserBankRunningWaterQryCmd; import com.red.circle.wallet.inner.model.cmd.UserBankTransferCmd; -import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawGoldApplyQryCmd; -import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyExportQryCmd; -import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyQryCmd; -import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent; +import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawGoldApplyQryCmd; +import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyExportQryCmd; +import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyQryCmd; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; +import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; @@ -57,12 +58,20 @@ public class UserBankBalanceRestController extends BaseController { * 用户余额列表. */ @GetMapping("/page") - public List page(UserBankBalanceQryCmd query) { - return userBankBalanceBackService.listUserBankBalance(query); - } - - /** - * 用户余额列表-导出. + public List page(UserBankBalanceQryCmd query) { + return userBankBalanceBackService.listUserBankBalance(query); + } + + /** + * 用户余额统计. + */ + @GetMapping("/statistics") + public UserBankBalanceStatisticsDTO statistics(UserBankBalanceQryCmd query) { + return userBankBalanceBackService.calculateStatistics(query); + } + + /** + * 用户余额列表-导出. */ @GetMapping("/export") public void export(HttpServletResponse response, String sysOrigin) throws IOException { diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackServiceImpl.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackServiceImpl.java index 61bc7787..40ccb430 100644 --- a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackServiceImpl.java +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackServiceImpl.java @@ -96,9 +96,9 @@ public class UserBankBalanceBackServiceImpl implements UserBankBalanceBackServic @Override - public List listUserBankBalance(UserBankBalanceQryCmd query) { - List userBankBalances = ResponseAssert.requiredSuccess( - userBankBalanceClient.listBalance(query)); + public List listUserBankBalance(UserBankBalanceQryCmd query) { + List userBankBalances = ResponseAssert.requiredSuccess( + userBankBalanceClient.listBalance(query)); if (CollectionUtils.isEmpty(userBankBalances)) { return Lists.newArrayList(); } @@ -122,11 +122,16 @@ public class UserBankBalanceBackServiceImpl implements UserBankBalanceBackServic .setUserProfile(userProfile) .setCreateTime(userBankBalance.getCreateTime()) .setUpdateTime(userBankBalance.getUpdateTime()); - }).filter(Objects::nonNull).collect(Collectors.toList()); - } - - - private Set getBankUserIds(List userBankBalances) { + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public UserBankBalanceStatisticsDTO calculateStatistics(UserBankBalanceQryCmd query) { + return ResponseAssert.requiredSuccess(userBankBalanceClient.calculateStatistics(query)); + } + + + private Set getBankUserIds(List userBankBalances) { return userBankBalances.stream().map(UserBankBalanceDTO::getId).collect(Collectors.toSet()); } diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackService.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackService.java index 5352cd1f..639f3f18 100644 --- a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackService.java +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/user/UserBankBalanceBackService.java @@ -9,10 +9,11 @@ import com.red.circle.wallet.inner.model.cmd.ApprovalMoneyApplyCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; import com.red.circle.wallet.inner.model.cmd.UserBankMoneyCmd; import com.red.circle.wallet.inner.model.cmd.UserBankRunningWaterQryCmd; -import com.red.circle.wallet.inner.model.cmd.UserBankTransferCmd; -import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawGoldApplyQryCmd; -import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyExportQryCmd; -import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyQryCmd; +import com.red.circle.wallet.inner.model.cmd.UserBankTransferCmd; +import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawGoldApplyQryCmd; +import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyExportQryCmd; +import com.red.circle.wallet.inner.model.cmd.UserBankWithdrawMoneyApplyQryCmd; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; import jakarta.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.util.List; @@ -25,13 +26,18 @@ import org.springframework.web.multipart.MultipartFile; */ public interface UserBankBalanceBackService { - /** - * 查询用户银行账户余额. - */ - List listUserBankBalance(UserBankBalanceQryCmd query); - - /** - * 导出余额列表. + /** + * 查询用户银行账户余额. + */ + List listUserBankBalance(UserBankBalanceQryCmd query); + + /** + * 统计用户银行账户余额. + */ + UserBankBalanceStatisticsDTO calculateStatistics(UserBankBalanceQryCmd query); + + /** + * 导出余额列表. */ void exportUserBankBalance(HttpServletResponse response, String sysOrigin); diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/PropsActivitySendCommon.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/PropsActivitySendCommon.java index 1c0232a9..c1433348 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/PropsActivitySendCommon.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/PropsActivitySendCommon.java @@ -84,9 +84,10 @@ public class PropsActivitySendCommon { private final RoomProfileManagerService roomProfileManagerService; private final ActivitySourceGroupGateway activitySourceGroupGateway; private final PropsMaterialInfraConvertor propsMaterialInfraConvertor; - private final PropsVipActualEquityService propsVipActualEquityService; - private final ActivityReceiveRewardLogService activityReceiveRewardLogService; - private final PropsActivityRewardConfigService propsActivityRewardConfigService; + private final PropsVipActualEquityService propsVipActualEquityService; + private final VipActivityRewardCommon vipActivityRewardCommon; + private final ActivityReceiveRewardLogService activityReceiveRewardLogService; + private final PropsActivityRewardConfigService propsActivityRewardConfigService; /** * 领取活动奖励. @@ -145,12 +146,20 @@ public class PropsActivitySendCommon { * 发送单个-活动道具. */ public void sendActivitySingle(SendRewardSingle param) { - if (Objects.isNull(param.getRewardConfig())) { - throw new IllegalArgumentException("Send rewardConfig is null."); - } - PropsActivityRewardConfig rewardConfig = param.getRewardConfig(); - sendGroup(param, Collections.singletonList(toPrizeDescribe(rewardConfig))); - } + if (Objects.isNull(param.getRewardConfig())) { + throw new IllegalArgumentException("Send rewardConfig is null."); + } + PropsActivityRewardConfig rewardConfig = param.getRewardConfig(); + if (isVipLevelReward(param, rewardConfig)) { + vipActivityRewardCommon.gift(param.getSysOrigin(), + param.getAcceptUserId(), + param.getTrackId(), + rewardConfig.getContent(), + param.getOrigin()); + return; + } + sendGroup(param, Collections.singletonList(toPrizeDescribe(rewardConfig))); + } /** * 发送奖励. @@ -159,17 +168,33 @@ public class PropsActivitySendCommon { List configs = propsActivityRewardConfigService.listByGroupId( param.getResourceGroupId()); - if (CollectionUtils.isEmpty(configs)) { - return; - } - propsSendCommon.send(new PrizeDescribeRewardCmd() - .setTrackId(param.getTrackId()) - .setAcceptUserId(param.getAcceptUserId()) - .setOrigin(param.getOrigin()) - .setSysOrigin(param.getSysOrigin()) - .setPrizes(toListPrizeDescribe(configs)) - ); - } + if (CollectionUtils.isEmpty(configs)) { + return; + } + + List vipConfigs = configs.stream() + .filter(config -> isVipLevelReward(param, config)) + .toList(); + vipConfigs.forEach(config -> vipActivityRewardCommon.gift(param.getSysOrigin(), + param.getAcceptUserId(), + param.getTrackId(), + config.getContent(), + param.getOrigin())); + + List normalConfigs = configs.stream() + .filter(config -> !isVipLevelReward(param, config)) + .toList(); + if (CollectionUtils.isEmpty(normalConfigs)) { + return; + } + propsSendCommon.send(new PrizeDescribeRewardCmd() + .setTrackId(param.getTrackId()) + .setAcceptUserId(param.getAcceptUserId()) + .setOrigin(param.getOrigin()) + .setSysOrigin(param.getSysOrigin()) + .setPrizes(toListPrizeDescribe(normalConfigs)) + ); + } /** @@ -196,10 +221,16 @@ public class PropsActivitySendCommon { return; } - if (PropsActivityRewardEnum.PROPS.eq(key)) { - sendProps(param, prizeArrays); - return; - } + if (PropsActivityRewardEnum.PROPS.eq(key)) { + sendVipLevelRewards(param, prizeArrays); + List normalProps = prizeArrays.stream() + .filter(prize -> !isVipLevelReward(param, prize)) + .toList(); + if (CollectionUtils.isNotEmpty(normalProps)) { + sendProps(param, normalProps); + } + return; + } if (PropsActivityRewardEnum.FRAGMENTS.eq(key)) { @@ -272,14 +303,36 @@ public class PropsActivitySendCommon { return configs.stream().map(this::toPrizeDescribe).collect(Collectors.toList()); } - private PrizeDescribe toPrizeDescribe(PropsActivityRewardConfig config) { - return new PrizeDescribe() + private PrizeDescribe toPrizeDescribe(PropsActivityRewardConfig config) { + return new PrizeDescribe() .setTrackId(config.getGroupId()) .setType(config.getType()) .setDetailType(config.getDetailType()) .setContent(config.getContent()) - .setQuantity(config.getQuantity()); - } + .setQuantity(config.getQuantity()); + } + + private void sendVipLevelRewards(SendRewardAbstract param, List prizeArrays) { + prizeArrays.stream() + .filter(prize -> isVipLevelReward(param, prize)) + .forEach(prize -> vipActivityRewardCommon.gift(param.getSysOrigin(), + param.getAcceptUserId(), + param.getTrackId(), + prize.getContent(), + param.getOrigin())); + } + + private boolean isVipLevelReward(SendRewardAbstract param, PropsActivityRewardConfig config) { + return Objects.nonNull(param.getSysOrigin()) + && vipActivityRewardCommon.isVipDetail(config.getType(), config.getDetailType()) + && vipActivityRewardCommon.existsEnabledConfig(param.getSysOrigin(), config.getContent()); + } + + private boolean isVipLevelReward(SendRewardAbstract param, PrizeDescribe prize) { + return Objects.nonNull(param.getSysOrigin()) + && vipActivityRewardCommon.isVipDetail(prize.getType(), prize.getDetailType()) + && vipActivityRewardCommon.existsEnabledConfig(param.getSysOrigin(), prize.getContent()); + } private void sendEmoji(SendRewardAbstract param, List val) { val.forEach(props -> diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/VipActivityRewardCommon.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/VipActivityRewardCommon.java new file mode 100644 index 00000000..5cb0ced4 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/common/activity/VipActivityRewardCommon.java @@ -0,0 +1,346 @@ +package com.red.circle.other.infra.common.activity; + +import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.common.business.enums.SendPropsOrigin; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.framework.core.response.ResponseErrorCode; +import com.red.circle.framework.mybatis.constant.PageConstant; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.infra.database.rds.entity.badge.BadgeBackpack; +import com.red.circle.other.infra.database.rds.entity.props.PropsBackpack; +import com.red.circle.other.infra.database.rds.entity.props.RunningWaterUserProps; +import com.red.circle.other.infra.database.rds.entity.vip.UserVipState; +import com.red.circle.other.infra.database.rds.entity.vip.VipLevelConfig; +import com.red.circle.other.infra.database.rds.entity.vip.VipOrderRecord; +import com.red.circle.other.infra.database.rds.service.badge.BadgeBackpackService; +import com.red.circle.other.infra.database.rds.service.props.PropsBackpackService; +import com.red.circle.other.infra.database.rds.service.props.RunningWaterUserPropsService; +import com.red.circle.other.infra.database.rds.service.vip.UserVipStateService; +import com.red.circle.other.infra.database.rds.service.vip.VipLevelConfigService; +import com.red.circle.other.infra.database.rds.service.vip.VipOrderRecordService; +import com.red.circle.other.inner.enums.material.BadgeBackpackExpireType; +import com.red.circle.other.inner.enums.material.PropsCommodityType; +import com.red.circle.tool.core.date.TimestampUtils; +import com.red.circle.tool.core.parse.DataTypeUtils; +import com.red.circle.tool.core.sequence.IdWorkerUtils; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * Sends operator-managed VIP rewards from resource groups. + */ +@Component +@RequiredArgsConstructor +public class VipActivityRewardCommon { + + public static final String VIP_DETAIL_TYPE = "NOBLE_VIP"; + + private static final int DEFAULT_DURATION_DAYS = 30; + private static final String STATUS_ACTIVE = "ACTIVE"; + private static final String STATUS_SUCCESS = "SUCCESS"; + private static final String ORDER_TYPE_RESOURCE_GROUP = "RESOURCE_GROUP"; + private static final String EVENT_PREFIX = "RESOURCE-GROUP-VIP-"; + private static final String WATER_ORIGIN = "VIP_PURCHASE"; + private static final String WATER_ORIGIN_DESC = "VIP purchase"; + private static final Timestamp MYSQL_DATETIME_MAX = Timestamp.valueOf("2038-01-19 00:00:00"); + + private final UserProfileGateway userProfileGateway; + private final BadgeBackpackService badgeBackpackService; + private final PropsBackpackService propsBackpackService; + private final UserVipStateService userVipStateService; + private final VipLevelConfigService vipLevelConfigService; + private final VipOrderRecordService vipOrderRecordService; + private final RunningWaterUserPropsService runningWaterUserPropsService; + + public boolean isVipDetail(String type, String detailType) { + return Objects.equals("PROPS", type) && Objects.equals(VIP_DETAIL_TYPE, detailType); + } + + public boolean existsEnabledConfig(SysOriginPlatformEnum sysOrigin, String content) { + Long configId = DataTypeUtils.toLong(content); + return Objects.nonNull(vipLevelConfigService.getEnabledById(sysOrigin.name(), configId)); + } + + @Transactional(rollbackFor = Exception.class) + public void gift(SysOriginPlatformEnum sysOrigin, Long userId, Long trackId, String content, + SendPropsOrigin origin) { + Long configId = DataTypeUtils.toLong(content); + VipLevelConfig config = vipLevelConfigService.getEnabledById(sysOrigin.name(), configId); + ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, config); + + int durationDays = durationDays(config); + Timestamp now = TimestampUtils.now(); + UserVipState currentState = getUserVipState(sysOrigin.name(), userId); + boolean currentActive = isActive(currentState, now); + int fromLevel = currentActive ? Objects.requireNonNullElse(currentState.getLevel(), 0) : 0; + + ResponseAssert.isFalse(ResponseErrorCode.REQUEST_PARAMETER_ERROR, + currentActive && fromLevel > Objects.requireNonNullElse(config.getLevel(), 0)); + + Timestamp startAt = currentActive && Objects.nonNull(currentState.getStartAt()) + ? currentState.getStartAt() + : now; + Timestamp expireAt = capExpireTime(resolveExpireAt(currentState, config, currentActive, now)); + Long orderId = IdWorkerUtils.getId(); + String eventId = EVENT_PREFIX + orderId; + + vipOrderRecordService.save(buildOrder(orderId, eventId, sysOrigin.name(), userId, fromLevel, + config, startAt, expireAt, now)); + upsertUserVipState(currentState, sysOrigin.name(), userId, config, startAt, expireAt, now); + giftVipResources(userId, trackId, config, expireAt, durationDays, now, origin); + userProfileGateway.removeCacheAll(userId); + } + + private VipOrderRecord buildOrder(Long orderId, String eventId, String sysOrigin, Long userId, + Integer fromLevel, VipLevelConfig config, Timestamp startAt, Timestamp expireAt, + Timestamp now) { + VipOrderRecord order = new VipOrderRecord() + .setId(orderId) + .setEventId(eventId) + .setSysOrigin(sysOrigin) + .setUserId(userId) + .setOrderType(ORDER_TYPE_RESOURCE_GROUP) + .setFromLevel(fromLevel) + .setToLevel(config.getLevel()) + .setDurationDays(durationDays(config)) + .setOriginalPriceGold(Objects.requireNonNullElse(config.getPriceGold(), 0L)) + .setPaidGold(0L) + .setStatus(STATUS_SUCCESS) + .setStartAt(startAt) + .setExpireAt(expireAt) + .setErrorMessage(""); + order.setCreateTime(now); + order.setUpdateTime(now); + return order; + } + + private void upsertUserVipState(UserVipState currentState, String sysOrigin, Long userId, + VipLevelConfig config, Timestamp startAt, Timestamp expireAt, Timestamp now) { + UserVipState state = Objects.nonNull(currentState) + ? currentState + : new UserVipState().setId(IdWorkerUtils.getId()); + if (Objects.isNull(currentState)) { + state.setCreateTime(now); + } + state.setSysOrigin(sysOrigin); + state.setUserId(userId); + state.setLevel(config.getLevel()); + state.setLevelCode(levelCode(config)); + state.setDisplayName(displayName(config)); + state.setStatus(STATUS_ACTIVE); + state.setStartAt(startAt); + state.setExpireAt(expireAt); + copyResources(state, config); + state.setUpdateTime(now); + if (Objects.nonNull(currentState)) { + userVipStateService.updateSelectiveById(state); + return; + } + userVipStateService.save(state); + } + + private void copyResources(UserVipState state, VipLevelConfig config) { + state.setBadgeResourceId(config.getBadgeResourceId()); + state.setBadgeName(config.getBadgeName()); + state.setBadgeUrl(config.getBadgeUrl()); + state.setShortBadgeResourceId(config.getShortBadgeResourceId()); + state.setShortBadgeName(config.getShortBadgeName()); + state.setShortBadgeUrl(config.getShortBadgeUrl()); + state.setAvatarFrameResourceId(config.getAvatarFrameResourceId()); + state.setAvatarFrameName(config.getAvatarFrameName()); + state.setAvatarFrameUrl(config.getAvatarFrameUrl()); + state.setEntryEffectResourceId(config.getEntryEffectResourceId()); + state.setEntryEffectName(config.getEntryEffectName()); + state.setEntryEffectUrl(config.getEntryEffectUrl()); + state.setChatBubbleResourceId(config.getChatBubbleResourceId()); + state.setChatBubbleName(config.getChatBubbleName()); + state.setChatBubbleUrl(config.getChatBubbleUrl()); + state.setFloatPictureResourceId(config.getFloatPictureResourceId()); + state.setFloatPictureName(config.getFloatPictureName()); + state.setFloatPictureUrl(config.getFloatPictureUrl()); + state.setBackgroundCardResourceId(config.getBackgroundCardResourceId()); + state.setBackgroundCardName(config.getBackgroundCardName()); + state.setBackgroundCardUrl(config.getBackgroundCardUrl()); + state.setEffectImageResourceId(config.getEffectImageResourceId()); + state.setEffectImageName(config.getEffectImageName()); + state.setEffectImageUrl(config.getEffectImageUrl()); + } + + private void giftVipResources(Long userId, Long trackId, VipLevelConfig config, + Timestamp expireAt, Integer days, Timestamp now, SendPropsOrigin origin) { + for (VipResourceGrant resource : vipResourcesFromConfig(config)) { + if (resource.badge) { + giveBadge(userId, resource.resourceId, expireAt, days); + continue; + } + giveProp(userId, trackId, resource.resourceId, resource.type, expireAt, days, now, origin); + } + } + + private List vipResourcesFromConfig(VipLevelConfig config) { + return List.of( + VipResourceGrant.badge(config.getBadgeResourceId()), + VipResourceGrant.badge(config.getShortBadgeResourceId()), + VipResourceGrant.prop(config.getAvatarFrameResourceId(), PropsCommodityType.AVATAR_FRAME), + VipResourceGrant.prop(config.getEntryEffectResourceId(), PropsCommodityType.RIDE), + VipResourceGrant.prop(config.getChatBubbleResourceId(), PropsCommodityType.CHAT_BUBBLE), + VipResourceGrant.prop(config.getFloatPictureResourceId(), PropsCommodityType.FLOAT_PICTURE), + VipResourceGrant.prop(config.getBackgroundCardResourceId(), PropsCommodityType.DATA_CARD) + ).stream().filter(VipResourceGrant::valid).toList(); + } + + private void giveBadge(Long userId, Long badgeId, Timestamp expireAt, Integer days) { + badgeBackpackService.activationTemporaryAndUse(userId, badgeId, days); + BadgeBackpack backpack = badgeBackpackService.getByUserIdByBadgeId(userId, badgeId); + if (Objects.isNull(backpack)) { + return; + } + backpack.setUseProps(Boolean.TRUE); + backpack.setExpireType(BadgeBackpackExpireType.TEMPORARY.name()); + backpack.setExpireTime(expireAt); + badgeBackpackService.updateSelectiveById(backpack); + } + + private void giveProp(Long userId, Long trackId, Long propsId, PropsCommodityType type, + Timestamp expireAt, Integer days, Timestamp now, SendPropsOrigin origin) { + PropsBackpack oldBackpack = propsBackpackService.getUserProps(userId, propsId, type); + boolean useProps = resolveUseProps(userId, propsId, type, oldBackpack, now); + propsBackpackService.addPropsBackpack(new PropsBackpack() + .setUserId(userId) + .setPropsId(propsId) + .setType(type.name()) + .setExpireTime(expireAt) + .setUseProps(useProps) + .setAllowGive(Boolean.FALSE)); + + RunningWaterUserProps water = new RunningWaterUserProps() + .setUserId(userId) + .setPropsId(propsId) + .setPropsCandy(BigDecimal.ZERO) + .setType(type.name()) + .setOrigin(Objects.nonNull(origin) ? origin.name() : WATER_ORIGIN) + .setOriginDesc(Objects.nonNull(origin) ? origin.getDesc() : WATER_ORIGIN_DESC) + .setDays(days); + water.setCreateTime(now); + water.setUpdateTime(now); + water.setCreateUser(Objects.requireNonNullElse(trackId, 0L)); + water.setUpdateUser(0L); + runningWaterUserPropsService.save(water); + } + + private boolean resolveUseProps(Long userId, Long propsId, PropsCommodityType type, + PropsBackpack oldBackpack, Timestamp now) { + if (Objects.nonNull(oldBackpack)) { + return Boolean.TRUE.equals(oldBackpack.getUseProps()); + } + List notExpiredSameType = propsBackpackService.query() + .eq(PropsBackpack::getUserId, userId) + .eq(PropsBackpack::getType, type.name()) + .gt(PropsBackpack::getExpireTime, now) + .list(); + return notExpiredSameType.stream() + .noneMatch(item -> Boolean.TRUE.equals(item.getUseProps()) + && !Objects.equals(item.getPropsId(), propsId)); + } + + private UserVipState getUserVipState(String sysOrigin, Long userId) { + return userVipStateService.query() + .eq(UserVipState::getSysOrigin, sysOrigin) + .eq(UserVipState::getUserId, userId) + .last(PageConstant.LIMIT_ONE) + .getOne(); + } + + private boolean isActive(UserVipState state, Timestamp now) { + return Objects.nonNull(state) + && STATUS_ACTIVE.equals(state.getStatus()) + && Objects.nonNull(state.getExpireAt()) + && state.getExpireAt().after(now) + && Objects.nonNull(state.getLevel()) + && state.getLevel() > 0; + } + + private Timestamp resolveExpireAt(UserVipState currentState, VipLevelConfig config, + boolean currentActive, Timestamp now) { + Timestamp plusDuration = plusDays(now, durationDays(config)); + if (!currentActive) { + return plusDuration; + } + if (Objects.equals(currentState.getLevel(), config.getLevel())) { + return plusDays(max(currentState.getExpireAt(), now), durationDays(config)); + } + return max(currentState.getExpireAt(), plusDuration); + } + + private int durationDays(VipLevelConfig config) { + Integer days = config.getDurationDays(); + return Objects.nonNull(days) && days > 0 ? days : DEFAULT_DURATION_DAYS; + } + + private String levelCode(VipLevelConfig config) { + if (Objects.nonNull(config.getLevelCode()) && !config.getLevelCode().isBlank()) { + return config.getLevelCode(); + } + return "VIP" + Objects.requireNonNullElse(config.getLevel(), 0); + } + + private String displayName(VipLevelConfig config) { + if (Objects.nonNull(config.getDisplayName()) && !config.getDisplayName().isBlank()) { + return config.getDisplayName(); + } + return "VIP " + Objects.requireNonNullElse(config.getLevel(), 0); + } + + private Timestamp max(Timestamp first, Timestamp second) { + if (Objects.isNull(first)) { + return second; + } + if (Objects.isNull(second)) { + return first; + } + return first.after(second) ? first : second; + } + + private Timestamp plusDays(Timestamp value, int days) { + return Timestamp.from(value.toInstant().plus(Duration.ofDays(days))); + } + + private Timestamp capExpireTime(Timestamp expireTime) { + if (Objects.isNull(expireTime)) { + return null; + } + return expireTime.after(MYSQL_DATETIME_MAX) ? MYSQL_DATETIME_MAX : expireTime; + } + + private static final class VipResourceGrant { + + private final Long resourceId; + private final PropsCommodityType type; + private final boolean badge; + + private VipResourceGrant(Long resourceId, PropsCommodityType type, boolean badge) { + this.resourceId = resourceId; + this.type = type; + this.badge = badge; + } + + private static VipResourceGrant badge(Long resourceId) { + return new VipResourceGrant(resourceId, null, true); + } + + private static VipResourceGrant prop(Long resourceId, PropsCommodityType type) { + return new VipResourceGrant(resourceId, type, false); + } + + private boolean valid() { + return Objects.nonNull(resourceId) && resourceId > 0; + } + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/UserVipStateDAO.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/UserVipStateDAO.java new file mode 100644 index 00000000..e806ff5f --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/UserVipStateDAO.java @@ -0,0 +1,7 @@ +package com.red.circle.other.infra.database.rds.dao.vip; + +import com.red.circle.framework.mybatis.dao.BaseDAO; +import com.red.circle.other.infra.database.rds.entity.vip.UserVipState; + +public interface UserVipStateDAO extends BaseDAO { +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipLevelConfigDAO.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipLevelConfigDAO.java new file mode 100644 index 00000000..511d20dd --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipLevelConfigDAO.java @@ -0,0 +1,7 @@ +package com.red.circle.other.infra.database.rds.dao.vip; + +import com.red.circle.framework.mybatis.dao.BaseDAO; +import com.red.circle.other.infra.database.rds.entity.vip.VipLevelConfig; + +public interface VipLevelConfigDAO extends BaseDAO { +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipOrderRecordDAO.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipOrderRecordDAO.java new file mode 100644 index 00000000..7fb5a245 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/vip/VipOrderRecordDAO.java @@ -0,0 +1,7 @@ +package com.red.circle.other.infra.database.rds.dao.vip; + +import com.red.circle.framework.mybatis.dao.BaseDAO; +import com.red.circle.other.infra.database.rds.entity.vip.VipOrderRecord; + +public interface VipOrderRecordDAO extends BaseDAO { +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/UserVipState.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/UserVipState.java new file mode 100644 index 00000000..9124031c --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/UserVipState.java @@ -0,0 +1,130 @@ +package com.red.circle.other.infra.database.rds.entity.vip; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serial; +import java.io.Serializable; +import java.sql.Timestamp; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * User VIP state. + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("user_vip_state") +public class UserVipState implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.INPUT) + private Long id; + + @TableField("sys_origin") + private String sysOrigin; + + @TableField("user_id") + private Long userId; + + @TableField("level") + private Integer level; + + @TableField("level_code") + private String levelCode; + + @TableField("display_name") + private String displayName; + + @TableField("status") + private String status; + + @TableField("start_at") + private Timestamp startAt; + + @TableField("expire_at") + private Timestamp expireAt; + + @TableField("badge_resource_id") + private Long badgeResourceId; + + @TableField("badge_name") + private String badgeName; + + @TableField("badge_url") + private String badgeUrl; + + @TableField("short_badge_resource_id") + private Long shortBadgeResourceId; + + @TableField("short_badge_name") + private String shortBadgeName; + + @TableField("short_badge_url") + private String shortBadgeUrl; + + @TableField("avatar_frame_resource_id") + private Long avatarFrameResourceId; + + @TableField("avatar_frame_name") + private String avatarFrameName; + + @TableField("avatar_frame_url") + private String avatarFrameUrl; + + @TableField("entry_effect_resource_id") + private Long entryEffectResourceId; + + @TableField("entry_effect_name") + private String entryEffectName; + + @TableField("entry_effect_url") + private String entryEffectUrl; + + @TableField("chat_bubble_resource_id") + private Long chatBubbleResourceId; + + @TableField("chat_bubble_name") + private String chatBubbleName; + + @TableField("chat_bubble_url") + private String chatBubbleUrl; + + @TableField("float_picture_resource_id") + private Long floatPictureResourceId; + + @TableField("float_picture_name") + private String floatPictureName; + + @TableField("float_picture_url") + private String floatPictureUrl; + + @TableField("background_card_resource_id") + private Long backgroundCardResourceId; + + @TableField("background_card_name") + private String backgroundCardName; + + @TableField("background_card_url") + private String backgroundCardUrl; + + @TableField("effect_image_resource_id") + private Long effectImageResourceId; + + @TableField("effect_image_name") + private String effectImageName; + + @TableField("effect_image_url") + private String effectImageUrl; + + @TableField("create_time") + private Timestamp createTime; + + @TableField("update_time") + private Timestamp updateTime; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipLevelConfig.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipLevelConfig.java new file mode 100644 index 00000000..7b924b7e --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipLevelConfig.java @@ -0,0 +1,127 @@ +package com.red.circle.other.infra.database.rds.entity.vip; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serial; +import java.io.Serializable; +import java.sql.Timestamp; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * VIP level config. + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("vip_level_config") +public class VipLevelConfig implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.INPUT) + private Long id; + + @TableField("sys_origin") + private String sysOrigin; + + @TableField("level") + private Integer level; + + @TableField("level_code") + private String levelCode; + + @TableField("display_name") + private String displayName; + + @TableField("enabled") + private Boolean enabled; + + @TableField("duration_days") + private Integer durationDays; + + @TableField("price_gold") + private Long priceGold; + + @TableField("badge_resource_id") + private Long badgeResourceId; + + @TableField("badge_name") + private String badgeName; + + @TableField("badge_url") + private String badgeUrl; + + @TableField("short_badge_resource_id") + private Long shortBadgeResourceId; + + @TableField("short_badge_name") + private String shortBadgeName; + + @TableField("short_badge_url") + private String shortBadgeUrl; + + @TableField("avatar_frame_resource_id") + private Long avatarFrameResourceId; + + @TableField("avatar_frame_name") + private String avatarFrameName; + + @TableField("avatar_frame_url") + private String avatarFrameUrl; + + @TableField("entry_effect_resource_id") + private Long entryEffectResourceId; + + @TableField("entry_effect_name") + private String entryEffectName; + + @TableField("entry_effect_url") + private String entryEffectUrl; + + @TableField("chat_bubble_resource_id") + private Long chatBubbleResourceId; + + @TableField("chat_bubble_name") + private String chatBubbleName; + + @TableField("chat_bubble_url") + private String chatBubbleUrl; + + @TableField("float_picture_resource_id") + private Long floatPictureResourceId; + + @TableField("float_picture_name") + private String floatPictureName; + + @TableField("float_picture_url") + private String floatPictureUrl; + + @TableField("background_card_resource_id") + private Long backgroundCardResourceId; + + @TableField("background_card_name") + private String backgroundCardName; + + @TableField("background_card_url") + private String backgroundCardUrl; + + @TableField("effect_image_resource_id") + private Long effectImageResourceId; + + @TableField("effect_image_name") + private String effectImageName; + + @TableField("effect_image_url") + private String effectImageUrl; + + @TableField("create_time") + private Timestamp createTime; + + @TableField("update_time") + private Timestamp updateTime; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipOrderRecord.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipOrderRecord.java new file mode 100644 index 00000000..12638038 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/vip/VipOrderRecord.java @@ -0,0 +1,73 @@ +package com.red.circle.other.infra.database.rds.entity.vip; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serial; +import java.io.Serializable; +import java.sql.Timestamp; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * VIP order record. + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("vip_order_record") +public class VipOrderRecord implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.INPUT) + private Long id; + + @TableField("event_id") + private String eventId; + + @TableField("sys_origin") + private String sysOrigin; + + @TableField("user_id") + private Long userId; + + @TableField("order_type") + private String orderType; + + @TableField("from_level") + private Integer fromLevel; + + @TableField("to_level") + private Integer toLevel; + + @TableField("duration_days") + private Integer durationDays; + + @TableField("original_price_gold") + private Long originalPriceGold; + + @TableField("paid_gold") + private Long paidGold; + + @TableField("status") + private String status; + + @TableField("start_at") + private Timestamp startAt; + + @TableField("expire_at") + private Timestamp expireAt; + + @TableField("error_message") + private String errorMessage; + + @TableField("create_time") + private Timestamp createTime; + + @TableField("update_time") + private Timestamp updateTime; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/UserVipStateService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/UserVipStateService.java new file mode 100644 index 00000000..ef54c607 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/UserVipStateService.java @@ -0,0 +1,7 @@ +package com.red.circle.other.infra.database.rds.service.vip; + +import com.red.circle.framework.mybatis.service.BaseService; +import com.red.circle.other.infra.database.rds.entity.vip.UserVipState; + +public interface UserVipStateService extends BaseService { +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipLevelConfigService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipLevelConfigService.java new file mode 100644 index 00000000..87ae77fa --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipLevelConfigService.java @@ -0,0 +1,15 @@ +package com.red.circle.other.infra.database.rds.service.vip; + +import com.red.circle.framework.mybatis.service.BaseService; +import com.red.circle.other.infra.database.rds.entity.vip.VipLevelConfig; +import java.util.Map; +import java.util.Set; + +public interface VipLevelConfigService extends BaseService { + + Map mapByIds(Set ids); + + Map mapBySysOriginAndIds(String sysOrigin, Set ids); + + VipLevelConfig getEnabledById(String sysOrigin, Long id); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipOrderRecordService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipOrderRecordService.java new file mode 100644 index 00000000..330c57d9 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/VipOrderRecordService.java @@ -0,0 +1,7 @@ +package com.red.circle.other.infra.database.rds.service.vip; + +import com.red.circle.framework.mybatis.service.BaseService; +import com.red.circle.other.infra.database.rds.entity.vip.VipOrderRecord; + +public interface VipOrderRecordService extends BaseService { +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/UserVipStateServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/UserVipStateServiceImpl.java new file mode 100644 index 00000000..efb31394 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/UserVipStateServiceImpl.java @@ -0,0 +1,12 @@ +package com.red.circle.other.infra.database.rds.service.vip.impl; + +import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl; +import com.red.circle.other.infra.database.rds.dao.vip.UserVipStateDAO; +import com.red.circle.other.infra.database.rds.entity.vip.UserVipState; +import com.red.circle.other.infra.database.rds.service.vip.UserVipStateService; +import org.springframework.stereotype.Service; + +@Service +public class UserVipStateServiceImpl extends BaseServiceImpl + implements UserVipStateService { +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipLevelConfigServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipLevelConfigServiceImpl.java new file mode 100644 index 00000000..3552f1f4 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipLevelConfigServiceImpl.java @@ -0,0 +1,59 @@ +package com.red.circle.other.infra.database.rds.service.vip.impl; + +import com.red.circle.framework.mybatis.constant.PageConstant; +import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl; +import com.red.circle.other.infra.database.rds.dao.vip.VipLevelConfigDAO; +import com.red.circle.other.infra.database.rds.entity.vip.VipLevelConfig; +import com.red.circle.other.infra.database.rds.service.vip.VipLevelConfigService; +import com.red.circle.tool.core.collection.CollectionUtils; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; + +@Service +public class VipLevelConfigServiceImpl extends BaseServiceImpl + implements VipLevelConfigService { + + @Override + public Map mapByIds(Set ids) { + if (CollectionUtils.isEmpty(ids)) { + return CollectionUtils.newHashMap(); + } + return Optional.ofNullable(query().in(VipLevelConfig::getId, ids).list()) + .map(list -> list.stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap(VipLevelConfig::getId, item -> item, (first, second) -> first))) + .orElseGet(CollectionUtils::newHashMap); + } + + @Override + public Map mapBySysOriginAndIds(String sysOrigin, Set ids) { + if (CollectionUtils.isEmpty(ids) || Objects.isNull(sysOrigin)) { + return CollectionUtils.newHashMap(); + } + return Optional.ofNullable(query() + .eq(VipLevelConfig::getSysOrigin, sysOrigin) + .in(VipLevelConfig::getId, ids) + .list()) + .map(list -> list.stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap(VipLevelConfig::getId, item -> item, (first, second) -> first))) + .orElseGet(CollectionUtils::newHashMap); + } + + @Override + public VipLevelConfig getEnabledById(String sysOrigin, Long id) { + if (Objects.isNull(id) || Objects.isNull(sysOrigin)) { + return null; + } + return query() + .eq(VipLevelConfig::getSysOrigin, sysOrigin) + .eq(VipLevelConfig::getId, id) + .eq(VipLevelConfig::getEnabled, Boolean.TRUE) + .last(PageConstant.LIMIT_ONE) + .getOne(); + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipOrderRecordServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipOrderRecordServiceImpl.java new file mode 100644 index 00000000..fcc69836 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/service/vip/impl/VipOrderRecordServiceImpl.java @@ -0,0 +1,12 @@ +package com.red.circle.other.infra.database.rds.service.vip.impl; + +import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl; +import com.red.circle.other.infra.database.rds.dao.vip.VipOrderRecordDAO; +import com.red.circle.other.infra.database.rds.entity.vip.VipOrderRecord; +import com.red.circle.other.infra.database.rds.service.vip.VipOrderRecordService; +import org.springframework.stereotype.Service; + +@Service +public class VipOrderRecordServiceImpl extends BaseServiceImpl + implements VipOrderRecordService { +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/props/ActivitySourceGroupGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/props/ActivitySourceGroupGatewayImpl.java index 92e88ea7..f4fe27a7 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/props/ActivitySourceGroupGatewayImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/props/ActivitySourceGroupGatewayImpl.java @@ -3,16 +3,21 @@ package com.red.circle.other.infra.gateway.props; import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.common.business.enums.PropsActivityRewardEnum; import com.red.circle.framework.core.asserts.ResponseAssert; -import com.red.circle.framework.core.response.CommonErrorCode; -import com.red.circle.other.domain.gateway.props.ActivitySourceGroupGateway; -import com.red.circle.other.infra.common.props.PropsResourcesCommon; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.other.domain.gateway.props.ActivitySourceGroupGateway; +import com.red.circle.other.infra.common.activity.VipActivityRewardCommon; +import com.red.circle.other.infra.common.props.PropsResourcesCommon; import com.red.circle.other.infra.convertor.material.PropsActivityInfraConvertor; -import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardConfig; -import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardGroup; -import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRuleConfig; -import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardConfigService; -import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardGroupService; -import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRuleConfigService; +import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardConfig; +import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardGroup; +import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRuleConfig; +import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord; +import com.red.circle.other.infra.database.rds.entity.vip.VipLevelConfig; +import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardConfigService; +import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardGroupService; +import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRuleConfigService; +import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService; +import com.red.circle.other.infra.database.rds.service.vip.VipLevelConfigService; import com.red.circle.other.inner.enums.activity.PropsActivityTypeEnum; import com.red.circle.other.inner.model.cmd.material.PropsResourcesMergeCmd; import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsGroup; @@ -50,11 +55,13 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGateway { - private final PropsResourcesCommon propsResourcesCommon; - private final PropsActivityInfraConvertor propsActivityInfraConvertor; - private final PropsActivityRuleConfigService propsActivityRuleConfigService; - private final PropsActivityRewardGroupService propsActivityRewardGroupService; - private final PropsActivityRewardConfigService propsActivityRewardConfigService; + private final PropsResourcesCommon propsResourcesCommon; + private final PropsActivityInfraConvertor propsActivityInfraConvertor; + private final PropsSourceRecordService propsSourceRecordService; + private final VipLevelConfigService vipLevelConfigService; + private final PropsActivityRuleConfigService propsActivityRuleConfigService; + private final PropsActivityRewardGroupService propsActivityRewardGroupService; + private final PropsActivityRewardConfigService propsActivityRewardConfigService; @Override public ActivityPropsRule getByRuleId(Long id) { @@ -122,16 +129,21 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa List rewardGroups = propsActivityRewardGroupService .listByIds(groupIds); - Map> rewardGroupConfigMap = propsActivityRewardConfigService - .mapGroupByIds(groupIds); - - PropsResourcesMergeDTO propsResourcesMerge = propsResourcesCommon.mapMergeByIds( - new PropsResourcesMergeCmd() - .setSysOrigin(sysOrigin) - .setPropsIds(getPropsAllIds(rewardGroupConfigMap)) - .setGiftIds( - getActivityRewardContentToId(rewardGroupConfigMap, PropsActivityRewardEnum.GIFT)) - .setBadgeIds( + Map> rewardGroupConfigMap = propsActivityRewardConfigService + .mapGroupByIds(groupIds); + + Map vipConfigMap = vipLevelConfigService.mapBySysOriginAndIds(sysOrigin, + getVipLevelConfigIds(rewardGroupConfigMap)); + Map vipCoverMap = propsSourceRecordService.mapByIds( + getVipCoverResourceIds(vipConfigMap.values())); + + PropsResourcesMergeDTO propsResourcesMerge = propsResourcesCommon.mapMergeByIds( + new PropsResourcesMergeCmd() + .setSysOrigin(sysOrigin) + .setPropsIds(getPropsAllIds(rewardGroupConfigMap, vipConfigMap.keySet())) + .setGiftIds( + getActivityRewardContentToId(rewardGroupConfigMap, PropsActivityRewardEnum.GIFT)) + .setBadgeIds( getActivityRewardContentToId(rewardGroupConfigMap, PropsActivityRewardEnum.BADGE)) .setEmojiIds( getActivityRewardContentToId(rewardGroupConfigMap, PropsActivityRewardEnum.EMOJI)) @@ -144,11 +156,12 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa List rewardConfigs = rewardGroupConfigMap .get(activityPropsGroup.getId()); - - activityPropsGroup.setActivityRewardProps(rewardConfigs.stream() - .map(rewardConfig -> getActivityRewardProps(propsResourcesMerge, rewardConfig)) - .filter(Objects::nonNull) - .collect(Collectors.toList())); + + activityPropsGroup.setActivityRewardProps(rewardConfigs.stream() + .map(rewardConfig -> getActivityRewardProps(propsResourcesMerge, vipConfigMap, + vipCoverMap, rewardConfig)) + .filter(Objects::nonNull) + .collect(Collectors.toList())); return activityPropsGroup; }) @@ -161,12 +174,18 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa .get(groupId); } - private ActivityRewardProps getActivityRewardProps(PropsResourcesMergeDTO propsSource, - PropsActivityRewardConfig rewardConfig) { - - if (PropsActivityRewardEnum.PROPS.eq(rewardConfig.getType()) - || PropsActivityRewardEnum.PROP_COUPON.eq(rewardConfig.getType()) - || PropsActivityRewardEnum.FRAGMENTS.eq(rewardConfig.getType()) + private ActivityRewardProps getActivityRewardProps(PropsResourcesMergeDTO propsSource, + Map vipConfigMap, + Map vipCoverMap, + PropsActivityRewardConfig rewardConfig) { + + if (isVipLevelReward(rewardConfig, vipConfigMap)) { + return buildActivityVip(vipConfigMap, vipCoverMap, rewardConfig); + } + + if (PropsActivityRewardEnum.PROPS.eq(rewardConfig.getType()) + || PropsActivityRewardEnum.PROP_COUPON.eq(rewardConfig.getType()) + || PropsActivityRewardEnum.FRAGMENTS.eq(rewardConfig.getType()) || PropsActivityRewardEnum.CUSTOMIZE.eq(rewardConfig.getType())) { return buildActivityProps(propsSource.getProps(), rewardConfig); } @@ -208,8 +227,8 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa return activityRewardProps; } - private ActivityRewardProps buildActivityGift( - Map giftConfigMap, PropsActivityRewardConfig rewardConfig) { + private ActivityRewardProps buildActivityGift( + Map giftConfigMap, PropsActivityRewardConfig rewardConfig) { GiftConfigDTO giftConfig = giftConfigMap.get(DataTypeUtils.toLong(rewardConfig.getContent())); @@ -221,8 +240,25 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa giftConfig.getGiftCandy().multiply(BigDecimal.valueOf(rewardConfig.getQuantity())) .setScale(0, RoundingMode.DOWN)); activityRewardProps.setDetailType(rewardConfig.getDetailType()); - return activityRewardProps; - } + return activityRewardProps; + } + + private ActivityRewardProps buildActivityVip( + Map vipConfigMap, + Map vipCoverMap, + PropsActivityRewardConfig rewardConfig) { + VipLevelConfig vipConfig = vipConfigMap.get(DataTypeUtils.toLong(rewardConfig.getContent())); + ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, vipConfig); + ActivityRewardProps activityRewardProps = propsActivityInfraConvertor + .toActivityRewardProps(rewardConfig); + activityRewardProps.setCover(resolveVipCover(vipConfig, vipCoverMap)); + activityRewardProps.setQuantity(durationDays(vipConfig)); + activityRewardProps.setAmount(BigDecimal.valueOf(Objects.requireNonNullElse( + vipConfig.getPriceGold(), 0L))); + activityRewardProps.setRemark(displayName(vipConfig)); + activityRewardProps.setDetailType(rewardConfig.getDetailType()); + return activityRewardProps; + } private ActivityRewardProps buildEmoji(Map emojiGroupMap, PropsActivityRewardConfig rewardConfig) { @@ -282,19 +318,28 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa return activityRewardProps; } - private Set getPropsAllIds( - Map> rewardGroupConfigMap) { - return getPropsAllIds( - rewardGroupConfigMap.values().stream().flatMap(Collection::stream).collect( - Collectors.toList()) - ); - } - - - private Set getPropsAllIds(List rewardConfigs) { - Set propsIds = getActivityRewardContentToId(rewardConfigs, PropsActivityRewardEnum.PROPS); - Set couponIds = getActivityRewardContentToId(rewardConfigs, PropsActivityRewardEnum.PROP_COUPON); - propsIds.addAll(couponIds); + private Set getPropsAllIds( + Map> rewardGroupConfigMap, + Set vipConfigIds) { + return getPropsAllIds( + rewardGroupConfigMap.values().stream().flatMap(Collection::stream).collect( + Collectors.toList()), + vipConfigIds + ); + } + + + private Set getPropsAllIds(List rewardConfigs, + Set vipConfigIds) { + Set propsIds = rewardConfigs.stream() + .filter(conf -> PropsActivityRewardEnum.PROPS.eq(conf.getType())) + .filter(conf -> !isVipLevelReward(conf, vipConfigIds)) + .map(propsActivityRewardConfig -> DataTypeUtils + .toLong(propsActivityRewardConfig.getContent())) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + Set couponIds = getActivityRewardContentToId(rewardConfigs, PropsActivityRewardEnum.PROP_COUPON); + propsIds.addAll(couponIds); Set fragmentIds = getActivityRewardContentToId(rewardConfigs, PropsActivityRewardEnum.FRAGMENTS); @@ -319,8 +364,8 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa .collect(Collectors.toSet()); } - private Set getActivityRewardContentToId( - List rewardGroupConfigList, + private Set getActivityRewardContentToId( + List rewardGroupConfigList, PropsActivityRewardEnum type) { if (CollectionUtils.isEmpty(rewardGroupConfigList)) { return CollectionUtils.newHashSet(); @@ -331,7 +376,90 @@ public class ActivitySourceGroupGatewayImpl implements ActivitySourceGroupGatewa .toLong(propsActivityRewardConfig.getContent())) .filter(Objects::nonNull) .collect(Collectors.toSet()); - } + } + + private Set getVipLevelConfigIds( + Map> rewardGroupConfigMap) { + return rewardGroupConfigMap.values().stream() + .flatMap(Collection::stream) + .filter(config -> PropsActivityRewardEnum.PROPS.eq(config.getType())) + .filter(config -> Objects.equals(config.getDetailType(), + VipActivityRewardCommon.VIP_DETAIL_TYPE)) + .map(config -> DataTypeUtils.toLong(config.getContent())) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + private Set getVipCoverResourceIds(Collection vipConfigs) { + if (CollectionUtils.isEmpty(vipConfigs)) { + return CollectionUtils.newHashSet(); + } + return vipConfigs.stream() + .map(this::firstVipResourceId) + .filter(Objects::nonNull) + .filter(id -> id > 0) + .collect(Collectors.toSet()); + } + + private boolean isVipLevelReward(PropsActivityRewardConfig rewardConfig, + Map vipConfigMap) { + return isVipLevelReward(rewardConfig, vipConfigMap.keySet()); + } + + private boolean isVipLevelReward(PropsActivityRewardConfig rewardConfig, Set vipConfigIds) { + return PropsActivityRewardEnum.PROPS.eq(rewardConfig.getType()) + && Objects.equals(rewardConfig.getDetailType(), VipActivityRewardCommon.VIP_DETAIL_TYPE) + && vipConfigIds.contains(DataTypeUtils.toLong(rewardConfig.getContent())); + } + + private Long firstVipResourceId(VipLevelConfig vipConfig) { + return Stream.of( + vipConfig.getBadgeResourceId(), + vipConfig.getShortBadgeResourceId(), + vipConfig.getAvatarFrameResourceId(), + vipConfig.getEntryEffectResourceId(), + vipConfig.getChatBubbleResourceId(), + vipConfig.getFloatPictureResourceId(), + vipConfig.getBackgroundCardResourceId(), + vipConfig.getEffectImageResourceId()) + .filter(Objects::nonNull) + .filter(id -> id > 0) + .findFirst() + .orElse(null); + } + + private String resolveVipCover(VipLevelConfig vipConfig, + Map vipCoverMap) { + PropsSourceRecord sourceRecord = vipCoverMap.get(firstVipResourceId(vipConfig)); + if (Objects.nonNull(sourceRecord) && Objects.nonNull(sourceRecord.getCover())) { + return sourceRecord.getCover(); + } + return Stream.of( + vipConfig.getBadgeUrl(), + vipConfig.getShortBadgeUrl(), + vipConfig.getAvatarFrameUrl(), + vipConfig.getEntryEffectUrl(), + vipConfig.getChatBubbleUrl(), + vipConfig.getFloatPictureUrl(), + vipConfig.getBackgroundCardUrl(), + vipConfig.getEffectImageUrl()) + .filter(Objects::nonNull) + .filter(value -> !value.isBlank()) + .findFirst() + .orElse(""); + } + + private int durationDays(VipLevelConfig vipConfig) { + Integer days = vipConfig.getDurationDays(); + return Objects.nonNull(days) && days > 0 ? days : 30; + } + + private String displayName(VipLevelConfig vipConfig) { + if (Objects.nonNull(vipConfig.getDisplayName()) && !vipConfig.getDisplayName().isBlank()) { + return vipConfig.getDisplayName(); + } + return "VIP " + Objects.requireNonNullElse(vipConfig.getLevel(), 0); + } private Set getGroupIds(List ruleConfigs) { diff --git a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/material/props/impl/PropsActivityRewardGroupClientServiceImpl.java b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/material/props/impl/PropsActivityRewardGroupClientServiceImpl.java index 53a347c4..ca61214f 100644 --- a/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/material/props/impl/PropsActivityRewardGroupClientServiceImpl.java +++ b/rc-service/rc-service-other/other-inner-endpoint/src/main/java/com/red/circle/other/app/inner/service/material/props/impl/PropsActivityRewardGroupClientServiceImpl.java @@ -2,24 +2,27 @@ package com.red.circle.other.app.inner.service.material.props.impl; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.red.circle.common.business.enums.PropsActivityRewardEnum; -import com.red.circle.framework.dto.PageResult; -import com.red.circle.other.app.inner.convertor.material.PropsInnerConvertor; -import com.red.circle.other.app.inner.service.material.props.PropsActivityRewardGroupClientService; -import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardConfig; -import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardGroup; +import com.red.circle.common.business.enums.PropsActivityRewardEnum; +import com.red.circle.framework.dto.PageResult; +import com.red.circle.other.infra.common.activity.VipActivityRewardCommon; +import com.red.circle.other.app.inner.convertor.material.PropsInnerConvertor; +import com.red.circle.other.app.inner.service.material.props.PropsActivityRewardGroupClientService; +import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardConfig; +import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardGroup; import com.red.circle.other.infra.database.rds.entity.badge.BadgeConfig; import com.red.circle.other.infra.database.rds.entity.badge.BadgePictureConfig; -import com.red.circle.other.infra.database.rds.entity.emoji.EmojiGroup; -import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; -import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord; -import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardConfigService; -import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardGroupService; -import com.red.circle.other.infra.database.rds.service.badge.BadgeConfigService; -import com.red.circle.other.infra.database.rds.service.badge.BadgePictureConfigService; +import com.red.circle.other.infra.database.rds.entity.emoji.EmojiGroup; +import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; +import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord; +import com.red.circle.other.infra.database.rds.entity.vip.VipLevelConfig; +import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardConfigService; +import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardGroupService; +import com.red.circle.other.infra.database.rds.service.badge.BadgeConfigService; +import com.red.circle.other.infra.database.rds.service.badge.BadgePictureConfigService; import com.red.circle.other.infra.database.rds.service.emoji.EmojiGroupService; -import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService; -import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService; +import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService; +import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService; +import com.red.circle.other.infra.database.rds.service.vip.VipLevelConfigService; import com.red.circle.other.inner.model.cmd.material.PropsActivityRewardGroupParamCmd; import com.red.circle.other.inner.model.cmd.material.PropsActivityRewardGroupQryCmd; import com.red.circle.other.inner.model.dto.material.ActivityGroupKeyValueDTO; @@ -28,16 +31,18 @@ import com.red.circle.other.inner.model.dto.material.PropsActivityRewardGroupDTO import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.parse.DataTypeUtils; -import com.red.circle.tool.core.text.StringUtils; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Comparator; +import com.red.circle.tool.core.text.StringUtils; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -56,9 +61,10 @@ public class PropsActivityRewardGroupClientServiceImpl implements private final EmojiGroupService emojiGroupService; private final GiftConfigService giftConfigService; - private final BadgeConfigService badgeConfigService; - private final PropsInnerConvertor propsInnerConvertor; - private final PropsSourceRecordService propsSourceRecordService; + private final BadgeConfigService badgeConfigService; + private final PropsInnerConvertor propsInnerConvertor; + private final VipLevelConfigService vipLevelConfigService; + private final PropsSourceRecordService propsSourceRecordService; private final BadgePictureConfigService badgePictureConfigService; private final PropsActivityRewardGroupService propsActivityRewardGroupService; private final PropsActivityRewardConfigService propsActivityRewardConfigService; @@ -215,14 +221,21 @@ public class PropsActivityRewardGroupClientServiceImpl implements List propsActivityRewardConfigs = propsActivityRewardConfigService.listByGroupIds( groupIds); - if (CollectionUtils.isEmpty(propsActivityRewardConfigs)) { - return Lists.newArrayList(); - } - - Set sourceIds = propsActivityRewardConfigs.stream().filter(config -> - Objects.equals(config.getType(), PropsActivityRewardEnum.PROPS.name()) || Objects.equals(config.getType(), PropsActivityRewardEnum.PROP_COUPON.name())) - .map(config -> Long.valueOf(config.getContent())) - .collect(Collectors.toSet()); + if (CollectionUtils.isEmpty(propsActivityRewardConfigs)) { + return Lists.newArrayList(); + } + + Map vipConfigMap = vipLevelConfigService.mapByIds( + getVipLevelConfigIds(propsActivityRewardConfigs)); + Map vipCoverMap = propsSourceRecordService.mapByIds( + getVipCoverResourceIds(vipConfigMap.values())); + + Set sourceIds = propsActivityRewardConfigs.stream().filter(config -> + (Objects.equals(config.getType(), PropsActivityRewardEnum.PROPS.name()) + && !isVipLevelReward(config, vipConfigMap)) + || Objects.equals(config.getType(), PropsActivityRewardEnum.PROP_COUPON.name())) + .map(config -> Long.valueOf(config.getContent())) + .collect(Collectors.toSet()); Set fragmentsIds = propsActivityRewardConfigs.stream().filter(config -> Objects.equals(config.getType(), PropsActivityRewardEnum.FRAGMENTS.name())) @@ -262,12 +275,17 @@ public class PropsActivityRewardGroupClientServiceImpl implements .collect(Collectors.toSet())); return propsActivityRewardConfigs.stream().map(config -> { - PropsActivityRewardConfigInfoDTO propsActivity = propsInnerConvertor.toPropsActivityRewardConfigInfoDTO( - config); - propsActivity.setSort(Objects.isNull(propsActivity.getSort()) ? 0 : propsActivity.getSort()); - if (Objects.equals(config.getType(), PropsActivityRewardEnum.PROPS.name()) || Objects.equals(config.getType(), PropsActivityRewardEnum.PROP_COUPON.name())) { - PropsSourceRecord sourceRecord = sourceRecordMap - .get(Long.valueOf(config.getContent())); + PropsActivityRewardConfigInfoDTO propsActivity = propsInnerConvertor.toPropsActivityRewardConfigInfoDTO( + config); + propsActivity.setSort(Objects.isNull(propsActivity.getSort()) ? 0 : propsActivity.getSort()); + if (isVipLevelReward(config, vipConfigMap)) { + setVipConfig(propsActivity, vipConfigMap.get(Long.valueOf(config.getContent())), + vipCoverMap); + return propsActivity; + } + if (Objects.equals(config.getType(), PropsActivityRewardEnum.PROPS.name()) || Objects.equals(config.getType(), PropsActivityRewardEnum.PROP_COUPON.name())) { + PropsSourceRecord sourceRecord = sourceRecordMap + .get(Long.valueOf(config.getContent())); if (Objects.nonNull(sourceRecord)) { propsActivity.setCover(sourceRecord.getCover()); propsActivity.setSourceUrl(sourceRecord.getSourceUrl()); @@ -330,6 +348,102 @@ public class PropsActivityRewardGroupClientServiceImpl implements propsActivity.setAmount(giftConfig.getGiftCandy()); return; } - log.error("礼物数据错误:{}", config); - } -} + log.error("礼物数据错误:{}", config); + } + + private Set getVipLevelConfigIds(List configs) { + return configs.stream() + .filter(config -> Objects.equals(config.getType(), PropsActivityRewardEnum.PROPS.name())) + .filter(config -> Objects.equals(config.getDetailType(), + VipActivityRewardCommon.VIP_DETAIL_TYPE)) + .map(config -> DataTypeUtils.toLong(config.getContent())) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + private Set getVipCoverResourceIds(Collection vipConfigs) { + if (CollectionUtils.isEmpty(vipConfigs)) { + return Set.of(); + } + return vipConfigs.stream() + .map(this::firstVipResourceId) + .filter(Objects::nonNull) + .filter(id -> id > 0) + .collect(Collectors.toSet()); + } + + private boolean isVipLevelReward(PropsActivityRewardConfig config, + Map vipConfigMap) { + return Objects.equals(config.getType(), PropsActivityRewardEnum.PROPS.name()) + && Objects.equals(config.getDetailType(), VipActivityRewardCommon.VIP_DETAIL_TYPE) + && vipConfigMap.containsKey(DataTypeUtils.toLong(config.getContent())); + } + + private void setVipConfig(PropsActivityRewardConfigInfoDTO propsActivity, + VipLevelConfig vipConfig, Map vipCoverMap) { + propsActivity.setCover(resolveVipCover(vipConfig, vipCoverMap)); + propsActivity.setSourceUrl(resolveVipSourceUrl(vipConfig, vipCoverMap)); + propsActivity.setAmount(BigDecimal.valueOf(Objects.requireNonNullElse( + vipConfig.getPriceGold(), 0L))); + propsActivity.setQuantity(durationDays(vipConfig)); + propsActivity.setName(displayName(vipConfig)); + propsActivity.setRemark(displayName(vipConfig)); + } + + private Long firstVipResourceId(VipLevelConfig vipConfig) { + return Stream.of( + vipConfig.getBadgeResourceId(), + vipConfig.getShortBadgeResourceId(), + vipConfig.getAvatarFrameResourceId(), + vipConfig.getEntryEffectResourceId(), + vipConfig.getChatBubbleResourceId(), + vipConfig.getFloatPictureResourceId(), + vipConfig.getBackgroundCardResourceId(), + vipConfig.getEffectImageResourceId()) + .filter(Objects::nonNull) + .filter(id -> id > 0) + .findFirst() + .orElse(null); + } + + private String resolveVipCover(VipLevelConfig vipConfig, + Map vipCoverMap) { + PropsSourceRecord sourceRecord = vipCoverMap.get(firstVipResourceId(vipConfig)); + if (Objects.nonNull(sourceRecord) && StringUtils.isNotBlank(sourceRecord.getCover())) { + return sourceRecord.getCover(); + } + return resolveVipSourceUrl(vipConfig, vipCoverMap); + } + + private String resolveVipSourceUrl(VipLevelConfig vipConfig, + Map vipCoverMap) { + PropsSourceRecord sourceRecord = vipCoverMap.get(firstVipResourceId(vipConfig)); + if (Objects.nonNull(sourceRecord) && StringUtils.isNotBlank(sourceRecord.getSourceUrl())) { + return sourceRecord.getSourceUrl(); + } + return Stream.of( + vipConfig.getBadgeUrl(), + vipConfig.getShortBadgeUrl(), + vipConfig.getAvatarFrameUrl(), + vipConfig.getEntryEffectUrl(), + vipConfig.getChatBubbleUrl(), + vipConfig.getFloatPictureUrl(), + vipConfig.getBackgroundCardUrl(), + vipConfig.getEffectImageUrl()) + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(""); + } + + private int durationDays(VipLevelConfig vipConfig) { + Integer days = vipConfig.getDurationDays(); + return Objects.nonNull(days) && days > 0 ? days : 30; + } + + private String displayName(VipLevelConfig vipConfig) { + if (StringUtils.isNotBlank(vipConfig.getDisplayName())) { + return vipConfig.getDisplayName(); + } + return "VIP " + Objects.requireNonNullElse(vipConfig.getLevel(), 0); + } +} diff --git a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/UserBankBalanceService.java b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/UserBankBalanceService.java index 7f3b94b0..f474898a 100644 --- a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/UserBankBalanceService.java +++ b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/UserBankBalanceService.java @@ -3,8 +3,9 @@ package com.red.circle.wallet.infra.database.mongo.service.bank; import com.red.circle.tool.core.tuple.PennyAmount; import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankBalance; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; -import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; -import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; import java.util.Collection; import java.util.List; import java.util.Map; @@ -52,11 +53,16 @@ public interface UserBankBalanceService { */ Map mapBalance(Collection userIds); - /** - * 查看用户银行卡账户. - */ - List listBalance(UserBankBalanceQryCmd query); - - List listAll(); + /** + * 查看用户银行卡账户. + */ + List listBalance(UserBankBalanceQryCmd query); + + /** + * 统计用户银行卡账户. + */ + UserBankBalanceStatisticsDTO calculateStatistics(UserBankBalanceQryCmd query); + + List listAll(); } diff --git a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/impl/UserBankBalanceServiceImpl.java b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/impl/UserBankBalanceServiceImpl.java index de048f1d..30b0bb17 100644 --- a/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/impl/UserBankBalanceServiceImpl.java +++ b/rc-service/rc-service-wallet/wallet-infrastructure/src/main/java/com/red/circle/wallet/infra/database/mongo/service/bank/impl/UserBankBalanceServiceImpl.java @@ -8,20 +8,24 @@ import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.tuple.PennyAmount; import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankBalance; import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankBalanceService; -import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; -import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; -import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; -import java.util.Collection; -import java.util.List; +import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; +import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.bson.Document; import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.FindAndModifyOptions; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.aggregation.Aggregation; +import org.springframework.data.mongodb.core.FindAndModifyOptions; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Service; @@ -33,10 +37,14 @@ import org.springframework.stereotype.Service; */ @Service @RequiredArgsConstructor -public class UserBankBalanceServiceImpl implements UserBankBalanceService { - - private final MongoTemplate mongoTemplate; - private final MongoPrimaryService mongoPrimaryService; +public class UserBankBalanceServiceImpl implements UserBankBalanceService { + + private static final int DEFAULT_LIMIT = 20; + private static final Set SORTABLE_FIELDS = Set.of( + "balance", "earnPoints", "consumptionPoints"); + + private final MongoTemplate mongoTemplate; + private final MongoPrimaryService mongoPrimaryService; @Override public boolean incr(Long userId, String sysOrigin, PennyAmount amount) { @@ -112,32 +120,26 @@ public class UserBankBalanceServiceImpl implements UserBankBalanceService { balance -> BankBalanceDTO.of(balance.getBalance()))); } - @Override - public List listBalance(UserBankBalanceQryCmd query) { - - Criteria criteria = Criteria.where("sysOrigin").is(query.getSysOrigin()); - - if (Objects.nonNull(query.getUserId())) { - if (Objects.nonNull(query.getLastId())) { - query.setLastId(null); - criteria.and("id").lt(query.getLastId()); - } else { - query.setLastId(null); - criteria.and("id").is(query.getUserId()); - } - } - - if (Objects.nonNull(query.getLastId())) { - criteria.and("id").lt(query.getLastId()); - } - - return mongoTemplate.find(Query.query(criteria) - .with(Sort.by(Sort.Order.desc("id"))) - .limit(query.getLimit()), - UserBankBalance.class).stream() - .map(balance -> new UserBankBalanceDTO() - .setBalance(BankBalanceDTO.of( - balance.getBalance()).getAccurateBalance()) + @Override + public List listBalance(UserBankBalanceQryCmd query) { + + boolean sorted = isSorted(query); + Criteria criteria = buildCriteria(query); + if (!sorted && Objects.nonNull(query.getLastId()) && Objects.isNull(query.getUserId())) { + criteria.and("id").lt(query.getLastId()); + } + + Query mongoQuery = Query.query(criteria) + .with(buildSort(query, sorted)) + .limit(getLimit(query)); + if (sorted) { + mongoQuery.skip((long) (getPageNo(query) - 1) * getLimit(query)); + } + + return mongoTemplate.find(mongoQuery, UserBankBalance.class).stream() + .map(balance -> new UserBankBalanceDTO() + .setBalance(BankBalanceDTO.of( + balance.getBalance()).getAccurateBalance()) .setSysOrigin(balance.getSysOrigin()) .setId(balance.getId()) .setEarnPoints(BankBalanceDTO.of(balance.getEarnPoints()).getAccurateBalance()) @@ -146,12 +148,74 @@ public class UserBankBalanceServiceImpl implements UserBankBalanceService { .setCreateTime(balance.getCreateTime()) .setUpdateTime(balance.getUpdateTime()) ) - .toList(); - - } - - @Override - public List listAll() { + .toList(); + + } + + @Override + public UserBankBalanceStatisticsDTO calculateStatistics(UserBankBalanceQryCmd query) { + Document document = mongoTemplate.aggregate( + Aggregation.newAggregation( + Aggregation.match(buildCriteria(query)), + Aggregation.group() + .sum("earnPoints").as("totalEarnPoints") + .sum("consumptionPoints").as("totalConsumptionPoints") + .sum("balance").as("totalBalance") + ), + UserBankBalance.class, + Document.class) + .getUniqueMappedResult(); + + return new UserBankBalanceStatisticsDTO() + .setTotalEarnPoints(getAccurateAmount(document, "totalEarnPoints")) + .setTotalConsumptionPoints(getAccurateAmount(document, "totalConsumptionPoints")) + .setTotalBalance(getAccurateAmount(document, "totalBalance")); + } + + private Criteria buildCriteria(UserBankBalanceQryCmd query) { + Criteria criteria = new Criteria(); + if (Objects.nonNull(query.getSysOrigin())) { + criteria.and("sysOrigin").is(query.getSysOrigin()); + } + if (Objects.nonNull(query.getUserId())) { + criteria.and("id").is(query.getUserId()); + } + return criteria; + } + + private Sort buildSort(UserBankBalanceQryCmd query, boolean sorted) { + if (!sorted) { + return Sort.by(Sort.Order.desc("id")); + } + Sort.Direction direction = "ascend".equalsIgnoreCase(query.getSortOrder()) + ? Sort.Direction.ASC + : Sort.Direction.DESC; + return Sort.by(new Sort.Order(direction, query.getSortField()), Sort.Order.desc("id")); + } + + private boolean isSorted(UserBankBalanceQryCmd query) { + return SORTABLE_FIELDS.contains(query.getSortField()) + && ("ascend".equalsIgnoreCase(query.getSortOrder()) + || "descend".equalsIgnoreCase(query.getSortOrder())); + } + + private int getLimit(UserBankBalanceQryCmd query) { + return Objects.nonNull(query.getLimit()) && query.getLimit() > 0 + ? query.getLimit() + : DEFAULT_LIMIT; + } + + private int getPageNo(UserBankBalanceQryCmd query) { + return Objects.nonNull(query.getPageNo()) && query.getPageNo() > 0 ? query.getPageNo() : 1; + } + + private BigDecimal getAccurateAmount(Document document, String key) { + return BankBalanceDTO.of(Objects.isNull(document) ? 0L : getLongValue(document, key)) + .getAccurateBalance(); + } + + @Override + public List listAll() { return mongoTemplate.find(Query.query(new Criteria()), UserBankBalance.class); } diff --git a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/UserBankBalanceClientEndpoint.java b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/UserBankBalanceClientEndpoint.java index a45b85ec..dba89f83 100644 --- a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/UserBankBalanceClientEndpoint.java +++ b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/endpoint/wallet/UserBankBalanceClientEndpoint.java @@ -6,8 +6,9 @@ import com.red.circle.wallet.inner.endpoint.bank.api.UserBankBalanceClientApi; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceDecrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceIncrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; -import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; -import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; import com.red.circle.wallet.inner.service.wallet.UserBankBalanceClientService; import java.util.List; import lombok.RequiredArgsConstructor; @@ -56,8 +57,14 @@ public class UserBankBalanceClientEndpoint implements UserBankBalanceClientApi { @Override - public ResultResponse> listBalance(UserBankBalanceQryCmd query) { - return ResultResponse.success(userBankBalanceClientService.listBalance(query)); - } - -} + public ResultResponse> listBalance(UserBankBalanceQryCmd query) { + return ResultResponse.success(userBankBalanceClientService.listBalance(query)); + } + + @Override + public ResultResponse calculateStatistics( + UserBankBalanceQryCmd query) { + return ResultResponse.success(userBankBalanceClientService.calculateStatistics(query)); + } + +} diff --git a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/UserBankBalanceClientService.java b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/UserBankBalanceClientService.java index 19384e08..97f9a1cf 100644 --- a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/UserBankBalanceClientService.java +++ b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/UserBankBalanceClientService.java @@ -3,8 +3,9 @@ package com.red.circle.wallet.inner.service.wallet; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceDecrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceIncrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; -import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; -import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; import java.util.List; /** @@ -40,8 +41,13 @@ public interface UserBankBalanceClientService { */ BankBalanceDTO decr(UserBankBalanceDecrCmd cmd); - /** - * 查看用户银行卡账户. - */ - List listBalance(UserBankBalanceQryCmd query); -} + /** + * 查看用户银行卡账户. + */ + List listBalance(UserBankBalanceQryCmd query); + + /** + * 统计用户银行卡账户. + */ + UserBankBalanceStatisticsDTO calculateStatistics(UserBankBalanceQryCmd query); +} diff --git a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/UserBankBalanceClientServiceImpl.java b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/UserBankBalanceClientServiceImpl.java index 03cbf310..7db59984 100644 --- a/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/UserBankBalanceClientServiceImpl.java +++ b/rc-service/rc-service-wallet/wallet-inner-endpoint/src/main/java/com/red/circle/wallet/inner/service/wallet/impl/UserBankBalanceClientServiceImpl.java @@ -4,8 +4,9 @@ import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankBalanceSe import com.red.circle.wallet.inner.model.cmd.UserBankBalanceDecrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceIncrCmd; import com.red.circle.wallet.inner.model.cmd.UserBankBalanceQryCmd; -import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; -import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.BankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceDTO; +import com.red.circle.wallet.inner.model.dto.UserBankBalanceStatisticsDTO; import com.red.circle.wallet.inner.service.wallet.UserBankBalanceClientService; import java.util.List; import lombok.RequiredArgsConstructor; @@ -44,8 +45,13 @@ public class UserBankBalanceClientServiceImpl implements UserBankBalanceClientSe @Override - public List listBalance(UserBankBalanceQryCmd query) { - return userBankBalanceService.listBalance(query); - } - -} + public List listBalance(UserBankBalanceQryCmd query) { + return userBankBalanceService.listBalance(query); + } + + @Override + public UserBankBalanceStatisticsDTO calculateStatistics(UserBankBalanceQryCmd query) { + return userBankBalanceService.calculateStatistics(query); + } + +}