From c39dac772a1dbf627d0cef5c4c422827d2b337e1 Mon Sep 17 00:00:00 2001 From: hy001 Date: Thu, 25 Jun 2026 16:13:09 +0800 Subject: [PATCH] =?UTF-8?q?order=20=E5=92=8C=20console?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../endpoint/api/PayCountryClientApi.java | 30 +-- .../cmd/SysPayCountryCurrencyRateSyncCmd.java | 33 +++ .../dto/PayCountryCurrencyRateSyncDTO.java | 47 +++++ .../sys/pay/SysPayOpenCountryController.java | 33 ++- .../app/sys/pay/SysPayCountryServiceImpl.java | 31 +-- .../app/sys/pay/SysPayCountryService.java | 25 ++- .../web/strategy/PayPlaceAnOrderService.java | 58 ++++-- .../rds/service/pay/PayCountryService.java | 14 +- .../pay/impl/PayCountryServiceImpl.java | 55 +++-- .../endpoint/PayCountryClientEndpoint.java | 36 ++-- .../service/PayCountryClientService.java | 22 +- .../impl/PayCountryClientServiceImpl.java | 195 ++++++++++++++---- 12 files changed, 432 insertions(+), 147 deletions(-) create mode 100644 rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayCountryCurrencyRateSyncCmd.java create mode 100644 rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/dto/PayCountryCurrencyRateSyncDTO.java diff --git a/rc-service/rc-inner-api/order-inner/order-inner-api/src/main/java/com/red/circle/order/inner/endpoint/api/PayCountryClientApi.java b/rc-service/rc-inner-api/order-inner/order-inner-api/src/main/java/com/red/circle/order/inner/endpoint/api/PayCountryClientApi.java index 40450ad0..386a32aa 100644 --- a/rc-service/rc-inner-api/order-inner/order-inner-api/src/main/java/com/red/circle/order/inner/endpoint/api/PayCountryClientApi.java +++ b/rc-service/rc-inner-api/order-inner/order-inner-api/src/main/java/com/red/circle/order/inner/endpoint/api/PayCountryClientApi.java @@ -3,11 +3,13 @@ package com.red.circle.order.inner.endpoint.api; import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.ResultResponse; -import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; -import com.red.circle.order.inner.model.dto.PayCountryDTO; +import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; +import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO; +import com.red.circle.order.inner.model.dto.PayCountryDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; import java.math.BigDecimal; @@ -85,10 +87,14 @@ public interface PayCountryClientApi { @GetMapping("/updateSort") ResultResponse updateSort(@RequestParam("id") Long id, @RequestParam("sort") Integer sort); - @GetMapping("/shelf") - ResultResponse shelf(@RequestParam("id") Long id, @RequestParam("shelf") Boolean shelf); - - @GetMapping("/listPayCountryByRegionId") - ResultResponse> listPayCountryByRegionId( - @RequestParam("regionId") String regionId); -} + @GetMapping("/shelf") + ResultResponse shelf(@RequestParam("id") Long id, @RequestParam("shelf") Boolean shelf); + + @PostMapping("/syncCurrencyRates") + ResultResponse syncCurrencyRates( + @RequestBody SysPayCountryCurrencyRateSyncCmd cmd); + + @GetMapping("/listPayCountryByRegionId") + ResultResponse> listPayCountryByRegionId( + @RequestParam("regionId") String regionId); +} diff --git a/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayCountryCurrencyRateSyncCmd.java b/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayCountryCurrencyRateSyncCmd.java new file mode 100644 index 00000000..06ad1ea9 --- /dev/null +++ b/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/cmd/SysPayCountryCurrencyRateSyncCmd.java @@ -0,0 +1,33 @@ +package com.red.circle.order.inner.model.cmd; + +import com.red.circle.framework.core.dto.CommonCommand; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Digits; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 支付国家货币汇率同步命令. + * + * @author system + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class SysPayCountryCurrencyRateSyncCmd extends CommonCommand { + + private static final long serialVersionUID = 1L; + + /** + * 上浮比例,5 表示在实时汇率基础上增加 5%. + */ + @NotNull + @DecimalMin("0") + @DecimalMax("100") + @Digits(integer = 3, fraction = 4) + private BigDecimal markupPercent; +} diff --git a/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/dto/PayCountryCurrencyRateSyncDTO.java b/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/dto/PayCountryCurrencyRateSyncDTO.java new file mode 100644 index 00000000..0bd10222 --- /dev/null +++ b/rc-service/rc-inner-api/order-inner/order-inner-model/src/main/java/com/red/circle/order/inner/model/dto/PayCountryCurrencyRateSyncDTO.java @@ -0,0 +1,47 @@ +package com.red.circle.order.inner.model.dto; + +import com.red.circle.framework.dto.DTO; +import java.io.Serial; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 支付国家货币汇率同步结果. + * + * @author system + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class PayCountryCurrencyRateSyncDTO extends DTO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 参与同步的支付国家数量. + */ + private Integer totalCount; + + /** + * 已更新的支付国家数量. + */ + private Integer updatedCount; + + /** + * 跳过的支付国家数量. + */ + private Integer skippedCount; + + /** + * 汇率来源. + */ + private String sourceName; + + /** + * 被跳过的国家说明. + */ + private List skippedCountries; +} diff --git a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/sys/pay/SysPayOpenCountryController.java b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/sys/pay/SysPayOpenCountryController.java index 0681b4ea..0a346421 100644 --- a/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/sys/pay/SysPayOpenCountryController.java +++ b/rc-service/rc-service-console/console-adapter/src/main/java/com/red/circle/console/adapter/app/sys/pay/SysPayOpenCountryController.java @@ -4,10 +4,12 @@ package com.red.circle.console.adapter.app.sys.pay; import com.red.circle.console.app.service.app.sys.pay.SysPayCountryService; import com.red.circle.console.infra.annotations.OpsOperationReqLog; import com.red.circle.framework.dto.PageResult; -import com.red.circle.framework.web.controller.BaseController; -import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; -import com.red.circle.order.inner.model.dto.PayCountryDTO; +import com.red.circle.framework.web.controller.BaseController; +import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; +import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO; +import com.red.circle.order.inner.model.dto.PayCountryDTO; import java.math.BigDecimal; import java.util.HashSet; import java.util.List; @@ -111,10 +113,19 @@ public class SysPayOpenCountryController extends BaseController { */ @OpsOperationReqLog("修改国家上下架") @GetMapping("/shelf") - public void shelf(@RequestParam Long id, - @RequestParam Boolean shelf) { - sysPayCountryService.shelf(id, shelf); - } - - -} + public void shelf(@RequestParam Long id, + @RequestParam Boolean shelf) { + sysPayCountryService.shelf(id, shelf); + } + + /** + * 同步全球支付国家货币和实时汇率. + */ + @OpsOperationReqLog("同步全球支付国家货币汇率") + @PostMapping("/sync-currency-rates") + public PayCountryCurrencyRateSyncDTO syncCurrencyRates( + @RequestBody @Validated SysPayCountryCurrencyRateSyncCmd cmd) { + return sysPayCountryService.syncCurrencyRates(cmd); + } + +} diff --git a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryServiceImpl.java b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryServiceImpl.java index 7e6cacde..4c16ea56 100644 --- a/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryServiceImpl.java +++ b/rc-service/rc-service-console/console-application/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryServiceImpl.java @@ -2,10 +2,12 @@ package com.red.circle.console.app.service.app.sys.pay; import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.dto.PageResult; -import com.red.circle.order.inner.endpoint.PayCountryClient; -import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; -import com.red.circle.order.inner.model.dto.PayCountryDTO; +import com.red.circle.order.inner.endpoint.PayCountryClient; +import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; +import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO; +import com.red.circle.order.inner.model.dto.PayCountryDTO; import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -64,14 +66,19 @@ public class SysPayCountryServiceImpl implements SysPayCountryService { } @Override - public void shelf(Long id, Boolean shelf) { - ResponseAssert.requiredSuccess(payCountryClient.shelf(id, shelf)); - } - - @Override - public Map mapCountryByIds(Set ids) { - return ResponseAssert.requiredSuccess(payCountryClient.mapCountryByIds(ids)); - } + public void shelf(Long id, Boolean shelf) { + ResponseAssert.requiredSuccess(payCountryClient.shelf(id, shelf)); + } + + @Override + public PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd) { + return ResponseAssert.requiredSuccess(payCountryClient.syncCurrencyRates(cmd)); + } + + @Override + public Map mapCountryByIds(Set ids) { + return ResponseAssert.requiredSuccess(payCountryClient.mapCountryByIds(ids)); + } @Override public List listPayCountryByRegionId(String regionId) { diff --git a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryService.java b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryService.java index 9202c001..fa80f79f 100644 --- a/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryService.java +++ b/rc-service/rc-service-console/console-client/src/main/java/com/red/circle/console/app/service/app/sys/pay/SysPayCountryService.java @@ -1,9 +1,11 @@ package com.red.circle.console.app.service.app.sys.pay; import com.red.circle.framework.dto.PageResult; -import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; -import com.red.circle.order.inner.model.dto.PayCountryDTO; +import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; +import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO; +import com.red.circle.order.inner.model.dto.PayCountryDTO; import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -57,12 +59,17 @@ public interface SysPayCountryService { /** * 修改国家上下架状态. */ - void shelf(Long id, Boolean shelf); - - /** - * 根据id集合获得. - */ - Map mapCountryByIds(Set ids); + void shelf(Long id, Boolean shelf); + + /** + * 同步支付国家默认货币和实时美元汇率. + */ + PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd); + + /** + * 根据id集合获得. + */ + Map mapCountryByIds(Set ids); /** * 根据区域id集合获得支持支付的国家. diff --git a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/strategy/PayPlaceAnOrderService.java b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/strategy/PayPlaceAnOrderService.java index 10d25a64..465d6a4f 100644 --- a/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/strategy/PayPlaceAnOrderService.java +++ b/rc-service/rc-service-order/order-application/src/main/java/com/red/circle/order/app/command/pay/web/strategy/PayPlaceAnOrderService.java @@ -18,13 +18,18 @@ import com.red.circle.order.infra.database.rds.service.order.OrderUserPurchasePa import com.red.circle.order.inner.model.enums.inapp.InAppPurchaseStatus; import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.http.RcHttpClient; -import com.red.circle.tool.core.json.JacksonUtils; -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; +import com.red.circle.tool.core.json.JacksonUtils; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashMap; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; /** * @author pengliang on 2022/11/2 @@ -120,10 +125,31 @@ public class PayPlaceAnOrderService { * @param targetCurrency 目标币种(如CNY) * @return 汇率值 */ - public BigDecimal getRealTimeExchangeRate(String baseCurrency, String targetCurrency) { - try { - QuoteQueryParam param = new QuoteQueryParam(); - QuoteQueryParam.QuoteQueryData data = new QuoteQueryParam.QuoteQueryData(); + public BigDecimal getRealTimeExchangeRate(String baseCurrency, String targetCurrency) { + return getRealTimeExchangeRates(baseCurrency, List.of(targetCurrency)).get(targetCurrency); + } + + public Map getRealTimeExchangeRates(String baseCurrency, + Collection targetCurrencies) { + Map result = new HashMap<>(); + Optional.ofNullable(targetCurrencies).orElseGet(List::of).stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(currency -> !currency.isEmpty()) + .distinct() + .forEach(targetCurrency -> { + BigDecimal rate = queryRealTimeExchangeRate(baseCurrency, targetCurrency); + if (Objects.nonNull(rate)) { + result.put(targetCurrency, rate); + } + }); + return result; + } + + private BigDecimal queryRealTimeExchangeRate(String baseCurrency, String targetCurrency) { + try { + QuoteQueryParam param = new QuoteQueryParam(); + QuoteQueryParam.QuoteQueryData data = new QuoteQueryParam.QuoteQueryData(); // 币种对格式:基础币种+目标币种,如USDCNY String ccyPair = baseCurrency + targetCurrency; @@ -163,10 +189,10 @@ public class PayPlaceAnOrderService { return null; } } catch (Exception e) { - log.error("Error occurred while querying real-time exchange rate from {} to {}", - baseCurrency, targetCurrency, e); - return null; - } - } + log.error("Error occurred while querying real-time exchange rate from {} to {}", + baseCurrency, targetCurrency, e); + return null; + } + } } diff --git a/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/PayCountryService.java b/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/PayCountryService.java index a7d884cf..bed037e8 100644 --- a/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/PayCountryService.java +++ b/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/PayCountryService.java @@ -41,9 +41,11 @@ public interface PayCountryService extends BaseService { PageResult pageOpenPayCountry(SysPayCountryQryCmd query); - List listOpenPayCountry(Set ids); - - void delOpenPayCountry(List ids); + List listOpenPayCountry(Set ids); + + List listAllOpenPayCountry(); + + void delOpenPayCountry(List ids); void updateUsdExchangeRate(Long id, BigDecimal rate); @@ -51,5 +53,7 @@ public interface PayCountryService extends BaseService { void updateSort(Long id, Integer sort); - void shelf(Long id, Boolean shelf); -} + void shelf(Long id, Boolean shelf); + + boolean updateCurrencyRates(List countries); +} diff --git a/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/impl/PayCountryServiceImpl.java b/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/impl/PayCountryServiceImpl.java index c01583b9..9f8518c4 100644 --- a/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/impl/PayCountryServiceImpl.java +++ b/rc-service/rc-service-order/order-infrastructure/src/main/java/com/red/circle/order/infra/database/rds/service/pay/impl/PayCountryServiceImpl.java @@ -15,8 +15,9 @@ 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; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; /** *

@@ -96,14 +97,21 @@ public class PayCountryServiceImpl extends BaseServiceImpl listOpenPayCountry(Set ids) { - return query() - .in(CollectionUtils.isNotEmpty(ids), PayCountry::getId, ids) - .eq(PayCountry::getShelf, Boolean.TRUE) - .orderByDesc(PayCountry::getId) - .list(); - } - + public List listOpenPayCountry(Set ids) { + return query() + .in(CollectionUtils.isNotEmpty(ids), PayCountry::getId, ids) + .eq(PayCountry::getShelf, Boolean.TRUE) + .orderByDesc(PayCountry::getId) + .list(); + } + + @Override + public List listAllOpenPayCountry() { + return query() + .orderByDesc(PayCountry::getId) + .list(); + } + @Override public void delOpenPayCountry(List ids) { delete() @@ -139,12 +147,21 @@ public class PayCountryServiceImpl extends BaseServiceImpl countries) { + if (CollectionUtils.isEmpty(countries)) { + return Boolean.TRUE; + } + return updateBatchById(countries); + } + +} diff --git a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/endpoint/PayCountryClientEndpoint.java b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/endpoint/PayCountryClientEndpoint.java index 6b729cb0..2cf9d69f 100644 --- a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/endpoint/PayCountryClientEndpoint.java +++ b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/endpoint/PayCountryClientEndpoint.java @@ -3,11 +3,13 @@ package com.red.circle.order.inner.endpoint; import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.ResultResponse; import com.red.circle.order.inner.endpoint.api.PayCountryClientApi; -import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; -import com.red.circle.order.inner.model.dto.PayCountryDTO; +import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; +import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO; +import com.red.circle.order.inner.model.dto.PayCountryDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; import com.red.circle.order.inner.service.PayCountryClientService; @@ -133,14 +135,20 @@ public class PayCountryClientEndpoint implements PayCountryClientApi { } @Override - public ResultResponse shelf(Long id, Boolean shelf) { - payCountryClientService.shelf(id, shelf); - return ResultResponse.success(); - } - - @Override - public ResultResponse> listPayCountryByRegionId(String regionId) { - return ResultResponse.success(payCountryClientService.listPayCountryByRegionId(regionId)); - } + public ResultResponse shelf(Long id, Boolean shelf) { + payCountryClientService.shelf(id, shelf); + return ResultResponse.success(); + } + + @Override + public ResultResponse syncCurrencyRates( + SysPayCountryCurrencyRateSyncCmd cmd) { + return ResultResponse.success(payCountryClientService.syncCurrencyRates(cmd)); + } + + @Override + public ResultResponse> listPayCountryByRegionId(String regionId) { + return ResultResponse.success(payCountryClientService.listPayCountryByRegionId(regionId)); + } } diff --git a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/PayCountryClientService.java b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/PayCountryClientService.java index 0547d51c..3602c461 100644 --- a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/PayCountryClientService.java +++ b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/PayCountryClientService.java @@ -1,11 +1,13 @@ package com.red.circle.order.inner.service; import com.red.circle.framework.dto.PageResult; -import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; -import com.red.circle.order.inner.model.dto.PayCountryDTO; +import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; +import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO; +import com.red.circle.order.inner.model.dto.PayCountryDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; import java.math.BigDecimal; @@ -55,7 +57,9 @@ public interface PayCountryClientService { void updateSort(Long id, Integer sort); - void shelf(Long id, Boolean shelf); - - List listPayCountryByRegionId(String regionId); -} + void shelf(Long id, Boolean shelf); + + PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd); + + List listPayCountryByRegionId(String regionId); +} diff --git a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayCountryClientServiceImpl.java b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayCountryClientServiceImpl.java index 7f8bf4fb..183e652c 100644 --- a/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayCountryClientServiceImpl.java +++ b/rc-service/rc-service-order/order-inner-endpoint/src/main/java/com/red/circle/order/inner/service/impl/PayCountryClientServiceImpl.java @@ -1,38 +1,48 @@ package com.red.circle.order.inner.service.impl; -import com.alibaba.nacos.shaded.com.google.common.collect.Lists; -import com.google.api.client.util.Maps; -import com.red.circle.framework.core.asserts.ResponseAssert; -import com.red.circle.framework.dto.PageResult; -import com.red.circle.framework.mybatis.constant.PageConstant; -import com.red.circle.order.infra.database.rds.entity.pay.PayCountry; -import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannel; -import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannelDetails; +import com.alibaba.nacos.shaded.com.google.common.collect.Lists; +import com.google.api.client.util.Maps; +import com.red.circle.framework.core.response.CommonErrorCode; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.framework.dto.PageResult; +import com.red.circle.framework.mybatis.constant.PageConstant; +import com.red.circle.order.app.command.pay.web.strategy.PayPlaceAnOrderService; +import com.red.circle.order.infra.database.rds.entity.pay.PayCountry; +import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannel; +import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannelDetails; import com.red.circle.order.infra.database.rds.service.pay.PayCountryChannelDetailsService; import com.red.circle.order.infra.database.rds.service.pay.PayCountryChannelService; import com.red.circle.order.infra.database.rds.service.pay.PayCountryService; import com.red.circle.order.inner.convertor.WebPayInnerConvertor; -import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; -import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; -import com.red.circle.order.inner.model.dto.PayCountryDTO; -import com.red.circle.order.inner.model.dto.SysPayCountryChannelDTO; -import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; +import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd; +import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; +import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO; +import com.red.circle.order.inner.model.dto.PayCountryDTO; +import com.red.circle.order.inner.model.dto.SysPayCountryChannelDTO; +import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; import com.red.circle.order.inner.service.PayCountryClientService; 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.date.TimestampUtils; import com.red.circle.tool.core.text.StringUtils; -import com.red.circle.other.inner.enums.user.RegionRelationGroupEnum; -import com.red.circle.other.inner.endpoint.user.region.RegionRelationClient; -import java.math.BigDecimal; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; +import com.red.circle.other.inner.enums.user.RegionRelationGroupEnum; +import com.red.circle.other.inner.endpoint.user.region.RegionRelationClient; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Currency; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -42,16 +52,22 @@ import org.springframework.stereotype.Service; * @author pengliang on 2023/11/3 */ @Service -@RequiredArgsConstructor -public class PayCountryClientServiceImpl implements PayCountryClientService { - - private final PayCountryService payCountryService; - private final SysCountryCodeClient countryCodeClient; - private final WebPayInnerConvertor webPayInnerConvertor; - private final RegionRelationClient regionRelationClient; - private final SysCountryCodeClient sysCountryCodeClient; - private final PayCountryChannelService payCountryChannelService; - private final PayCountryChannelDetailsService payCountryChannelDetailsService; +@RequiredArgsConstructor +public class PayCountryClientServiceImpl implements PayCountryClientService { + + private static final String BASE_CURRENCY = "USD"; + private static final String SOURCE_NAME = "ISO4217/PayerMax"; + private static final BigDecimal HUNDRED = BigDecimal.valueOf(100); + private static final int RATE_SCALE = 8; + + private final PayCountryService payCountryService; + private final SysCountryCodeClient countryCodeClient; + private final WebPayInnerConvertor webPayInnerConvertor; + private final RegionRelationClient regionRelationClient; + private final SysCountryCodeClient sysCountryCodeClient; + private final PayCountryChannelService payCountryChannelService; + private final PayCountryChannelDetailsService payCountryChannelDetailsService; + private final PayPlaceAnOrderService payPlaceAnOrderService; @Override @@ -257,13 +273,112 @@ public class PayCountryClientServiceImpl implements PayCountryClientService { } @Override - public void shelf(Long id, Boolean shelf) { - payCountryService.shelf(id, shelf); - } - - - @Override - public List listPayCountryByRegionId(String regionId) { + public void shelf(Long id, Boolean shelf) { + payCountryService.shelf(id, shelf); + } + + @Override + public PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd) { + BigDecimal markupPercent = Optional.ofNullable(cmd) + .map(SysPayCountryCurrencyRateSyncCmd::getMarkupPercent) + .orElse(BigDecimal.ZERO); + BigDecimal markupMultiplier = BigDecimal.ONE.add( + markupPercent.divide(HUNDRED, RATE_SCALE, RoundingMode.HALF_UP)); + List payCountries = payCountryService.listAllOpenPayCountry(); + if (CollectionUtils.isEmpty(payCountries)) { + return new PayCountryCurrencyRateSyncDTO() + .setTotalCount(0) + .setUpdatedCount(0) + .setSkippedCount(0) + .setSourceName(SOURCE_NAME) + .setSkippedCountries(Lists.newArrayList()); + } + + Map countryMap = ResponseAssert.requiredSuccess( + sysCountryCodeClient.mapByIdes(payCountries.stream() + .map(PayCountry::getCountryId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()))); + Map currencyMap = Maps.newHashMap(); + List skippedCountries = new ArrayList<>(); + Set targetCurrencies = payCountries.stream() + .map(payCountry -> { + SysCountryCodeDTO country = countryMap.get(payCountry.getCountryId()); + String currency = resolveCountryCurrency(country); + if (StringUtils.isBlank(currency)) { + skippedCountries.add(formatSkippedCountry(payCountry, country, "未找到国家默认货币")); + return null; + } + currencyMap.put(payCountry.getId(), currency); + return currency; + }) + .filter(StringUtils::isNotBlank) + .filter(currency -> !Objects.equals(BASE_CURRENCY, currency)) + .collect(Collectors.toSet()); + + Map rateMap = Maps.newHashMap(); + rateMap.put(BASE_CURRENCY, BigDecimal.ONE); + rateMap.putAll(payPlaceAnOrderService.getRealTimeExchangeRates(BASE_CURRENCY, targetCurrencies)); + + List updates = payCountries.stream() + .map(payCountry -> buildSyncUpdate(payCountry, countryMap.get(payCountry.getCountryId()), + currencyMap.get(payCountry.getId()), rateMap, markupMultiplier, skippedCountries)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE, + payCountryService.updateCurrencyRates(updates)); + + return new PayCountryCurrencyRateSyncDTO() + .setTotalCount(payCountries.size()) + .setUpdatedCount(updates.size()) + .setSkippedCount(skippedCountries.size()) + .setSourceName(SOURCE_NAME) + .setSkippedCountries(skippedCountries); + } + + private PayCountry buildSyncUpdate(PayCountry payCountry, SysCountryCodeDTO country, + String currency, Map rateMap, BigDecimal markupMultiplier, + List skippedCountries) { + if (StringUtils.isBlank(currency)) { + return null; + } + BigDecimal rate = rateMap.get(currency); + if (Objects.isNull(rate) || rate.compareTo(BigDecimal.ZERO) <= 0) { + skippedCountries.add(formatSkippedCountry(payCountry, country, "未获取到实时汇率")); + return null; + } + payCountry.setCurrency(currency); + payCountry.setUsdExchangeRate(rate.multiply(markupMultiplier).setScale(RATE_SCALE, + RoundingMode.HALF_UP)); + payCountry.setUpdateTime(TimestampUtils.now()); + return payCountry; + } + + private String resolveCountryCurrency(SysCountryCodeDTO country) { + if (Objects.isNull(country) || StringUtils.isBlank(country.getAlphaTwo())) { + return ""; + } + try { + Locale locale = new Locale.Builder() + .setRegion(country.getAlphaTwo().trim().toUpperCase(Locale.ROOT)) + .build(); + return Currency.getInstance(locale).getCurrencyCode(); + } catch (IllegalArgumentException | MissingResourceException e) { + return ""; + } + } + + private String formatSkippedCountry(PayCountry payCountry, SysCountryCodeDTO country, + String reason) { + String countryName = Optional.ofNullable(country) + .map(item -> StringUtils.isNotBlank(item.getAliasName()) ? item.getAliasName() + : item.getAlphaTwo()) + .orElse(Objects.toString(payCountry.getCountryId(), "-")); + return countryName + "(" + payCountry.getId() + "): " + reason; + } + + @Override + public List listPayCountryByRegionId(String regionId) { Set relationIds = ResponseAssert.requiredSuccess(regionRelationClient .getRelationIdsByRegionId(regionId, RegionRelationGroupEnum.OPEN_PAY_COUNTRY.name()));