diff --git a/FIX_COMPILE.md b/FIX_COMPILE.md deleted file mode 100644 index 7880574..0000000 --- a/FIX_COMPILE.md +++ /dev/null @@ -1,108 +0,0 @@ -# 修复编译问题指南 - -## 问题描述 -IDE提示"Java file is located outside of the module source root, so it won't be compiled" - -## 解决步骤 - -### 方法1:在 IntelliJ IDEA 中重新导入项目(推荐) - -1. **关闭项目** - - File → Close Project - -2. **重新打开项目** - - File → Open - - 选择 `E:\MTKJPAY` 目录 - - 选择 "Open as Project" - -3. **等待 Maven 导入完成** - - 右下角会显示 "Importing Maven projects..." - - 等待完成 - -4. **刷新 Maven 项目** - - 右键根目录项目 → Maven → Reload Project - - 或者:View → Tool Windows → Maven → 点击刷新按钮 - -5. **重新构建项目** - - Build → Rebuild Project - -### 方法2:手动配置 Source Root - -1. **打开项目结构** - - File → Project Structure (Ctrl+Alt+Shift+S) - -2. **检查 Modules** - - 左侧选择 "Modules" - - 确认有两个模块: - - `MTKJPAY` (根模块) - - `mt-pay` (子模块) - -3. **配置 MTKJPAY 模块** - - 选择 `MTKJPAY` 模块 - - 在 "Sources" 标签页 - - 确认 `src/main/java` 标记为蓝色(Source Folders) - - 确认 `src/main/resources` 标记为绿色(Resources Folders) - - 如果没有标记,右键文件夹 → Mark Directory as → Sources Root / Resources Root - -4. **配置 mt-pay 模块** - - 选择 `mt-pay` 模块 - - 在 "Sources" 标签页 - - 确认 `mt-pay/src/main/java` 标记为蓝色 - - 确认 `mt-pay/src/main/resources` 标记为绿色 - -5. **应用并确定** - - 点击 "Apply" → "OK" - -### 方法3:使用 Maven 命令编译 - -```bash -# 进入项目根目录 -cd E:\MTKJPAY - -# 清理并编译 -mvn clean compile - -# 或者安装到本地仓库 -mvn clean install -``` - -### 方法4:检查 IDE 设置 - -1. **检查 Maven 设置** - - File → Settings → Build, Execution, Deployment → Build Tools → Maven - - 确认 "Maven home directory" 正确 - - 确认 "User settings file" 正确 - -2. **检查 Java 设置** - - File → Settings → Build, Execution, Deployment → Compiler → Java Compiler - - 确认 "Project bytecode version" 是 17 - -3. **检查项目 SDK** - - File → Project Structure → Project - - 确认 "SDK" 是 Java 17 - - 确认 "Language level" 是 17 - -## 验证修复 - -编译成功后: -1. 在 IDE 中,Java 文件不应该有红色波浪线 -2. 可以正常启动 `MtkjpayApplication` -3. 控制台没有编译错误 - -## 如果仍然有问题 - -1. **删除 .idea 文件夹**(需要关闭项目) - - 关闭 IntelliJ IDEA - - 删除 `E:\MTKJPAY\.idea` 文件夹 - - 重新打开项目 - -2. **删除 target 文件夹** - ```bash - cd E:\MTKJPAY - rmdir /s /q target - rmdir /s /q mt-pay\target - ``` - -3. **重新导入项目** - - 按照方法1重新导入 - diff --git a/HOW_TO_START.md b/HOW_TO_START.md deleted file mode 100644 index d69c6f5..0000000 --- a/HOW_TO_START.md +++ /dev/null @@ -1,121 +0,0 @@ -# 如何正确启动后端服务 - -## ⚠️ 重要:启动正确的模块 - -项目有两个启动类,**必须启动 `mt-pay` 模块的启动类**: - -### ✅ 正确的启动类 -- **类名**:`com.mtkj.mtpay.MtPayApplication` -- **位置**:`MTKJPAY/mt-pay/src/main/java/com/mtkj/mtpay/MtPayApplication.java` -- **端口**:8082 -- **包含**:所有业务代码、控制器、服务等 - -### ❌ 错误的启动类(不要启动这个!) -- **类名**:`com.mtkj.mtkjpay.MtkjpayApplication` -- **位置**:`MTKJPAY/src/main/java/com/mtkj/mtkjpay/MtkjpayApplication.java` -- **端口**:8080 -- **包含**:仅占位代码,无业务功能 - -## 在 IntelliJ IDEA 中启动 - -### 步骤1:确认项目结构 -确保在 Project 视图中能看到 `mt-pay` 模块: -``` -MTKJPAY - ├── mt-pay ← 这个模块 - │ └── src - │ └── main - │ └── java - │ └── com - │ └── mtkj - │ └── mtpay - │ └── MtPayApplication.java ← 启动这个 - └── src ← 根目录(不要启动这个) - └── main - └── java - └── com - └── mtkj - └── mtkjpay - └── MtkjpayApplication.java ← 不要启动这个 -``` - -### 步骤2:打开正确的启动类 -1. 在 Project 视图中,导航到: - `MTKJPAY` → `mt-pay` → `src` → `main` → `java` → `com` → `mtkj` → `mtpay` → `MtPayApplication.java` - -2. 双击打开 `MtPayApplication.java` - -### 步骤3:运行启动类 -1. 右键点击 `MtPayApplication.java` 文件 -2. 选择 **"Run 'MtPayApplication.main()'"** -3. 或者点击类名旁边的绿色运行按钮 - -### 步骤4:确认启动成功 -启动成功后,控制台会显示: -``` -╔══════════════════════════════════════════════════════════╗ -║ ║ -║ ✅ MTKJ PAY 支付系统启动成功! ✅ ║ -║ ║ -╠══════════════════════════════════════════════════════════╣ -║ 应用名称: mt-pay ║ -║ 运行环境: dev ║ -║ 服务端口: 8082 ║ -║ 后端服务: http://localhost:8082/ ║ -║ API接口: http://localhost:8082/api ║ -║ 状态: 🟢 服务运行中,可以接收请求 ║ -╚══════════════════════════════════════════════════════════╝ -``` - -## 使用 Maven 命令启动 - -```bash -# 进入 mt-pay 模块目录 -cd E:\MTKJPAY\mt-pay - -# 启动应用 -mvn spring-boot:run -``` - -## 验证启动成功 - -1. **检查端口**: - ```bash - netstat -ano | findstr :8082 - ``` - 应该能看到端口 8082 在监听 - -2. **访问接口**: - 在浏览器访问:http://localhost:8082/api/product/1 - 如果返回 JSON 响应,说明启动成功 - -3. **查看日志**: - 控制台应该显示启动成功的标识框 - -## 常见错误 - -### 错误1:启动的是根目录的 Application -- **症状**:端口是 8080,没有业务功能 -- **解决**:确保启动的是 `mt-pay` 模块的 `MtPayApplication` - -### 错误2:找不到启动类 -- **症状**:IDE 中找不到 `MtPayApplication` -- **解决**: - 1. 确认 `mt-pay` 模块已正确导入 - 2. 刷新 Maven 项目:右键项目 → Maven → Reload Project - 3. 重新构建项目:Build → Rebuild Project - -### 错误3:端口被占用 -- **症状**:启动失败,提示端口 8082 被占用 -- **解决**: - 1. 查找占用进程:`netstat -ano | findstr :8082` - 2. 结束进程或修改端口配置 - -## 快速检查清单 - -- [ ] 启动的是 `com.mtkj.mtpay.MtPayApplication` -- [ ] 不是 `com.mtkj.mtkjpay.MtkjpayApplication` -- [ ] 端口是 8082(不是 8080) -- [ ] 看到启动成功的标识框 -- [ ] 可以访问 http://localhost:8082/api/product/1 - diff --git a/PAYPAL_CONFIG_CHECK.md b/PAYPAL_CONFIG_CHECK.md deleted file mode 100644 index f3c8568..0000000 --- a/PAYPAL_CONFIG_CHECK.md +++ /dev/null @@ -1,68 +0,0 @@ -# PayPal配置检查指南 - -## 错误信息 -``` -Username must not be null -``` - -这个错误表示PayPal的`clientId`(Client ID)配置为空。 - -## 问题原因 - -Spring Boot的`@ConfigurationProperties`在绑定配置时,需要确保: -1. 配置文件中的属性名格式正确 -2. 配置类能够正确加载 -3. 配置文件被正确读取 - -## 解决方案 - -### 1. 检查配置文件 - -确保 `application-dev.yml` 中有以下配置: - -```yaml -paypal: - client-id: AdGYUZpvLuHR30dybOApvM-RNB1pVKtd74SVfh-6TK52xV-1JEBddHVMCWuDdyyHri4DXd4kABBi7Icb - client-secret: ENblspyRmwsOU_PWFurlhEYUF5Da6aYKl0pjK4ehm7p3R5aSqvbpaF_YsIIs8v0ty1c9WJu15XP-Fe_1 - mode: sandbox - enabled: true -``` - -### 2. 检查Spring Boot配置 - -确保主应用类或配置类启用了配置属性绑定。 - -### 3. 重启应用 - -修改配置后,**必须重启应用**才能生效。 - -### 4. 验证配置加载 - -启动应用后,检查日志中是否有PayPal配置相关的错误信息。 - -## 配置属性映射 - -Spring Boot会自动将以下格式进行映射: -- `client-id` (kebab-case) → `clientId` (camelCase) -- `client-secret` (kebab-case) → `clientSecret` (camelCase) - -## 调试方法 - -如果配置仍然无法加载,可以: - -1. **添加启动日志**:在`PayPalProperties`类中添加`@PostConstruct`方法打印配置值 -2. **检查环境变量**:确认使用的是`dev`环境(`spring.profiles.active=dev`) -3. **检查配置文件位置**:确保`application-dev.yml`在`src/main/resources`目录下 - -## 临时解决方案 - -如果配置仍然无法加载,可以在代码中临时硬编码(仅用于测试): - -```java -// 仅用于测试,生产环境必须使用配置文件 -if (clientId == null) { - clientId = "AdGYUZpvLuHR30dybOApvM-RNB1pVKtd74SVfh-6TK52xV-1JEBddHVMCWuDdyyHri4DXd4kABBi7Icb"; - clientSecret = "ENblspyRmwsOU_PWFurlhEYUF5Da6aYKl0pjK4ehm7p3R5aSqvbpaF_YsIIs8v0ty1c9WJu15XP-Fe_1"; -} -``` - diff --git a/PORT_CONFIG.md b/PORT_CONFIG.md deleted file mode 100644 index 5e30cbf..0000000 --- a/PORT_CONFIG.md +++ /dev/null @@ -1,46 +0,0 @@ -# 端口配置说明 - -## 端口分配 - -- **前端(Vite开发服务器)**: `3000` -- **后端(Spring Boot应用)**: `8082` - -## 访问地址 - -- **前端访问地址**: `http://localhost:3000` -- **后端API地址**: `http://localhost:8082` -- **Druid监控**: `http://localhost:8082/druid` - -## 代理配置 - -前端通过 Vite 代理将 `/api/*` 请求转发到后端: - -```javascript -// vite.config.js -proxy: { - '/api': { - target: 'http://127.0.0.1:8082', - changeOrigin: true - } -} -``` - -## 启动顺序 - -1. **启动后端**:运行 `MtPayApplication`,监听 `8082` 端口 -2. **启动前端**:运行 `npm run dev`,监听 `3000` 端口 - -## 配置文件位置 - -### 前端 -- `MTKJPAY-FRONT/vite.config.js` - Vite配置,端口 3000 - -### 后端 -- `MTKJPAY/mt-pay/src/main/resources/application.yml` - 主配置,端口 8082 - -## 注意事项 - -1. 确保两个端口都没有被其他程序占用 -2. 前端启动后会自动通过代理访问后端 -3. 如果修改端口,需要同步更新相关配置 - diff --git a/START_BACKEND.md b/START_BACKEND.md deleted file mode 100644 index 6aebc25..0000000 --- a/START_BACKEND.md +++ /dev/null @@ -1,64 +0,0 @@ -# 启动后端服务 - -## ⚠️ 重要提示 - -**请启动 `mt-startup` 模块的 `MtkjpayApplication`,这是项目的唯一启动类!** - -- ✅ 正确:`com.mtkj.mtkjpay.MtkjpayApplication` (mt-startup 模块,唯一启动类) -- 启动类位置:`MTKJPAY/mt-startup/src/main/java/com/mtkj/mtkjpay/MtkjpayApplication.java` - -## 问题诊断 - -如果前端出现 `connect ECONNREFUSED ::1:8082` 错误,说明后端服务没有启动。 - -## 启动步骤 - -### 方法1:使用 IDE 启动(推荐) - -1. **打开根目录项目 `MTKJPAY`** -2. 找到 `mt-startup/src/main/java/com/mtkj/mtkjpay/MtkjpayApplication.java` 文件 -3. 右键点击文件,选择 **"Run 'MtkjpayApplication.main()'"** -4. 确认启动的是 `com.mtkj.mtkjpay.MtkjpayApplication`(来自 mt-startup 模块) -4. 等待启动完成,看到以下日志表示启动成功: - ``` - ╔══════════════════════════════════════════════════════════╗ - ║ ║ - ║ ✅ MTKJ PAY 支付系统启动成功! ✅ ║ - ║ ║ - ╠══════════════════════════════════════════════════════════╣ - ║ 应用名称: mt-pay ║ - ║ 运行环境: dev ║ - ║ 服务端口: 8082 ║ - ║ 后端服务: http://localhost:8082/ ║ - ║ API接口: http://localhost:8082/api ║ - ║ 状态: 🟢 服务运行中,可以接收请求 ║ - ╚══════════════════════════════════════════════════════════╝ - ``` - -### 方法2:使用 Maven 命令启动 - -```bash -cd E:\MTKJPAY -mvn spring-boot:run -``` - -### 方法3:打包后启动 - -```bash -cd E:\MTKJPAY -mvn clean package -java -jar target/MTKJPAY-0.0.1-SNAPSHOT.jar -``` - -## 验证后端是否启动 - -在浏览器访问:http://localhost:8082/api/product/1 - -如果返回 JSON 响应,说明后端已启动成功。 - -## 常见问题 - -1. **端口被占用**:检查 8082 端口是否被其他程序占用 -2. **数据库连接失败**:检查 `application-dev.yml` 中的数据库配置 -3. **依赖缺失**:运行 `mvn clean install` 安装依赖 - diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md deleted file mode 100644 index 03e8d03..0000000 --- a/TROUBLESHOOTING.md +++ /dev/null @@ -1,104 +0,0 @@ -# 后端启动问题排查指南 - -## 问题:前端无法连接到后端 (ECONNREFUSED) - -### 1. 检查后端服务是否启动 - -**方法1:检查端口占用** -```bash -netstat -ano | findstr :8082 -``` -如果没有任何输出,说明后端服务没有启动。 - -**方法2:访问后端接口** -在浏览器访问:http://localhost:8082/api/product/1 -如果无法访问,说明后端服务没有启动。 - -### 2. 启动后端服务 - -#### 在 IDE 中启动(推荐) - -1. 打开 `MTKJPAY/mt-pay` 项目 -2. 找到 `MtPayApplication.java` 文件 -3. 右键点击 → Run 'MtPayApplication.main()' -4. 查看控制台输出,确认启动成功 - -#### 使用 Maven 命令启动 - -```bash -cd E:\MTKJPAY\mt-pay -mvn clean spring-boot:run -``` - -### 3. 常见启动失败原因 - -#### 问题1:Spring Boot 版本错误 -- **症状**:Maven 依赖下载失败,找不到 Spring Boot 4.0.0 -- **解决**:已修复为 Spring Boot 3.2.0 - -#### 问题2:数据库连接失败 -- **症状**:启动时报错 "Cannot create PoolableConnectionFactory" -- **解决**: - 1. 检查 `application-dev.yml` 中的数据库配置 - 2. 确认数据库服务是否运行 - 3. 确认网络是否可以访问数据库服务器 - -#### 问题3:端口被占用 -- **症状**:启动时报错 "Port 8082 is already in use" -- **解决**: - 1. 查找占用端口的进程:`netstat -ano | findstr :8082` - 2. 结束进程或修改端口配置 - -#### 问题4:依赖缺失 -- **症状**:编译错误或 ClassNotFoundException -- **解决**: -```bash -cd E:\MTKJPAY\mt-pay -mvn clean install -``` - -### 4. 验证启动成功 - -启动成功后,控制台应该显示: -``` -======================================== -应用启动成功! -======================================== -应用名称: mt-pay -运行环境: dev -访问地址: http://localhost:8082/ -======================================== -``` - -### 5. 测试后端接口 - -启动成功后,测试接口: -- 商品详情:http://localhost:8082/api/product/1 -- 图片上传:POST http://localhost:8082/api/product/upload/image - -### 6. 检查日志 - -如果启动失败,查看日志文件: -- 位置:`MTKJPAY/mt-pay/logs/` 目录 -- 或查看控制台输出的错误信息 - -## 快速诊断命令 - -```bash -# 1. 检查端口占用 -netstat -ano | findstr :8082 - -# 2. 检查 Java 进程 -jps -l | findstr mtpay - -# 3. 测试后端接口(需要先安装 curl) -curl http://localhost:8082/api/product/1 -``` - -## 如果仍然无法启动 - -请提供以下信息: -1. 启动时的完整错误日志 -2. IDE 控制台的错误信息 -3. Maven 构建输出(如果使用 Maven 启动) - diff --git a/mt-pay/ARCHITECTURE.md b/mt-pay/ARCHITECTURE.md deleted file mode 100644 index b96f074..0000000 --- a/mt-pay/ARCHITECTURE.md +++ /dev/null @@ -1,275 +0,0 @@ -# PingPong支付对接架构设计文档 - -## 一、整体架构分析 - -### 1.1 业务流程 - -``` -用户发起支付 - ↓ -创建支付订单(PaymentOrderService) - ↓ -调用PingPong API(PingPongPayService) - ↓ -生成签名(SignatureService) - ↓ -返回token和收银台地址 - ↓ -用户跳转到收银台完成支付 - ↓ -PingPong回调通知(CallbackService) - ↓ -更新订单状态 - ↓ -业务系统处理 -``` - -### 1.2 技术架构 - -``` -Controller层(API接口) - ↓ -Service层(业务逻辑) - ↓ -Repository层(数据访问) - ↓ -Entity层(数据模型) -``` - -## 二、核心模块设计 - -### 2.1 数据库模型设计 - -#### PaymentOrder(支付订单表) -**设计思路:** -- 存储支付订单的完整信息 -- 支持订单状态跟踪 -- 通过merchantTransactionId保证唯一性 - -**关键字段:** -- `merchantTransactionId`:商户订单号(唯一索引) -- `transactionId`:PingPong交易流水号(回调时更新) -- `status`:订单状态(PENDING/SUCCESS/FAILED/REVIEW/CANCELLED) -- `token`:收银台token -- `paymentUrl`:收银台地址 - -#### PaymentRecord(支付记录表) -**设计思路:** -- 记录所有支付相关操作 -- 用于对账和问题排查 -- 支持多种记录类型(CHECKOUT/CALLBACK/QUERY等) - -**关键字段:** -- `recordType`:记录类型 -- `requestData`:原始请求数据(JSON) -- `responseData`:原始响应数据(JSON) - -### 2.2 配置管理 - -#### PingPongProperties -**设计思路:** -- 使用Spring Boot的@ConfigurationProperties -- 支持多环境配置(dev/prod) -- 配置项包括:商户号、密钥、网关地址、环境模式等 - -**关键配置:** -```properties -pingpong.client-id=商户号 -pingpong.acc-id=店铺编号 -pingpong.secret=签名密钥 -pingpong.gateway=API网关地址 -pingpong.mode=环境模式(sandbox/test/build) -``` - -### 2.3 签名服务(SignatureService) - -#### 设计思路 -- 实现PingPong的签名算法 -- 支持MD5和SHA256 -- 自动筛选参与签名的参数 -- 提供签名生成和验证功能 - -#### 签名流程 -1. 筛选加签参数(根据SIGN_SCOPE) -2. 按字典序排序 -3. 构建签名串:{secret}key1=val1&key2=val2... -4. MD5/SHA256运算并转大写 - -#### 关键方法 -- `generateSign()`:生成签名 -- `verifySign()`:验证签名 -- `filterSignParams()`:筛选签名参数 - -### 2.4 支付服务(PingPongPayService) - -#### 设计思路 -- 封装PingPong API调用 -- 自动处理签名生成 -- 验证响应签名 -- 使用RestClient进行HTTP请求 - -#### 关键方法 -- `checkout()`:创建支付订单 -- `buildRequestMap()`:构建请求参数 - -### 2.5 订单服务(PaymentOrderService) - -#### 设计思路 -- 管理支付订单生命周期 -- 调用PingPong API -- 保存订单和记录 -- 提供订单查询功能 - -#### 关键方法 -- `createPaymentOrder()`:创建支付订单 -- `findByMerchantTransactionId()`:查询订单 -- `updateOrderStatus()`:更新订单状态 - -### 2.6 回调服务(CallbackService) - -#### 设计思路 -- 处理PingPong异步回调 -- 验证回调签名 -- 更新订单状态 -- 触发业务逻辑处理 - -#### 关键方法 -- `handleCallback()`:处理回调 -- `mapCallbackStatusToOrderStatus()`:状态映射 -- `handleBusinessLogic()`:业务逻辑处理 - -## 三、API接口设计 - -### 3.1 支付接口 - -#### POST /api/payment/checkout -**功能:** 创建支付订单 - -**请求体:** CheckoutRequestDTO -**响应:** 包含token和paymentUrl - -#### GET /api/payment/order/{merchantTransactionId} -**功能:** 查询订单状态 - -**响应:** 订单详细信息 - -#### GET /api/payment/checkout/page?token={token} -**功能:** 获取收银台页面 - -**响应:** HTML页面(包含SDK初始化) - -### 3.2 回调接口 - -#### POST /api/callback/pingpong -**功能:** 接收PingPong回调通知 - -**请求体:** Map -**响应:** 处理结果 - -#### GET /api/callback/result -**功能:** 支付结果页面(用户重定向) - -**参数:** merchantTransactionId, status, message - -## 四、DTO设计 - -### 4.1 请求DTO - -#### CheckoutRequestDTO -- 包含所有checkout接口必需参数 -- 使用Jakarta Validation进行参数校验 -- 嵌套RiskInfoDTO - -#### RiskInfoDTO -- 包含风控相关信息 -- 嵌套多个子DTO(CustomerDTO、GoodsDTO等) - -### 4.2 响应DTO - -#### CheckoutResponseDTO -- 对应PingPong API响应 -- 包含token、paymentUrl等 - -## 五、异常处理 - -### 5.1 GlobalExceptionHandler -- 统一异常处理 -- 参数验证异常处理 -- 运行时异常处理 -- 返回统一错误格式 - -## 六、安全考虑 - -### 6.1 签名验证 -- 所有回调都进行签名验证 -- 防止数据篡改 - -### 6.2 订单号唯一性 -- 数据库唯一索引保证 -- 业务层检查重复 - -### 6.3 敏感信息 -- 密钥存储在配置文件中 -- 生产环境使用环境变量 - -## 七、扩展性设计 - -### 7.1 多支付平台支持 -- 可以扩展为支付平台抽象接口 -- 每个平台实现独立服务 - -### 7.2 预授权功能 -- 支持AUTH/CAPTURE/VOID -- 通过paymentType区分 - -### 7.3 订单状态机 -- 可扩展为状态机模式 -- 支持更复杂的状态流转 - -## 八、最佳实践 - -### 8.1 日志记录 -- 关键操作记录日志 -- 包含订单号、状态等信息 - -### 8.2 事务管理 -- 订单创建和更新使用事务 -- 保证数据一致性 - -### 8.3 错误处理 -- 友好的错误提示 -- 详细的错误日志 - -### 8.4 配置管理 -- 多环境配置分离 -- 敏感信息加密 - -## 九、测试建议 - -### 9.1 单元测试 -- 签名服务测试 -- 服务层逻辑测试 - -### 9.2 集成测试 -- API接口测试 -- 回调处理测试 - -### 9.3 沙箱测试 -- 使用PingPong沙箱环境 -- 测试各种支付场景 - -## 十、部署注意事项 - -### 10.1 环境配置 -- 开发环境:使用sandbox模式 -- 生产环境:必须使用build模式 - -### 10.2 数据库 -- 生产环境使用连接池 -- 定期备份订单数据 - -### 10.3 监控 -- 监控API调用成功率 -- 监控回调处理情况 -- 监控订单状态分布 - diff --git a/mt-pay/ARCHITECTURE_COMPLETE.md b/mt-pay/ARCHITECTURE_COMPLETE.md deleted file mode 100644 index df8b939..0000000 --- a/mt-pay/ARCHITECTURE_COMPLETE.md +++ /dev/null @@ -1,179 +0,0 @@ -# 系统架构完整性说明 - -## 后端架构(mt-pay) - -### 1. 包结构 - -``` -com.mtkj.mtpay/ -├── common/ # 通用组件 -│ ├── Result.java # 统一响应结果类 -│ ├── ResultCode.java # 响应码枚举 -│ ├── constants/ # 常量类 -│ │ └── PaymentConstants.java -│ └── enums/ # 枚举类 -│ ├── OrderStatus.java -│ ├── PaymentType.java -│ └── RecordType.java -├── config/ # 配置类 -│ ├── DruidDataSourceConfig.java -│ ├── MyBatisPlusConfig.java -│ ├── MyMetaObjectHandler.java -│ ├── PingPongProperties.java -│ ├── RestClientConfig.java -│ └── WebConfig.java # Web配置(跨域等) -├── controller/ # 控制器层 -│ ├── PaymentController.java -│ └── CallbackController.java -├── dto/ # 数据传输对象 -│ ├── request/ -│ ├── response/ -│ └── risk/ -├── entity/ # 实体类 -│ ├── PaymentOrder.java -│ └── PaymentRecord.java -├── exception/ # 异常处理 -│ ├── BusinessException.java -│ └── GlobalExceptionHandler.java -├── mapper/ # 数据访问层 -│ ├── PaymentOrderMapper.java -│ └── PaymentRecordMapper.java -├── service/ # 服务层(接口) -│ ├── SignatureService.java -│ ├── PingPongPayService.java -│ ├── PaymentOrderService.java -│ └── CallbackService.java -├── service/impl/ # 服务层(实现) -│ ├── SignatureServiceImpl.java -│ ├── PingPongPayServiceImpl.java -│ ├── PaymentOrderServiceImpl.java -│ └── CallbackServiceImpl.java -└── util/ # 工具类 - ├── DateUtils.java - ├── StringUtils.java - └── OrderIdGenerator.java -``` - -### 2. 核心组件说明 - -#### 统一响应格式 -- **Result**: 统一响应结果类,所有接口返回统一格式 -- **ResultCode**: 响应码枚举,定义所有业务响应码 - -#### 异常处理 -- **BusinessException**: 业务异常类 -- **GlobalExceptionHandler**: 全局异常处理器,统一处理异常 - -#### 枚举类 -- **OrderStatus**: 订单状态枚举 -- **PaymentType**: 支付类型枚举 -- **RecordType**: 记录类型枚举 - -#### 工具类 -- **DateUtils**: 日期时间工具类 -- **StringUtils**: 字符串工具类 -- **OrderIdGenerator**: 订单号生成器 - -#### 常量类 -- **PaymentConstants**: 支付相关常量 - -#### 配置类 -- **WebConfig**: Web配置(跨域、拦截器等) - -## 前端架构(MTKJPAY-FRONT) - -### 1. 目录结构 - -``` -MTKJPAY-FRONT/ -├── src/ -│ ├── api/ # API接口 -│ │ ├── request.js # Axios封装 -│ │ └── payment.js # 支付相关API -│ ├── components/ # 通用组件 -│ │ ├── PageHeader.vue -│ │ └── Loading.vue -│ ├── config/ # 配置文件 -│ │ └── index.js -│ ├── router/ # 路由配置 -│ │ └── index.js -│ ├── store/ # 状态管理 -│ │ └── index.js -│ ├── utils/ # 工具函数 -│ │ ├── constants.js # 常量定义 -│ │ ├── helpers.js # 工具函数 -│ │ └── request.js # 请求工具 -│ ├── views/ # 页面组件 -│ │ ├── CreateOrder.vue -│ │ ├── Checkout.vue -│ │ ├── PaymentResult.vue -│ │ └── OrderQuery.vue -│ ├── App.vue # 根组件 -│ └── main.js # 入口文件 -├── .env.development # 开发环境配置 -├── .env.production # 生产环境配置 -└── vite.config.js # Vite配置 -``` - -### 2. 核心组件说明 - -#### 工具类 -- **constants.js**: 常量定义(订单状态、支付类型、币种等) -- **helpers.js**: 工具函数(格式化、验证、防抖节流等) -- **request.js**: 请求工具函数 - -#### 通用组件 -- **PageHeader.vue**: 页面头部组件 -- **Loading.vue**: 加载组件 - -#### 配置管理 -- **config/index.js**: 统一配置管理 -- **.env.development**: 开发环境配置 -- **.env.production**: 生产环境配置 - -#### 状态管理 -- **store/index.js**: 简单状态管理(可扩展为Pinia) - -## 架构特点 - -### 1. 分层清晰 -- Controller → Service → Mapper → Entity -- 接口与实现分离 -- DTO与Entity分离 - -### 2. 统一规范 -- 统一响应格式(Result) -- 统一异常处理 -- 统一响应码 - -### 3. 可扩展性 -- 枚举类便于扩展 -- 工具类可复用 -- 组件化设计 - -### 4. 可维护性 -- 代码结构清晰 -- 命名规范统一 -- 注释完整 - -### 5. 完整性 -- 包含工具类、常量、枚举 -- 包含异常处理、统一响应 -- 包含配置管理、状态管理 - -## 最佳实践 - -1. **统一响应格式**: 所有接口使用Result返回 -2. **异常处理**: 使用BusinessException抛出业务异常 -3. **枚举使用**: 使用枚举替代魔法字符串 -4. **工具类**: 通用功能封装为工具类 -5. **配置管理**: 配置统一管理,支持多环境 - -## 扩展建议 - -1. **日志切面**: 添加AOP日志切面 -2. **接口文档**: 集成Swagger/OpenAPI -3. **缓存**: 添加Redis缓存支持 -4. **消息队列**: 添加消息队列支持 -5. **监控**: 添加系统监控和链路追踪 - diff --git a/mt-pay/CONFIG_FILES.md b/mt-pay/CONFIG_FILES.md deleted file mode 100644 index 31328d5..0000000 --- a/mt-pay/CONFIG_FILES.md +++ /dev/null @@ -1,98 +0,0 @@ -# Config文件夹文件说明 - -## 文件清单 - -config文件夹中共包含5个配置文件,所有文件都是必需的: - -### 1. DruidDataSourceConfig.java -**作用**:配置Druid数据源 -- 配置主数据源(@Primary) -- 支持从数据源配置(可选) -- 从application.yml中读取数据源配置 - -**是否必需**:✅ 必需 -- 用于配置Druid连接池 -- 支持主从数据源切换 - -### 2. MyBatisPlusConfig.java -**作用**:MyBatis-Plus配置 -- 配置Mapper扫描路径:`com.mtkj.mtpay.mapper` -- 配置分页插件(MySQL) - -**是否必需**:✅ 必需 -- MyBatis-Plus核心配置 -- 启用分页功能 - -### 3. MyMetaObjectHandler.java(原MetaObjectHandler.java) -**作用**:MyBatis-Plus自动填充处理器 -- 自动填充创建时间(insert时) -- 自动填充更新时间(insert和update时) - -**是否必需**:✅ 必需 -- 自动处理实体类的createTime和updateTime字段 -- 替代JPA的@CreationTimestamp和@UpdateTimestamp -- 注意:类名改为MyMetaObjectHandler避免与接口名冲突 - -### 4. PingPongProperties.java -**作用**:PingPong支付配置属性类 -- 读取application.yml中的pingpong配置 -- 提供PingPong相关配置属性 - -**是否必需**:✅ 必需 -- 支付功能的核心配置 -- 包含商户号、密钥、网关地址等 - -### 5. RestClientConfig.java -**作用**:RestClient配置 -- 配置HTTP客户端 -- 用于调用PingPong API - -**是否必需**:✅ 必需 -- PingPong API调用需要 -- 配置JSON消息转换器 - -## 文件依赖关系 - -``` -DruidDataSourceConfig - ↓ -MyBatisPlusConfig (依赖数据源) - ↓ -MetaObjectHandler (MyBatis-Plus自动填充) - ↓ -PingPongProperties (支付配置) - ↓ -RestClientConfig (HTTP客户端) -``` - -## 配置说明 - -### 数据源配置流程 - -1. **DruidDataSourceConfig** 创建数据源Bean -2. **MyBatisPlusConfig** 配置MyBatis-Plus,使用数据源 -3. **MyMetaObjectHandler** 处理自动填充逻辑 - -### 支付功能配置流程 - -1. **PingPongProperties** 读取配置属性 -2. **RestClientConfig** 配置HTTP客户端 -3. 支付服务使用这两个配置调用API - -## 总结 - -**所有配置文件都是必需的,不能删除!** - -- **DruidDataSourceConfig**:数据源配置(必需) -- **MyBatisPlusConfig**:MyBatis-Plus配置(必需) -- **MyMetaObjectHandler**:自动填充处理(必需) -- **PingPongProperties**:支付配置(必需) -- **RestClientConfig**:HTTP客户端(必需) - -## 注意事项 - -1. **MyBatisPlusConfig** 中的 `@MapperScan` 必须指向正确的包路径 -2. **MyMetaObjectHandler** 必须实现 `MetaObjectHandler` 接口 -3. **DruidDataSourceConfig** 中的主数据源必须使用 `@Primary` 注解 -4. 所有配置类都需要使用 `@Configuration` 或 `@Component` 注解 - diff --git a/mt-pay/DATASOURCE_CONFIG.md b/mt-pay/DATASOURCE_CONFIG.md deleted file mode 100644 index 7d1e88a..0000000 --- a/mt-pay/DATASOURCE_CONFIG.md +++ /dev/null @@ -1,182 +0,0 @@ -# Druid数据源配置说明 - -## 配置概述 - -项目已配置使用Alibaba Druid数据源,支持主从数据源配置。 - -## 依赖配置 - -在 `pom.xml` 中已添加Druid依赖: - -```xml - - com.alibaba - druid-spring-boot-3-starter - 1.2.20 - -``` - -## 配置文件 - -### 主配置文件:application.yml - -数据源配置位于 `spring.datasource.druid` 节点下: - -```yaml -spring: - datasource: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: com.mysql.cj.jdbc.Driver - druid: - # 主库数据源 - master: - url: jdbc:mysql://... - username: ... - password: ... - initial-size: 5 - min-idle: 10 - max-active: 200 - # ... 其他连接池配置 - # 从库数据源 - slave: - enabled: false - url: - username: - password: - # Druid监控配置 - stat-view-servlet: - enabled: true - url-pattern: /druid/* - login-username: ruoyi - login-password: 123456 -``` - -## 配置类 - -### DruidDataSourceConfig - -位置:`com.mtkj.mtpay.config.DruidDataSourceConfig` - -功能: -- 配置主数据源(@Primary) -- 支持从数据源(可选,通过 `slave.enabled` 控制) -- 使用 `@ConfigurationProperties` 自动绑定配置属性 - -## 连接池参数说明 - -| 参数 | 说明 | 默认值 | -|------|------|--------| -| initial-size | 初始连接数 | 5 | -| min-idle | 最小空闲连接数 | 10 | -| max-active | 最大活跃连接数 | 200 | -| max-wait | 获取连接最大等待时间(毫秒) | 60000 | -| connect-timeout | 连接超时时间(毫秒) | 30000 | -| socket-timeout | 网络超时时间(毫秒) | 60000 | -| time-between-eviction-runs-millis | 空闲连接检测间隔(毫秒) | 60000 | -| min-evictable-idle-time-millis | 连接最小生存时间(毫秒) | 300000 | -| max-evictable-idle-time-millis | 连接最大生存时间(毫秒) | 900000 | -| validation-query | 连接验证SQL | SELECT 1 FROM DUAL | -| test-while-idle | 空闲时验证连接 | true | -| test-on-borrow | 获取连接时验证 | false | -| test-on-return | 归还连接时验证 | false | - -## Druid监控 - -### 访问地址 - -- 监控页面:`http://localhost:8080/druid/index.html` -- 登录用户名:`ruoyi` -- 登录密码:`123456` - -### 监控功能 - -- SQL监控:查看SQL执行情况 -- 连接池监控:查看连接池状态 -- Web应用监控:查看Web请求统计 -- URI监控:查看URI访问统计 -- Session监控:查看Session统计 - -## 环境配置 - -### 开发环境(application-dev.yml) - -使用本地数据库配置,可覆盖主配置中的数据库连接信息。 - -### 生产环境(application-prod.yml) - -使用环境变量配置数据库连接: -- `DB_HOST`:数据库主机 -- `DB_NAME`:数据库名称 -- `DB_USERNAME`:数据库用户名 -- `DB_PASSWORD`:数据库密码 - -## 主从数据源 - -### 启用从数据源 - -在配置文件中设置: - -```yaml -spring: - datasource: - druid: - slave: - enabled: true - url: jdbc:mysql://... - username: ... - password: ... -``` - -### 使用从数据源 - -从数据源Bean名称为 `slaveDataSource`,可以通过以下方式注入: - -```java -@Resource(name = "slaveDataSource") -private DataSource slaveDataSource; -``` - -## 注意事项 - -1. **主数据源**:使用 `@Primary` 注解,是Spring的默认数据源 -2. **连接池大小**:根据实际并发量调整 `max-active` 参数 -3. **监控安全**:生产环境建议修改监控页面的用户名和密码 -4. **慢SQL记录**:可通过 `log-slow-sql` 和 `slow-sql-millis` 配置慢SQL记录 -5. **防火墙配置**:生产环境建议配置 `allow` 白名单限制访问 - -## 常见问题 - -### 1. 连接池耗尽 - -**现象**:`maxWait` 超时异常 - -**解决**: -- 增加 `max-active` 参数 -- 检查是否有连接泄漏 -- 优化SQL性能 - -### 2. 连接验证失败 - -**现象**:`validation-query` 执行失败 - -**解决**: -- 检查数据库连接是否正常 -- 确认 `validation-query` SQL语句正确 -- 检查数据库用户权限 - -### 3. 监控页面无法访问 - -**现象**:404错误 - -**解决**: -- 确认 `stat-view-servlet.enabled` 为 `true` -- 检查 `url-pattern` 配置 -- 确认Spring Boot版本兼容性 - -## 性能优化建议 - -1. **连接池大小**:根据并发量设置,一般 `max-active` 为 50-200 -2. **连接验证**:使用 `test-while-idle` 而不是 `test-on-borrow`,减少性能开销 -3. **连接回收**:合理设置 `min-evictable-idle-time-millis` 和 `max-evictable-idle-time-millis` -4. **监控统计**:生产环境可以关闭部分监控功能以提升性能 - diff --git a/mt-pay/ENV_CONFIG.md b/mt-pay/ENV_CONFIG.md deleted file mode 100644 index b96c4a8..0000000 --- a/mt-pay/ENV_CONFIG.md +++ /dev/null @@ -1,190 +0,0 @@ -# 环境配置文件说明 - -## 配置文件结构 - -项目采用多环境配置,共包含4个配置文件: - -1. **application.yml** - 主配置文件(通用配置) -2. **application-dev.yml** - 开发环境配置 -3. **application-test.yml** - 测试环境配置 -4. **application-prod.yml** - 生产环境配置 - -## 配置文件说明 - -### application.yml(主配置) - -包含所有环境的通用配置: -- 应用名称 -- Druid监控配置 -- JPA配置 -- PingPong支付默认配置 -- 服务器配置 - -**注意**:主配置文件中不包含数据库连接信息,由各环境配置文件提供。 - -### application-dev.yml(开发环境) - -**数据库配置**: -- 使用阿里云RDS数据库(已配置具体连接信息) -- 使用沙箱模式的PingPong支付 - -**使用方式**: -```bash -java -jar app.jar --spring.profiles.active=dev -``` - -### application-test.yml(测试环境) - -**数据库配置**: -- 使用环境变量配置数据库连接 -- 环境变量: - - `test.db.url` - 数据库连接URL - - `test.db.username` - 数据库用户名 - - `test.db.password` - 数据库密码 -- 使用测试模式的PingPong支付 - -**使用方式**: -```bash -# 设置环境变量 -export test.db.url=jdbc:mysql://host:3306/dbname?... -export test.db.username=username -export test.db.password=password - -# 启动应用 -java -jar app.jar --spring.profiles.active=test -``` - -### application-prod.yml(生产环境) - -**数据库配置**: -- 使用环境变量配置数据库连接 -- 环境变量: - - `prod.db.url` - 数据库连接URL - - `prod.db.username` - 数据库用户名 - - `prod.db.password` - 数据库密码 -- 使用生产模式的PingPong支付(build模式) - -**使用方式**: -```bash -# 设置环境变量 -export prod.db.url=jdbc:mysql://host:3306/dbname?... -export prod.db.username=username -export prod.db.password=password - -# 启动应用 -java -jar app.jar --spring.profiles.active=prod -``` - -## 环境变量配置示例 - -### 测试环境环境变量 - -```bash -# Linux/Mac -export test.db.url="jdbc:mysql://test-host:3306/test_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai" -export test.db.username="test_user" -export test.db.password="test_password" -``` - -```powershell -# Windows PowerShell -$env:test.db.url="jdbc:mysql://test-host:3306/test_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai" -$env:test.db.username="test_user" -$env:test.db.password="test_password" -``` - -### 生产环境环境变量 - -```bash -# Linux/Mac -export prod.db.url="jdbc:mysql://prod-host:3306/prod_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai" -export prod.db.username="prod_user" -export prod.db.password="prod_password" -``` - -```powershell -# Windows PowerShell -$env:prod.db.url="jdbc:mysql://prod-host:3306/prod_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai" -$env:prod.db.username="prod_user" -$env:prod.db.password="prod_password" -``` - -## Docker部署环境变量 - -在Docker Compose或Kubernetes中配置环境变量: - -```yaml -# docker-compose.yml -services: - mt-pay: - image: mt-pay:latest - environment: - - SPRING_PROFILES_ACTIVE=prod - - prod.db.url=jdbc:mysql://prod-host:3306/prod_db?... - - prod.db.username=prod_user - - prod.db.password=prod_password -``` - -```yaml -# kubernetes deployment -apiVersion: v1 -kind: ConfigMap -metadata: - name: mt-pay-config -data: - SPRING_PROFILES_ACTIVE: "prod" - prod.db.url: "jdbc:mysql://prod-host:3306/prod_db?..." - prod.db.username: "prod_user" ---- -apiVersion: v1 -kind: Secret -metadata: - name: mt-pay-secret -type: Opaque -stringData: - prod.db.password: "prod_password" -``` - -## IDE中切换环境 - -### IntelliJ IDEA - -1. 打开 `Run/Debug Configurations` -2. 在 `Active profiles` 中输入:`dev`、`test` 或 `prod` -3. 或者在 `Environment variables` 中设置环境变量 - -### Eclipse - -1. 右键项目 -> `Run As` -> `Run Configurations` -2. 在 `Arguments` 标签页的 `Program arguments` 中添加: - ``` - --spring.profiles.active=dev - ``` - -## 配置优先级 - -Spring Boot配置加载顺序(优先级从高到低): -1. 命令行参数 -2. 环境变量 -3. application-{profile}.yml -4. application.yml - -## 注意事项 - -1. **开发环境**:使用具体的数据库连接信息,方便本地开发 -2. **测试/生产环境**:使用环境变量,提高安全性 -3. **PingPong模式**: - - dev: `sandbox`(沙箱) - - test: `test`(测试) - - prod: `build`(生产) -4. **数据库密码**:生产环境务必使用强密码,并通过环境变量或密钥管理服务管理 -5. **监控页面**:生产环境建议修改Druid监控页面的用户名和密码 - -## 验证配置 - -启动应用后,可以通过以下方式验证配置是否正确: - -1. 查看启动日志,确认加载的配置文件 -2. 访问 `/druid/index.html` 验证数据库连接 -3. 检查应用日志中的数据库连接信息(注意:密码不会显示) - diff --git a/mt-pay/LOGGING_GUIDE.md b/mt-pay/LOGGING_GUIDE.md deleted file mode 100644 index 5d0c7e2..0000000 --- a/mt-pay/LOGGING_GUIDE.md +++ /dev/null @@ -1,218 +0,0 @@ -# 日志使用指南 - -## 概述 - -项目已全面集成SLF4J日志功能,使用Logback作为日志实现框架。所有关键类都已添加日志记录,便于问题排查和系统监控。 - -## 日志配置 - -### 日志配置文件 - -- **位置**: `src/main/resources/logback-spring.xml` -- **功能**: 配置日志输出格式、文件路径、日志级别等 - -### 日志级别 - -- **DEBUG**: 详细的调试信息,通常只在开发环境使用 -- **INFO**: 一般信息,记录系统运行状态和关键操作 -- **WARN**: 警告信息,表示潜在问题但不影响系统运行 -- **ERROR**: 错误信息,表示系统错误需要关注 - -### 日志输出 - -1. **控制台输出**: 开发环境默认输出到控制台 -2. **文件输出**: - - 所有日志: `logs/mt-pay.{日期}.log` - - 错误日志: `logs/mt-pay-error.{日期}.log` -3. **日志轮转**: 按天轮转,保留30天,单文件最大10MB - -## 已添加日志的类 - -### 1. 配置类 - -- ✅ `MyBatisPlusConfig` - MyBatis-Plus配置日志 -- ✅ `RestClientConfig` - RestClient配置日志 -- ✅ `WebConfig` - Web配置(跨域)日志 -- ✅ `DruidDataSourceConfig` - 数据源配置日志(已有) -- ✅ `MyMetaObjectHandler` - 自动填充日志(已有) - -### 2. 控制器类 - -- ✅ `ProductController` - 商品管理接口日志(已有) -- ✅ `PaymentController` - 支付接口日志(已有) -- ✅ `CallbackController` - 回调接口日志(已有) - -### 3. 服务实现类 - -- ✅ `ProductServiceImpl` - 商品服务日志(已增强) -- ✅ `PaymentOrderServiceImpl` - 支付订单服务日志(已增强) -- ✅ `PingPongPayServiceImpl` - PingPong支付服务日志(已有) -- ✅ `SignatureServiceImpl` - 签名服务日志(已有) -- ✅ `CallbackServiceImpl` - 回调服务日志(已有) -- ✅ `OssServiceImpl` - OSS服务日志(已有) - -### 4. 工具类 - -- ✅ `OrderIdGenerator` - 订单号生成器日志(已添加) - -### 5. 异常处理 - -- ✅ `GlobalExceptionHandler` - 全局异常处理日志(已有) - -### 6. 主应用类 - -- ✅ `MtPayApplication` - 应用启动日志(已增强) - -## 日志使用示例 - -### 在类中使用日志 - -```java -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class YourService { - - public void doSomething() { - // 记录信息日志 - log.info("开始执行操作,参数: {}", param); - - try { - // 业务逻辑 - log.debug("调试信息: {}", debugInfo); - - } catch (Exception e) { - // 记录错误日志 - log.error("操作失败,参数: {}", param, e); - throw e; - } - - log.info("操作完成,结果: {}", result); - } -} -``` - -### 日志级别使用建议 - -1. **DEBUG**: - - 详细的执行流程 - - 中间变量值 - - SQL参数值(MyBatis) - -2. **INFO**: - - 关键业务操作开始/结束 - - 重要状态变更 - - 外部接口调用 - -3. **WARN**: - - 业务异常(如订单不存在但继续处理) - - 配置问题 - - 性能警告 - -4. **ERROR**: - - 系统异常 - - 数据库操作失败 - - 外部接口调用失败 - -## 日志格式 - -### 控制台格式 -``` -2025-12-19 16:37:50.530 [restartedMain] INFO com.mtkj.mtpay.MtPayApplication - 应用启动成功! -``` - -### 文件格式 -``` -2025-12-19 16:37:50.530 [http-nio-8080-exec-1] INFO com.mtkj.mtpay.controller.ProductController - 创建商品请求:ProductRequestDTO(...) -``` - -## 环境配置 - -### 开发环境 (dev) -- 控制台输出: ✅ -- 文件输出: ✅ -- 日志级别: DEBUG -- MyBatis SQL日志: ✅ - -### 测试环境 (test) -- 控制台输出: ❌ -- 文件输出: ✅ -- 日志级别: INFO -- MyBatis SQL日志: ❌ - -### 生产环境 (prod) -- 控制台输出: ❌ -- 文件输出: ✅ -- 日志级别: INFO -- MyBatis SQL日志: ❌ - -## 日志文件位置 - -- **日志目录**: `./logs/`(项目根目录) -- **所有日志**: `logs/mt-pay.{日期}.log` -- **错误日志**: `logs/mt-pay-error.{日期}.log` - -## 日志查看 - -### 开发环境 -- 直接在IDE控制台查看 -- 或查看 `logs/` 目录下的日志文件 - -### 生产环境 -- 使用 `tail -f logs/mt-pay-*.log` 实时查看 -- 使用 `grep ERROR logs/mt-pay-*.log` 查找错误 -- 使用日志分析工具(如ELK、Graylog等) - -## 最佳实践 - -1. **使用占位符**: 使用 `{}` 占位符而不是字符串拼接 - ```java - // ✅ 推荐 - log.info("用户ID: {}, 操作: {}", userId, action); - - // ❌ 不推荐 - log.info("用户ID: " + userId + ", 操作: " + action); - ``` - -2. **异常日志**: 记录异常时包含堆栈信息 - ```java - // ✅ 推荐 - log.error("操作失败", e); - - // ❌ 不推荐 - log.error("操作失败: " + e.getMessage()); - ``` - -3. **敏感信息**: 不要记录密码、密钥等敏感信息 - ```java - // ❌ 不要这样做 - log.info("用户密码: {}", password); - ``` - -4. **日志级别**: 根据重要性选择合适的日志级别 - - 关键业务操作: INFO - - 调试信息: DEBUG - - 警告: WARN - - 错误: ERROR - -## 常见问题 - -### Q: 如何修改日志级别? -A: 修改 `logback-spring.xml` 中对应环境的日志级别配置。 - -### Q: 如何查看SQL日志? -A: 开发环境默认开启,生产环境已关闭。可在 `logback-spring.xml` 中修改 `com.mtkj.mtpay.mapper` 的日志级别。 - -### Q: 日志文件太大怎么办? -A: 已配置日志轮转,按天轮转,单文件最大10MB,保留30天。 - -### Q: 如何禁用某个类的日志? -A: 在 `logback-spring.xml` 中添加: -```xml - -``` - -## 总结 - -项目已全面集成SLF4J日志功能,所有关键操作都有日志记录。通过合理的日志级别和格式,可以快速定位问题、监控系统运行状态。 - diff --git a/mt-pay/README.md b/mt-pay/README.md index 41d3c7d..07caa1a 100644 --- a/mt-pay/README.md +++ b/mt-pay/README.md @@ -1,218 +1,157 @@ -# MT Pay - PingPong支付对接 +# MT Pay - 支付系统 ## 项目简介 -本项目实现了PingPong支付平台的内嵌SDK接入,支持收银台模式支付。 +面向东南亚地区的电商支付系统,支持PayPal支付、商品管理、订单管理、货币转换等功能。 ## 功能特性 -- ✅ 支付订单创建(checkout接口) -- ✅ MD5/SHA256签名生成和验证 -- ✅ 支付回调处理 -- ✅ 订单状态查询 -- ✅ 收银台页面集成 +- ✅ PayPal支付集成(创建订单、捕获支付、Webhook处理) +- ✅ 商品管理(商品、SKU、商品链接) +- ✅ 客户订单管理(支持东南亚地址格式) +- ✅ 货币转换(实时汇率,支持多币种) +- ✅ 百度翻译集成(商品名称、SKU名称自动翻译) +- ✅ 订单状态管理 - ✅ 支付记录管理 -- ✅ 预授权支持(AUTH/CAPTURE/VOID) ## 技术栈 - Spring Boot 4.0.0 -- Spring Data JPA -- MySQL +- MyBatis-Plus +- MySQL 5.7+ +- Jackson (JSON处理) - Lombok -- RestClient +- RestTemplate -## 数据库表结构 +## 快速开始 -### payment_order(支付订单表) -- 存储支付订单基本信息 -- 包含商户订单号、PingPong交易流水号、金额、状态等 +### 1. 数据库配置 -### payment_record(支付记录表) -- 存储支付操作记录 -- 包含回调记录、查询记录等 +执行数据库脚本(按顺序): +```sql +-- 1. 商品相关表 +source database/customer_order_schema.sql; + +-- 2. 货币转换字段 +source database/customer_order_currency_update.sql; + +-- 3. 地址字段(混合方案) +source database/customer_order_address_optimized.sql; +``` + +### 2. 配置文件 + +编辑 `src/main/resources/application-dev.yml`: +- 数据库连接信息 +- PayPal API凭证(Client ID、Client Secret) +- 百度翻译配置(App ID、Security Key) +- 阿里云OSS配置 + +### 3. 启动项目 + +```bash +mvn spring-boot:run +``` + +或 + +```bash +mvn clean package +java -jar target/mt-pay-0.0.1-SNAPSHOT.jar +``` + +### 4. 访问地址 + +- 后端API: http://localhost:8082/api +- Druid监控: http://localhost:8082/druid +- 前端地址: http://localhost:3000 + +## 核心API + +### 商品管理 +- `GET /api/product/{id}` - 获取商品详情 +- `GET /api/product/link/{linkCode}` - 通过链接码获取商品 +- `POST /api/product` - 创建商品 + +### 订单管理 +- `POST /api/order` - 创建客户订单 +- `GET /api/order/{orderNo}` - 获取订单详情 +- `POST /api/order/calculate-currency-conversion` - 计算货币转换 + +### PayPal支付 +- `POST /api/paypal/order` - 创建PayPal订单 +- `POST /api/paypal/capture` - 捕获支付 +- `POST /api/paypal/webhook` - Webhook回调 + +## 项目结构 + +``` +com.mtkj.mtpay/ +├── config/ # 配置类(PayPal、百度翻译、数据源等) +├── controller/ # REST控制器 +├── dto/ # 数据传输对象 +│ ├── request/ # 请求DTO +│ └── response/ # 响应DTO +├── entity/ # 实体类 +├── exception/ # 异常处理 +├── mapper/ # MyBatis Mapper +├── service/ # 业务服务层 +│ └── impl/ # 服务实现 +└── util/ # 工具类 +``` + +## 核心服务 + +- **ProductService**: 商品管理服务 +- **CustomerOrderService**: 客户订单服务 +- **PayPalService**: PayPal支付服务 +- **PayPalWebhookService**: PayPal Webhook处理服务 +- **ExchangeRateService**: 汇率转换服务 +- **BaiduTranslatorUtils**: 百度翻译工具 ## 配置说明 -在 `application.properties` 中配置以下参数: - -```properties -# PingPong支付配置 -pingpong.client-id=your-client-id # PingPong商户号 -pingpong.acc-id=your-acc-id # PingPong商户店铺编号 -pingpong.secret=your-secret-key # 签名密钥 -pingpong.sign-type=MD5 # 签名类型(MD5或SHA256) -pingpong.gateway=https://sandbox-acquirer-payment.pingpongx.com # API网关地址 -pingpong.mode=sandbox # 环境模式(sandbox/test/build) +### PayPal配置 +```yaml +paypal: + client-id: your-client-id + client-secret: your-client-secret + mode: sandbox # sandbox 或 production + enabled: true ``` -## API接口 - -### 1. 创建支付订单 - -**接口地址:** `POST /api/payment/checkout` - -**请求示例:** -```json -{ - "accId": "2018092714313010016291", - "amount": "20.00", - "currency": "USD", - "merchantTransactionId": "MTN193495030728", - "paymentType": "SALE", - "shopperResultUrl": "http://your-domain.com/api/callback/result", - "shopperCancelUrl": "http://your-domain.com/api/callback/result", - "signType": "MD5", - "riskInfo": { - "customer": { - "firstName": "James", - "lastName": "LeBron", - "email": "demo@pingpongx.com", - "phone": "15988890852", - "registerTime": "20191101122000", - "registerIp": "222.126.52.23", - "orderTime": "20191201122000", - "orderIp": "222.126.52.23" - }, - "goods": [{ - "name": "Macaron", - "description": "Colorful macaron", - "sku": "20191201331", - "averageUnitPrice": "20", - "number": "1", - "virtualProduct": "N" - }], - "shipping": { - "firstName": "James", - "lastName": "LeBron", - "phone": "13588185079", - "street": "1986 Broad Street", - "postcode": "35222", - "city": "Birmingham", - "state": "Alabama", - "country": "US" - }, - "billing": { - "firstName": "James", - "lastName": "LeBron", - "phone": "13588185079", - "street": "1986 Broad Street", - "postcode": "35222", - "city": "Birmingham", - "state": "Alabama", - "country": "US" - } - }, - "notificationUrl": "http://your-domain.com/api/callback/pingpong" -} +### 百度翻译配置 +```yaml +baidu: + translator: + app-id: your-app-id + securityKey: your-security-key + transApiHost: https://fanyi-api.baidu.com/api/trans/vip/translate ``` -**响应示例:** -```json -{ - "code": "0000", - "message": "订单创建成功", - "data": { - "merchantTransactionId": "MTN193495030728", - "token": "vr_YVR8u7rn7C1gG97DOg5W0OxzazxNYIE56ShWjA4lJrY4wchTnb47oNmp-9ubP", - "paymentUrl": "https://sandbox-pay-checkout.pingpongx.com/index.html?token=...", - "status": "PENDING" - } -} -``` +## 数据库表 -### 2. 查询订单状态 +- `mt_product` - 商品表 +- `mt_product_sku` - SKU表 +- `mt_product_link` - 商品链接表 +- `customer_order` - 客户订单表 +- `payment_order` - 支付订单表 +- `payment_record` - 支付记录表 -**接口地址:** `GET /api/payment/order/{merchantTransactionId}` +## 地址字段设计 -### 3. 获取收银台页面 +采用混合方案: +- **基础字段**(独立列):国家、城市、州/省、邮编、详细地址1/2 +- **特殊字段**(JSON存储):各国特殊字段(组屋号、Barangay、泰文地址等) -**接口地址:** `GET /api/payment/checkout/page?token={token}` +详见:`database/customer_order_address_optimized.sql` -### 4. 支付回调接口 +## 相关文档 -**接口地址:** `POST /api/callback/pingpong` - -### 5. 支付结果页面 - -**接口地址:** `GET /api/callback/result?merchantTransactionId={id}&status={status}` - -## 使用流程 - -1. **创建支付订单** - - 调用 `/api/payment/checkout` 接口创建订单 - - 获取返回的 `token` - -2. **跳转到收银台** - - 方式1:直接使用返回的 `paymentUrl` 跳转 - - 方式2:调用 `/api/payment/checkout/page?token={token}` 获取收银台页面 - -3. **用户完成支付** - - 用户在收银台页面完成支付 - - 支付完成后跳转到 `shopperResultUrl` - -4. **接收回调通知** - - PingPong会异步调用 `notificationUrl` 通知支付结果 - - 系统自动更新订单状态 - -## 注意事项 - -1. **订单号唯一性**:`merchantTransactionId` 必须全局唯一,不可重复 -2. **金额格式**:`amount` 必须精确到两位小数,如 "20.00" -3. **环境模式**:开发测试使用 `sandbox`,生产环境必须使用 `build` -4. **签名验证**:所有回调都会进行签名验证,确保数据安全 -5. **风控信息**:`riskInfo` 中的 `shipping`、`billing`、`goods` 不能为空,影响交易成功率 -6. **REVIEW状态**:如果订单状态为 `REVIEW`,需要及时进行内部审核 - -## 预授权功能 - -### AUTH(预授权) -- 在创建订单时设置 `paymentType` 为 `AUTH` - -### CAPTURE(预授权完成) -- 调用二次交易接口,设置 `paymentType` 为 `CAPTURE` -- 需要在预授权成功后的7天内完成 - -### VOID(预授权撤销) -- 调用二次交易接口,设置 `paymentType` 为 `VOID` -- 只能全额撤销 - -## 开发说明 - -### 项目结构 -``` -com.mtkj.mtpay/ -├── config/ # 配置类 -├── controller/ # 控制器 -├── dto/ # 数据传输对象 -├── entity/ # 实体类 -├── exception/ # 异常处理 -├── repository/ # 数据访问层 -└── service/ # 业务服务层 -``` - -### 核心服务 - -- **SignatureService**:签名生成和验证服务 -- **PingPongPayService**:PingPong API调用服务 -- **PaymentOrderService**:支付订单业务服务 -- **CallbackService**:回调处理服务 - -## 数据库初始化 - -项目启动后,JPA会自动创建表结构。也可以手动执行SQL: - -```sql --- 根据实体类自动生成,或参考实体类定义手动创建 -``` - -## 测试 - -1. 配置沙箱环境参数 -2. 调用创建订单接口 -3. 使用沙箱测试卡号完成支付测试 +- `SYSTEM_ARCHITECTURE.md` - 系统架构文档 +- `../PAYPAL_WEBHOOK_GUIDE.md` - PayPal Webhook配置指南 +- `../PAYPAL_TEST_ACCOUNT.md` - PayPal测试账号说明 ## 许可证 MIT - diff --git a/mt-pay/SERVICE_STRUCTURE.md b/mt-pay/SERVICE_STRUCTURE.md deleted file mode 100644 index c077a08..0000000 --- a/mt-pay/SERVICE_STRUCTURE.md +++ /dev/null @@ -1,135 +0,0 @@ -# Service层结构说明 - -## 目录结构 - -``` -service/ -├── SignatureService.java # 签名服务接口 -├── PingPongPayService.java # PingPong支付服务接口 -├── PaymentOrderService.java # 支付订单服务接口 -├── CallbackService.java # 回调处理服务接口 -└── impl/ # 实现类目录 - ├── SignatureServiceImpl.java # 签名服务实现类 - ├── PingPongPayServiceImpl.java # PingPong支付服务实现类 - ├── PaymentOrderServiceImpl.java # 支付订单服务实现类 - └── CallbackServiceImpl.java # 回调处理服务实现类 -``` - -## 设计原则 - -### 1. 接口与实现分离 -- **service文件夹**:只存放接口文件 -- **service/impl文件夹**:存放所有实现类 - -### 2. 命名规范 -- 接口:`XxxService` -- 实现类:`XxxServiceImpl` - -### 3. 依赖注入 -- Controller层注入接口,不直接依赖实现类 -- Spring会自动根据接口找到对应的实现类(通过@Service注解) - -## Service接口说明 - -### SignatureService -**功能**:签名生成和验证服务 - -**方法**: -- `generateSign(Map params)` - 生成签名 -- `generateSign(Map params, String secret, String signType)` - 生成签名(指定密钥和类型) -- `verifySign(Map params)` - 验证签名 - -### PingPongPayService -**功能**:PingPong支付API调用服务 - -**方法**: -- `checkout(CheckoutRequestDTO request)` - 创建支付订单 - -### PaymentOrderService -**功能**:支付订单业务服务 - -**方法**: -- `createPaymentOrder(CheckoutRequestDTO request)` - 创建支付订单 -- `findByMerchantTransactionId(String merchantTransactionId)` - 根据商户订单号查询 -- `findByTransactionId(String transactionId)` - 根据PingPong交易流水号查询 -- `updateOrderStatus(String merchantTransactionId, String status, String transactionId)` - 更新订单状态 - -### CallbackService -**功能**:回调处理服务 - -**方法**: -- `handleCallback(Map callbackData)` - 处理支付回调 - -## 实现类说明 - -### SignatureServiceImpl -- 实现签名生成和验证逻辑 -- 支持MD5和SHA256签名算法 -- 自动筛选参与签名的参数 - -### PingPongPayServiceImpl -- 实现PingPong API调用 -- 自动生成请求签名 -- 验证响应签名 - -### PaymentOrderServiceImpl -- 实现支付订单业务逻辑 -- 调用PingPong API创建订单 -- 保存订单和支付记录 - -### CallbackServiceImpl -- 实现回调处理逻辑 -- 验证回调签名 -- 更新订单状态 -- 保存回调记录 - -## 使用示例 - -### Controller中使用Service - -```java -@RestController -@RequiredArgsConstructor -public class PaymentController { - // 注入接口,Spring会自动找到对应的实现类 - private final PaymentOrderService paymentOrderService; - - @PostMapping("/checkout") - public ResponseEntity checkout(@RequestBody CheckoutRequestDTO request) { - PaymentOrder order = paymentOrderService.createPaymentOrder(request); - return ResponseEntity.ok(order); - } -} -``` - -### Service实现类中注入其他Service - -```java -@Service -@RequiredArgsConstructor -public class PaymentOrderServiceImpl implements PaymentOrderService { - // 注入其他Service接口 - private final PingPongPayService pingPongPayService; - - // 实现接口方法 - @Override - public PaymentOrder createPaymentOrder(CheckoutRequestDTO request) { - // 实现逻辑 - } -} -``` - -## 优势 - -1. **解耦**:Controller只依赖接口,不依赖具体实现 -2. **扩展性**:可以轻松替换实现类,不影响调用方 -3. **测试**:可以方便地创建Mock实现进行单元测试 -4. **规范**:统一的代码结构,便于维护 - -## 注意事项 - -1. 所有Service实现类必须使用`@Service`注解 -2. 实现类必须实现对应的接口 -3. Controller中注入的是接口类型,不是实现类 -4. 如果接口有多个实现类,需要使用`@Qualifier`指定 - diff --git a/mt-pay/database/README.md b/mt-pay/database/README.md deleted file mode 100644 index 7c948fc..0000000 --- a/mt-pay/database/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# 数据库建表SQL说明 - -## 文件说明 - -- `schema.sql` - 完整的数据库建表SQL脚本 - -## 表结构说明 - -### 1. payment_order(支付订单表) - -**表说明**:存储支付订单的完整信息 - -**主要字段**: -- `id` - 主键ID(自增) -- `merchant_transaction_id` - 商户订单号(唯一索引) -- `transaction_id` - PingPong交易流水号 -- `amount` - 交易金额(DECIMAL(12,2)) -- `currency` - 交易币种(3位ISO 4217代码) -- `payment_type` - 交易类型(SALE/AUTH) -- `status` - 订单状态(PENDING/SUCCESS/FAILED/REVIEW/CANCELLED) -- `token` - PingPong返回的token -- `payment_url` - 支付收银台地址 -- `create_time` - 创建时间(自动填充) -- `update_time` - 更新时间(自动更新) - -**索引**: -- 主键:`id` -- 唯一索引:`merchant_transaction_id` -- 普通索引:`transaction_id`、`status`、`create_time` - -### 2. payment_record(支付记录表) - -**表说明**:记录所有支付相关操作(回调、查询等) - -**主要字段**: -- `id` - 主键ID(自增) -- `transaction_id` - PingPong交易流水号 -- `merchant_transaction_id` - 商户订单号 -- `record_type` - 记录类型(CHECKOUT/CALLBACK/QUERY/REFUND/CAPTURE/VOID) -- `status` - 交易状态 -- `code` - 响应码 -- `description` - 响应描述 -- `request_data` - 原始请求数据(JSON,TEXT类型) -- `response_data` - 原始响应数据(JSON,TEXT类型) -- `create_time` - 创建时间(自动填充) - -**索引**: -- 主键:`id` -- 普通索引:`transaction_id`、`merchant_transaction_id`、`record_type`、`create_time` - -## 执行方式 - -### 方式1:直接执行SQL文件 - -```bash -mysql -u用户名 -p密码 数据库名 < schema.sql -``` - -### 方式2:在MySQL客户端中执行 - -```sql --- 1. 连接到MySQL -mysql -u用户名 -p密码 - --- 2. 选择数据库 -USE mtpay; - --- 3. 执行SQL文件 -SOURCE /path/to/schema.sql; -``` - -### 方式3:使用IDE工具 - -1. 打开数据库管理工具(如Navicat、DBeaver、DataGrip等) -2. 连接到数据库 -3. 打开 `schema.sql` 文件 -4. 执行SQL脚本 - -## 注意事项 - -1. **字符集**:使用 `utf8mb4` 字符集,支持emoji和特殊字符 -2. **排序规则**:使用 `utf8mb4_unicode_ci` 排序规则 -3. **存储引擎**:使用 `InnoDB` 存储引擎,支持事务和外键 -4. **时间字段**:使用 `DATETIME` 类型,支持自动填充和更新 -5. **金额字段**:使用 `DECIMAL(12,2)` 类型,精确到分 -6. **唯一约束**:`merchant_transaction_id` 必须全局唯一 - -## 字段长度说明 - -- `merchant_transaction_id`:64字符(根据PingPong文档) -- `transaction_id`:64字符(根据PingPong文档) -- `token`:255字符(根据PingPong文档) -- `payment_url`:500字符(URL可能较长) -- `remark`:500字符(备注信息) -- `description`:500字符(响应描述) -- `request_data`/`response_data`:TEXT类型(JSON数据可能很长) - -## 索引优化建议 - -1. **查询优化**:根据实际查询场景添加复合索引 -2. **时间范围查询**:`create_time` 索引有助于时间范围查询 -3. **状态查询**:`status` 索引有助于按状态筛选订单 -4. **关联查询**:`transaction_id` 和 `merchant_transaction_id` 索引有助于关联查询 - -## 数据备份建议 - -1. 定期备份数据库 -2. 重要操作前先备份 -3. 使用 `mysqldump` 命令备份: - -```bash -mysqldump -u用户名 -p密码 数据库名 > backup_$(date +%Y%m%d).sql -``` - -## 版本更新 - -如果后续需要修改表结构,建议: - -1. 创建迁移SQL文件(如:`migration_v1.1.sql`) -2. 使用 `ALTER TABLE` 语句修改 -3. 记录版本变更日志 - diff --git a/mt-pay/database/README_CUSTOMER_ORDER.md b/mt-pay/database/README_CUSTOMER_ORDER.md deleted file mode 100644 index 4c34882..0000000 --- a/mt-pay/database/README_CUSTOMER_ORDER.md +++ /dev/null @@ -1,111 +0,0 @@ -# 客户订单表创建说明 - -## 问题 -如果遇到错误:`Table 'mtpay.customer_order' doesn't exist` - -说明数据库中没有 `customer_order` 表,需要执行SQL脚本创建。 - -## 解决方案 - -### 方法1:使用MySQL客户端执行(推荐) - -1. 连接到数据库: -```bash -mysql -h rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com -P 3306 -u mtkj2025 -p mtpay -``` - -2. 执行SQL脚本: -```sql -source E:/MTKJPAY/mt-pay/database/customer_order_schema.sql -``` - -或者直接复制SQL内容执行: -```sql -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='客户订单表'; -``` - -### 方法2:使用数据库管理工具 - -使用Navicat、DBeaver、MySQL Workbench等工具: - -1. 连接到数据库 `mtpay` -2. 打开SQL编辑器 -3. 复制 `customer_order_schema.sql` 文件内容 -4. 执行SQL脚本 - -### 方法3:使用命令行(Windows PowerShell) - -```powershell -# 读取SQL文件内容 -$sql = Get-Content "E:\MTKJPAY\mt-pay\database\customer_order_schema.sql" -Raw - -# 执行SQL(需要先安装mysql客户端) -mysql -h rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com -P 3306 -u mtkj2025 -p mtpay -e $sql -``` - -## 验证 - -执行完成后,可以验证表是否创建成功: - -```sql --- 查看表是否存在 -SHOW TABLES LIKE 'customer_order'; - --- 查看表结构 -DESC customer_order; - --- 或者 -SHOW CREATE TABLE customer_order; -``` - -## 注意事项 - -1. 确保数据库连接信息正确 -2. 确保有创建表的权限 -3. 如果表已存在,可以先删除再创建: - ```sql - DROP TABLE IF EXISTS `customer_order`; - ``` - 然后重新执行创建脚本 - diff --git a/mt-pay/database/README_DATABASE.md b/mt-pay/database/README_DATABASE.md deleted file mode 100644 index b2fad23..0000000 --- a/mt-pay/database/README_DATABASE.md +++ /dev/null @@ -1,67 +0,0 @@ -# 数据库配置说明 - -## 数据库信息 - -- **数据库名称**:`mtpay` -- **字符集**:`utf8mb4` -- **排序规则**:`utf8mb4_general_ci` - -## 数据库表 - -### 支付相关表 - -1. **payment_order** - 支付订单表 -2. **payment_record** - 支付记录表 - -### 商品相关表 - -1. **mt_product** - 商品表 -2. **mt_product_sku** - 商品SKU表 - -## 执行建表SQL - -### 方式1:执行所有SQL文件 - -```bash -# 在MySQL中执行 -mysql -u用户名 -p密码 mtpay < mt_product_schema.sql -``` - -### 方式2:在MySQL客户端中执行 - -```sql --- 1. 连接到MySQL -mysql -u用户名 -p密码 - --- 2. 选择数据库 -USE mtpay; - --- 3. 执行SQL文件 -SOURCE /path/to/mt_product_schema.sql; -``` - -### 方式3:使用IDE工具 - -1. 打开数据库管理工具(如Navicat、DBeaver、DataGrip等) -2. 连接到数据库服务器 -3. 选择 `mtpay` 数据库 -4. 打开 `mt_product_schema.sql` 文件 -5. 执行SQL脚本 - -## 注意事项 - -1. **执行顺序**:先执行 `mt_product_schema.sql` 创建商品表 -2. **外键约束**:`mt_product_sku` 表有外键关联 `mt_product` 表,删除商品时会级联删除SKU -3. **字符集**:所有表使用 `utf8mb4` 字符集,支持emoji和特殊字符 -4. **索引**:已创建必要的索引,提高查询性能 - -## 表结构概览 - -``` -mtpay -├── payment_order (支付订单表) -├── payment_record (支付记录表) -├── mt_product (商品表) -└── mt_product_sku (商品SKU表) -``` - diff --git a/mt-pay/database/README_PRODUCT.md b/mt-pay/database/README_PRODUCT.md deleted file mode 100644 index 395162f..0000000 --- a/mt-pay/database/README_PRODUCT.md +++ /dev/null @@ -1,139 +0,0 @@ -# 商品表结构说明 - -## 表名规范 - -所有表名使用 `mt_` 前缀,如: -- `mt_product` - 商品表 -- `mt_product_sku` - 商品SKU表 - -## 表结构 - -### 1. mt_product(商品表) - -**字段说明:** - -| 字段名 | 类型 | 说明 | 备注 | -|--------|------|------|------| -| id | BIGINT | 商品ID | 主键,自增 | -| name | VARCHAR(255) | 商品名称 | 必填 | -| price | DECIMAL(12,2) | 商品价格 | 基础价格,默认0.00 | -| main_image | VARCHAR(4000) | 主图URL | 可选 | -| status | VARCHAR(20) | 商品状态 | ACTIVE-上架,INACTIVE-下架,DELETED-已删除 | -| shop_id | BIGINT | 店铺ID | 必填,关联店铺 | -| create_time | DATETIME | 创建时间 | 自动填充 | -| update_time | DATETIME | 更新时间 | 自动更新 | - -**索引:** -- 主键:`id` -- 普通索引:`shop_id`、`status`、`create_time` - -**排序规则:** -- 使用 `utf8mb4_general_ci` 排序规则 - -### 2. mt_product_sku(商品SKU表) - -**字段说明:** - -| 字段名 | 类型 | 说明 | 备注 | -|--------|------|------|------| -| id | BIGINT | SKU ID | 主键,自增 | -| product_id | BIGINT | 商品ID | 外键,关联mt_product | -| sku | VARCHAR(2000) | SKU编码 | 必填 | -| price | DECIMAL(12,2) | 价格 | 必填 | -| currency | VARCHAR(3) | 货币 | ISO 4217代码,默认USD | -| stock | INT | 库存数量 | 默认0 | -| sales_attrs | LONGTEXT | 销售属性 | JSON格式,如:[{"name":"颜色","value":"红色"}] | -| sku_image | VARCHAR(4000) | SKU图片URL | 可选 | -| weight | DECIMAL(10,2) | 重量 | 单位:克 | -| size | VARCHAR(200) | 大小/尺寸 | JSON格式,如:{"length":10,"width":5,"height":3} | -| specification | VARCHAR(2000) | 规格 | 文本描述 | -| status | VARCHAR(20) | SKU状态 | ACTIVE-启用,INACTIVE-禁用 | -| create_time | DATETIME | 创建时间 | 自动填充 | -| update_time | DATETIME | 更新时间 | 自动更新 | - -**索引:** -- 主键:`id` -- 普通索引:`product_id`、`status` -- 外键:`product_id` → `mt_product.id`(级联删除) - -## 数据示例 - -### 商品示例 - -```sql -INSERT INTO `mt_product` (`name`, `price`, `main_image`, `status`, `shop_id`) -VALUES ('Macaron 马卡龙礼盒装', 20.00, 'https://example.com/image.jpg', 'ACTIVE', 1); -``` - -### SKU示例 - -```sql --- SKU 1:粉色6枚装 -INSERT INTO `mt_product_sku` ( - `product_id`, `sku`, `price`, `currency`, `stock`, - `sales_attrs`, `sku_image`, `weight`, `size`, `specification`, `status` -) VALUES ( - 1, 'SKU001', 18.00, 'USD', 50, - '[{"name":"颜色","value":"粉色"},{"name":"规格","value":"6枚装"}]', - 'https://example.com/pink-6pcs.jpg', 500.00, - '{"length":20,"width":15,"height":5}', - '6枚装马卡龙礼盒', 'ACTIVE' -); - --- SKU 2:蓝色12枚装 -INSERT INTO `mt_product_sku` ( - `product_id`, `sku`, `price`, `currency`, `stock`, - `sales_attrs`, `sku_image`, `weight`, `size`, `specification`, `status` -) VALUES ( - 1, 'SKU002', 20.00, 'USD', 100, - '[{"name":"颜色","value":"蓝色"},{"name":"规格","value":"12枚装"}]', - 'https://example.com/blue-12pcs.jpg', 800.00, - '{"length":25,"width":20,"height":8}', - '12枚装马卡龙礼盒', 'ACTIVE' -); -``` - -## JSON字段格式说明 - -### sales_attrs(销售属性) - -```json -[ - {"name": "颜色", "value": "粉色"}, - {"name": "规格", "value": "6枚装"} -] -``` - -### size(尺寸) - -```json -{ - "length": 20, - "width": 15, - "height": 5, - "unit": "cm" -} -``` - -## 执行SQL - -```bash -# 在MySQL中执行 -mysql -u用户名 -p密码 数据库名 < mt_product_schema.sql -``` - -或在MySQL客户端中: - -```sql -SOURCE /path/to/mt_product_schema.sql; -``` - -## 注意事项 - -1. **字符集**:使用 `utf8mb4` 字符集,支持emoji和特殊字符 -2. **排序规则**:使用 `utf8mb4_unicode_ci` -3. **存储引擎**:使用 `InnoDB`,支持事务和外键 -4. **外键约束**:删除商品时会级联删除所有SKU -5. **SKU字段**:`sku` 字段最大2000字符,不再设置唯一索引(可根据业务需求决定是否唯一) -6. **JSON字段**:`sales_attrs` 和 `size` 字段存储JSON格式数据,需要应用层进行解析 - diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/MtPayApplication.java b/mt-pay/src/main/java/com/mtkj/mtpay/MtPayApplication.java index 4eb501d..80168c5 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/MtPayApplication.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/MtPayApplication.java @@ -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) { diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/CreateCustomerOrderRequestDTO.java b/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/CreateCustomerOrderRequestDTO.java index 70a7fa1..eb154e3 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/CreateCustomerOrderRequestDTO.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/dto/request/CreateCustomerOrderRequestDTO.java @@ -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; } diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/CustomerOrderResponseDTO.java b/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/CustomerOrderResponseDTO.java index c242115..b4c0286 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/CustomerOrderResponseDTO.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/dto/response/CustomerOrderResponseDTO.java @@ -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 shippingSpecialFields; + private Long paymentOrderId; private String paymentStatus; private String remark; diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/entity/CustomerOrder.java b/mt-pay/src/main/java/com/mtkj/mtpay/entity/CustomerOrder.java index e2a420f..1eb6d4b 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/entity/CustomerOrder.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/entity/CustomerOrder.java @@ -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 getShippingSpecialFieldsMap() { + if (shippingSpecialFields == null || shippingSpecialFields.trim().isEmpty()) { + return new HashMap<>(); + } + try { + return objectMapper.readValue(shippingSpecialFields, + new TypeReference>() {}); + } catch (Exception e) { + log.warn("解析特殊字段JSON失败: {}", shippingSpecialFields, e); + return new HashMap<>(); + } + } + + /** + * 设置特殊字段Map(转换为JSON字符串) + */ + public void setShippingSpecialFieldsMap(Map 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 map = getShippingSpecialFieldsMap(); + Object value = map.get(key); + return value != null ? value.toString() : null; + } + + /** + * 设置特殊字段值 + */ + public void setSpecialField(String key, String value) { + Map map = getShippingSpecialFieldsMap(); + if (value != null && !value.trim().isEmpty()) { + map.put(key, value); + } else { + map.remove(key); + } + setShippingSpecialFieldsMap(map); + } } diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java index 930ef51..bbcbb4c 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java @@ -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 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); + // 翻译失败不影响订单数据返回,只记录日志 + } + } } diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java index 02aa6ed..63401ed 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java @@ -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); + // 翻译失败不影响商品数据返回,只记录日志 + } + } } diff --git a/mt-pay/src/main/resources/application-dev.yml b/mt-pay/src/main/resources/application-dev.yml index e83957a..2698a76 100644 --- a/mt-pay/src/main/resources/application-dev.yml +++ b/mt-pay/src/main/resources/application-dev.yml @@ -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 +