order 和 console

This commit is contained in:
hy001 2026-06-25 16:13:09 +08:00
parent 1826aa10ab
commit c39dac772a
12 changed files with 432 additions and 147 deletions

View File

@ -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.PageResult;
import com.red.circle.framework.dto.ResultResponse; import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; 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.SysPayCountryChannelCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; 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.cmd.SysPayCountryCurrencyRateSyncCmd;
import com.red.circle.order.inner.model.dto.PayCountryDTO; 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.SysPayCountryChannelDTO;
import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -85,10 +87,14 @@ public interface PayCountryClientApi {
@GetMapping("/updateSort") @GetMapping("/updateSort")
ResultResponse<Void> updateSort(@RequestParam("id") Long id, @RequestParam("sort") Integer sort); ResultResponse<Void> updateSort(@RequestParam("id") Long id, @RequestParam("sort") Integer sort);
@GetMapping("/shelf") @GetMapping("/shelf")
ResultResponse<Void> shelf(@RequestParam("id") Long id, @RequestParam("shelf") Boolean shelf); ResultResponse<Void> shelf(@RequestParam("id") Long id, @RequestParam("shelf") Boolean shelf);
@GetMapping("/listPayCountryByRegionId") @PostMapping("/syncCurrencyRates")
ResultResponse<List<PayCountryDTO>> listPayCountryByRegionId( ResultResponse<PayCountryCurrencyRateSyncDTO> syncCurrencyRates(
@RequestParam("regionId") String regionId); @RequestBody SysPayCountryCurrencyRateSyncCmd cmd);
}
@GetMapping("/listPayCountryByRegionId")
ResultResponse<List<PayCountryDTO>> listPayCountryByRegionId(
@RequestParam("regionId") String regionId);
}

View File

@ -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;
}

View File

@ -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<String> skippedCountries;
}

View File

@ -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.app.service.app.sys.pay.SysPayCountryService;
import com.red.circle.console.infra.annotations.OpsOperationReqLog; import com.red.circle.console.infra.annotations.OpsOperationReqLog;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.web.controller.BaseController; 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.SysPayCountryAddCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
import com.red.circle.order.inner.model.dto.PayCountryDTO; 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.math.BigDecimal;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -111,10 +113,19 @@ public class SysPayOpenCountryController extends BaseController {
*/ */
@OpsOperationReqLog("修改国家上下架") @OpsOperationReqLog("修改国家上下架")
@GetMapping("/shelf") @GetMapping("/shelf")
public void shelf(@RequestParam Long id, public void shelf(@RequestParam Long id,
@RequestParam Boolean shelf) { @RequestParam Boolean shelf) {
sysPayCountryService.shelf(id, shelf); sysPayCountryService.shelf(id, shelf);
} }
/**
} * 同步全球支付国家货币和实时汇率.
*/
@OpsOperationReqLog("同步全球支付国家货币汇率")
@PostMapping("/sync-currency-rates")
public PayCountryCurrencyRateSyncDTO syncCurrencyRates(
@RequestBody @Validated SysPayCountryCurrencyRateSyncCmd cmd) {
return sysPayCountryService.syncCurrencyRates(cmd);
}
}

View File

