feat(order): 扩展订单地址字段并集成百度翻译服务
- 添加东南亚地址扩展字段到CreateCustomerOrderRequestDTO - 在CustomerOrder实体类中新增详细地址字段和特殊地址字段JSON存储 - 实现CustomerOrderServiceImpl中地址字段的存储和转换逻辑 - 集成BaiduTranslatorUtils实现订单内容自动翻译功能 - 在CustomerOrderResponseDTO中添加特殊地址字段Map格式支持 - 配置百度翻译API相关参数到application-dev.yml - 移除过时的架构文档和配置说明文件
This commit is contained in:
108
FIX_COMPILE.md
108
FIX_COMPILE.md
@@ -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重新导入
|
||||
|
||||
121
HOW_TO_START.md
121
HOW_TO_START.md
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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. 如果修改端口,需要同步更新相关配置
|
||||
|
||||
@@ -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` 安装依赖
|
||||
|
||||
@@ -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 启动)
|
||||
|
||||
@@ -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<String, Object>
|
||||
**响应:** 处理结果
|
||||
|
||||
#### 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调用成功率
|
||||
- 监控回调处理情况
|
||||
- 监控订单状态分布
|
||||
|
||||
@@ -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<T>**: 统一响应结果类,所有接口返回统一格式
|
||||
- **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<T>返回
|
||||
2. **异常处理**: 使用BusinessException抛出业务异常
|
||||
3. **枚举使用**: 使用枚举替代魔法字符串
|
||||
4. **工具类**: 通用功能封装为工具类
|
||||
5. **配置管理**: 配置统一管理,支持多环境
|
||||
|
||||
## 扩展建议
|
||||
|
||||
1. **日志切面**: 添加AOP日志切面
|
||||
2. **接口文档**: 集成Swagger/OpenAPI
|
||||
3. **缓存**: 添加Redis缓存支持
|
||||
4. **消息队列**: 添加消息队列支持
|
||||
5. **监控**: 添加系统监控和链路追踪
|
||||
|
||||
@@ -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` 注解
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
# Druid数据源配置说明
|
||||
|
||||
## 配置概述
|
||||
|
||||
项目已配置使用Alibaba Druid数据源,支持主从数据源配置。
|
||||
|
||||
## 依赖配置
|
||||
|
||||
在 `pom.xml` 中已添加Druid依赖:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
<version>1.2.20</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 配置文件
|
||||
|
||||
### 主配置文件: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. **监控统计**:生产环境可以关闭部分监控功能以提升性能
|
||||
|
||||
@@ -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. 检查应用日志中的数据库连接信息(注意:密码不会显示)
|
||||
|
||||
@@ -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
|
||||
<logger name="com.mtkj.mtpay.YourClass" level="OFF"/>
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
项目已全面集成SLF4J日志功能,所有关键操作都有日志记录。通过合理的日志级别和格式,可以快速定位问题、监控系统运行状态。
|
||||
|
||||
311
mt-pay/README.md
311
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
|
||||
|
||||
|
||||
@@ -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<String, Object> params)` - 生成签名
|
||||
- `generateSign(Map<String, Object> params, String secret, String signType)` - 生成签名(指定密钥和类型)
|
||||
- `verifySign(Map<String, Object> 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<String, Object> 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`指定
|
||||
|
||||
@@ -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. 记录版本变更日志
|
||||
|
||||
@@ -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`;
|
||||
```
|
||||
然后重新执行创建脚本
|
||||
|
||||
@@ -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表)
|
||||
```
|
||||
|
||||
@@ -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格式数据,需要应用层进行解析
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
// 翻译失败不影响订单数据返回,只记录日志
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
// 翻译失败不影响商品数据返回,只记录日志
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user