refactor(config): 重构配置管理并优化翻译功能

- 移除 @Component 注解,改用 @EnableConfigurationProperties 在启动类中启用配置
- 将PingPong支付配置从主配置文件移至开发环境配置文件
- 添加PayPal支付配置和百度翻译配置到开发环境
- 将商品和SKU名称翻译逻辑从查询时移至创建时,提高性能
- 移除运行时翻译方法,改为数据库中存储已翻译内容
- 标记PaymentController为过时,系统已切换到PayPal支付
- 优化pom.xml配置,添加classifier属性
This commit is contained in:
2025-12-24 17:39:36 +08:00
parent 425c46217e
commit 0f2f5c8630
10 changed files with 133 additions and 172 deletions

View File

@@ -1,43 +0,0 @@
CREATE TABLE `customer_order` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`order_no` VARCHAR(64) NOT NULL UNIQUE COMMENT '订单号(全局唯一)',
`product_id` BIGINT NOT NULL COMMENT '商品ID',
`product_name` VARCHAR(500) NOT NULL COMMENT '商品名称',
`sku_id` BIGINT NOT NULL COMMENT 'SKU ID',
`sku_name` VARCHAR(500) NOT NULL COMMENT 'SKU名称/描述',
`quantity` INT NOT NULL COMMENT '购买数量',
`unit_price` DECIMAL(10, 2) NOT NULL COMMENT '单价',
`total_amount` DECIMAL(10, 2) NOT NULL COMMENT '订单总金额',
`currency` VARCHAR(3) NOT NULL COMMENT '货币代码',
`status` VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT '订单状态PENDING-待支付PAID-已支付SHIPPED-已发货COMPLETED-已完成CANCELLED-已取消',
-- 客户信息
`customer_name` VARCHAR(100) NOT NULL COMMENT '客户姓名',
`customer_phone` VARCHAR(20) NOT NULL COMMENT '客户电话',
`customer_email` VARCHAR(100) COMMENT '客户邮箱',
-- 收货地址
`shipping_name` VARCHAR(100) NOT NULL COMMENT '收货人姓名',
`shipping_phone` VARCHAR(20) NOT NULL COMMENT '收货人电话',
`shipping_country` VARCHAR(50) NOT NULL COMMENT '收货国家',
`shipping_state` VARCHAR(50) COMMENT '收货州/省',
`shipping_city` VARCHAR(50) NOT NULL COMMENT '收货城市',
`shipping_street` VARCHAR(200) NOT NULL COMMENT '收货街道地址',
`shipping_postcode` VARCHAR(20) COMMENT '收货邮编',
-- 支付信息
`payment_order_id` BIGINT COMMENT '关联的支付订单ID',
`payment_status` VARCHAR(20) DEFAULT 'UNPAID' COMMENT '支付状态UNPAID-未支付PAID-已支付FAILED-支付失败',
-- 备注
`remark` VARCHAR(500) COMMENT '订单备注',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_order_no` (`order_no`),
KEY `idx_product_id` (`product_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户订单表';

View File

@@ -145,6 +145,7 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration> <configuration>
<classifier>boot</classifier>
<excludes> <excludes>
<exclude> <exclude>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>

View File

@@ -2,13 +2,12 @@ package com.mtkj.mtpay.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** /**
* 阿里云OSS配置属性 * 阿里云OSS配置属性
* 注意:不使用 @Component而是通过 @EnableConfigurationProperties 在启动类中启用
*/ */
@Data @Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss") @ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties { public class AliyunOSSProperties {

View File

