feat(order): 扩展订单地址字段并集成百度翻译服务

- 添加东南亚地址扩展字段到CreateCustomerOrderRequestDTO
- 在CustomerOrder实体类中新增详细地址字段和特殊地址字段JSON存储
- 实现CustomerOrderServiceImpl中地址字段的存储和转换逻辑
- 集成BaiduTranslatorUtils实现订单内容自动翻译功能
- 在CustomerOrderResponseDTO中添加特殊地址字段Map格式支持
- 配置百度翻译API相关参数到application-dev.yml
- 移除过时的架构文档和配置说明文件
This commit is contained in:
2025-12-24 11:19:48 +08:00
parent 48eece45e5
commit 425c46217e
25 changed files with 490 additions and 2415 deletions

View File

@@ -1,5 +1,6 @@
package com.mtkj.mtpay;
import com.mtkj.mtpay.config.BaiduTranslatorConfig;
import com.mtkj.mtpay.config.PingPongProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
@@ -9,7 +10,7 @@ import org.springframework.core.env.Environment;
@Slf4j
@SpringBootApplication
@EnableConfigurationProperties({PingPongProperties.class})
@EnableConfigurationProperties({PingPongProperties.class, BaiduTranslatorConfig.class})
public class MtPayApplication {
public static void main(String[] args) {

View File

@@ -62,6 +62,79 @@ public class CreateCustomerOrderRequestDTO implements Serializable {
@Size(max = 20, message = "收货邮编长度不能超过20")
private String shippingPostcode;
// 东南亚地址扩展字段
/**
* 详细地址1门牌号、街道、楼栋
*/
@Size(max = 200, message = "详细地址1长度不能超过200")
private String shippingAddressLine1;
/**
* 详细地址2楼层、单元号可选
*/
@Size(max = 200, message = "详细地址2长度不能超过200")
private String shippingAddressLine2;
/**
* 行政区域(如新加坡的"社区"、菲律宾的"Barangay"
*/
@Size(max = 100, message = "行政区域长度不能超过100")
private String shippingAdministrativeArea;
/**
* 组屋号(新加坡,如 Blk 123
*/
@Size(max = 50, message = "组屋号长度不能超过50")
private String shippingBlockNumber;
/**
* 单元号(新加坡,如 #01-234
*/
@Size(max = 50, message = "单元号长度不能超过50")
private String shippingUnitNumber;
/**
* Barangay菲律宾社区编号
*/
@Size(max = 100, message = "Barangay长度不能超过100")
private String shippingBarangay;
/**
* 泰文地址(泰国,支持双语)
*/
@Size(max = 500, message = "泰文地址长度不能超过500")
private String shippingAddressThai;
/**
* 省(越南)
*/
@Size(max = 100, message = "省长度不能超过100")
private String shippingProvince;
/**
* 市/郡(越南)
*/
@Size(max = 100, message = "市/郡长度不能超过100")
private String shippingDistrict;
/**
* 区/坊(越南)
*/
@Size(max = 100, message = "区/坊长度不能超过100")
private String shippingWard;
/**
* 州属(马来西亚,如 Selangor
*/
@Size(max = 100, message = "州属长度不能超过100")
private String shippingStateMalaysia;
/**
* 楼层/单元/代收点(补充信息)
*/
@Size(max = 100, message = "楼层/单元/代收点长度不能超过100")
private String shippingFloorUnit;
@Size(max = 500, message = "订单备注长度不能超过500")
private String remark;
}

View File

@@ -42,6 +42,17 @@ public class CustomerOrderResponseDTO implements Serializable {
private String shippingCity;
private String shippingStreet;
private String shippingPostcode;
// 东南亚地址扩展字段
private String shippingAddressLine1;
private String shippingAddressLine2;
/**
* 特殊地址字段Map格式从JSON解析
* 包含blockNumber, unitNumber, barangay, addressThai, province, district, ward, stateMalaysia, administrativeArea, floorUnit
*/
private java.util.Map<String, Object> shippingSpecialFields;
private Long paymentOrderId;
private String paymentStatus;
private String remark;

View File

@@ -1,18 +1,26 @@
package com.mtkj.mtpay.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* 客户订单实体类
*/
@Slf4j
@TableName(value = "customer_order")
@Data
public class CustomerOrder {
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 主键ID
*/
@@ -175,6 +183,37 @@ public class CustomerOrder {
@TableField(value = "shipping_postcode", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR)
private String shippingPostcode;
/**
* 详细地址1门牌号、街道、楼栋
*/
@TableField(value = "shipping_address_line1", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR)
private String shippingAddressLine1;
/**
* 详细地址2楼层、单元号可选
*/
@TableField(value = "shipping_address_line2", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR)
private String shippingAddressLine2;
/**
* 特殊地址字段JSON格式存储各国特殊字段
* 示例:
* {
* "blockNumber": "Blk 123", // 新加坡:组屋号
* "unitNumber": "#01-234", // 新加坡:单元号
* "barangay": "Barangay 12", // 菲律宾:社区编号
* "addressThai": "123 ถนนสุขุมวิท", // 泰国:泰文地址
* "province": "Thành phố Hồ Chí Minh", // 越南:省
* "district": "Quận 1", // 越南:市/郡
* "ward": "Phường Bến Nghé", // 越南:区/坊
* "stateMalaysia": "Selangor", // 马来西亚:州属
* "administrativeArea": "Tambon XXX", // 泰国Tambon
* "floorUnit": "5楼单元A" // 通用:楼层/单元/代收点
* }
*/
@TableField(value = "shipping_special_fields", jdbcType = org.apache.ibatis.type.JdbcType.VARCHAR)
private String shippingSpecialFields;
/**
* 关联的支付订单ID
*/
@@ -204,5 +243,59 @@ public class CustomerOrder {
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 获取特殊字段Map从JSON字符串解析
*/
public Map<String, Object> getShippingSpecialFieldsMap() {
if (shippingSpecialFields == null || shippingSpecialFields.trim().isEmpty()) {
return new HashMap<>();
}
try {
return objectMapper.readValue(shippingSpecialFields,
new TypeReference<Map<String, Object>>() {});
} catch (Exception e) {
log.warn("解析特殊字段JSON失败: {}", shippingSpecialFields, e);
return new HashMap<>();
}
}
/**
* 设置特殊字段Map转换为JSON字符串
*/
public void setShippingSpecialFieldsMap(Map<String, Object> map) {
if (map == null || map.isEmpty()) {
this.shippingSpecialFields = null;
return;
}
try {
this.shippingSpecialFields = objectMapper.writeValueAsString(map);
} catch (Exception e) {
log.error("序列化特殊字段为JSON失败", e);
this.shippingSpecialFields = "{}";
}
}
/**
* 获取特殊字段值
*/
public String getSpecialField(String key) {
Map<String, Object> map = getShippingSpecialFieldsMap();
Object value = map.get(key);
return value != null ? value.toString() : null;
}
/**
* 设置特殊字段值
*/
public void setSpecialField(String key, String value) {
Map<String, Object> map = getShippingSpecialFieldsMap();
if (value != null && !value.trim().isEmpty()) {
map.put(key, value);
} else {
map.remove(key);
}
setShippingSpecialFieldsMap(map);
}
}

View File

@@ -12,12 +12,17 @@ import com.mtkj.mtpay.mapper.CustomerOrderMapper;
import com.mtkj.mtpay.mapper.MtProductMapper;
import com.mtkj.mtpay.mapper.MtProductSkuMapper;
import com.mtkj.mtpay.service.CustomerOrderService;
import com.mtkj.mtpay.util.BaiduTranslatorUtils;
import com.mtkj.mtpay.util.OrderIdGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.math.BigDecimal;
@@ -32,6 +37,7 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
private final CustomerOrderMapper customerOrderMapper;
private final MtProductMapper productMapper;
private final MtProductSkuMapper productSkuMapper;
private final BaiduTranslatorUtils baiduTranslatorUtils;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -90,7 +96,7 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
order.setCustomerPhone(request.getCustomerPhone());
order.setCustomerEmail(request.getCustomerEmail());
// 收货地址
// 收货地址(基础字段)
order.setShippingName(request.getShippingName());
order.setShippingPhone(request.getShippingPhone());
order.setShippingCountry(request.getShippingCountry());
@@ -98,6 +104,45 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
order.setShippingCity(request.getShippingCity());
order.setShippingStreet(request.getShippingStreet());
order.setShippingPostcode(request.getShippingPostcode());
// 收货地址(通用扩展字段)
order.setShippingAddressLine1(request.getShippingAddressLine1());
order.setShippingAddressLine2(request.getShippingAddressLine2());
// 特殊字段转换为Map并存储到JSON
Map<String, Object> specialFields = new HashMap<>();
if (StringUtils.hasText(request.getShippingBlockNumber())) {
specialFields.put("blockNumber", request.getShippingBlockNumber());
}
if (StringUtils.hasText(request.getShippingUnitNumber())) {
specialFields.put("unitNumber", request.getShippingUnitNumber());
}
if (StringUtils.hasText(request.getShippingBarangay())) {
specialFields.put("barangay", request.getShippingBarangay());
}
if (StringUtils.hasText(request.getShippingAddressThai())) {
specialFields.put("addressThai", request.getShippingAddressThai());
}
if (StringUtils.hasText(request.getShippingProvince())) {
specialFields.put("province", request.getShippingProvince());
}
if (StringUtils.hasText(request.getShippingDistrict())) {
specialFields.put("district", request.getShippingDistrict());
}
if (StringUtils.hasText(request.getShippingWard())) {
specialFields.put("ward", request.getShippingWard());
}
if (StringUtils.hasText(request.getShippingStateMalaysia())) {
specialFields.put("stateMalaysia", request.getShippingStateMalaysia());
}
if (StringUtils.hasText(request.getShippingAdministrativeArea())) {
specialFields.put("administrativeArea", request.getShippingAdministrativeArea());
}
if (StringUtils.hasText(request.getShippingFloorUnit())) {
specialFields.put("floorUnit", request.getShippingFloorUnit());
}
order.setShippingSpecialFieldsMap(specialFields);
order.setRemark(request.getRemark());
// 保存订单
@@ -113,6 +158,12 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
CustomerOrderResponseDTO response = new CustomerOrderResponseDTO();
BeanUtils.copyProperties(order, response);
// 将JSON特殊字段转换为Map
response.setShippingSpecialFields(order.getShippingSpecialFieldsMap());
// 翻译订单内容商品名称和SKU名称
translateOrderContent(response);
return response;
}
@@ -130,6 +181,13 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
CustomerOrderResponseDTO response = new CustomerOrderResponseDTO();
BeanUtils.copyProperties(order, response);
// 将JSON特殊字段转换为Map
response.setShippingSpecialFields(order.getShippingSpecialFieldsMap());
// 翻译订单内容商品名称和SKU名称
translateOrderContent(response);
return response;
}
@@ -145,6 +203,13 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
CustomerOrderResponseDTO response = new CustomerOrderResponseDTO();
BeanUtils.copyProperties(order, response);
// 将JSON特殊字段转换为Map
response.setShippingSpecialFields(order.getShippingSpecialFieldsMap());
// 翻译订单内容商品名称和SKU名称
translateOrderContent(response);
return response;
}
@@ -222,5 +287,48 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
customerOrderMapper.updateById(order);
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

@@ -14,6 +14,7 @@ import com.mtkj.mtpay.exception.BusinessException;
import com.mtkj.mtpay.mapper.MtProductMapper;
import com.mtkj.mtpay.mapper.MtProductSkuMapper;
import com.mtkj.mtpay.service.ProductService;
import com.mtkj.mtpay.util.BaiduTranslatorUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,6 +46,9 @@ public class ProductServiceImpl implements ProductService {
@Autowired
private com.mtkj.mtpay.service.ProductLinkService productLinkService;
@Autowired
private BaiduTranslatorUtils baiduTranslatorUtils;
@Value("${server.port:8082}")
private String serverPort;
@@ -203,6 +207,9 @@ public class ProductServiceImpl implements ProductService {
response.setSkus(skuDTOs);
// 翻译功能根据SKU的货币代码推断目标语言并翻译
translateProductContent(response);
log.info("获取商品详情成功商品ID: {}, 商品名称: {}, SKU数量: {}, 主图数量: {}",
id, product.getName(), skuDTOs.size(),
response.getMainImages() != null ? response.getMainImages().size() : 0);
@@ -307,11 +314,68 @@ public class ProductServiceImpl implements ProductService {
}).collect(Collectors.toList());
dto.setSkus(skuDTOs);
// 翻译功能根据SKU的货币代码推断目标语言并翻译
translateProductContent(dto);
result.add(dto);
}
log.info("获取商品列表成功,商品数量: {}", result.size());
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

@@ -110,4 +110,17 @@ paypal:
# 是否启用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