@ -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.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.order.inner.endpoint.PayCountryClient; 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.SysPayCountryAddCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
import com.red.circle.order.inner.model.dto.PayCountryDTO; 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.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -64,14 +66,19 @@ public class SysPayCountryServiceImpl implements SysPayCountryService {
} }
@Override @Override
public void shelf(Long id, Boolean shelf) { public void shelf(Long id, Boolean shelf) {
ResponseAssert.requiredSuccess(payCountryClient.shelf(id, shelf)); ResponseAssert.requiredSuccess(payCountryClient.shelf(id, shelf));
} }
@Override @Override
public Map<Long, PayCountryDTO> mapCountryByIds(Set<Long> ids) { public PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd) {
return ResponseAssert.requiredSuccess(payCountryClient.mapCountryByIds(ids)); return ResponseAssert.requiredSuccess(payCountryClient.syncCurrencyRates(cmd));
} }
@Override
public Map<Long, PayCountryDTO> mapCountryByIds(Set<Long> ids) {
return ResponseAssert.requiredSuccess(payCountryClient.mapCountryByIds(ids));
}
@Override @Override
public List<PayCountryDTO> listPayCountryByRegionId(String regionId) { public List<PayCountryDTO> listPayCountryByRegionId(String regionId) {

View File

@ -1,9 +1,11 @@
package com.red.circle.console.app.service.app.sys.pay; package com.red.circle.console.app.service.app.sys.pay;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; 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.cmd.SysPayCountryCurrencyRateSyncCmd;
import com.red.circle.order.inner.model.dto.PayCountryDTO; 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.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -57,12 +59,17 @@ public interface SysPayCountryService {
/** /**
* 修改国家上下架状态. * 修改国家上下架状态.
*/ */
void shelf(Long id, Boolean shelf); void shelf(Long id, Boolean shelf);
/** /**
* 根据id集合获得. * 同步支付国家默认货币和实时美元汇率.
*/ */
Map<Long, PayCountryDTO> mapCountryByIds(Set<Long> ids); PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd);
/**
* 根据id集合获得.
*/
Map<Long, PayCountryDTO> mapCountryByIds(Set<Long> ids);
/** /**
* 根据区域id集合获得支持支付的国家. * 根据区域id集合获得支持支付的国家.

View File

@ -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.order.inner.model.enums.inapp.InAppPurchaseStatus;
import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.http.RcHttpClient; import com.red.circle.tool.core.http.RcHttpClient;
import com.red.circle.tool.core.json.JacksonUtils; import com.red.circle.tool.core.json.JacksonUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Arrays; import java.util.Collection;
import java.util.List; import java.util.HashMap;
import lombok.RequiredArgsConstructor; import java.util.Arrays;
import lombok.extern.slf4j.Slf4j; import java.util.List;
import org.springframework.stereotype.Service; 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 * @author pengliang on 2022/11/2
@ -120,10 +125,31 @@ public class PayPlaceAnOrderService {
* @param targetCurrency 目标币种如CNY * @param targetCurrency 目标币种如CNY
* @return 汇率值 * @return 汇率值
*/ */
public BigDecimal getRealTimeExchangeRate(String baseCurrency, String targetCurrency) { public BigDecimal getRealTimeExchangeRate(String baseCurrency, String targetCurrency) {
try { return getRealTimeExchangeRates(baseCurrency, List.of(targetCurrency)).get(targetCurrency);
QuoteQueryParam param = new QuoteQueryParam(); }
QuoteQueryParam.QuoteQueryData data = new QuoteQueryParam.QuoteQueryData();
public Map<String, BigDecimal> getRealTimeExchangeRates(String baseCurrency,
Collection<String> targetCurrencies) {
Map<String, BigDecimal> 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 // 币种对格式基础币种+目标币种如USDCNY
String ccyPair = baseCurrency + targetCurrency; String ccyPair = baseCurrency + targetCurrency;
@ -163,10 +189,10 @@ public class PayPlaceAnOrderService {
return null; return null;
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Error occurred while querying real-time exchange rate from {} to {}", log.error("Error occurred while querying real-time exchange rate from {} to {}",
baseCurrency, targetCurrency, e); baseCurrency, targetCurrency, e);
return null; return null;
} }
} }
} }

View File

@ -41,9 +41,11 @@ public interface PayCountryService extends BaseService<PayCountry> {
PageResult<PayCountry> pageOpenPayCountry(SysPayCountryQryCmd query); PageResult<PayCountry> pageOpenPayCountry(SysPayCountryQryCmd query);
List<PayCountry> listOpenPayCountry(Set<Long> ids); List<PayCountry> listOpenPayCountry(Set<Long> ids);
void delOpenPayCountry(List<Long> ids); List<PayCountry> listAllOpenPayCountry();
void delOpenPayCountry(List<Long> ids);
void updateUsdExchangeRate(Long id, BigDecimal rate); void updateUsdExchangeRate(Long id, BigDecimal rate);
@ -51,5 +53,7 @@ public interface PayCountryService extends BaseService<PayCountry> {
void updateSort(Long id, Integer sort); void updateSort(Long id, Integer sort);
void shelf(Long id, Boolean shelf); void shelf(Long id, Boolean shelf);
}
boolean updateCurrencyRates(List<PayCountry> countries);
}

View File