@@ -2,13 +2,12 @@ package com.mtkj.mtpay.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/** /**
* PingPong支付配置属性 * PingPong支付配置属性
* 注意:不使用 @Component而是通过 @EnableConfigurationProperties 在启动类中启用
*/ */
@Data @Data
@Component
@ConfigurationProperties(prefix = "pingpong") @ConfigurationProperties(prefix = "pingpong")
public class PingPongProperties { public class PingPongProperties {

View File

@@ -16,11 +16,15 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
/** /**
* 支付控制器 * 支付控制器PingPong支付
*
* @deprecated 系统已切换到 PayPal 支付,此控制器保留以备将来需要时使用
* 如需重新启用,请取消注释 @RestController 和 @RequestMapping 注解
*/ */
@Deprecated
@Slf4j @Slf4j
@RestController // @RestController
@RequestMapping("/api/payment") // @RequestMapping("/api/payment")
@RequiredArgsConstructor @RequiredArgsConstructor
public class PaymentController { public class PaymentController {

View File

@@ -71,8 +71,30 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
CustomerOrder order = new CustomerOrder(); CustomerOrder order = new CustomerOrder();
order.setOrderNo(OrderIdGenerator.generateMerchantTransactionId()); order.setOrderNo(OrderIdGenerator.generateMerchantTransactionId());
order.setProductId(request.getProductId()); order.setProductId(request.getProductId());
order.setProductName(product.getName());
// 根据SKU的货币代码翻译商品名称并存入订单表
String currency = sku.getCurrency();
String translatedProductName = product.getName(); // 默认使用原始名称
if (currency != null && !currency.trim().isEmpty()) {
try {
String targetLanguage = baiduTranslatorUtils.getLanguageByCurrency(currency);
log.debug("订单货币: {}, 推断目标语言: {}, 原始商品名称: {}",
currency, targetLanguage, product.getName());
String translated = baiduTranslatorUtils.getTransResult(product.getName(), targetLanguage);
if (translated != null && !translated.equals(product.getName())) {
translatedProductName = translated;
log.info("商品名称翻译: {} -> {} (货币: {}, 语言: {})",
product.getName(), translatedProductName, currency, targetLanguage);
}
} catch (Exception e) {
log.warn("翻译商品名称失败使用原始名称商品ID: {}, 货币: {}",
request.getProductId(), currency, e);
}
}
order.setProductName(translatedProductName);
order.setSkuId(request.getSkuId()); order.setSkuId(request.getSkuId());
// SKU名称已在创建商品时翻译并存入数据库直接使用即可
order.setSkuName(sku.getSku()); order.setSkuName(sku.getSku());
order.setQuantity(request.getQuantity()); order.setQuantity(request.getQuantity());
order.setUnitPrice(sku.getPrice()); order.setUnitPrice(sku.getPrice());
@@ -161,8 +183,7 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
// 将JSON特殊字段转换为Map // 将JSON特殊字段转换为Map
response.setShippingSpecialFields(order.getShippingSpecialFieldsMap()); response.setShippingSpecialFields(order.getShippingSpecialFieldsMap());
// 翻译订单内容(商品名称和SKU名称 // 注意:商品名称和SKU名称已在创建时翻译并存入数据库,查询时直接返回即可
translateOrderContent(response);
return response; return response;
} }
@@ -185,8 +206,7 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
// 将JSON特殊字段转换为Map // 将JSON特殊字段转换为Map
response.setShippingSpecialFields(order.getShippingSpecialFieldsMap()); response.setShippingSpecialFields(order.getShippingSpecialFieldsMap());
// 翻译订单内容(商品名称和SKU名称 // 注意:商品名称和SKU名称已在创建时翻译并存入数据库,查询时直接返回即可
translateOrderContent(response);
return response; return response;
} }
@@ -207,8 +227,7 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
// 将JSON特殊字段转换为Map // 将JSON特殊字段转换为Map
response.setShippingSpecialFields(order.getShippingSpecialFieldsMap()); response.setShippingSpecialFields(order.getShippingSpecialFieldsMap());
// 翻译订单内容(商品名称和SKU名称 // 注意:商品名称和SKU名称已在创建时翻译并存入数据库,查询时直接返回即可
translateOrderContent(response);
return response; return response;
} }
@@ -288,47 +307,5 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
log.info("订单状态更新成功,订单号: {}", orderNo); log.info("订单状态更新成功,订单号: {}", orderNo);
} }
/**
* 翻译订单内容商品名称和SKU名称
* 根据订单的货币代码推断目标语言
*/
private void translateOrderContent(CustomerOrderResponseDTO orderDTO) {
try {
// 如果没有货币信息,不进行翻译
String currency = orderDTO.getCurrency();
if (currency == null || currency.trim().isEmpty()) {
// 尝试使用支付货币
currency = orderDTO.getPaymentCurrency();
if (currency == null || currency.trim().isEmpty()) {
log.debug("订单货币代码为空,跳过翻译,订单号: {}", orderDTO.getOrderNo());
return;
}
}
String targetLanguage = baiduTranslatorUtils.getLanguageByCurrency(currency);
log.debug("根据货币代码 {} 推断目标语言: {}, 订单号: {}", currency, targetLanguage, orderDTO.getOrderNo());
// 翻译商品名称
if (orderDTO.getProductName() != null && !orderDTO.getProductName().trim().isEmpty()) {
String translatedName = baiduTranslatorUtils.getTransResult(orderDTO.getProductName(), targetLanguage);
if (translatedName != null && !translatedName.equals(orderDTO.getProductName())) {
log.debug("商品名称翻译: {} -> {}, 订单号: {}", orderDTO.getProductName(), translatedName, orderDTO.getOrderNo());
orderDTO.setProductName(translatedName);
}
}
// 翻译SKU名称
if (orderDTO.getSkuName() != null && !orderDTO.getSkuName().trim().isEmpty()) {
String translatedSku = baiduTranslatorUtils.getTransResult(orderDTO.getSkuName(), targetLanguage);
if (translatedSku != null && !translatedSku.equals(orderDTO.getSkuName())) {
log.debug("SKU名称翻译: {} -> {}, 订单号: {}", orderDTO.getSkuName(), translatedSku, orderDTO.getOrderNo());
orderDTO.setSkuName(translatedSku);
}
}
} catch (Exception e) {
log.error("翻译订单内容失败,订单号: {}", orderDTO.getOrderNo(), e);
// 翻译失败不影响订单数据返回,只记录日志
}
}
} }

