From 21bab83eea5f26a29e6c696056e1faa003547ab6 Mon Sep 17 00:00:00 2001 From: hy001 Date: Wed, 24 Jun 2026 12:57:33 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nacos_config/rc-gateway/application.yml | 1 + .../cmd/SysPayApplicationCommodityCmd.java | 31 ++-- .../PayWebApplicationCommodityV2QueryExe.java | 30 +++- .../pay/web/PayWebOrderStatusQueryExe.java | 4 +- .../web/UserProfileAndCountryQueryExe.java | 114 +++++++------- .../order/app/dto/cmd/PayOrderStatusCmd.java | 5 + .../cmd/PayWebApplicationCommodityCmd.java | 19 ++- .../impl/PayApplicationClientServiceImpl.java | 58 ++++--- .../team/query/TeamMemberListQryExe.java | 144 ++++++++++-------- .../team/query/TeamMemberListQryExeTest.java | 88 +++++++++++ 10 files changed, 336 insertions(+), 158 deletions(-) create mode 100644 rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExeTest.java diff --git a/nacos_config/rc-gateway/application.yml b/nacos_config/rc-gateway/application.yml index 1dbde5bf..1c584efb 100644 --- a/nacos_config/rc-gateway/application.yml +++ b/nacos_config/rc-gateway/application.yml @@ -175,6 +175,7 @@ gateway: - /product/apple/config - /game/sud/** - /web/pay/** + - /order/web/pay/** - /external/oss/upload - /telegram/webhook - /account/create/getRegion diff --git a/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayApplicationCommodityCmd.java b/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayApplicationCommodityCmd.java index 30b8ce3e..3610706b 100644 --- a/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayApplicationCommodityCmd.java +++ b/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayApplicationCommodityCmd.java @@ -3,13 +3,14 @@ package com.red.circle.order.inner.model.cmd; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.red.circle.framework.core.dto.CommonCommand; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import java.io.Serial; -import java.math.BigDecimal; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.io.Serial; +import java.math.BigDecimal; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; /** *

@@ -43,11 +44,17 @@ public class SysPayApplicationCommodityCmd extends CommonCommand { /** * 开通支付国家. */ - @JsonSerialize(using = ToStringSerializer.class) - private Long payCountryId; - - /** - * 区域ID. + @JsonSerialize(using = ToStringSerializer.class) + private Long payCountryId; + + /** + * 批量创建时的国家列表;表结构仍是一条商品对应一个国家,所以只在新增时拆成多条记录. + */ + @JsonSerialize(contentUsing = ToStringSerializer.class) + private List payCountryIds; + + /** + * 区域ID. */ private String regionId; diff --git a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebApplicationCommodityV2QueryExe.java b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebApplicationCommodityV2QueryExe.java index c5655d38..a3f50b40 100644 --- a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebApplicationCommodityV2QueryExe.java +++ b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebApplicationCommodityV2QueryExe.java @@ -1,12 +1,13 @@ package com.red.circle.order.app.command.pay.web; -import com.google.api.client.util.Lists; -import com.google.api.client.util.Maps; -import com.google.api.client.util.Sets; -import com.red.circle.common.business.core.amount.PayAmountArithmeticUtils; -import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; -import com.red.circle.framework.core.asserts.ResponseAssert; -import com.red.circle.framework.core.response.CommonErrorCode; +import com.google.api.client.util.Lists; +import com.google.api.client.util.Maps; +import com.google.api.client.util.Sets; +import com.red.circle.common.business.core.amount.PayAmountArithmeticUtils; +import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; +import com.red.circle.common.business.core.util.CountryCodeAliasUtils; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.core.response.CommonErrorCode; import com.red.circle.order.app.convertor.PayAppConvertor; import com.red.circle.order.app.dto.clientobject.pay.ApplicationCommodityCardV2CO; import com.red.circle.order.app.dto.clientobject.pay.PayApplicationCO; @@ -23,6 +24,8 @@ import com.red.circle.order.infra.database.rds.service.pay.PayApplicationCommodi import com.red.circle.order.infra.database.rds.service.pay.PayApplicationService; import com.red.circle.order.infra.database.rds.service.pay.PayChannelService; import com.red.circle.order.infra.database.rds.service.pay.PayCountryChannelDetailsService; +import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient; +import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.num.ArithmeticUtils; import com.red.circle.tool.core.text.StringUtils; @@ -54,6 +57,7 @@ public class PayWebApplicationCommodityV2QueryExe { private final PayCountryAliasResolver payCountryAliasResolver; private final PayApplicationCommodityService payApplicationCommodityService; private final PayCountryChannelDetailsService payCountryChannelDetailsService; + private final SysCountryCodeClient sysCountryCodeClient; public ApplicationCommodityCardV2CO execute(PayWebApplicationCommodityCmd cmd) { @@ -165,7 +169,17 @@ public class PayWebApplicationCommodityV2QueryExe { } private PayCountry getPayCountry(PayWebApplicationCommodityCmd cmd) { - return payCountryAliasResolver.resolveUsablePayCountry(cmd.getPayCountryId()); + if (Objects.nonNull(cmd.getPayCountryId()) || StringUtils.isBlank(cmd.getCountryCode())) { + return payCountryAliasResolver.resolveUsablePayCountry(cmd.getPayCountryId()); + } + + String normalizedCountryCode = CountryCodeAliasUtils.normalizePaymentCountryCode( + cmd.getCountryCode()); + SysCountryCodeDTO countryCode = ResponseAssert.requiredSuccess( + sysCountryCodeClient.getByCode(normalizedCountryCode)); + ResponseAssert.notNull(CommonErrorCode.NOT_SUPPORTED_REGION, countryCode); + // H5 只传用户国家码;这里仅把真实国家转成商品表使用的支付国家ID,是否开放支付国家由 H5 JSON 控制. + return payCountryAliasResolver.resolveUsablePayCountryByCountryId(countryCode.getId()); } private List getPayChannels(List payChannels, diff --git a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebOrderStatusQueryExe.java b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebOrderStatusQueryExe.java index 49d44cb5..5c8e25e6 100644 --- a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebOrderStatusQueryExe.java +++ b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/PayWebOrderStatusQueryExe.java @@ -26,8 +26,10 @@ public class PayWebOrderStatusQueryExe { public PayOrderStatusCO execute(PayOrderStatusCmd cmd) { OrderUserPurchasePay mysqlOrder = getMysqlOrder(cmd.getOrderId()); ResponseAssert.notNull(OrderErrorCode.NO_PURCHASE_RECORD_FOUND, mysqlOrder); + Long expectedUserId = Objects.nonNull(cmd.getReqUserId()) ? cmd.getReqUserId() : cmd.getUserId(); + // 登录态请求继续使用网关注入的 reqUserId;公开 H5 轮询没有登录态,只允许用下单用户ID校验同一笔订单. ResponseAssert.isTrue(OrderErrorCode.NO_PURCHASE_RECORD_FOUND, - Objects.equals(mysqlOrder.getUserId(), cmd.getReqUserId())); + Objects.nonNull(expectedUserId) && Objects.equals(mysqlOrder.getUserId(), expectedUserId)); InAppPurchaseStatus status = MiFaPayMysqlOrderSupport .toInAppPurchaseStatus(mysqlOrder.getPayStatus()); return new PayOrderStatusCO() diff --git a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/UserProfileAndCountryQueryExe.java b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/UserProfileAndCountryQueryExe.java index 67f799c2..8d6f47d7 100644 --- a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/UserProfileAndCountryQueryExe.java +++ b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/UserProfileAndCountryQueryExe.java @@ -14,17 +14,15 @@ import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient; import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.parse.DataTypeUtils; -import com.red.circle.tool.core.regex.RegexConstant; -import com.red.circle.tool.core.text.StringUtils; -import com.red.circle.other.inner.asserts.user.UserErrorCode; -import com.red.circle.other.inner.model.dto.user.UserProfileDTO; -import com.red.circle.other.inner.model.dto.user.reigon.RegionRelationDTO; -import com.red.circle.other.inner.enums.user.RegionRelationGroupEnum; -import com.red.circle.other.inner.endpoint.user.region.RegionRelationClient; -import com.red.circle.other.inner.endpoint.user.region.UserRegionClient; -import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; +import com.red.circle.tool.core.regex.RegexConstant; +import com.red.circle.tool.core.text.StringUtils; +import com.red.circle.other.inner.asserts.user.UserErrorCode; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import com.red.circle.other.inner.endpoint.user.region.UserRegionClient; +import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; import com.red.circle.wallet.inner.endpoint.freight.FreightGoldClient; import java.util.AbstractMap; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -47,9 +45,8 @@ public class UserProfileAndCountryQueryExe { private final PayCountryAliasResolver payCountryAliasResolver; private final UserProfileClient userProfileClient; private final SysCountryCodeClient sysCountryCodeClient; - private final RegionRelationClient regionRelationClient; - - public UserProfileAndCountryCO execute(PayWebUserCmd cmd) { + + public UserProfileAndCountryCO execute(PayWebUserCmd cmd) { UserProfileDTO userProfile = getUserProfile(cmd); @@ -64,27 +61,17 @@ public class UserProfileAndCountryQueryExe { .toWebSiteUseProfileCO(userProfile); webSiteUseProfile.setAccount(userProfile.actualAccount()); - List payCountryList; - - String regionId = ResponseAssert.requiredSuccess( - userRegionClient.getRegionId(userProfile.getId()) - ); - - if (Objects.equals(cmd.getSysOrigin(), SysOriginPlatformEnum.LIKEI.name())) { - ResponseAssert.isTrue(CommonErrorCode.NOT_SUPPORTED_REGION, StringUtils.isNotBlank(regionId)); - - List regionRelationList = getRegionRelationList(userProfile, regionId); - ResponseAssert.notEmpty(CommonErrorCode.NOT_SUPPORTED_REGION, regionRelationList); - - payCountryList = getPayCountryList(regionRelationList); - ResponseAssert.notEmpty(CommonErrorCode.NOT_SUPPORTED_REGION, payCountryList); - - } else { - payCountryList = payCountryService.listAllShelf(); - } - - Map mapCountryCode = getMapCountryCode(payCountryList); - ResponseAssert.notEmpty(CommonErrorCode.NOT_SUPPORTED_REGION, mapCountryCode); + String regionId = ResponseAssert.requiredSuccess( + userRegionClient.getRegionId(userProfile.getId()) + ); + + if (Objects.equals(cmd.getSysOrigin(), SysOriginPlatformEnum.LIKEI.name())) { + return toLikeiUserCountryResult(userProfile, webSiteUseProfile, regionId); + } + + List payCountryList = payCountryService.listAllShelf(); + Map mapCountryCode = getMapCountryCode(payCountryList); + ResponseAssert.notEmpty(CommonErrorCode.NOT_SUPPORTED_REGION, mapCountryCode); Map compatiblePayCountryMap = getCompatiblePayCountryMap(payCountryList); return new UserProfileAndCountryCO() @@ -105,9 +92,48 @@ public class UserProfileAndCountryQueryExe { }).filter(Objects::nonNull).collect(Collectors.toList())) .setUserProfile(webSiteUseProfile) - .setRegionId(regionId); - - } + .setRegionId(regionId); + + } + + private UserProfileAndCountryCO toLikeiUserCountryResult(UserProfileDTO userProfile, + WebSiteUseProfileCO webSiteUseProfile, String regionId) { + + SysCountryCodeDTO countryCode = getUserCountryCode(userProfile); + ResponseAssert.notNull(CommonErrorCode.NOT_SUPPORTED_REGION, countryCode); + + PayCountry compatiblePayCountry = payCountryAliasResolver + .resolveUsablePayCountryByCountryId(countryCode.getId()); + + // LIKEI/Yumi 的支付国家开放由 H5 JSON 控制;这里仅返回用户真实国家,商品接口再按国家码查该国商品. + PayCountryCO payCountryCO = new PayCountryCO() + .setId(Objects.nonNull(compatiblePayCountry) ? compatiblePayCountry.getId() : null) + .setPayCountryId(Objects.nonNull(compatiblePayCountry) ? compatiblePayCountry.getId() : null) + .setCountryId(countryCode.getId()) + .setCurrency(Objects.nonNull(compatiblePayCountry) ? compatiblePayCountry.getCurrency() : null) + .setCountry(countryCode); + + return new UserProfileAndCountryCO() + .setCountryList(Collections.singletonList(payCountryCO)) + .setUserProfile(webSiteUseProfile) + .setRegionId(regionId); + } + + private SysCountryCodeDTO getUserCountryCode(UserProfileDTO userProfile) { + if (Objects.nonNull(userProfile.getCountryId())) { + return ResponseAssert.requiredSuccess( + sysCountryCodeClient.getById(userProfile.getCountryId()) + ); + } + + if (StringUtils.isNotBlank(userProfile.getCountryCode())) { + return ResponseAssert.requiredSuccess( + sysCountryCodeClient.getByCode(userProfile.getCountryCode()) + ); + } + + return null; + } private UserProfileDTO getUserProfile(PayWebUserCmd cmd) { if (StringUtils.isNotBlank(cmd.getAccount()) && StringUtils.isNotBlank(cmd.getSysOrigin())) { @@ -146,18 +172,4 @@ public class UserProfileAndCountryQueryExe { (left, right) -> left)); } - private List getRegionRelationList(UserProfileDTO userProfile, - String regionId) { - return ResponseAssert.requiredSuccess( - regionRelationClient - .listByRegionId(regionId, userProfile.getOriginSys(), - RegionRelationGroupEnum.OPEN_PAY_COUNTRY) - ); - } - - private List getPayCountryList(List regionRelationList) { - return payCountryService - .listByIds(regionRelationList.stream().map(RegionRelationDTO::getRelationId).collect( - Collectors.toSet())); - } -} +} diff --git a/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayOrderStatusCmd.java b/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayOrderStatusCmd.java index 4602efed..0c63c1ec 100644 --- a/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayOrderStatusCmd.java +++ b/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayOrderStatusCmd.java @@ -23,4 +23,9 @@ public class PayOrderStatusCmd extends AppExtCommand { */ @NotBlank(message = "orderId required.") private String orderId; + + /** + * H5公开支付页没有登录态,轮询状态时必须用下单用户ID继续做订单归属校验. + */ + private Long userId; } diff --git a/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayWebApplicationCommodityCmd.java b/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayWebApplicationCommodityCmd.java index ea70330e..411e8b34 100644 --- a/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayWebApplicationCommodityCmd.java +++ b/rc-service/rc-service-order/order-client/src/main/java/com/red/circle/order/app/dto/cmd/PayWebApplicationCommodityCmd.java @@ -27,13 +27,18 @@ public class PayWebApplicationCommodityCmd extends AppExtCommand { @NotNull(message = "applicationId required.") private Long applicationId; - /** - * 国家. - */ - private Long payCountryId; - - /** - * 金币类型. + /** + * 国家. + */ + private Long payCountryId; + + /** + * 国家编码;H5 先拿用户国家,再用国家码查询商品,避免前端硬编码支付国家ID. + */ + private String countryCode; + + /** + * 金币类型. */ private PayApplicationCommodityType type; diff --git a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayApplicationClientServiceImpl.java b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayApplicationClientServiceImpl.java index 327411a2..bd1c7e85 100644 --- a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayApplicationClientServiceImpl.java +++ b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayApplicationClientServiceImpl.java @@ -1,8 +1,9 @@ package com.red.circle.order.inner.service.impl; -import com.alibaba.nacos.shaded.com.google.common.collect.Lists; -import com.red.circle.framework.core.asserts.ResponseAssert; -import com.red.circle.framework.dto.PageResult; +import com.alibaba.nacos.shaded.com.google.common.collect.Lists; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.framework.dto.PageResult; import com.red.circle.order.infra.database.rds.entity.pay.PayApplication; import com.red.circle.order.infra.database.rds.entity.pay.PayApplicationCommodity; import com.red.circle.order.infra.database.rds.service.pay.PayApplicationCommodityService; @@ -13,16 +14,17 @@ import com.red.circle.order.inner.model.cmd.SysPayApplicationCommodityQryCmd; import com.red.circle.order.inner.model.dto.SysPayApplicationCommodityDTO; import com.red.circle.order.inner.model.dto.SysPayApplicationDTO; import com.red.circle.order.inner.service.PayApplicationClientService; -import com.red.circle.tool.core.collection.CollectionUtils; -import com.red.circle.tool.core.text.StringUtils; -import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; +import com.red.circle.tool.core.collection.CollectionUtils; +import com.red.circle.tool.core.text.StringUtils; +import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; /** * 开通支付应用. @@ -106,11 +108,31 @@ public class PayApplicationClientServiceImpl implements PayApplicationClientServ Collectors.toSet()))); } - @Override - public void addOrUpdateCommodity(SysPayApplicationCommodityCmd param) { - payApplicationCommodityService.saveOrUpdate( - webPayInnerConvertor.toPayApplicationCommodity(param)); - } + @Override + @Transactional(rollbackFor = Exception.class) + public void addOrUpdateCommodity(SysPayApplicationCommodityCmd param) { + if (Objects.nonNull(param.getId()) || CollectionUtils.isEmpty(param.getPayCountryIds())) { + // 编辑沿用原来的单条更新;批量国家只服务新增,避免一次编辑误伤多个已存在商品. + payApplicationCommodityService.saveOrUpdate( + webPayInnerConvertor.toPayApplicationCommodity(param)); + return; + } + + List commodities = param.getPayCountryIds().stream() + .filter(Objects::nonNull) + .distinct() + .map(payCountryId -> { + PayApplicationCommodity commodity = webPayInnerConvertor.toPayApplicationCommodity(param); + // 商品表当前以国家维度存储,新增多选国家时需要拆成多条独立商品记录. + commodity.setId(null); + commodity.setPayCountryId(payCountryId); + return commodity; + }) + .collect(Collectors.toList()); + + ResponseAssert.notEmpty(CommonErrorCode.NOT_FOUND_RECORD_INFO, commodities); + payApplicationCommodityService.saveBatch(commodities); + } @Override public void switchShelf(Long id, Boolean shelf) { diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExe.java index 402a7999..1a67e3a9 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExe.java @@ -1,5 +1,5 @@ package com.red.circle.other.app.command.team.query; - + import com.google.common.collect.Lists; import com.red.circle.common.business.dto.cmd.app.AppIdLongCmd; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; @@ -24,10 +24,10 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.stream.Collectors; - + import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; - + /** * 团队成员列表. * @@ -36,58 +36,43 @@ import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class TeamMemberListQryExe { - + private final TeamMemberService teamMemberService; private final UserProfileGateway userProfileGateway; private final UserProfileAppConvertor userProfileAppConvertor; private final TeamMemberTargetService teamMemberTargetService; private final TeamPolicyManagerService teamPolicyManagerService; private final TeamProfileService teamProfileService; - + public List execute(AppIdLongCmd cmd) { - + List teamMembers = teamMemberService.listTeamMember(cmd.getId(), 3000); if (CollectionUtils.isEmpty(teamMembers)) { return Lists.newArrayList(); } - + Set memberUserIds = teamMembers.stream().map(TeamMember::getMemberId) .collect(Collectors.toSet()); Map userProfileMap = userProfileAppConvertor.toMapUserProfileDTO( userProfileGateway.mapByUserIds(memberUserIds)); - + Map teamMemberTargetMap = teamMemberTargetService.mapMemberBillTarget( cmd.getId(), memberUserIds, ZonedDateTimeAsiaRiyadhUtils.nowYearMonthToInt()); - - List teamMemberCOs = teamMembers.stream().map(teamMember -> new TeamMemberCO() - .setId(teamMember.getMemberId()) - .setUserProfile(userProfileMap.get(teamMember.getMemberId())) - .setTargetFormat(Optional.ofNullable(teamMemberTargetMap.get(teamMember.getMemberId())) - .map(target -> NumUtils.formatLong(target.sumAcceptGiftValue())) - .orElse("0")) - .setTimeHours(Optional.ofNullable(teamMemberTargetMap.get(teamMember.getMemberId())) - .map(target -> BigDecimal.valueOf(target.sumAllTargetTime()) - .divide(BigDecimal.valueOf(60), 2, RoundingMode.DOWN) - .stripTrailingZeros().toPlainString()) - .orElse("0")) - .setEffectiveDay(Optional.ofNullable(teamMemberTargetMap.get(teamMember.getMemberId())) - .map(TeamMemberTarget::sumEffectiveDay) - .orElse(0)) - .setUseDiamonds("0") - .setSurplusDiamonds("0") - .setRole(Objects.toString(teamMember.getRole())) - .setWalletWithdrawal(Objects.isNull(teamMember.getWalletWithdrawal()) || Objects.equals( - teamMember.getWalletWithdrawal(), Boolean.TRUE)) - .setCreateTime(teamMember.getCreateTime()) - ).collect(Collectors.toList()); + + List teamMemberCOs = teamMembers.stream() + .map(teamMember -> assembleTeamMemberCO( + teamMember, + userProfileMap.get(teamMember.getMemberId()), + teamMemberTargetMap.get(teamMember.getMemberId()))) + .collect(Collectors.toList()); return teamMemberCOs; } - + public TeamMemberCountResultCO executeCount(AppIdLongCmd cmd) { List teamMembers = teamMemberService.listTeamMember(cmd.getId(), 3000); - + if (CollectionUtils.isEmpty(teamMembers)) { return new TeamMemberCountResultCO() .setMemberQuantity(0) @@ -95,7 +80,7 @@ public class TeamMemberListQryExe { .setTargetFormatSum("0") .setTargetSum(0L); } - + // 获取团队信息和政策管理器 TeamProfile teamProfile = teamProfileService.getById(cmd.getId()); if (teamProfile == null) { @@ -105,10 +90,10 @@ public class TeamMemberListQryExe { .setTargetFormatSum("0") .setTargetSum(0L); } - + TeamPolicyManager teamPolicyManager = teamPolicyManagerService.getReleaseByRegionAndType( teamProfile.getSysOrigin(), teamProfile.getRegion()); - + if (teamPolicyManager == null || CollectionUtils.isEmpty(teamPolicyManager.getPolicy())) { return new TeamMemberCountResultCO() .setMemberQuantity(teamMembers.size()) @@ -116,30 +101,30 @@ public class TeamMemberListQryExe { .setTargetFormatSum("0") .setTargetSum(0L); } - + // 获取团队成员的目标总和 Set memberUserIds = teamMembers.stream().map(TeamMember::getMemberId) .collect(Collectors.toSet()); - + Map teamMemberTargetMap = teamMemberTargetService.mapMemberBillTarget( cmd.getId(), memberUserIds, ZonedDateTimeAsiaRiyadhUtils.nowYearMonthToInt()); - + // 计算目标总和 Long targetSum = teamMemberTargetMap.values().stream() .filter(Objects::nonNull) .mapToLong(target -> Optional.ofNullable(target.sumAcceptGiftValue()).orElse(0L)) .sum(); - + // 根据团队目标总和匹配对应的TeamPolicy返回对应的挡位 Integer matchedLevel = findMatchedLevel(teamPolicyManager.getPolicy(), targetSum); - + return new TeamMemberCountResultCO() .setMemberQuantity(teamMembers.size()) .setLevel(matchedLevel) .setTargetFormatSum(NumUtils.formatLong(targetSum)) .setTargetSum(targetSum); } - + /** * 根据目标总和查找匹配的等级. * 逻辑:从高等级到低等级遍历,找到第一个满足条件的等级 @@ -152,7 +137,7 @@ public class TeamMemberListQryExe { if (CollectionUtils.isEmpty(policies) || targetSum == null) { return null; } - + // 按等级降序排列,从高等级到低等级匹配 return policies.stream() .sorted((p1, p2) -> Integer.compare(p2.getLevel(), p1.getLevel())) @@ -161,24 +146,61 @@ public class TeamMemberListQryExe { .map(TeamPolicy::getLevel) .orElse(0); } - - public List searchTeamMember(Long reqUserId, String account) { - TeamMember reqTeamMember = teamMemberService.getByMemberId(reqUserId); - if (reqTeamMember == null) { - return Collections.emptyList(); - } - - List userProfiles = userProfileGateway.getByAccount(account); - if (CollectionUtils.isEmpty(userProfiles)) { - return Collections.emptyList(); - } - UserProfile userProfile = userProfiles.get(0); - TeamMember teamMember = teamMemberService.getByMemberId(userProfile.getId()); - if (teamMember == null || !teamMember.getTeamId().equals(reqTeamMember.getTeamId())) { - return Collections.emptyList(); - } - return Collections.singletonList(new TeamMemberCO().setUserProfile(userProfileAppConvertor.toUserProfileDTO(userProfile))); + + private TeamMemberCO assembleTeamMemberCO( + TeamMember teamMember, + UserProfileDTO userProfile, + TeamMemberTarget teamMemberTarget) { + return new TeamMemberCO() + .setId(teamMember.getMemberId()) + .setUserProfile(userProfile) + .setTargetFormat(Optional.ofNullable(teamMemberTarget) + .map(target -> NumUtils.formatLong(target.sumAcceptGiftValue())) + .orElse("0")) + .setTimeHours(Optional.ofNullable(teamMemberTarget) + .map(target -> BigDecimal.valueOf(target.sumAllTargetTime()) + .divide(BigDecimal.valueOf(60), 2, RoundingMode.DOWN) + .stripTrailingZeros().toPlainString()) + .orElse("0")) + .setEffectiveDay(Optional.ofNullable(teamMemberTarget) + .map(TeamMemberTarget::sumEffectiveDay) + .orElse(0)) + .setUseDiamonds("0") + .setSurplusDiamonds("0") + .setRole(Objects.toString(teamMember.getRole())) + .setWalletWithdrawal(Objects.isNull(teamMember.getWalletWithdrawal()) || Objects.equals( + teamMember.getWalletWithdrawal(), Boolean.TRUE)) + .setCreateTime(teamMember.getCreateTime()); + } + + public List searchTeamMember(Long reqUserId, String account) { + TeamMember reqTeamMember = teamMemberService.getByMemberId(reqUserId); + if (reqTeamMember == null) { + return Collections.emptyList(); } - - + + List userProfiles = userProfileGateway.getByAccount(account); + if (CollectionUtils.isEmpty(userProfiles)) { + return Collections.emptyList(); + } + UserProfile userProfile = userProfiles.get(0); + TeamMember teamMember = teamMemberService.getByMemberId(userProfile.getId()); + if (teamMember == null || !teamMember.getTeamId().equals(reqTeamMember.getTeamId())) { + return Collections.emptyList(); + } + + TeamMemberTarget teamMemberTarget = teamMemberTargetService + .mapMemberBillTarget( + reqTeamMember.getTeamId(), + Collections.singleton(teamMember.getMemberId()), + ZonedDateTimeAsiaRiyadhUtils.nowYearMonthToInt()) + .get(teamMember.getMemberId()); + + return Collections.singletonList(assembleTeamMemberCO( + teamMember, + userProfileAppConvertor.toUserProfileDTO(userProfile), + teamMemberTarget)); + } + + } diff --git a/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExeTest.java b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExeTest.java new file mode 100644 index 00000000..cf063919 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/test/java/com/red/circle/other/app/command/team/query/TeamMemberListQryExeTest.java @@ -0,0 +1,88 @@ +package com.red.circle.other.app.command.team.query; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; +import com.red.circle.other.app.dto.clientobject.team.TeamMemberCO; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember; +import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService; +import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService; +import com.red.circle.other.inner.enums.team.TeamMemberTargetIndex; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class TeamMemberListQryExeTest { + + @Test + void searchTeamMemberShouldReturnCurrentWorkSummary() { + TeamMemberService teamMemberService = mock(TeamMemberService.class); + UserProfileGateway userProfileGateway = mock(UserProfileGateway.class); + UserProfileAppConvertor userProfileAppConvertor = mock(UserProfileAppConvertor.class); + TeamMemberTargetService teamMemberTargetService = mock(TeamMemberTargetService.class); + TeamPolicyManagerService teamPolicyManagerService = mock(TeamPolicyManagerService.class); + TeamProfileService teamProfileService = mock(TeamProfileService.class); + TeamMemberListQryExe exe = new TeamMemberListQryExe( + teamMemberService, + userProfileGateway, + userProfileAppConvertor, + teamMemberTargetService, + teamPolicyManagerService, + teamProfileService + ); + + Long requesterUserId = 2064760394110529538L; + Long targetUserId = 2065155499803865089L; + Long teamId = 2064760394110529538L; + UserProfile targetProfile = new UserProfile(); + targetProfile.setId(targetUserId); + targetProfile.setAccount("1018375"); + targetProfile.setUserNickname("misra"); + UserProfileDTO targetProfileDTO = new UserProfileDTO(); + TeamMember requesterMember = new TeamMember() + .setMemberId(requesterUserId) + .setTeamId(teamId); + TeamMember targetMember = new TeamMember() + .setMemberId(targetUserId) + .setTeamId(teamId); + TeamMemberTarget target = new TeamMemberTarget() + .setUserId(targetUserId) + .setTeamId(teamId) + .setDailyTargets(List.of( + new TeamMemberTargetIndex() + .setAcceptGiftValue(425_289L) + .setOwnOnlineTime(2L) + .setEffectiveDay(1) + )); + + when(teamMemberService.getByMemberId(requesterUserId)).thenReturn(requesterMember); + when(userProfileGateway.getByAccount("1018375")).thenReturn(List.of(targetProfile)); + when(teamMemberService.getByMemberId(targetUserId)).thenReturn(targetMember); + when(userProfileAppConvertor.toUserProfileDTO(targetProfile)).thenReturn(targetProfileDTO); + when(teamMemberTargetService.mapMemberBillTarget( + eq(teamId), + eq(Collections.singleton(targetUserId)), + anyInt())).thenReturn(Map.of(targetUserId, target)); + + List result = exe.searchTeamMember(requesterUserId, "1018375"); + + assertEquals(1, result.size()); + assertEquals(targetUserId, result.get(0).getId()); + assertSame(targetProfileDTO, result.get(0).getUserProfile()); + assertEquals("425.28K", result.get(0).getTargetFormat()); + assertEquals("0.03", result.get(0).getTimeHours()); + assertEquals(1, result.get(0).getEffectiveDay()); + } +}