@ -15,8 +15,9 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/** /**
* <p> * <p>
@ -96,14 +97,21 @@ public class PayCountryServiceImpl extends BaseServiceImpl<PayCountryDAO, PayCou
} }
@Override @Override
public List<PayCountry> listOpenPayCountry(Set<Long> ids) { public List<PayCountry> listOpenPayCountry(Set<Long> ids) {
return query() return query()
.in(CollectionUtils.isNotEmpty(ids), PayCountry::getId, ids) .in(CollectionUtils.isNotEmpty(ids), PayCountry::getId, ids)
.eq(PayCountry::getShelf, Boolean.TRUE) .eq(PayCountry::getShelf, Boolean.TRUE)
.orderByDesc(PayCountry::getId) .orderByDesc(PayCountry::getId)
.list(); .list();
} }
@Override
public List<PayCountry> listAllOpenPayCountry() {
return query()
.orderByDesc(PayCountry::getId)
.list();
}
@Override @Override
public void delOpenPayCountry(List<Long> ids) { public void delOpenPayCountry(List<Long> ids) {
delete() delete()
@ -139,12 +147,21 @@ public class PayCountryServiceImpl extends BaseServiceImpl<PayCountryDAO, PayCou
} }
@Override @Override
public void shelf(Long id, Boolean shelf) { public void shelf(Long id, Boolean shelf) {
update() update()
.set(PayCountry::getShelf, shelf) .set(PayCountry::getShelf, shelf)
.set(PayCountry::getUpdateTime, TimestampUtils.now()) .set(PayCountry::getUpdateTime, TimestampUtils.now())
.eq(PayCountry::getId, id) .eq(PayCountry::getId, id)
.execute(); .execute();
} }
} @Override
@Transactional(rollbackFor = Exception.class)
public boolean updateCurrencyRates(List<PayCountry> countries) {
if (CollectionUtils.isEmpty(countries)) {
return Boolean.TRUE;
}
return updateBatchById(countries);
}
}

View File

@ -3,11 +3,13 @@ package com.red.circle.order.inner.endpoint;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.dto.ResultResponse; import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.order.inner.endpoint.api.PayCountryClientApi; 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.SysPayCountryAddCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; 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.SysPayCountryChannelDetailsCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
import com.red.circle.order.inner.model.dto.PayCountryDTO; 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.SysPayCountryChannelDTO;
import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO;
import com.red.circle.order.inner.service.PayCountryClientService; import com.red.circle.order.inner.service.PayCountryClientService;
@ -133,14 +135,20 @@ public class PayCountryClientEndpoint implements PayCountryClientApi {
} }
@Override @Override
public ResultResponse<Void> shelf(Long id, Boolean shelf) { public ResultResponse<Void> shelf(Long id, Boolean shelf) {
payCountryClientService.shelf(id, shelf); payCountryClientService.shelf(id, shelf);
return ResultResponse.success(); return ResultResponse.success();
} }
@Override @Override
public ResultResponse<List<PayCountryDTO>> listPayCountryByRegionId(String regionId) { public ResultResponse<PayCountryCurrencyRateSyncDTO> syncCurrencyRates(
return ResultResponse.success(payCountryClientService.listPayCountryByRegionId(regionId)); SysPayCountryCurrencyRateSyncCmd cmd) {
} return ResultResponse.success(payCountryClientService.syncCurrencyRates(cmd));
}
@Override
public ResultResponse<List<PayCountryDTO>> listPayCountryByRegionId(String regionId) {
return ResultResponse.success(payCountryClientService.listPayCountryByRegionId(regionId));
}
} }

View File

@ -1,11 +1,13 @@
package com.red.circle.order.inner.service; package com.red.circle.order.inner.service;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd; 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.SysPayCountryChannelCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd; 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.cmd.SysPayCountryCurrencyRateSyncCmd;
import com.red.circle.order.inner.model.dto.PayCountryDTO; 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.SysPayCountryChannelDTO;
import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -55,7 +57,9 @@ public interface PayCountryClientService {
void updateSort(Long id, Integer sort); void updateSort(Long id, Integer sort);
void shelf(Long id, Boolean shelf); void shelf(Long id, Boolean shelf);
List<PayCountryDTO> listPayCountryByRegionId(String regionId); PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd);
}
List<PayCountryDTO> listPayCountryByRegionId(String regionId);
}

View File

@ -1,38 +1,48 @@
package com.red.circle.order.inner.service.impl; package com.red.circle.order.inner.service.impl;
import com.alibaba.nacos.shaded.com.google.common.collect.Lists; import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.google.api.client.util.Maps; import com.google.api.client.util.Maps;
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.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.mybatis.constant.PageConstant; import com.red.circle.framework.dto.PageResult;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountry; import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannel; import com.red.circle.order.app.command.pay.web.strategy.PayPlaceAnOrderService;
import com.red.circle.order.infra.database.rds.entity.pay.PayCountryChannelDetails; 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.PayCountryChannelDetailsService;
import com.red.circle.order.infra.database.rds.service.pay.PayCountryChannelService; 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.infra.database.rds.service.pay.PayCountryService;
import com.red.circle.order.inner.convertor.WebPayInnerConvertor; 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.SysPayCountryAddCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd; 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.SysPayCountryChannelDetailsCmd;
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd; import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
import com.red.circle.order.inner.model.dto.PayCountryDTO; import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd;
import com.red.circle.order.inner.model.dto.SysPayCountryChannelDTO; import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO;
import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO; 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.order.inner.service.PayCountryClientService;
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient; import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO; import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.text.StringUtils; import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.enums.user.RegionRelationGroupEnum; 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.RegionRelationClient;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.math.RoundingMode;
import java.util.Map; import java.util.ArrayList;
import java.util.Optional; import java.util.Currency;
import java.util.Set; import java.util.List;
import java.util.stream.Collectors; 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 lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -42,16 +52,22 @@ import org.springframework.stereotype.Service;
* @author pengliang on 2023/11/3 * @author pengliang on 2023/11/3
*/ */
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class PayCountryClientServiceImpl implements PayCountryClientService { public class PayCountryClientServiceImpl implements PayCountryClientService {
private final PayCountryService payCountryService; private static final String BASE_CURRENCY = "USD";
private final SysCountryCodeClient countryCodeClient; private static final String SOURCE_NAME = "ISO4217/PayerMax";
private final WebPayInnerConvertor webPayInnerConvertor; private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
private final RegionRelationClient regionRelationClient; private static final int RATE_SCALE = 8;
private final SysCountryCodeClient sysCountryCodeClient;
private final PayCountryChannelService payCountryChannelService; private final PayCountryService payCountryService;
private final PayCountryChannelDetailsService payCountryChannelDetailsService; 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 @Override
@ -257,13 +273,112 @@ public class PayCountryClientServiceImpl implements PayCountryClientService {
} }
@Override @Override
public void shelf(Long id, Boolean shelf) { public void shelf(Long id, Boolean shelf) {
payCountryService.shelf(id, shelf); payCountryService.shelf(id, shelf);
} }
@Override
@Override public PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd) {
public List<PayCountryDTO> listPayCountryByRegionId(String regionId) { 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<PayCountry> payCountries = payCountryService.listAllOpenPayCountry();
if (CollectionUtils.isEmpty(payCountries)) {
return new PayCountryCurrencyRateSyncDTO()
.setTotalCount(0)
.setUpdatedCount(0)
.setSkippedCount(0)
.setSourceName(SOURCE_NAME)
.setSkippedCountries(Lists.newArrayList());
}
Map<Long, SysCountryCodeDTO> countryMap = ResponseAssert.requiredSuccess(
sysCountryCodeClient.mapByIdes(payCountries.stream()
.map(PayCountry::getCountryId)
.filter(Objects::nonNull)
.collect(Collectors.toSet())));
Map<Long, String> currencyMap = Maps.newHashMap();
List<String> skippedCountries = new ArrayList<>();
Set<String> 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<String, BigDecimal> rateMap = Maps.newHashMap();
rateMap.put(BASE_CURRENCY, BigDecimal.ONE);
rateMap.putAll(payPlaceAnOrderService.getRealTimeExchangeRates(BASE_CURRENCY, targetCurrencies));
List<PayCountry> 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<String, BigDecimal> rateMap, BigDecimal markupMultiplier,
List<String> 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<PayCountryDTO> listPayCountryByRegionId(String regionId) {
Set<Long> relationIds = ResponseAssert.requiredSuccess(regionRelationClient Set<Long> relationIds = ResponseAssert.requiredSuccess(regionRelationClient
.getRelationIdsByRegionId(regionId, RegionRelationGroupEnum.OPEN_PAY_COUNTRY.name())); .getRelationIdsByRegionId(regionId, RegionRelationGroupEnum.OPEN_PAY_COUNTRY.name()));