feat(order): 添加订单货币转换功能
- 在CustomerOrder实体中添加原始货币、支付货币、汇率等相关字段 - 实现货币转换计算接口,支持自动转换不支持的货币到USD - 添加货币转换信息更新服务方法 - 在订单创建时初始化货币转换相关字段 - 扩展订单响应DTO包含完整的货币转换信息 - 实现汇率锁定和转换记录功能
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
package com.mtkj.mtpay.controller;
|
||||
|
||||
import com.mtkj.mtpay.common.Result;
|
||||
import com.mtkj.mtpay.dto.request.CalculateCurrencyConversionRequestDTO;
|
||||
import com.mtkj.mtpay.dto.request.CreateCustomerOrderRequestDTO;
|
||||
import com.mtkj.mtpay.dto.response.CurrencyConversionDTO;
|
||||
import com.mtkj.mtpay.dto.response.CustomerOrderResponseDTO;
|
||||
import com.mtkj.mtpay.service.CustomerOrderService;
|
||||
import com.mtkj.mtpay.service.ExchangeRateService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -19,6 +22,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
public class CustomerOrderController {
|
||||
|
||||
private final CustomerOrderService customerOrderService;
|
||||
private final ExchangeRateService exchangeRateService;
|
||||
|
||||
/**
|
||||
* 创建客户订单
|
||||
@@ -30,8 +34,84 @@ public class CustomerOrderController {
|
||||
return Result.success("订单创建成功", order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算并更新订单的货币转换信息
|
||||
* 在订单确认页面加载时调用,提前计算货币转换信息
|
||||
* 注意:这个路由必须放在 /{orderNo} 之前,避免路由冲突
|
||||
*/
|
||||
@PostMapping("/calculate-currency-conversion")
|
||||
public Result<CurrencyConversionDTO> calculateCurrencyConversion(
|
||||
@Valid @RequestBody CalculateCurrencyConversionRequestDTO request) {
|
||||
log.info("计算订单货币转换信息,订单号:{},原始货币:{},原始金额:{}",
|
||||
request.getOrderNo(), request.getOriginalCurrency(), request.getOriginalAmount());
|
||||
|
||||
// 检查货币是否需要转换
|
||||
java.util.Set<String> supportedCurrencies = java.util.Set.of(
|
||||
"USD", "EUR", "GBP", "AUD", "CAD", "JPY", "CNY", "HKD", "SGD", "NZD",
|
||||
"CHF", "SEK", "NOK", "DKK", "PLN", "MXN", "BRL", "INR", "KRW", "THB"
|
||||
);
|
||||
|
||||
String originalCurrency = request.getOriginalCurrency();
|
||||
String paymentCurrency = originalCurrency;
|
||||
java.math.BigDecimal paymentAmount = request.getOriginalAmount();
|
||||
java.math.BigDecimal exchangeRate = java.math.BigDecimal.ONE;
|
||||
boolean conversionRequired = false;
|
||||
|
||||
// 如果货币不支持,转换为USD
|
||||
if (!supportedCurrencies.contains(originalCurrency)) {
|
||||
conversionRequired = true;
|
||||
paymentCurrency = "USD";
|
||||
|
||||
// 计算汇率和转换后的金额
|
||||
Double rate = exchangeRateService.getExchangeRate(originalCurrency, paymentCurrency);
|
||||
Double convertedAmount = exchangeRateService.convertAmount(
|
||||
request.getOriginalAmount().doubleValue(),
|
||||
originalCurrency,
|
||||
paymentCurrency
|
||||
);
|
||||
|
||||
exchangeRate = java.math.BigDecimal.valueOf(rate);
|
||||
paymentAmount = java.math.BigDecimal.valueOf(convertedAmount);
|
||||
|
||||
log.info("货币转换:{} {} -> {} {} (汇率: {})",
|
||||
request.getOriginalAmount(), originalCurrency,
|
||||
paymentAmount, paymentCurrency, exchangeRate);
|
||||
}
|
||||
|
||||
// 构建货币转换DTO
|
||||
CurrencyConversionDTO conversionDTO = CurrencyConversionDTO.builder()
|
||||
.originalCurrency(originalCurrency)
|
||||
.originalAmount(request.getOriginalAmount())
|
||||
.paymentCurrency(paymentCurrency)
|
||||
.paymentAmount(paymentAmount)
|
||||
.exchangeRate(exchangeRate)
|
||||
.rateLockedAt(java.time.LocalDateTime.now())
|
||||
.conversionRequired(conversionRequired)
|
||||
.rateDescription(buildRateDescription(originalCurrency, paymentCurrency, exchangeRate.doubleValue()))
|
||||
.build();
|
||||
|
||||
// 更新订单的货币转换信息
|
||||
try {
|
||||
customerOrderService.updateCurrencyConversion(
|
||||
request.getOrderNo(),
|
||||
originalCurrency,
|
||||
request.getOriginalAmount(),
|
||||
paymentCurrency,
|
||||
paymentAmount,
|
||||
exchangeRate
|
||||
);
|
||||
log.info("订单货币转换信息已更新,订单号:{}", request.getOrderNo());
|
||||
} catch (Exception e) {
|
||||
log.warn("更新订单货币转换信息失败,订单号:{}", request.getOrderNo(), e);
|
||||
// 不抛出异常,仍然返回计算结果
|
||||
}
|
||||
|
||||
return Result.success("货币转换信息计算成功", conversionDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单号获取订单详情
|
||||
* 注意:这个路由必须放在 /calculate-currency-conversion 之后,避免路由冲突
|
||||
*/
|
||||
@GetMapping("/{orderNo}")
|
||||
public Result<CustomerOrderResponseDTO> getOrderByOrderNo(@PathVariable String orderNo) {
|
||||
@@ -49,5 +129,15 @@ public class CustomerOrderController {
|
||||
CustomerOrderResponseDTO order = customerOrderService.getOrderById(id);
|
||||
return Result.success(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建汇率说明文本
|
||||
*/
|
||||
private String buildRateDescription(String fromCurrency, String toCurrency, Double rate) {
|
||||
if (fromCurrency.equals(toCurrency)) {
|
||||
return "无需货币转换";
|
||||
}
|
||||
return String.format("1 %s = %.6f %s", fromCurrency, rate, toCurrency);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,15 @@ public class CustomerOrderResponseDTO implements Serializable {
|
||||
private BigDecimal unitPrice;
|
||||
private BigDecimal totalAmount;
|
||||
private String currency;
|
||||
|
||||
// 货币转换信息
|
||||
private String originalCurrency; // 原始货币代码
|
||||
private BigDecimal originalAmount; // 原始订单金额
|
||||
private String paymentCurrency; // 实际支付货币代码
|
||||
private BigDecimal paymentAmount; // 实际支付金额
|
||||
private BigDecimal exchangeRate; // 使用的汇率
|
||||
private LocalDateTime rateLockedAt; // 汇率锁定时间
|
||||
|
||||
private String status;
|
||||
private String customerName;
|
||||
private String customerPhone;
|
||||
|
||||
@@ -68,11 +68,47 @@ public class CustomerOrder {
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
/**
|
||||
* 货币代码
|
||||
* 货币代码(保留字段,兼容旧数据)
|
||||
*/
|
||||
@TableField(value = "currency", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR)
|
||||
private String currency;
|
||||
|
||||
/**
|
||||
* 原始货币代码(客户选择的货币)
|
||||
*/
|
||||
@TableField(value = "original_currency", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR)
|
||||
private String originalCurrency;
|
||||
|
||||
/**
|
||||
* 原始订单金额(原始货币)
|
||||
*/
|
||||
@TableField(value = "original_amount", jdbcType = org.apache.ibatis.type.JdbcType.DECIMAL)
|
||||
private BigDecimal originalAmount;
|
||||
|
||||
/**
|
||||
* 实际支付货币代码(PayPal支持的货币,如USD)
|
||||
*/
|
||||
@TableField(value = "payment_currency", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR)
|
||||
private String paymentCurrency;
|
||||
|
||||
/**
|
||||
* 实际支付金额(支付货币)
|
||||
*/
|
||||
@TableField(value = "payment_amount", jdbcType = org.apache.ibatis.type.JdbcType.DECIMAL)
|
||||
private BigDecimal paymentAmount;
|
||||
|
||||
/**
|
||||
* 使用的汇率(原始货币 -> 支付货币)
|
||||
*/
|
||||
@TableField(value = "exchange_rate", jdbcType = org.apache.ibatis.type.JdbcType.DECIMAL)
|
||||
private BigDecimal exchangeRate;
|
||||
|
||||
/**
|
||||
* 汇率锁定时间
|
||||
*/
|
||||
@TableField(value = "rate_locked_at", jdbcType = org.apache.ibatis.type.JdbcType.TIMESTAMP)
|
||||
private LocalDateTime rateLockedAt;
|
||||
|
||||
/**
|
||||
* 订单状态:PENDING-待支付,PAID-已支付,SHIPPED-已发货,COMPLETED-已完成,CANCELLED-已取消
|
||||
*/
|
||||
|
||||
@@ -43,5 +43,20 @@ public interface CustomerOrderService {
|
||||
* @param status 订单状态
|
||||
*/
|
||||
void updateOrderStatus(String orderNo, String status);
|
||||
|
||||
/**
|
||||
* 更新订单货币转换信息
|
||||
* @param orderNo 订单号
|
||||
* @param originalCurrency 原始货币代码
|
||||
* @param originalAmount 原始金额
|
||||
* @param paymentCurrency 支付货币代码
|
||||
* @param paymentAmount 支付金额
|
||||
* @param exchangeRate 汇率
|
||||
*/
|
||||
void updateCurrencyConversion(String orderNo, String originalCurrency,
|
||||
java.math.BigDecimal originalAmount,
|
||||
String paymentCurrency,
|
||||
java.math.BigDecimal paymentAmount,
|
||||
java.math.BigDecimal exchangeRate);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,8 +70,18 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
|
||||
order.setSkuName(sku.getSku());
|
||||
order.setQuantity(request.getQuantity());
|
||||
order.setUnitPrice(sku.getPrice());
|
||||
order.setTotalAmount(sku.getPrice().multiply(new BigDecimal(request.getQuantity())));
|
||||
BigDecimal totalAmount = sku.getPrice().multiply(new BigDecimal(request.getQuantity()));
|
||||
order.setTotalAmount(totalAmount);
|
||||
order.setCurrency(sku.getCurrency());
|
||||
|
||||
// 设置原始货币信息(创建订单时的货币)
|
||||
order.setOriginalCurrency(sku.getCurrency());
|
||||
order.setOriginalAmount(totalAmount);
|
||||
// 支付货币和金额将在创建PayPal订单时设置
|
||||
order.setPaymentCurrency(sku.getCurrency()); // 默认与原始货币相同
|
||||
order.setPaymentAmount(totalAmount); // 默认与原始金额相同
|
||||
order.setExchangeRate(BigDecimal.ONE); // 默认汇率为1
|
||||
|
||||
order.setStatus("PENDING");
|
||||
order.setPaymentStatus("UNPAID");
|
||||
|
||||
@@ -165,6 +175,37 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
|
||||
log.info("订单支付状态更新成功,订单号: {}", orderNo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCurrencyConversion(String orderNo, String originalCurrency,
|
||||
BigDecimal originalAmount,
|
||||
String paymentCurrency,
|
||||
BigDecimal paymentAmount,
|
||||
BigDecimal exchangeRate) {
|
||||
log.info("更新订单货币转换信息,订单号: {}, 原始: {} {}, 支付: {} {} (汇率: {})",
|
||||
orderNo, originalAmount, originalCurrency, paymentAmount, paymentCurrency, exchangeRate);
|
||||
|
||||
CustomerOrder order = customerOrderMapper.selectOne(
|
||||
new LambdaQueryWrapper<CustomerOrder>()
|
||||
.eq(CustomerOrder::getOrderNo, orderNo)
|
||||
);
|
||||
|
||||
if (order == null) {
|
||||
log.warn("订单不存在,订单号: {}", orderNo);
|
||||
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在");
|
||||
}
|
||||
|
||||
// 更新货币转换信息
|
||||
order.setOriginalCurrency(originalCurrency);
|
||||
order.setOriginalAmount(originalAmount);
|
||||
order.setPaymentCurrency(paymentCurrency);
|
||||
order.setPaymentAmount(paymentAmount);
|
||||
order.setExchangeRate(exchangeRate);
|
||||
order.setRateLockedAt(java.time.LocalDateTime.now());
|
||||
|
||||
customerOrderMapper.updateById(order);
|
||||
log.info("订单货币转换信息更新成功,订单号: {}", orderNo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderStatus(String orderNo, String status) {
|
||||
log.info("更新订单状态,订单号: {}, 状态: {}", orderNo, status);
|
||||
|
||||
Reference in New Issue
Block a user