View File

@@ -70,6 +70,7 @@ public class ProductServiceImpl implements ProductService {
// 创建商品 // 创建商品
MtProduct product = new MtProduct(); MtProduct product = new MtProduct();
// 商品名称存储原始名称(不翻译,因为一个商品可能对应多个国家/货币)
product.setName(request.getName()); product.setName(request.getName());
product.setPrice(request.getPrice()); product.setPrice(request.getPrice());
@@ -102,13 +103,34 @@ public class ProductServiceImpl implements ProductService {
} }
log.debug("商品基础信息插入成功商品ID: {}", product.getId()); log.debug("商品基础信息插入成功商品ID: {}", product.getId());
// 创建SKU // 创建SKU(在存入数据库前完成翻译)
if (request.getSkus() != null && !request.getSkus().isEmpty()) { if (request.getSkus() != null && !request.getSkus().isEmpty()) {
log.debug("开始创建SKU数量: {}", request.getSkus().size()); log.debug("开始创建SKU数量: {}", request.getSkus().size());
for (CreateProductRequestDTO.CreateProductSkuDTO skuDTO : request.getSkus()) { for (CreateProductRequestDTO.CreateProductSkuDTO skuDTO : request.getSkus()) {
MtProductSku sku = new MtProductSku(); MtProductSku sku = new MtProductSku();
sku.setProductId(product.getId()); sku.setProductId(product.getId());
sku.setSku(skuDTO.getSku());
// 根据SKU的货币代码推断目标语言并翻译SKU名称
String currency = skuDTO.getCurrency();
if (currency != null && !currency.trim().isEmpty()) {
String targetLanguage = baiduTranslatorUtils.getLanguageByCurrency(currency);
log.debug("SKU货币: {}, 推断目标语言: {}, 原始SKU名称: {}",
currency, targetLanguage, skuDTO.getSku());
// 翻译SKU名称
String originalSkuName = skuDTO.getSku();
String translatedSkuName = baiduTranslatorUtils.getTransResult(originalSkuName, targetLanguage);
if (translatedSkuName != null && !translatedSkuName.equals(originalSkuName)) {
log.info("SKU名称翻译: {} -> {} (货币: {}, 语言: {})",
originalSkuName, translatedSkuName, currency, targetLanguage);
sku.setSku(translatedSkuName); // 存储翻译后的名称
} else {
sku.setSku(originalSkuName); // 翻译失败或无需翻译,使用原文
}
} else {
sku.setSku(skuDTO.getSku()); // 无货币信息,使用原文
}
sku.setPrice(skuDTO.getPrice()); sku.setPrice(skuDTO.getPrice());
sku.setCurrency(skuDTO.getCurrency()); sku.setCurrency(skuDTO.getCurrency());
sku.setStock(skuDTO.getStock()); sku.setStock(skuDTO.getStock());
@@ -125,7 +147,7 @@ public class ProductServiceImpl implements ProductService {
throw new BusinessException(ResultCode.SYSTEM_ERROR, "创建SKU失败"); throw new BusinessException(ResultCode.SYSTEM_ERROR, "创建SKU失败");
} }
log.debug("SKU创建成功商品ID: {}, SKU编码: {}, SKU ID: {}", log.debug("SKU创建成功商品ID: {}, SKU编码: {}, SKU ID: {}",
product.getId(), skuDTO.getSku(), sku.getId()); product.getId(), sku.getSku(), sku.getId());
} }
} }
@@ -207,8 +229,8 @@ public class ProductServiceImpl implements ProductService {
response.setSkus(skuDTOs); response.setSkus(skuDTOs);
// 翻译功能根据SKU的货币代码推断目标语言并翻译 // 注意商品名称和SKU名称已在创建时翻译并存入数据库查询时直接返回即可
translateProductContent(response); // 如果需要根据当前用户的语言再次翻译,可以在这里添加逻辑
log.info("获取商品详情成功商品ID: {}, 商品名称: {}, SKU数量: {}, 主图数量: {}", log.info("获取商品详情成功商品ID: {}, 商品名称: {}, SKU数量: {}, 主图数量: {}",
id, product.getName(), skuDTOs.size(), id, product.getName(), skuDTOs.size(),
@@ -314,8 +336,7 @@ public class ProductServiceImpl implements ProductService {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
dto.setSkus(skuDTOs); dto.setSkus(skuDTOs);
// 翻译功能根据SKU的货币代码推断目标语言并翻译 // 注意商品名称和SKU名称已在创建时翻译并存入数据库查询时直接返回即可
translateProductContent(dto);
result.add(dto); result.add(dto);
} }
@@ -324,58 +345,5 @@ public class ProductServiceImpl implements ProductService {
return result; return result;
} }
/**
* 翻译商品内容商品名称和SKU名称
* 根据SKU的货币代码推断目标语言
*/
private void translateProductContent(ProductResponseDTO productDTO) {
try {
// 如果没有SKU不进行翻译
if (productDTO.getSkus() == null || productDTO.getSkus().isEmpty()) {
return;
}
// 根据第一个SKU的货币代码推断目标语言
String firstCurrency = productDTO.getSkus().get(0).getCurrency();
if (firstCurrency == null || firstCurrency.trim().isEmpty()) {
log.debug("SKU货币代码为空跳过翻译商品ID: {}", productDTO.getId());
return;
}
String targetLanguage = baiduTranslatorUtils.getLanguageByCurrency(firstCurrency);
log.debug("根据货币代码 {} 推断目标语言: {}, 商品ID: {}", firstCurrency, targetLanguage, productDTO.getId());
// 翻译商品名称
if (productDTO.getName() != null && !productDTO.getName().trim().isEmpty()) {
String translatedName = baiduTranslatorUtils.getTransResult(productDTO.getName(), targetLanguage);
if (translatedName != null && !translatedName.equals(productDTO.getName())) {
log.debug("商品名称翻译: {} -> {}, 商品ID: {}", productDTO.getName(), translatedName, productDTO.getId());
productDTO.setName(translatedName);
}
}
// 翻译每个SKU的名称sku字段
for (ProductResponseDTO.ProductSkuResponseDTO skuDTO : productDTO.getSkus()) {
// 如果SKU的货币与第一个不同使用该SKU的货币推断语言
String skuCurrency = skuDTO.getCurrency();
String skuLanguage = targetLanguage;
if (skuCurrency != null && !skuCurrency.equals(firstCurrency)) {
skuLanguage = baiduTranslatorUtils.getLanguageByCurrency(skuCurrency);
}
// 翻译SKU名称
if (skuDTO.getSku() != null && !skuDTO.getSku().trim().isEmpty()) {
String translatedSku = baiduTranslatorUtils.getTransResult(skuDTO.getSku(), skuLanguage);
if (translatedSku != null && !translatedSku.equals(skuDTO.getSku())) {
log.debug("SKU名称翻译: {} -> {}, SKU ID: {}", skuDTO.getSku(), translatedSku, skuDTO.getId());
skuDTO.setSku(translatedSku);
}
}
}
} catch (Exception e) {
log.error("翻译商品内容失败商品ID: {}", productDTO.getId(), e);
// 翻译失败不影响商品数据返回,只记录日志
}
}
} }

