feat(order): 添加订单货币转换功能

- 在CustomerOrder实体中添加原始货币、支付货币、汇率等相关字段
- 实现货币转换计算接口,支持自动转换不支持的货币到USD
- 添加货币转换信息更新服务方法
- 在订单创建时初始化货币转换相关字段
- 扩展订单响应DTO包含完整的货币转换信息
- 实现汇率锁定和转换记录功能
This commit is contained in:
2025-12-23 18:03:15 +08:00
parent ca5e88cdf1
commit 48eece45e5
7 changed files with 1873 additions and 2 deletions

View File

@@ -1331,3 +1331,179 @@ org.springframework.web.client.HttpClientErrorException$UnprocessableEntity: 422
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:842)
2025-12-23 16:27:00.878 [main] ERROR com.mtkj.mtkjpay.MtkjpayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:33)
2025-12-23 16:57:03.165 [main] ERROR com.mtkj.mtkjpay.MtkjpayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:33)
2025-12-23 17:06:54.545 [http-nio-8082-exec-4] ERROR com.mtkj.mtpay.exception.GlobalExceptionHandler - 系统异常
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:265)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:441)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:382)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:126)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:68)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:507)
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1283)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1065)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:842)
2025-12-23 17:07:40.917 [http-nio-8082-exec-7] ERROR com.mtkj.mtpay.exception.GlobalExceptionHandler - 系统异常
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:265)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:441)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:382)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:126)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:68)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:507)
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1283)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1065)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:842)
2025-12-23 17:10:22.913 [main] ERROR com.mtkj.mtkjpay.MtkjpayApplication -
╔══════════════════════════════════════════════════════════╗
║ ║
║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║
║ ║
╚══════════════════════════════════════════════════════════╝
org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:33)

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -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-已取消
*/

View File

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

View File

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