View File

@@ -1,8 +1,12 @@
package com.mtkj.mtkjpay; package com.mtkj.mtkjpay;
import com.mtkj.mtpay.config.AliyunOSSProperties;
import com.mtkj.mtpay.config.BaiduTranslatorConfig;
import com.mtkj.mtpay.config.PingPongProperties;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
/** /**
@@ -16,6 +20,11 @@ import org.springframework.core.env.Environment;
com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure.class com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure.class
} }
) )
@EnableConfigurationProperties({
PingPongProperties.class,
BaiduTranslatorConfig.class,
AliyunOSSProperties.class
})
public class MtkjpayApplication { public class MtkjpayApplication {
public static void main(String[] args) { public static void main(String[] args) {

View File

@@ -60,8 +60,65 @@ spring:
config: config:
multi-statement-allow: true multi-statement-allow: true
# 服务器配置(所有环境通用)
server:
port: 8082
servlet:
context-path: /
# 文件上传配置
multipart:
# 单个文件最大大小10MB
max-file-size: 10MB
# 请求最大大小50MB支持多文件上传
max-request-size: 50MB
# 文件写入磁盘的阈值(超过此大小会写入临时文件)
file-size-threshold: 2MB
# 应用配置(所有环境通用)
app:
# 前端访问地址用于生成商品详情页URL等
frontend:
url: http://localhost:3000
# 阿里云OSS相关配置所有环境通用
aliyun:
oss:
accessId: LTAI5tHbwvzWfANvNxju2yN1
accessKey: sAQR2swByBgmMOofH97hSJT638aVcJ
endpoint: https://oss-cn-hangzhou.aliyuncs.com
bucketName: mtkj2025
# PingPong支付配置开发环境使用沙箱 # PingPong支付配置开发环境使用沙箱
pingpong: pingpong:
client-id: your-client-id
acc-id: your-acc-id
secret: your-secret-key
sign-type: MD5
gateway: https://sandbox-acquirer-payment.pingpongx.com gateway: https://sandbox-acquirer-payment.pingpongx.com
mode: sandbox mode: sandbox
enabled: false
# PayPal支付配置开发环境使用沙箱
paypal:
# PayPal Client IDAPI密钥
client-id: AdGYUZpvLuHR30dybOApvM-RNB1pVKtd74SVfh-6TK52xV-1JEBddHVMCWuDdyyHri4DXd4kABBi7Icb
# PayPal Client Secret密钥
client-secret: ENblspyRmwsOU_PWFurlhEYUF5Da6aYKl0pjK4ehm7p3R5aSqvbpaF_YsIIs8v0ty1c9WJu15XP-Fe_1
# 环境模式sandbox沙箱或 production生产
mode: sandbox
# 是否启用PayPal支付
enabled: true
# 百度翻译配置
baidu:
translator:
app-id: 20250909002450241
securityKey: o08lOpCcarl4Pb0BGdkq
transApiHost: https://fanyi-api.baidu.com/api/trans/vip/translate
qianfan:
app-key: ALTAKUyxxqjXU9YhPNYXj8zFQ2
app-secret: 2c43ca701e1641b0a945a3a65125c143
app_id: 9ae03450-0036-4c61-93bc-659a95cea443
Authorization: Bearer bce-v3/ALTAK-9pcPkK0ZLYIDyDJNtVwji/f32d29d61a86e6e4e8c60d5297a310538b3920a7
url: https://qianfan.baidubce.com/v2/app/conversation/runs

View File

@@ -61,17 +61,7 @@ spring:
# 逻辑未删除值 # 逻辑未删除值
logic-not-delete-value: 0 logic-not-delete-value: 0
# PingPong支付配置 # 服务器配置(所有环境通用)
pingpong:
client-id: your-client-id
acc-id: your-acc-id
secret: your-secret-key
sign-type: MD5
gateway: https://sandbox-acquirer-payment.pingpongx.com
mode: sandbox
enabled: true
# 服务器配置
server: server:
port: 8082 port: 8082
servlet: servlet:
@@ -85,13 +75,13 @@ server:
# 文件写入磁盘的阈值(超过此大小会写入临时文件) # 文件写入磁盘的阈值(超过此大小会写入临时文件)
file-size-threshold: 2MB file-size-threshold: 2MB
# 应用配置 # 应用配置(所有环境通用)
app: app:
# 前端访问地址用于生成商品详情页URL等 # 前端访问地址用于生成商品详情页URL等
frontend: frontend:
url: http://localhost:3000 url: http://localhost:3000
# 阿里云OSS相关配置 # 阿里云OSS相关配置(所有环境通用)
aliyun: aliyun:
oss: oss:
accessId: LTAI5tHbwvzWfANvNxju2yN1 accessId: LTAI5tHbwvzWfANvNxju2yN1