Compare commits

...

4 Commits

Author SHA1 Message Date
bbf235362c docs(guide): 添加502错误排查指南并移除端口修改指南
- 添加502 Bad Gateway错误排查指南文档
- 移除旧的端口修改指南文档
- 提供完整的错误排查步骤和解决方案
- 包含Nginx配置检查和后端服务验证流程
- 添加快速修复步骤和常见问题处理
- 移除过时的端口变更操作说明
2025-12-26 14:11:13 +08:00
56ae5a5892 chore(config): 更新服务器端口配置从8082到18082
- 修改application-dev.yml和application-prod.yml中的server.port配置
- 更新PayPal webhook URL中的端口号
- 修改DEPLOYMENT_README.md中的端口引用信息
- 更新ProductServiceImpl.java中的端口配置注入
- 创建PORT_CHANGE_GUIDE.md提供端口修改操作指南
- 更新部署文档中的防火墙端口和Nginx配置说明
2025-12-26 13:43:26 +08:00
2d9a9c3668 feat(config): 更新开发和生产环境配置
- 修改开发环境前端URL从localhost改为公网地址
- 更新PayPal Webhook URL使用公网地址配置
- 在生产环境配置中添加服务器端口和应用配置
- 添加PayPal支付配置的详细注释说明
- 简化ERP用户管理API文档格式,移除冗余说明
- 移除PayPal订单生命周期和Webhook指南文档
- 优化PayPal Webhook配置文档内容

fix(order): 修复订单创建和库存管理并发问题

- 实现SELECT FOR UPDATE锁定SKU记录防止超卖
- 添加库存扣减原子操作确保并发安全
- 简化日志输出,移除冗余调试信息
- 添加订单取消功能并恢复库存
- 优化订单查询和状态更新逻辑

feat(mapper): 添加库存扣减和恢复功能

- 实现deductStock方法用于扣减库存
- 添加restoreStock方法用于恢复库存
- 实现selectByIdForUpdate方法用于悲观锁
- 为Mapper接口添加必要的注解支持
2025-12-26 10:54:01 +08:00
f8d116f9a3 feat(order): 添加订单列表查询功能
- 新增 OrderQueryRequestDTO 请求对象,支持多条件查询和分页参数
- 新增 OrderListResponseDTO 响应对象,包含客户订单和 PayPal 订单信息
- 在 CustomerOrderController 中添加 /query 接口,支持 POST 方式查询订单列表
- 在 CustomerOrderService 和实现类中添加 queryOrders 方法,实现订单查询逻辑
- 支持按订单号、状态、客户信息、商品名称等条件查询
- 支持按 PayPal 订单相关条件查询,关联查询 PayPal 支付信息
- 实现分页查询功能,限制每页最大数量为 100
- 添加时间范围查询和多种排序功能
- 优化 N+1 查询问题,批量查询 PayPal 订单信息
- 添加详细的查询日志记录和性能监控
- 更新项目完善计划文档,记录待办功能和优化项
2025-12-25 18:11:57 +08:00
25 changed files with 7791 additions and 21754 deletions

View File

@@ -0,0 +1,220 @@
# 502 Bad Gateway 错误排查指南
## 错误说明
`502 Bad Gateway` 表示 Nginx 无法连接到后端服务。通常是因为:
1. **后端服务未启动**
2. **Nginx配置中的端口不正确**(最常见)
3. **后端服务启动失败**
4. **防火墙阻止连接**
## 排查步骤
### 1. 检查后端服务是否启动
在服务器上执行:
```bash
# 检查18082端口是否在监听
netstat -tlnp | grep 18082
# 或
ss -tlnp | grep 18082
# 检查Java进程
ps aux | grep java
# 或
jps -l
```
**如果端口没有监听:**
- 后端服务没有启动
- 需要启动后端服务
### 2. 检查Nginx配置最重要
在宝塔面板中:
1. 网站 → 设置 → 配置文件
2. 找到 `location /api/` 配置块
3. **确认 `proxy_pass` 端口是 `18082`**
**正确的配置:**
```nginx
location /api/ {
proxy_pass http://127.0.0.1:18082; # ← 必须是18082不是8082
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
```
**错误的配置会导致502**
```nginx
location /api/ {
proxy_pass http://127.0.0.1:8082; # ← 错误端口还是8082
...
}
```
**修改后必须:**
1. 点击"保存"
2. 点击"重载配置"或"重启Nginx"
### 3. 测试后端服务是否正常
在服务器上执行:
```bash
# 测试后端API是否可访问
curl http://127.0.0.1:18082/api/health
# 或
curl http://127.0.0.1:18082/api/erp/user/login -X POST -H "Content-Type: application/json" -d '{"username":"test","password":"test"}'
```
**如果返回连接拒绝:**
- 后端服务没有启动
- 检查启动日志
**如果返回正常响应:**
- 后端服务正常
- 问题在Nginx配置
### 4. 检查后端启动日志
查看后端启动日志,确认:
- 服务是否成功启动
- 是否监听在 `18082` 端口
- 是否有错误信息
```bash
# 如果使用nohup启动
tail -f app.log
# 或查看Spring Boot启动日志
# 应该看到类似信息:
# Tomcat started on port(s): 18082 (http)
```
### 5. 检查防火墙
在宝塔面板中:
1. 安全 → 防火墙
2. 确认端口 `18082` 已开放(如果后端需要外部访问)
3. **注意:** 如果后端只监听 `127.0.0.1`,不需要开放防火墙
### 6. 检查后端监听地址
确认后端配置:
**application-dev.yml:**
```yaml
server:
port: 18082
# 如果没有配置 address默认监听 0.0.0.0(所有网卡)
# 如果配置了 address: 127.0.0.1,只监听本地
```
**如果后端只监听 127.0.0.1**
- Nginx的 `proxy_pass` 必须使用 `http://127.0.0.1:18082`
- 不能使用 `http://175.178.252.59:18082`
## 快速修复步骤
### 步骤1确认后端服务已启动
```bash
# 在服务器上执行
java -jar mt-startup-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
# 或后台运行
nohup java -jar mt-startup-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev > app.log 2>&1 &
```
### 步骤2修改Nginx配置
在宝塔面板中修改配置文件:
```nginx
location /api/ {
proxy_pass http://127.0.0.1:18082; # 确保是18082
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 添加超时配置(可选)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
```
### 步骤3重载Nginx
在宝塔面板中:
1. 保存配置
2. 重载配置
### 步骤4验证
```bash
# 在服务器上测试
curl http://127.0.0.1/api/erp/user/login -X POST -H "Content-Type: application/json" -d '{"username":"test","password":"test"}'
```
## 常见问题
### Q1: 修改了Nginx配置但还是502
**A:** 检查是否:
- 保存了配置
- 重载了Nginx不是只保存
- 确认端口是 `18082`,不是 `8082`
### Q2: 后端服务启动了但Nginx还是502
**A:** 检查:
- Nginx配置中的端口是否正确
- 后端是否真的在 `18082` 端口监听
- Nginx错误日志`/www/wwwlogs/175.178.252.59.error.log`
### Q3: 如何查看Nginx错误日志
**A:** 在宝塔面板中:
- 网站 → 日志 → 错误日志
- 或直接查看:`/www/wwwlogs/175.178.252.59.error.log`
查看是否有类似错误:
```
connect() failed (111: Connection refused) while connecting to upstream
```
这表示无法连接到后端。
## 验证清单
- [ ] 后端服务已启动
- [ ] 后端监听在 `18082` 端口
- [ ] Nginx配置中 `proxy_pass` 端口是 `18082`
- [ ] Nginx配置已保存并重载
- [ ] 防火墙已开放(如果需要)
- [ ] 后端启动日志无错误
## 如果还是无法解决
1. **查看Nginx错误日志**
```bash
tail -f /www/wwwlogs/175.178.252.59.error.log
```
2. **查看后端启动日志**
```bash
tail -f app.log
```
3. **检查端口占用**
```bash
netstat -tlnp | grep 18082
```
4. **测试后端直接访问**
```bash
curl http://127.0.0.1:18082/api/health
```

72
DEPLOYMENT_README.md Normal file
View File

@@ -0,0 +1,72 @@
# 部署说明
## 快速部署指南
### 后端部署
1. **配置文件已更新**
-`app.frontend.url` 已设置为 `http://175.178.252.59:3000`
-`paypal.webhook-url` 已设置为 `http://175.178.252.59:18082/api/paypal/webhook`
- ✅ 前端 `.env.production` 已创建API地址为 `/api`(相对路径)
2. **打包应用**
```bash
cd E:\MTKJPAY
mvn clean package -DskipTests
```
3. **上传并启动**
```bash
# 上传 jar 文件到服务器
# 启动应用
java -jar mt-pay-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
# 或后台运行
nohup java -jar mt-pay-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev > app.log 2>&1 &
```
### 前端部署
1. **构建前端**
```bash
cd E:\MTKJPAY-FRONT
npm run build
```
2. **本地测试打包结果(验证打包是否正确)**
```bash
cd dist
python -m http.server 3000
# 或
npx http-server -p 3000
# 访问 http://localhost:3000应该能看到页面不是空白
```
3. **API地址配置**
- ✅ `.env.production` 文件已创建
- ✅ API地址已设置为 `/api`相对路径通过Nginx代理
- ⚠️ **重要:** 必须使用相对路径 `/api`不要使用完整URL
4. **上传到服务器**
```bash
# 上传dist目录下的所有文件到服务器
scp -r dist/* root@175.178.252.59:/path/to/web/root/
```
5. **服务器配置**
- 如果服务器已有Web服务器将dist内容放到Web根目录
- 确保支持Vue Router的history模式配置 `try_files`
## 配置检查清单
部署前请检查以下配置:
- [x] 后端 `app.frontend.url` 已设置为 `http://175.178.252.59:3000`
- [x] 后端 `paypal.webhook-url` 已设置为 `http://175.178.252.59:18082/api/paypal/webhook`
- [x] 前端API地址已配置为 `/api`相对路径通过Nginx代理
- [ ] 数据库连接信息正确
- [ ] 服务器防火墙已开放必要端口18082、3000等
- [ ] PayPal控制台Webhook URL已更新为 `http://175.178.252.59:18082/api/paypal/webhook`
- [ ] **Nginx配置中的proxy_pass端口已更新为18082**

View File

@@ -1,46 +0,0 @@
# PayPal沙箱测试账号说明
## API凭证已配置到 application-dev.yml
- **Client IDAPI密钥**: `AdGYUZpvLuHR30dybOApvM-RNB1pVKtd74SVfh-6TK52xV-1JEBddHVMCWuDdyyHri4DXd4kABBi7Icb`
- **Client Secret密钥**: `ENblspyRmwsOU_PWFurlhEYUF5Da6aYKl0pjK4ehm7p3R5aSqvbpaF_YsIIs8v0ty1c9WJu15XP-Fe_1`
这些凭证已自动配置到 `application-dev.yml` 中,无需手动配置。
## 沙箱测试账号(用于测试支付流程)
### 商家账号Business Account
- **用户名**: `sb-vtcmz48304367@business.example.com`
- **密码**: `iN)7:z#4`
### 买家账号Personal Account
PayPal沙箱环境通常会提供多个测试账号你可以在PayPal开发者控制台查看。
## 测试流程
1. **启动应用**:确保使用 `dev` 环境配置启动
2. **创建订单**:在系统中创建测试订单
3. **点击支付**:在订单确认页面点击"立即支付"
4. **PayPal登录**使用上述商家账号或买家账号登录PayPal沙箱
5. **完成支付**在PayPal页面完成支付流程
6. **查看结果**:支付完成后会自动跳转回系统,查看订单状态
## 注意事项
1. **环境切换**:当前配置为沙箱环境(`mode: sandbox`
2. **生产环境**:上线前需要:
- 在PayPal开发者控制台获取生产环境的Client ID和Secret
- 修改 `application-prod.yml` 中的配置
-`mode` 改为 `production`
3. **测试账号**:沙箱测试账号仅用于开发测试,不能用于真实交易
## PayPal沙箱控制台
访问地址https://developer.paypal.com/dashboard/
在这里可以:
- 查看API凭证
- 管理测试账号
- 查看交易记录
- 配置Webhook等

View File

@@ -1,170 +0,0 @@
# PayPal Webhook处理指南
## 概述
PayPal Webhook是PayPal向你的服务器发送事件通知的机制。当支付状态发生变化时PayPal会主动推送事件到你的Webhook URL。
## Webhook URL配置
在你的PayPal开发者控制台中配置Webhook URL
- **开发环境**: `https://你的域名/api/paypal/webhook`
- **生产环境**: `https://你的生产域名/api/paypal/webhook`
## 支持的事件类型
系统已实现以下事件类型的处理:
### 1. PAYMENT.CAPTURE.COMPLETED
**触发时机**: 当PayPal成功捕获资金时触发
**处理逻辑**:
- 提取capture信息金额、货币、状态等
- 通过`supplementary_data.related_ids.order_id``custom_id`获取ERP订单号
- 更新ERP订单状态为"已支付"PAID
### 2. PAYMENT.CAPTURE.DENIED
**触发时机**: 当支付捕获被拒绝时触发
**处理逻辑**:
- 记录支付失败信息
- 更新ERP订单状态为"支付失败"
### 3. PAYMENT.CAPTURE.REFUNDED
**触发时机**: 当支付被退款时触发
**处理逻辑**:
- 记录退款信息
- 更新ERP订单状态为"已退款"
### 4. CHECKOUT.ORDER.APPROVED
**触发时机**: 当订单被顾客批准时触发
**处理逻辑**:
- 记录订单批准信息
- 可以触发自动捕获逻辑(如果需要)
### 5. CHECKOUT.ORDER.COMPLETED
**触发时机**: 当订单完成时触发
**处理逻辑**:
- 记录订单完成信息
- 根据业务需求处理订单完成逻辑
### 6. CHECKOUT.ORDER.CANCELLED
**触发时机**: 当订单被取消时触发
**处理逻辑**:
- 记录订单取消信息
- 更新ERP订单状态为"已取消"
## 签名验证
### 开发环境(沙箱)
- 签名验证可以暂时跳过(已实现自动跳过)
- 但建议在测试时也启用验证,确保代码正确
### 生产环境
- **必须启用签名验证**
- 需要在配置文件中设置`webhook-id`
- 系统会自动验证每个Webhook请求的签名
## 配置Webhook ID
### 1. 获取Webhook ID
1. 登录PayPal开发者控制台https://developer.paypal.com/dashboard/
2. 进入你的应用设置
3. 找到Webhook配置部分
4. 创建或查看Webhook获取Webhook ID
### 2. 配置到系统
`application-dev.yml``application-prod.yml`中添加:
```yaml
paypal:
webhook-id: YOUR_WEBHOOK_ID
```
## 订单号关联
PayPal Webhook事件中需要关联ERP订单号系统会按以下顺序尝试获取
1. **从`supplementary_data.related_ids.order_id`获取**
- 这是PayPal自动填充的包含原始订单ID
- 然后通过查询PayPal订单详情获取`reference_id`即ERP订单号
2. **从`custom_id`获取**
- 如果创建PayPal订单时设置了`custom_id`,可以直接获取
3. **通过`order_id`查询订单详情**
- 如果上述方法都无法获取会通过PayPal API查询订单详情
- 从订单的`purchase_units[0].reference_id`获取ERP订单号
## 测试Webhook
### 使用PayPal Webhook模拟器
1. 登录PayPal开发者控制台
2. 进入Webhook配置页面
3. 使用"Send test webhook"功能
4. 选择要测试的事件类型
5. 系统会收到测试事件并处理
### 本地测试
如果需要在本地测试,可以使用以下工具:
1. **ngrok**(推荐):
```bash
ngrok http 8082
```
然后将ngrok提供的URL配置到PayPal Webhook URL
2. **PayPal Webhook模拟器**:
使用Postman等工具模拟Webhook请求
## 日志查看
Webhook处理的所有步骤都会记录日志包括
- 接收到的Webhook事件
- 签名验证结果
- 事件处理过程
- 订单状态更新结果
查看日志位置:
- 应用日志文件
- 控制台输出
## 常见问题
### 1. Webhook未收到事件
- 检查Webhook URL是否正确配置
- 确认服务器可以访问(防火墙、网络等)
- 检查PayPal控制台中的Webhook状态
### 2. 签名验证失败
- 确认Webhook ID配置正确
- 检查请求头是否完整
- 确认使用的是正确的环境(沙箱/生产)
### 3. 无法关联ERP订单
- 确认创建PayPal订单时设置了`reference_id`
- 检查订单号格式是否正确
- 查看日志中的详细信息
## 安全建议
1. **生产环境必须启用签名验证**
2. **使用HTTPS**PayPal要求
3. **记录所有Webhook事件**(用于审计和调试)
4. **实现幂等性处理**(防止重复处理同一事件)
5. **设置超时和重试机制**
## 代码位置
- **Webhook控制器**: `PayPalController.webhook()`
- **Webhook服务**: `PayPalWebhookService` 和 `PayPalWebhookServiceImpl`
- **事件DTO**: `PayPalWebhookEventDTO`
- **配置**: `PayPalProperties.webhookId`

View File

@@ -0,0 +1,39 @@
# 项目改进计划
## 已完成功能
- ✅ 商品管理(创建、查询、下架、分页)
- ✅ 订单管理(创建、查询、分页、状态管理)
- ✅ PayPal支付集成创建订单、捕获支付、Webhook处理
- ✅ 库存管理(扣减、恢复)
- ✅ 货币转换(实时汇率、多币种支持)
- ✅ ERP用户系统注册、登录、信息管理
- ✅ 国际化支持6种语言
## 待实现功能(按优先级)
### 高优先级
1. **商品编辑功能** - 支持修改商品信息
2. **订单详情页面** - 显示完整订单和支付信息
3. **订单状态管理** - 发货、完成、取消功能
4. **密码加密升级** - MD5升级为BCrypt
### 中优先级
5. **JWT Token实现** - 替换MD5+Base64 Token
6. **Redis缓存集成** - PayPal token和汇率缓存
7. **角色权限系统** - 角色和权限管理
8. **操作日志系统** - 记录用户操作
### 低优先级
9. **单元测试** - 测试覆盖率>70%
10. **Swagger文档** - API文档自动生成
11. **数据统计** - 销售和订单统计
12. **容器化部署** - Docker支持
## 实施建议
**第一阶段1-2周**:商品编辑、订单详情、订单状态管理、密码加密升级
**第二阶段2-3周**JWT Token、角色权限系统
**第三阶段3-4周**Redis缓存、操作日志、性能优化

763
hs_err_pid15540.log Normal file
View File

@@ -0,0 +1,763 @@
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffbad9b3ff4, pid=15540, tid=5008
#
# JRE version: Java(TM) SE Runtime Environment (17.0.12+8) (build 17.0.12+8-LTS-286)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (17.0.12+8-LTS-286, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# Problematic frame:
# V [jvm.dll+0x6a3ff4]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
# https://bugreport.java.com/bugreport/crash.jsp
#
--------------- S U M M A R Y ------------
Command Line: -Dmaven.multiModuleProjectDirectory=E:\MTKJPAY -Djansi.passthrough=true -Dmaven.home=D:/develop/Maven/apache-maven-3.9.4 -Dclassworlds.conf=D:\develop\Maven\apache-maven-3.9.4\bin\m2.conf -Dmaven.ext.class.path=D:\develop\IntelliJ IDEA 2025.1\plugins\maven\lib\maven-event-listener.jar -javaagent:D:\develop\IntelliJ IDEA 2025.1\lib\idea_rt.jar=57907 -Dfile.encoding=UTF-8 org.codehaus.classworlds.Launcher -Didea.version=2025.1.1.1 -s D:\develop\Maven\apache-maven-3.9.4\conf\settings.xml -Dmaven.repo.local=D:\develop\Maven\repository package
Host: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz, 12 cores, 31G, Windows 11 , 64 bit Build 26100 (10.0.26100.7309)
Time: Fri Dec 26 13:43:45 2025 Windows 11 , 64 bit Build 26100 (10.0.26100.7309) elapsed time: 11.659119 seconds (0d 0h 0m 11s)
--------------- T H R E A D ---------------
Current thread (0x000000007c137d50): JavaThread "C2 CompilerThread1" daemon [_thread_in_native, id=5008, stack(0x0000000076500000,0x0000000076600000)]
Current CompileTask:
C2: 11659 12251 ! 4 java.lang.ClassLoader::loadClass (121 bytes)
Stack: [0x0000000076500000,0x0000000076600000], sp=0x00000000765fbd50, free space=1007k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [jvm.dll+0x6a3ff4]
V [jvm.dll+0x1ba717]
V [jvm.dll+0x216997]
V [jvm.dll+0x215c6f]
V [jvm.dll+0x1a2960]
V [jvm.dll+0x22610b]
V [jvm.dll+0x2242ab]
V [jvm.dll+0x79075c]
V [jvm.dll+0x78abea]
V [jvm.dll+0x678f35]
C [ucrtbase.dll+0x37b0]
C [KERNEL32.DLL+0x2e8d7]
C [ntdll.dll+0x8c53c]
siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), reading address 0x00007ffbacbe6160
Registers:
RAX=0x00007ffbacbe6070, RBX=0x000000000000026c, RCX=0x000000007be2a4f8, RDX=0x000000007e434050
RSP=0x00000000765fbd50, RBP=0x00000000774112b0, RSI=0x00000000765fc3c0, RDI=0x000000000000026c
R8 =0x0000000000030101, R9 =0x00000000000002ac, R10=0x000000000000000d, R11=0x00000000785b23f0
R12=0x000000007be2a4f8, R13=0x000000007dcf0c50, R14=0x000000007dcf0c50, R15=0x000000000000026d
RIP=0x00007ffbad9b3ff4, EFLAGS=0x0000000000010246
Register to memory mapping:
RIP=0x00007ffbad9b3ff4 jvm.dll
RAX=0x00007ffbacbe6070 is an unknown value
RBX=0x000000000000026c is an unknown value
RCX=0x000000007be2a4f8 points into unknown readable memory: 0x00007ffbacbe6070 | 70 60 be ac fb 7f 00 00
RDX=0x000000007e434050 points into unknown readable memory: 0x0000000000000000 | 00 00 00 00 00 00 00 00
RSP=0x00000000765fbd50 is pointing into the stack for thread: 0x000000007c137d50
RBP=0x00000000774112b0 points into unknown readable memory: 0x000000007930b540 | 40 b5 30 79 00 00 00 00
RSI=0x00000000765fc3c0 is pointing into the stack for thread: 0x000000007c137d50
RDI=0x000000000000026c is an unknown value
R8 =0x0000000000030101 points into unknown readable memory: 00 00 00 c8 5c 00 00
R9 =0x00000000000002ac is an unknown value
R10=0x000000000000000d is an unknown value
R11=0x00000000785b23f0 points into unknown readable memory: 0x00007ffbadbe6070 | 70 60 be ad fb 7f 00 00
R12=0x000000007be2a4f8 points into unknown readable memory: 0x00007ffbacbe6070 | 70 60 be ac fb 7f 00 00
R13=0x000000007dcf0c50 points into unknown readable memory: 0x000000007930b540 | 40 b5 30 79 00 00 00 00
R14=0x000000007dcf0c50 points into unknown readable memory: 0x000000007930b540 | 40 b5 30 79 00 00 00 00
R15=0x000000000000026d is an unknown value
Top of Stack: (sp=0x00000000765fbd50)
0x00000000765fbd50: 00000000785b23f0 0000000000001310
0x00000000765fbd60: 00000000774112b0 0000000081f3fd80
0x00000000765fbd70: 00000000774112b0 000000007dcf0c50
0x00000000765fbd80: 0000000000000262 00000000ffffffff
0x00000000765fbd90: 000000007dcf0c50 000000007be2a4f8
0x00000000765fbda0: 0000000081f3fd80 000000007f6bae38
0x00000000765fbdb0: 0000000081318ff0 0000000000000510
0x00000000765fbdc0: 00000000000022f4 00000000785eb5d0
0x00000000765fbdd0: 000000007f7ea3f0 000000007c36c550
0x00000000765fbde0: 0000004000000022 000000007c36c550
0x00000000765fbdf0: 0000000000000000 00000000785eb5a0
0x00000000765fbe00: 000000007930b540 000000007f7e88c0
0x00000000765fbe10: 000000007f7ea3f0 000000007f7f08a8
0x00000000765fbe20: 000000000041f988 000000007d23aab0
0x00000000765fbe30: 00000000000062f8 0000000000000000
0x00000000765fbe40: 000000000000752f 00007ffbad4cadc3
Instructions: (pc=0x00007ffbad9b3ff4)
0x00007ffbad9b3ef4: 70 48 69 d2 b0 00 00 00 48 03 51 20 8b c8 e8 79
0x00007ffbad9b3f04: 58 02 00 be 01 00 00 00 44 8b e0 3b c6 0f 8e 9e
0x00007ffbad9b3f14: 00 00 00 66 0f 1f 84 00 00 00 00 00 44 8b 4d 08
0x00007ffbad9b3f24: 41 8b df 2b de 8b fb 41 3b d9 73 0b 48 8b 45 10
0x00007ffbad9b3f34: 48 83 3c d8 00 75 6f 81 fb bf 02 00 00 7d 33 48
0x00007ffbad9b3f44: 6b 44 24 70 16 4c 8b c7 49 c1 e8 06 4c 03 c0 48
0x00007ffbad9b3f54: 8b 84 24 f0 01 00 00 48 8b 40 70 48 8b 50 20 8b
0x00007ffbad9b3f64: c3 83 e0 3f 0f b6 c8 4a 8b 44 c2 38 48 0f a3 c8
0x00007ffbad9b3f74: 73 34 41 3b d9 72 0a 8b d3 48 8b cd e8 6b 4e fb
0x00007ffbad9b3f84: ff 48 8b 45 10 48 8b 4c 24 58 48 89 0c d8 41 3b
0x00007ffbad9b3f94: 5d 08 72 0a 8b d3 49 8b cd e8 4e 4e fb ff 49 8b
0x00007ffbad9b3fa4: 45 10 4c 89 34 d8 ff c6 41 3b f4 0f 8c 6b ff ff
0x00007ffbad9b3fb4: ff 48 8b b4 24 f0 01 00 00 4c 8b 64 24 48 8b 84
0x00007ffbad9b3fc4: 24 f8 01 00 00 ff c0 89 84 24 f8 01 00 00 41 3b
0x00007ffbad9b3fd4: 44 24 18 0f 82 f3 fd ff ff 4c 8b 74 24 40 41 8b
0x00007ffbad9b3fe4: 44 24 2c 24 03 3c 02 75 11 49 8b 04 24 49 8b cc
0x00007ffbad9b3ff4: ff 90 f0 00 00 00 8b f8 eb 02 33 ff bb 01 00 00
0x00007ffbad9b4004: 00 41 39 5c 24 18 76 59 4c 8b 6c 24 40 4c 8b 7c
0x00007ffbad9b4014: 24 50 66 66 0f 1f 84 00 00 00 00 00 3b fb 4d 8b
0x00007ffbad9b4024: cf 44 8b c3 49 8b d4 0f 95 c0 48 8b ce 88 44 24
0x00007ffbad9b4034: 30 4c 89 6c 24 28 48 89 6c 24 20 e8 dc e6 ff ff
0x00007ffbad9b4044: 44 8b 94 24 00 02 00 00 ff c3 44 2b d0 44 89 94
0x00007ffbad9b4054: 24 00 02 00 00 41 3b 5c 24 18 72 c0 4d 8b f5 eb
0x00007ffbad9b4064: 08 44 8b 94 24 00 02 00 00 49 63 4c 24 28 48 8b
0x00007ffbad9b4074: 86 08 01 00 00 8b 14 88 85 d2 0f 84 be 06 00 00
0x00007ffbad9b4084: 48 8b 46 70 44 8b f2 4c 8b bc 24 f0 01 00 00 48
0x00007ffbad9b4094: 69 fa b0 00 00 00 48 8b 48 20 49 8b d4 8b 74 39
0x00007ffbad9b40a4: 30 49 8b cf e8 e3 07 00 00 48 8b d8 48 89 44 24
0x00007ffbad9b40b4: 70 48 8b 44 24 40 44 8b ee 44 8b e6 3b 70 08 73
0x00007ffbad9b40c4: 46 48 8b 48 10 48 8d 50 10 48 89 54 24 58 48 83
0x00007ffbad9b40d4: 3c f1 00 74 3b 48 8b c1 48 8b 0c f1 83 79 20 00
0x00007ffbad9b40e4: 75 2e 48 c7 04 f0 00 00 00 00 3b 75 08 72 0a 8b
Stack slot to memory mapping:
stack at sp + 0 slots: 0x00000000785b23f0 points into unknown readable memory: 0x00007ffbadbe6070 | 70 60 be ad fb 7f 00 00
stack at sp + 1 slots: 0x0000000000001310 is an unknown value
stack at sp + 2 slots: 0x00000000774112b0 points into unknown readable memory: 0x000000007930b540 | 40 b5 30 79 00 00 00 00
stack at sp + 3 slots: 0x0000000081f3fd80 points into unknown readable memory: 0x00007ffbadbd1c30 | 30 1c bd ad fb 7f 00 00
stack at sp + 4 slots: 0x00000000774112b0 points into unknown readable memory: 0x000000007930b540 | 40 b5 30 79 00 00 00 00
stack at sp + 5 slots: 0x000000007dcf0c50 points into unknown readable memory: 0x000000007930b540 | 40 b5 30 79 00 00 00 00
stack at sp + 6 slots: 0x0000000000000262 is an unknown value
stack at sp + 7 slots: 0x00000000ffffffff is an unknown value
--------------- P R O C E S S ---------------
Threads class SMR info:
_java_thread_list=0x00000000764809e0, length=15, elements={
0x0000000002c571b0, 0x000000002ed7d0f0, 0x000000002ed7e1d0, 0x000000002ed973e0,
0x000000002ed97cb0, 0x000000002ed98990, 0x000000002ed9a270, 0x000000002eda2e80,
0x000000002eda5210, 0x000000002edadc60, 0x000000002fc10d30, 0x000000002fc11240,
0x000000002fc11750, 0x000000007c137d50, 0x000000007c139d30
}
Java Threads: ( => current thread )
0x0000000002c571b0 JavaThread "main" [_thread_in_native, id=7516, stack(0x0000000002320000,0x0000000002420000)]
0x000000002ed7d0f0 JavaThread "Reference Handler" daemon [_thread_blocked, id=13644, stack(0x0000000075000000,0x0000000075100000)]
0x000000002ed7e1d0 JavaThread "Finalizer" daemon [_thread_blocked, id=1312, stack(0x0000000075100000,0x0000000075200000)]
0x000000002ed973e0 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=25640, stack(0x0000000075200000,0x0000000075300000)]
0x000000002ed97cb0 JavaThread "Attach Listener" daemon [_thread_blocked, id=23972, stack(0x0000000075300000,0x0000000075400000)]
0x000000002ed98990 JavaThread "Service Thread" daemon [_thread_blocked, id=9868, stack(0x0000000075400000,0x0000000075500000)]
0x000000002ed9a270 JavaThread "Monitor Deflation Thread" daemon [_thread_blocked, id=25172, stack(0x0000000075500000,0x0000000075600000)]
0x000000002eda2e80 JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=17288, stack(0x0000000075600000,0x0000000075700000)]
0x000000002eda5210 JavaThread "C1 CompilerThread0" daemon [_thread_blocked, id=12136, stack(0x0000000075700000,0x0000000075800000)]
0x000000002edadc60 JavaThread "Sweeper thread" daemon [_thread_blocked, id=17968, stack(0x0000000075800000,0x0000000075900000)]
0x000000002fc10d30 JavaThread "Common-Cleaner" daemon [_thread_blocked, id=21392, stack(0x0000000075900000,0x0000000075a00000)]
0x000000002fc11240 JavaThread "Monitor Ctrl-Break" daemon [_thread_in_native, id=5348, stack(0x0000000075a00000,0x0000000075b00000)]
0x000000002fc11750 JavaThread "Notification Thread" daemon [_thread_blocked, id=1984, stack(0x0000000075b00000,0x0000000075c00000)]
=>0x000000007c137d50 JavaThread "C2 CompilerThread1" daemon [_thread_in_native, id=5008, stack(0x0000000076500000,0x0000000076600000)]
0x000000007c139d30 JavaThread "C2 CompilerThread2" daemon [_thread_in_native, id=18648, stack(0x00000000775d0000,0x00000000776d0000)]
Other Threads:
0x000000002ed7a710 VMThread "VM Thread" [stack: 0x000000002fea0000,0x000000002ffa0000] [id=9784]
0x0000000002d1e780 WatcherThread [stack: 0x0000000075c00000,0x0000000075d00000] [id=11184]
0x0000000002d04cd0 GCTaskThread "GC Thread#0" [stack: 0x000000002c8d0000,0x000000002c9d0000] [id=19496]
0x0000000076634f80 GCTaskThread "GC Thread#1" [stack: 0x00000000776d0000,0x00000000777d0000] [id=11360]
0x0000000076635240 GCTaskThread "GC Thread#2" [stack: 0x00000000777d0000,0x00000000778d0000] [id=1392]
0x00000000764fe650 GCTaskThread "GC Thread#3" [stack: 0x00000000778d0000,0x00000000779d0000] [id=16964]
0x00000000762f3540 GCTaskThread "GC Thread#4" [stack: 0x00000000779d0000,0x0000000077ad0000] [id=8364]
0x00000000762f3800 GCTaskThread "GC Thread#5" [stack: 0x0000000077ad0000,0x0000000077bd0000] [id=4364]
0x00000000762f3ac0 GCTaskThread "GC Thread#6" [stack: 0x0000000077bd0000,0x0000000077cd0000] [id=15400]
0x00000000762f3f90 GCTaskThread "GC Thread#7" [stack: 0x0000000077cd0000,0x0000000077dd0000] [id=12028]
0x00000000764eb660 GCTaskThread "GC Thread#8" [stack: 0x0000000077dd0000,0x0000000077ed0000] [id=21332]
0x00000000764eb920 GCTaskThread "GC Thread#9" [stack: 0x0000000077ed0000,0x0000000077fd0000] [id=18796]
0x0000000002d176a0 ConcurrentGCThread "G1 Main Marker" [stack: 0x000000002c9d0000,0x000000002cad0000] [id=21656]
0x0000000002d17fc0 ConcurrentGCThread "G1 Conc#0" [stack: 0x000000002cad0000,0x000000002cbd0000] [id=11828]
0x00000000769f6a20 ConcurrentGCThread "G1 Conc#1" [stack: 0x00000000790a0000,0x00000000791a0000] [id=9600]
0x0000000076b1a8a0 ConcurrentGCThread "G1 Conc#2" [stack: 0x000000007a320000,0x000000007a420000] [id=25132]
0x000000002ecb5ee0 ConcurrentGCThread "G1 Refine#0" [stack: 0x000000002f8a0000,0x000000002f9a0000] [id=2668]
0x0000000079057ee0 ConcurrentGCThread "G1 Refine#1" [stack: 0x000000007b3f0000,0x000000007b4f0000] [id=10724]
0x0000000079059950 ConcurrentGCThread "G1 Refine#2" [stack: 0x000000007b4f0000,0x000000007b5f0000] [id=10936]
0x00000000790581d0 ConcurrentGCThread "G1 Refine#3" [stack: 0x000000007b5f0000,0x000000007b6f0000] [id=14896]
0x00000000790584c0 ConcurrentGCThread "G1 Refine#4" [stack: 0x000000007b6f0000,0x000000007b7f0000] [id=11324]
0x0000000079058aa0 ConcurrentGCThread "G1 Refine#5" [stack: 0x000000007ba10000,0x000000007bb10000] [id=24032]
0x000000007905a800 ConcurrentGCThread "G1 Refine#6" [stack: 0x000000007cd50000,0x000000007ce50000] [id=24052]
0x0000000079057bf0 ConcurrentGCThread "G1 Refine#7" [stack: 0x000000007b7f0000,0x000000007b8f0000] [id=15260]
0x0000000076a78150 ConcurrentGCThread "G1 Refine#8" [stack: 0x000000007b8f0000,0x000000007b9f0000] [id=18652]
0x000000002ecb66c0 ConcurrentGCThread "G1 Service" [stack: 0x000000002f9a0000,0x000000002faa0000] [id=1916]
Threads with active compile tasks:
C2 CompilerThread0 11691 12541 4 com.sun.tools.javac.jvm.ClassReader::setParameters (334 bytes)
C2 CompilerThread1 11691 12251 ! 4 java.lang.ClassLoader::loadClass (121 bytes)
C2 CompilerThread2 11691 12575 4 com.sun.tools.javac.code.Types$SignatureGenerator::assembleSig (525 bytes)
VM state: not at safepoint (normal execution)
VM Mutex/Monitor currently owned by a thread: None
Heap address: 0x0000000602c00000, size: 8148 MB, Compressed Oops mode: Zero based, Oop shift amount: 3
CDS archive(s) mapped at: [0x0000000030000000-0x0000000030bd0000-0x0000000030bd0000), size 12386304, SharedBaseAddress: 0x0000000030000000, ArchiveRelocationMode: 1.
Compressed class space mapped at: 0x0000000031000000-0x0000000071000000, reserved size: 1073741824
Narrow klass base: 0x0000000030000000, Narrow klass shift: 0, Narrow klass range: 0x100000000
GC Precious Log:
CPUs: 12 total, 12 available
Memory: 32591M
Large Page Support: Disabled
NUMA Support: Disabled
Compressed Oops: Enabled (Zero based)
Heap Region Size: 4M
Heap Min Capacity: 8M
Heap Initial Capacity: 512M
Heap Max Capacity: 8148M
Pre-touch: Disabled
Parallel Workers: 10
Concurrent Workers: 3
Concurrent Refinement Workers: 10
Periodic GC: Disabled
Heap:
garbage-first heap total 270336K, used 175129K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 24 young (98304K), 1 survivors (4096K)
Metaspace used 41184K, committed 41664K, reserved 1114112K
class space used 4542K, committed 4800K, reserved 1048576K
Heap Regions: E=young(eden), S=young(survivor), O=old, HS=humongous(starts), HC=humongous(continues), CS=collection set, F=free, OA=open archive, CA=closed archive, TAMS=top-at-mark-start (previous, next)
| 0|0x0000000602c00000, 0x0000000603000000, 0x0000000603000000|100%| O| |TAMS 0x0000000603000000, 0x0000000602c00000| Untracked
| 1|0x0000000603000000, 0x0000000603400000, 0x0000000603400000|100%| O| |TAMS 0x0000000603400000, 0x0000000603000000| Untracked
| 2|0x0000000603400000, 0x0000000603800000, 0x0000000603800000|100%| O| |TAMS 0x0000000603800000, 0x0000000603400000| Untracked
| 3|0x0000000603800000, 0x0000000603c00000, 0x0000000603c00000|100%| O| |TAMS 0x0000000603c00000, 0x0000000603800000| Untracked
| 4|0x0000000603c00000, 0x0000000604000000, 0x0000000604000000|100%| O| |TAMS 0x0000000604000000, 0x0000000603c00000| Untracked
| 5|0x0000000604000000, 0x0000000604400000, 0x0000000604400000|100%| O| |TAMS 0x0000000604400000, 0x0000000604000000| Untracked
| 6|0x0000000604400000, 0x0000000604800000, 0x0000000604800000|100%| O| |TAMS 0x0000000604800000, 0x0000000604400000| Untracked
| 7|0x0000000604800000, 0x0000000604c00000, 0x0000000604c00000|100%| O| |TAMS 0x0000000604c00000, 0x0000000604800000| Untracked
| 8|0x0000000604c00000, 0x0000000605000000, 0x0000000605000000|100%| O| |TAMS 0x0000000605000000, 0x0000000604c00000| Untracked
| 9|0x0000000605000000, 0x0000000605400000, 0x0000000605400000|100%| O| |TAMS 0x0000000605400000, 0x0000000605000000| Untracked
| 10|0x0000000605400000, 0x0000000605800000, 0x0000000605800000|100%| O| |TAMS 0x0000000605800000, 0x0000000605400000| Untracked
| 11|0x0000000605800000, 0x0000000605c00000, 0x0000000605c00000|100%|HS| |TAMS 0x0000000605800000, 0x0000000605800000| Complete
| 12|0x0000000605c00000, 0x0000000606000000, 0x0000000606000000|100%| O| |TAMS 0x0000000606000000, 0x0000000605c00000| Untracked
| 13|0x0000000606000000, 0x0000000606400000, 0x0000000606400000|100%| O| |TAMS 0x0000000606400000, 0x0000000606000000| Untracked
| 14|0x0000000606400000, 0x0000000606800000, 0x0000000606800000|100%| O| |TAMS 0x0000000606800000, 0x0000000606400000| Untracked
| 15|0x0000000606800000, 0x0000000606c00000, 0x0000000606c00000|100%| O| |TAMS 0x00000006068f1200, 0x0000000606800000| Untracked
| 16|0x0000000606c00000, 0x0000000607000000, 0x0000000607000000|100%| O| |TAMS 0x0000000606c00000, 0x0000000606c00000| Untracked
| 17|0x0000000607000000, 0x0000000607400000, 0x0000000607400000|100%| O| |TAMS 0x0000000607000000, 0x0000000607000000| Untracked
| 18|0x0000000607400000, 0x0000000607800000, 0x0000000607800000|100%| O| |TAMS 0x0000000607400000, 0x0000000607400000| Untracked
| 19|0x0000000607800000, 0x0000000607c00000, 0x0000000607c00000|100%| O| |TAMS 0x0000000607800000, 0x0000000607800000| Untracked
| 20|0x0000000607c00000, 0x0000000607e57600, 0x0000000608000000| 58%| O| |TAMS 0x0000000607c00000, 0x0000000607c00000| Untracked
| 21|0x0000000608000000, 0x0000000608000000, 0x0000000608400000| 0%| F| |TAMS 0x0000000608000000, 0x0000000608000000| Untracked
| 22|0x0000000608400000, 0x0000000608400000, 0x0000000608800000| 0%| F| |TAMS 0x0000000608400000, 0x0000000608400000| Untracked
| 23|0x0000000608800000, 0x0000000608800000, 0x0000000608c00000| 0%| F| |TAMS 0x0000000608800000, 0x0000000608800000| Untracked
| 24|0x0000000608c00000, 0x0000000608c00000, 0x0000000609000000| 0%| F| |TAMS 0x0000000608c00000, 0x0000000608c00000| Untracked
| 25|0x0000000609000000, 0x0000000609000000, 0x0000000609400000| 0%| F| |TAMS 0x0000000609000000, 0x0000000609000000| Untracked
| 26|0x0000000609400000, 0x0000000609400000, 0x0000000609800000| 0%| F| |TAMS 0x0000000609400000, 0x0000000609400000| Untracked
| 27|0x0000000609800000, 0x0000000609800000, 0x0000000609c00000| 0%| F| |TAMS 0x0000000609800000, 0x0000000609800000| Untracked
| 28|0x0000000609c00000, 0x0000000609c00000, 0x000000060a000000| 0%| F| |TAMS 0x0000000609c00000, 0x0000000609c00000| Untracked
| 29|0x000000060a000000, 0x000000060a000000, 0x000000060a400000| 0%| F| |TAMS 0x000000060a000000, 0x000000060a000000| Untracked
| 30|0x000000060a400000, 0x000000060a400000, 0x000000060a800000| 0%| F| |TAMS 0x000000060a400000, 0x000000060a400000| Untracked
| 31|0x000000060a800000, 0x000000060a800000, 0x000000060ac00000| 0%| F| |TAMS 0x000000060a800000, 0x000000060a800000| Untracked
| 32|0x000000060ac00000, 0x000000060ac00000, 0x000000060b000000| 0%| F| |TAMS 0x000000060ac00000, 0x000000060ac00000| Untracked
| 33|0x000000060b000000, 0x000000060b000000, 0x000000060b400000| 0%| F| |TAMS 0x000000060b000000, 0x000000060b000000| Untracked
| 34|0x000000060b400000, 0x000000060b400000, 0x000000060b800000| 0%| F| |TAMS 0x000000060b400000, 0x000000060b400000| Untracked
| 35|0x000000060b800000, 0x000000060b800000, 0x000000060bc00000| 0%| F| |TAMS 0x000000060b800000, 0x000000060b800000| Untracked
| 36|0x000000060bc00000, 0x000000060bc00000, 0x000000060c000000| 0%| F| |TAMS 0x000000060bc00000, 0x000000060bc00000| Untracked
| 37|0x000000060c000000, 0x000000060c000000, 0x000000060c400000| 0%| F| |TAMS 0x000000060c000000, 0x000000060c000000| Untracked
| 38|0x000000060c400000, 0x000000060c400000, 0x000000060c800000| 0%| F| |TAMS 0x000000060c400000, 0x000000060c400000| Untracked
| 39|0x000000060c800000, 0x000000060c800000, 0x000000060cc00000| 0%| F| |TAMS 0x000000060c800000, 0x000000060c800000| Untracked
| 40|0x000000060cc00000, 0x000000060cc00000, 0x000000060d000000| 0%| F| |TAMS 0x000000060cc00000, 0x000000060cc00000| Untracked
| 41|0x000000060d000000, 0x000000060d000000, 0x000000060d400000| 0%| F| |TAMS 0x000000060d000000, 0x000000060d000000| Untracked
| 42|0x000000060d400000, 0x000000060d800000, 0x000000060d800000|100%| E| |TAMS 0x000000060d400000, 0x000000060d400000| Complete
| 43|0x000000060d800000, 0x000000060dc00000, 0x000000060dc00000|100%| E|CS|TAMS 0x000000060d800000, 0x000000060d800000| Complete
| 44|0x000000060dc00000, 0x000000060e000000, 0x000000060e000000|100%| E|CS|TAMS 0x000000060dc00000, 0x000000060dc00000| Complete
| 45|0x000000060e000000, 0x000000060e400000, 0x000000060e400000|100%| E|CS|TAMS 0x000000060e000000, 0x000000060e000000| Complete
| 46|0x000000060e400000, 0x000000060e800000, 0x000000060e800000|100%| E|CS|TAMS 0x000000060e400000, 0x000000060e400000| Complete
| 47|0x000000060e800000, 0x000000060ec00000, 0x000000060ec00000|100%| E|CS|TAMS 0x000000060e800000, 0x000000060e800000| Complete
| 48|0x000000060ec00000, 0x000000060f000000, 0x000000060f000000|100%| E|CS|TAMS 0x000000060ec00000, 0x000000060ec00000| Complete
| 49|0x000000060f000000, 0x000000060f400000, 0x000000060f400000|100%| E|CS|TAMS 0x000000060f000000, 0x000000060f000000| Complete
| 50|0x000000060f400000, 0x000000060f800000, 0x000000060f800000|100%| E|CS|TAMS 0x000000060f400000, 0x000000060f400000| Complete
| 51|0x000000060f800000, 0x000000060fc00000, 0x000000060fc00000|100%| E|CS|TAMS 0x000000060f800000, 0x000000060f800000| Complete
| 52|0x000000060fc00000, 0x0000000610000000, 0x0000000610000000|100%| E|CS|TAMS 0x000000060fc00000, 0x000000060fc00000| Complete
| 53|0x0000000610000000, 0x0000000610400000, 0x0000000610400000|100%| E|CS|TAMS 0x0000000610000000, 0x0000000610000000| Complete
| 54|0x0000000610400000, 0x0000000610800000, 0x0000000610800000|100%| E|CS|TAMS 0x0000000610400000, 0x0000000610400000| Complete
| 55|0x0000000610800000, 0x0000000610c00000, 0x0000000610c00000|100%| E|CS|TAMS 0x0000000610800000, 0x0000000610800000| Complete
| 56|0x0000000610c00000, 0x0000000611000000, 0x0000000611000000|100%| E|CS|TAMS 0x0000000610c00000, 0x0000000610c00000| Complete
| 57|0x0000000611000000, 0x0000000611400000, 0x0000000611400000|100%| E|CS|TAMS 0x0000000611000000, 0x0000000611000000| Complete
| 58|0x0000000611400000, 0x0000000611800000, 0x0000000611800000|100%| E|CS|TAMS 0x0000000611400000, 0x0000000611400000| Complete
| 92|0x0000000619c00000, 0x000000061a000000, 0x000000061a000000|100%| E|CS|TAMS 0x0000000619c00000, 0x0000000619c00000| Complete
| 93|0x000000061a000000, 0x000000061a400000, 0x000000061a400000|100%| E|CS|TAMS 0x000000061a000000, 0x000000061a000000| Complete
| 94|0x000000061a400000, 0x000000061a800000, 0x000000061a800000|100%| E|CS|TAMS 0x000000061a400000, 0x000000061a400000| Complete
| 95|0x000000061a800000, 0x000000061ac00000, 0x000000061ac00000|100%| E|CS|TAMS 0x000000061a800000, 0x000000061a800000| Complete
| 96|0x000000061ac00000, 0x000000061b000000, 0x000000061b000000|100%| E|CS|TAMS 0x000000061ac00000, 0x000000061ac00000| Complete
| 97|0x000000061b000000, 0x000000061b0af1a0, 0x000000061b400000| 17%| S|CS|TAMS 0x000000061b000000, 0x000000061b000000| Complete
| 127|0x0000000622800000, 0x0000000622c00000, 0x0000000622c00000|100%| E|CS|TAMS 0x0000000622800000, 0x0000000622800000| Complete
Card table byte_map: [0x000000001aa50000,0x000000001ba40000] _byte_map_base: 0x0000000017a3a000
Marking Bits (Prev, Next): (CMBitMap*) 0x0000000002d06210, (CMBitMap*) 0x0000000002d06250
Prev Bits: [0x000000001ca30000, 0x0000000024980000)
Next Bits: [0x0000000024980000, 0x000000002c8d0000)
Polling page: 0x0000000000a50000
Metaspace:
Usage:
Non-class: 35.78 MB used.
Class: 4.44 MB used.
Both: 40.22 MB used.
Virtual space:
Non-class space: 64.00 MB reserved, 36.00 MB ( 56%) committed, 1 nodes.
Class space: 1.00 GB reserved, 4.69 MB ( <1%) committed, 1 nodes.
Both: 1.06 GB reserved, 40.69 MB ( 4%) committed.
Chunk freelists:
Non-Class: 11.94 MB
Class: 11.16 MB
Both: 23.09 MB
MaxMetaspaceSize: unlimited
CompressedClassSpaceSize: 1.00 GB
Initial GC threshold: 21.00 MB
Current GC threshold: 59.06 MB
CDS: on
MetaspaceReclaimPolicy: balanced
- commit_granule_bytes: 65536.
- commit_granule_words: 8192.
- virtual_space_node_default_size: 8388608.
- enlarge_chunks_in_place: 1.
- new_chunks_are_fully_committed: 0.
- uncommit_free_chunks: 1.
- use_allocation_guard: 0.
- handle_deallocations: 1.
Internal statistics:
num_allocs_failed_limit: 6.
num_arena_births: 372.
num_arena_deaths: 0.
num_vsnodes_births: 2.
num_vsnodes_deaths: 0.
num_space_committed: 651.
num_space_uncommitted: 0.
num_chunks_returned_to_freelist: 6.
num_chunks_taken_from_freelist: 1957.
num_chunk_merges: 6.
num_chunk_splits: 1360.
num_chunks_enlarged: 1023.
num_inconsistent_stats: 0.
CodeHeap 'non-profiled nmethods': size=120000Kb used=7852Kb max_used=7865Kb free=112147Kb
bounds [0x0000000011fe0000, 0x0000000012790000, 0x0000000019510000]
CodeHeap 'profiled nmethods': size=120000Kb used=22769Kb max_used=22769Kb free=97230Kb
bounds [0x000000000a510000, 0x000000000bb50000, 0x0000000011a40000]
CodeHeap 'non-nmethods': size=5760Kb used=1252Kb max_used=1438Kb free=4507Kb
bounds [0x0000000011a40000, 0x0000000011cb0000, 0x0000000011fe0000]
total_blobs=10766 nmethods=10205 adapters=472
compilation: enabled
stopped_count=0, restarted_count=0
full_count=0
Compilation events (20 events):
Event: 11.646 Thread 0x000000002eda5210 12663 ! 3 java.util.zip.Deflater::needsInput (55 bytes)
Event: 11.646 Thread 0x000000002eda5210 nmethod 12663 0x000000000bb3fa90 code [0x000000000bb3fc40, 0x000000000bb3fff8]
Event: 11.646 Thread 0x000000002eda5210 12664 3 org.springframework.boot.loader.tools.AbstractJarWriter::updateLayerIndex (64 bytes)
Event: 11.647 Thread 0x000000002eda5210 nmethod 12664 0x000000000bb40110 code [0x000000000bb40340, 0x000000000bb40dd8]
Event: 11.647 Thread 0x000000002eda5210 12665 3 org.springframework.boot.loader.tools.JarWriter::writeToArchive (60 bytes)
Event: 11.647 Thread 0x000000002eda5210 nmethod 12665 0x000000000bb41110 code [0x000000000bb41380, 0x000000000bb42158]
Event: 11.647 Thread 0x000000002eda5210 12666 3 org.springframework.boot.loader.tools.AbstractJarWriter$InputStreamEntryWriter::<init> (10 bytes)
Event: 11.647 Thread 0x000000002eda5210 nmethod 12666 0x000000000bb42590 code [0x000000000bb42720, 0x000000000bb428d8]
Event: 11.649 Thread 0x000000002eda5210 12667 3 org.apache.commons.compress.archivers.zip.ZipArchiveEntry::<init> (126 bytes)
Event: 11.650 Thread 0x000000002eda5210 nmethod 12667 0x000000000bb42990 code [0x000000000bb42ca0, 0x000000000bb44178]
Event: 11.650 Thread 0x000000002eda5210 12668 3 java.time.LocalTime::create (26 bytes)
Event: 11.650 Thread 0x000000002eda5210 nmethod 12668 0x000000000bb44690 code [0x000000000bb44840, 0x000000000bb44af8]
Event: 11.654 Thread 0x000000002eda5210 12669 3 java.time.LocalDateTime::ofEpochSecond (77 bytes)
Event: 11.654 Thread 0x000000002eda5210 nmethod 12669 0x000000000bb44c10 code [0x000000000bb44e40, 0x000000000bb45578]
Event: 11.654 Thread 0x000000002eda5210 12670 3 java.util.zip.ZipUtils::get64 (17 bytes)
Event: 11.654 Thread 0x000000002eda5210 nmethod 12670 0x000000000bb45990 code [0x000000000bb45b40, 0x000000000bb45cb8]
Event: 11.654 Thread 0x000000002eda5210 12671 3 java.time.temporal.ValueRange::checkValidIntValue (25 bytes)
Event: 11.654 Thread 0x000000002eda5210 nmethod 12671 0x000000000bb45d90 code [0x000000000bb45f80, 0x000000000bb46558]
Event: 11.654 Thread 0x000000002eda5210 12672 3 org.apache.commons.compress.archivers.zip.ZipShort::getBytes (17 bytes)
Event: 11.654 Thread 0x000000002eda5210 nmethod 12672 0x000000000bb46710 code [0x000000000bb468c0, 0x000000000bb46ac8]
GC Heap History (20 events):
Event: 5.489 GC heap before
{Heap before GC invocations=10 (full 0):
garbage-first heap total 135168K, used 101138K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 13 young (53248K), 1 survivors (4096K)
Metaspace used 29754K, committed 30080K, reserved 1114112K
class space used 3318K, committed 3456K, reserved 1048576K
}
Event: 5.496 GC heap after
{Heap after GC invocations=11 (full 0):
garbage-first heap total 176128K, used 54864K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 2 young (8192K), 2 survivors (8192K)
Metaspace used 29754K, committed 30080K, reserved 1114112K
class space used 3318K, committed 3456K, reserved 1048576K
}
Event: 5.696 GC heap before
{Heap before GC invocations=11 (full 0):
garbage-first heap total 176128K, used 132688K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 21 young (86016K), 2 survivors (8192K)
Metaspace used 29800K, committed 30144K, reserved 1114112K
class space used 3319K, committed 3456K, reserved 1048576K
}
Event: 5.704 GC heap after
{Heap after GC invocations=12 (full 0):
garbage-first heap total 176128K, used 59591K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 3 young (12288K), 3 survivors (12288K)
Metaspace used 29800K, committed 30144K, reserved 1114112K
class space used 3319K, committed 3456K, reserved 1048576K
}
Event: 5.808 GC heap before
{Heap before GC invocations=12 (full 0):
garbage-first heap total 176128K, used 133319K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 21 young (86016K), 3 survivors (12288K)
Metaspace used 29812K, committed 30144K, reserved 1114112K
class space used 3319K, committed 3456K, reserved 1048576K
}
Event: 5.817 GC heap after
{Heap after GC invocations=13 (full 0):
garbage-first heap total 176128K, used 60441K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 2 young (8192K), 2 survivors (8192K)
Metaspace used 29812K, committed 30144K, reserved 1114112K
class space used 3319K, committed 3456K, reserved 1048576K
}
Event: 6.071 GC heap before
{Heap before GC invocations=13 (full 0):
garbage-first heap total 176128K, used 134169K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 20 young (81920K), 2 survivors (8192K)
Metaspace used 29952K, committed 30272K, reserved 1114112K
class space used 3322K, committed 3456K, reserved 1048576K
}
Event: 6.078 GC heap after
{Heap after GC invocations=14 (full 0):
garbage-first heap total 176128K, used 61766K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 2 young (8192K), 2 survivors (8192K)
Metaspace used 29952K, committed 30272K, reserved 1114112K
class space used 3322K, committed 3456K, reserved 1048576K
}
Event: 6.302 GC heap before
{Heap before GC invocations=14 (full 0):
garbage-first heap total 176128K, used 135494K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 20 young (81920K), 2 survivors (8192K)
Metaspace used 29976K, committed 30336K, reserved 1114112K
class space used 3322K, committed 3456K, reserved 1048576K
}
Event: 6.312 GC heap after
{Heap after GC invocations=15 (full 0):
garbage-first heap total 270336K, used 61575K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 1 young (4096K), 1 survivors (4096K)
Metaspace used 29976K, committed 30336K, reserved 1114112K
class space used 3322K, committed 3456K, reserved 1048576K
}
Event: 7.593 GC heap before
{Heap before GC invocations=15 (full 0):
garbage-first heap total 270336K, used 213127K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 38 young (155648K), 1 survivors (4096K)
Metaspace used 32451K, committed 32832K, reserved 1114112K
class space used 3563K, committed 3776K, reserved 1048576K
}
Event: 7.598 GC heap after
{Heap after GC invocations=16 (full 0):
garbage-first heap total 270336K, used 63871K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 2 young (8192K), 2 survivors (8192K)
Metaspace used 32451K, committed 32832K, reserved 1114112K
class space used 3563K, committed 3776K, reserved 1048576K
}
Event: 9.282 GC heap before
{Heap before GC invocations=16 (full 0):
garbage-first heap total 270336K, used 215423K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 39 young (159744K), 2 survivors (8192K)
Metaspace used 33942K, committed 34304K, reserved 1114112K
class space used 3709K, committed 3904K, reserved 1048576K
}
Event: 9.294 GC heap after
{Heap after GC invocations=17 (full 0):
garbage-first heap total 270336K, used 82884K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 5 young (20480K), 5 survivors (20480K)
Metaspace used 33942K, committed 34304K, reserved 1114112K
class space used 3709K, committed 3904K, reserved 1048576K
}
Event: 9.761 GC heap before
{Heap before GC invocations=17 (full 0):
garbage-first heap total 270336K, used 132036K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 18 young (73728K), 5 survivors (20480K)
Metaspace used 35675K, committed 36096K, reserved 1114112K
class space used 3911K, committed 4096K, reserved 1048576K
}
Event: 9.768 GC heap after
{Heap after GC invocations=18 (full 0):
garbage-first heap total 270336K, used 80167K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 1 young (4096K), 1 survivors (4096K)
Metaspace used 35675K, committed 36096K, reserved 1114112K
class space used 3911K, committed 4096K, reserved 1048576K
}
Event: 10.950 GC heap before
{Heap before GC invocations=19 (full 0):
garbage-first heap total 270336K, used 248103K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 27 young (110592K), 1 survivors (4096K)
Metaspace used 39221K, committed 39680K, reserved 1114112K
class space used 4328K, committed 4544K, reserved 1048576K
}
Event: 10.954 GC heap after
{Heap after GC invocations=20 (full 0):
garbage-first heap total 270336K, used 82867K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 2 young (8192K), 2 survivors (8192K)
Metaspace used 39221K, committed 39680K, reserved 1114112K
class space used 4328K, committed 4544K, reserved 1048576K
}
Event: 10.988 GC heap before
{Heap before GC invocations=20 (full 0):
garbage-first heap total 270336K, used 82867K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 3 young (12288K), 2 survivors (8192K)
Metaspace used 39234K, committed 39744K, reserved 1114112K
class space used 4328K, committed 4544K, reserved 1048576K
}
Event: 10.992 GC heap after
{Heap after GC invocations=21 (full 0):
garbage-first heap total 270336K, used 80921K [0x0000000602c00000, 0x0000000800000000)
region size 4096K, 1 young (4096K), 1 survivors (4096K)
Metaspace used 39234K, committed 39744K, reserved 1114112K
class space used 4328K, committed 4544K, reserved 1048576K
}
Deoptimization events (20 events):
Event: 11.567 Thread 0x0000000002c571b0 DEOPT PACKING pc=0x00000000126c85e0 sp=0x000000000241db60
Event: 11.567 Thread 0x0000000002c571b0 DEOPT UNPACKING pc=0x0000000011a923a3 sp=0x000000000241db00 mode 2
Event: 11.579 Thread 0x0000000002c571b0 Uncommon trap: trap_request=0xffffff6e fr.pc=0x000000001269723c relative=0x000000000000065c
Event: 11.579 Thread 0x0000000002c571b0 Uncommon trap: reason=loop_limit_check action=maybe_recompile pc=0x000000001269723c method=org.codehaus.plexus.util.SelectorUtils.matchAntPathPattern([[C[[CZ)Z @ 231 c2
Event: 11.579 Thread 0x0000000002c571b0 DEOPT PACKING pc=0x000000001269723c sp=0x000000000241dd20
Event: 11.579 Thread 0x0000000002c571b0 DEOPT UNPACKING pc=0x0000000011a923a3 sp=0x000000000241dc70 mode 2
Event: 11.580 Thread 0x0000000002c571b0 Uncommon trap: trap_request=0xffffffde fr.pc=0x00000000121e3028 relative=0x0000000000002ae8
Event: 11.580 Thread 0x0000000002c571b0 Uncommon trap: reason=class_check action=maybe_recompile pc=0x00000000121e3028 method=org.apache.maven.model.Plugin.clone()Lorg/apache/maven/model/Plugin; @ 94 c2
Event: 11.580 Thread 0x0000000002c571b0 DEOPT PACKING pc=0x00000000121e3028 sp=0x000000000241d9d0
Event: 11.580 Thread 0x0000000002c571b0 DEOPT UNPACKING pc=0x0000000011a923a3 sp=0x000000000241d9f0 mode 2
Event: 11.593 Thread 0x0000000002c571b0 Uncommon trap: trap_request=0xffffffde fr.pc=0x00000000124f7304 relative=0x0000000000000064
Event: 11.593 Thread 0x0000000002c571b0 Uncommon trap: reason=class_check action=maybe_recompile pc=0x00000000124f7304 method=java.lang.invoke.Invokers$Holder.linkToTargetMethod(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @ 1 c2
Event: 11.593 Thread 0x0000000002c571b0 DEOPT PACKING pc=0x00000000124f7304 sp=0x000000000241e000
Event: 11.594 Thread 0x0000000002c571b0 DEOPT UNPACKING pc=0x0000000011a923a3 sp=0x000000000241df90 mode 2
Event: 11.595 Thread 0x0000000002c571b0 DEOPT PACKING pc=0x000000000aec2cfe sp=0x000000000241d7f0
Event: 11.595 Thread 0x0000000002c571b0 DEOPT UNPACKING pc=0x0000000011a92b43 sp=0x000000000241cd70 mode 0
Event: 11.599 Thread 0x0000000002c571b0 DEOPT PACKING pc=0x000000000aec2cfe sp=0x000000000241d7f0
Event: 11.599 Thread 0x0000000002c571b0 DEOPT UNPACKING pc=0x0000000011a92b43 sp=0x000000000241cd70 mode 0
Event: 11.608 Thread 0x0000000002c571b0 DEOPT PACKING pc=0x000000000aec2cfe sp=0x000000000241d850
Event: 11.608 Thread 0x0000000002c571b0 DEOPT UNPACKING pc=0x0000000011a92b43 sp=0x000000000241cdd0 mode 0
Classes unloaded (0 events):
No events
Classes redefined (0 events):
No events
Internal exceptions (20 events):
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f221a40}> (0x000000060f221a40)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f22cc88}> (0x000000060f22cc88)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f22cf58}> (0x000000060f22cf58)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f236488}> (0x000000060f236488)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f2366c8}> (0x000000060f2366c8)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f24d168}> (0x000000060f24d168)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f24d438}> (0x000000060f24d438)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f24ee10}> (0x000000060f24ee10)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.509 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f24f170}> (0x000000060f24f170)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f262b00}> (0x000000060f262b00)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f262de8}> (0x000000060f262de8)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f2690f0}> (0x000000060f2690f0)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f269348}> (0x000000060f269348)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f2792b8}> (0x000000060f2792b8)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f279598}> (0x000000060f279598)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f27af78}> (0x000000060f27af78)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.510 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f27b2d8}> (0x000000060f27b2d8)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.516 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f3ceca0}> (0x000000060f3ceca0)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.518 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f3e6198}> (0x000000060f3e6198)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
Event: 11.519 Thread 0x0000000002c571b0 Exception <a 'sun/nio/fs/WindowsException'{0x000000060f3fd538}> (0x000000060f3fd538)
thrown [s\open\src\hotspot\share\prims\jni.cpp, line 516]
VM Operations (20 events):
Event: 10.085 Executing VM operation: HandshakeAllThreads
Event: 10.085 Executing VM operation: HandshakeAllThreads done
Event: 10.365 Executing VM operation: HandshakeAllThreads
Event: 10.365 Executing VM operation: HandshakeAllThreads done
Event: 10.429 Executing VM operation: HandshakeAllThreads
Event: 10.429 Executing VM operation: HandshakeAllThreads done
Event: 10.854 Executing VM operation: ICBufferFull
Event: 10.854 Executing VM operation: ICBufferFull done
Event: 10.878 Executing VM operation: HandshakeAllThreads
Event: 10.878 Executing VM operation: HandshakeAllThreads done
Event: 10.879 Executing VM operation: HandshakeAllThreads
Event: 10.879 Executing VM operation: HandshakeAllThreads done
Event: 10.950 Executing VM operation: G1CollectForAllocation
Event: 10.954 Executing VM operation: G1CollectForAllocation done
Event: 10.988 Executing VM operation: G1CollectForAllocation
Event: 10.992 Executing VM operation: G1CollectForAllocation done
Event: 11.523 Executing VM operation: HandshakeAllThreads
Event: 11.523 Executing VM operation: HandshakeAllThreads done
Event: 11.577 Executing VM operation: ICBufferFull
Event: 11.577 Executing VM operation: ICBufferFull done
Events (20 events):
Event: 11.571 loading class java/lang/constant/DirectMethodHandleDescImpl$1
Event: 11.571 loading class java/lang/constant/DirectMethodHandleDescImpl$1 done
Event: 11.571 loading class java/lang/constant/DirectMethodHandleDesc$1
Event: 11.571 loading class java/lang/constant/DirectMethodHandleDesc$1 done
Event: 11.571 loading class java/lang/constant/PrimitiveClassDescImpl
Event: 11.571 loading class java/lang/constant/DynamicConstantDesc
Event: 11.571 loading class java/lang/constant/DynamicConstantDesc done
Event: 11.571 loading class java/lang/constant/PrimitiveClassDescImpl done
Event: 11.571 loading class java/lang/constant/DynamicConstantDesc$AnonymousDynamicConstantDesc
Event: 11.571 loading class java/lang/constant/DynamicConstantDesc$AnonymousDynamicConstantDesc done
Event: 11.591 Thread 0x00000000784a98e0 Thread added: 0x00000000784a98e0
Event: 11.591 Thread 0x00000000784ab230 Thread added: 0x00000000784ab230
Event: 11.591 Thread 0x00000000784a7a80 Thread added: 0x00000000784a7a80
Event: 11.591 Thread 0x00000000784ab740 Thread added: 0x00000000784ab740
Event: 11.591 Thread 0x00000000784a7f90 Thread added: 0x00000000784a7f90
Event: 11.592 Thread 0x00000000784a7f90 Thread exited: 0x00000000784a7f90
Event: 11.592 Thread 0x00000000784ab230 Thread exited: 0x00000000784ab230
Event: 11.592 Thread 0x00000000784a98e0 Thread exited: 0x00000000784a98e0
Event: 11.592 Thread 0x00000000784ab740 Thread exited: 0x00000000784ab740
Event: 11.592 Thread 0x00000000784a7a80 Thread exited: 0x00000000784a7a80
Dynamic libraries:
0x00007ff767f90000 - 0x00007ff767fa0000 D:\develop\JDK17\bin\java.exe
0x00007ffcd8da0000 - 0x00007ffcd9007000 C:\WINDOWS\SYSTEM32\ntdll.dll
0x00007ffcd7bc0000 - 0x00007ffcd7c89000 C:\WINDOWS\System32\KERNEL32.DLL
0x00007ffcd62f0000 - 0x00007ffcd66df000 C:\WINDOWS\System32\KERNELBASE.dll
0x00007ffcd5ef0000 - 0x00007ffcd603b000 C:\WINDOWS\System32\ucrtbase.dll
0x00007ffccfe30000 - 0x00007ffccfe49000 D:\develop\JDK17\bin\jli.dll
0x00007ffca8f40000 - 0x00007ffca8f5b000 D:\develop\JDK17\bin\VCRUNTIME140.dll
0x00007ffcd6eb0000 - 0x00007ffcd6f64000 C:\WINDOWS\System32\ADVAPI32.dll
0x00007ffcd84d0000 - 0x00007ffcd8579000 C:\WINDOWS\System32\msvcrt.dll
0x00007ffcd7e70000 - 0x00007ffcd7f16000 C:\WINDOWS\System32\sechost.dll
0x00007ffcd8bb0000 - 0x00007ffcd8cc8000 C:\WINDOWS\System32\RPCRT4.dll
0x00007ffcd89e0000 - 0x00007ffcd8ba5000 C:\WINDOWS\System32\USER32.dll
0x00007ffcd6b10000 - 0x00007ffcd6b37000 C:\WINDOWS\System32\win32u.dll
0x00007ffcd4ff0000 - 0x00007ffcd5283000 C:\WINDOWS\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.26100.7309_none_3e05feeae336a044\COMCTL32.dll
0x00007ffcd83f0000 - 0x00007ffcd841b000 C:\WINDOWS\System32\GDI32.dll
0x00007ffcd66f0000 - 0x00007ffcd681c000 C:\WINDOWS\System32\gdi32full.dll
0x00007ffcd60d0000 - 0x00007ffcd6173000 C:\WINDOWS\System32\msvcp_win.dll
0x00007ffccc960000 - 0x00007ffccc96b000 C:\WINDOWS\SYSTEM32\VERSION.dll
0x00007ffcd7f20000 - 0x00007ffcd7f51000 C:\WINDOWS\System32\IMM32.DLL
0x00007ffccff40000 - 0x00007ffccff4c000 D:\develop\JDK17\bin\vcruntime140_1.dll
0x00007ffc79be0000 - 0x00007ffc79c6e000 D:\develop\JDK17\bin\msvcp140.dll
0x00007ffbad310000 - 0x00007ffbadef0000 D:\develop\JDK17\bin\server\jvm.dll
0x00007ffcd8d50000 - 0x00007ffcd8d58000 C:\WINDOWS\System32\PSAPI.DLL
0x00007ffcbb3e0000 - 0x00007ffcbb3ea000 C:\WINDOWS\SYSTEM32\WSOCK32.dll
0x00007ffcc6bf0000 - 0x00007ffcc6c25000 C:\WINDOWS\SYSTEM32\WINMM.dll
0x00007ffcd8960000 - 0x00007ffcd89d4000 C:\WINDOWS\System32\WS2_32.dll
0x00007ffcd4370000 - 0x00007ffcd438b000 C:\WINDOWS\SYSTEM32\kernel.appcore.dll
0x00007ffcc2eb0000 - 0x00007ffcc2eba000 D:\develop\JDK17\bin\jimage.dll
0x00007ffcd56a0000 - 0x00007ffcd58e2000 C:\WINDOWS\SYSTEM32\DBGHELP.DLL
0x00007ffcd8580000 - 0x00007ffcd8905000 C:\WINDOWS\System32\combase.dll
0x00007ffcd7c90000 - 0x00007ffcd7d66000 C:\WINDOWS\System32\OLEAUT32.dll
0x00007ffcd5390000 - 0x00007ffcd53cc000 C:\WINDOWS\SYSTEM32\dbgcore.DLL
0x00007ffcd6a60000 - 0x00007ffcd6b05000 C:\WINDOWS\System32\bcryptPrimitives.dll
0x00007ffcb1ab0000 - 0x00007ffcb1abe000 D:\develop\JDK17\bin\instrument.dll
0x00007ffc9a370000 - 0x00007ffc9a395000 D:\develop\JDK17\bin\java.dll
0x00007ffc79760000 - 0x00007ffc79837000 D:\develop\JDK17\bin\jsvml.dll
0x00007ffcd6f70000 - 0x00007ffcd76c3000 C:\WINDOWS\System32\SHELL32.dll
0x00007ffcd6180000 - 0x00007ffcd62ea000 C:\WINDOWS\System32\wintypes.dll
0x00007ffcd30a0000 - 0x00007ffcd38fe000 C:\WINDOWS\SYSTEM32\windows.storage.dll
0x00007ffcd6d30000 - 0x00007ffcd6e25000 C:\WINDOWS\System32\SHCORE.dll
0x00007ffcd6e30000 - 0x00007ffcd6e96000 C:\WINDOWS\System32\shlwapi.dll
0x00007ffcd5e10000 - 0x00007ffcd5e39000 C:\WINDOWS\SYSTEM32\profapi.dll
0x00007ffc91c60000 - 0x00007ffc91c78000 D:\develop\JDK17\bin\zip.dll
0x00007ffc8a530000 - 0x00007ffc8a549000 D:\develop\JDK17\bin\net.dll
0x00007ffccd3a0000 - 0x00007ffccd4c8000 C:\WINDOWS\SYSTEM32\WINHTTP.dll
0x00007ffcd48a0000 - 0x00007ffcd490b000 C:\WINDOWS\system32\mswsock.dll
0x00007ffc79bc0000 - 0x00007ffc79bd6000 D:\develop\JDK17\bin\nio.dll
0x0000000000d30000 - 0x0000000000d54000 C:\Users\18969\AppData\Local\Temp\jansi-2.4.0-7461ad60e422ef5a-jansi.dll
0x00007ffca8f30000 - 0x00007ffca8f40000 D:\develop\JDK17\bin\verify.dll
dbghelp: loaded successfully - version: 4.0.5 - missing functions: none
symbol engine: initialized successfully - sym options: 0x614 - pdb path: .;D:\develop\JDK17\bin;C:\WINDOWS\SYSTEM32;C:\WINDOWS\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.26100.7309_none_3e05feeae336a044;D:\develop\JDK17\bin\server;C:\Users\18969\AppData\Local\Temp
VM Arguments:
jvm_args: -Dmaven.multiModuleProjectDirectory=E:\MTKJPAY -Djansi.passthrough=true -Dmaven.home=D:/develop/Maven/apache-maven-3.9.4 -Dclassworlds.conf=D:\develop\Maven\apache-maven-3.9.4\bin\m2.conf -Dmaven.ext.class.path=D:\develop\IntelliJ IDEA 2025.1\plugins\maven\lib\maven-event-listener.jar -javaagent:D:\develop\IntelliJ IDEA 2025.1\lib\idea_rt.jar=57907 -Dfile.encoding=UTF-8
java_command: org.codehaus.classworlds.Launcher -Didea.version=2025.1.1.1 -s D:\develop\Maven\apache-maven-3.9.4\conf\settings.xml -Dmaven.repo.local=D:\develop\Maven\repository package
java_class_path (initial): D:\develop\Maven\apache-maven-3.9.4\boot\plexus-classworlds-2.7.0.jar;D:\develop\Maven\apache-maven-3.9.4\boot\plexus-classworlds.license
Launcher Type: SUN_STANDARD
[Global flags]
intx CICompilerCount = 4 {product} {ergonomic}
uint ConcGCThreads = 3 {product} {ergonomic}
uint G1ConcRefinementThreads = 10 {product} {ergonomic}
size_t G1HeapRegionSize = 4194304 {product} {ergonomic}
uintx GCDrainStackTargetSize = 64 {product} {ergonomic}
size_t InitialHeapSize = 536870912 {product} {ergonomic}
size_t MarkStackSize = 4194304 {product} {ergonomic}
size_t MaxHeapSize = 8543797248 {product} {ergonomic}
size_t MaxNewSize = 5125439488 {product} {ergonomic}
size_t MinHeapDeltaBytes = 4194304 {product} {ergonomic}
size_t MinHeapSize = 8388608 {product} {ergonomic}
uintx NonNMethodCodeHeapSize = 5839372 {pd product} {ergonomic}
uintx NonProfiledCodeHeapSize = 122909434 {pd product} {ergonomic}
uintx ProfiledCodeHeapSize = 122909434 {pd product} {ergonomic}
uintx ReservedCodeCacheSize = 251658240 {pd product} {ergonomic}
bool SegmentedCodeCache = true {product} {ergonomic}
size_t SoftMaxHeapSize = 8543797248 {manageable} {ergonomic}
bool UseCompressedClassPointers = true {product lp64_product} {ergonomic}
bool UseCompressedOops = true {product lp64_product} {ergonomic}
bool UseG1GC = true {product} {ergonomic}
bool UseLargePagesIndividualAllocation = false {pd product} {ergonomic}
Logging:
Log output configuration:
#0: stdout all=warning uptime,level,tags
#1: stderr all=off uptime,level,tags
Environment Variables:
PATH=D:\develop\VMware\VMware Workstation\bin\;C:\Program Files\Common Files\Oracle\Java\javapath;D:\develop\JDK17bin;D:\Develop\jdk14\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;D:\bandizip\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\NVIDIA Corporation\NVIDIA App\NvDLISR;D:\develop\Maven\apache-maven-3.9.4\bin;D:\develop\Maven\apache-maven-3.9.4\bin;D:\Apps\MyselfApp\Bandizip\;D:\develop\MySQL\Program Files\bin;D:\develop\Git\cmd;D:\develop\nodejs\;D:\develop\;D:\develop\΢<><CEA2>web<65><62><EFBFBD><EFBFBD><EFBFBD>߹<EFBFBD><DFB9><EFBFBD>\dll;D:\develop\OllamaModels;C:\Program Files\dotnet\;D:\develop\cursor\resources\app\bin;C:\Users\18969\AppData\Local\Microsoft\WindowsApps;;D:\develop\IntelliJ IDEA 2025.1\bin;;D:\develop\DataGrip 2024.3.4\bin;;D:\develop\Microsoft VS Code\Microsoft VS Code\bin;C:\Users\18969\AppData\Roaming\npm;D:\develop\cpolar\;D:\develop\Ollama
USERNAME=18969
OS=Windows_NT
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 165 Stepping 2, GenuineIntel
--------------- S Y S T E M ---------------
OS:
Windows 11 , 64 bit Build 26100 (10.0.26100.7309)
OS uptime: 0 days 0:15 hours
CPU: total 12 (initial active 12) (6 cores per cpu, 2 threads per core) family 6 model 165 stepping 2 microcode 0xe0, cx8, cmov, fxsr, ht, mmx, 3dnowpref, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, lzcnt, tsc, tscinvbit, avx, avx2, aes, erms, clmul, bmi1, bmi2, adx, fma, vzeroupper, clflush, clflushopt
Memory: 4k page, system-wide physical 32591M (11550M free)
TotalPageFile size 38479M (AvailPageFile size 17112M)
current process WorkingSet (physical memory assigned to process): 563M, peak: 564M
current process commit charge ("private bytes"): 597M, peak: 691M
vm_info: Java HotSpot(TM) 64-Bit Server VM (17.0.12+8-LTS-286) for windows-amd64 JRE (17.0.12+8-LTS-286), built on Jun 5 2024 06:46:59 by "mach5one" with MS VC++ 17.6 (VS2022)
END.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,43 +1,9 @@
# ERP用户管理API文档
# ERP用户管理API
## 概述
ERP用户管理模块提供了用户注册、登录等基础功能支持账号、密码、店铺号管理。
## 数据库表
### erp_user 表结构
执行以下SQL创建表
```sql
-- 文件位置mt-pay/database/erp_user_schema.sql
```
表字段说明:
- `id`: 主键ID
- `username`: 账号唯一3-50个字符只能包含字母、数字和下划线
- `password`: 密码MD5加密6-20个字符
- `nick_name`: 用户名称可选最大50个字符
- `phone`: 手机号可选唯一格式1开头11位数字
- `email`: 邮箱可选唯一最大100个字符
- `store_code`: 店铺号最大50个字符
- `status`: 状态ACTIVE-激活DISABLED-禁用)
- `last_login_time`: 最后登录时间
- `last_login_ip`: 最后登录IP
- `create_time`: 创建时间
- `update_time`: 更新时间
## API接口
## 接口列表
### 1. 用户注册
**接口地址:** `POST /api/erp/user/register`
**请求头:**
```
Content-Type: application/json
```
**POST** `/api/erp/user/register`
**请求体:**
```json
@@ -51,49 +17,8 @@ Content-Type: application/json
}
```
**参数说明:**
- `username` (必填): 账号3-50个字符只能包含字母、数字和下划线
- `password` (必填): 密码6-20个字符
- `storeCode` (必填): 店铺号最大50个字符
- `nickName` (可选): 用户名称最大50个字符
- `phone` (可选): 手机号格式1开头11位数字13800138000必须唯一
- `email` (可选): 邮箱最大100个字符必须唯一
**响应示例:**
```json
{
"code": "0000",
"message": "注册成功",
"data": {
"id": 1,
"username": "testuser",
"nickName": "测试用户",
"phone": "13800138000",
"email": "test@example.com",
"storeCode": "STORE001",
"status": "ACTIVE",
"createTime": "2024-12-24T10:00:00"
},
"timestamp": 1703412000000
}
```
**错误响应:**
- 账号已存在:`{"code": "6000", "message": "账号已存在"}`
- 手机号已被注册:`{"code": "6000", "message": "手机号已被注册"}`
- 邮箱已被注册:`{"code": "6000", "message": "邮箱已被注册"}`
- 参数验证失败:`{"code": "4001", "message": "参数验证失败"}`
---
### 2. 用户登录
**接口地址:** `POST /api/erp/user/login`
**请求头:**
```
Content-Type: application/json
```
**POST** `/api/erp/user/login`
**请求体:**
```json
@@ -103,151 +28,59 @@ Content-Type: application/json
}
```
**参数说明**
- `username` (必填): 账号
- `password` (必填): 密码
**响应示例:**
**响应**
```json
{
"code": "0000",
"message": "登录成功",
"data": {
"id": 1,
"username": "testuser",
"nickName": "测试用户",
"phone": "13800138000",
"email": "test@example.com",
"storeCode": "STORE001",
"status": "ACTIVE",
"token": "MTIzNDU2Nzg5MGFiY2RlZjoxMjM0NTY3ODkwYWJjZGVmOjE3MDM0MTIwMDAwMDA6YWJjZGVmMTIzNDU2Nzg5MA==",
"tokenExpireTime": 1704016800000,
"lastLoginTime": "2024-12-24T10:00:00",
"lastLoginIp": "192.168.1.100"
},
"timestamp": 1703412000000
"token": "xxx",
"userInfo": {
"id": 1,
"username": "testuser",
"nickName": "测试用户"
}
}
}
```
**错误响应:**
- 账号或密码错误:`{"code": "4002", "message": "账号或密码错误"}`
- 账号已被禁用:`{"code": "4003", "message": "账号已被禁用"}`
---
## Token使用说明
### Token生成
登录成功后,系统会返回一个`token`字段该token的有效期为7天。
### Token验证
后续需要认证的接口可以在请求头中携带token
### 3. 获取用户信息
**GET** `/api/erp/user/info`
**请求头:**
```
Authorization: Bearer {token}
```
或者使用自定义header
### 4. 更新用户信息
**PUT** `/api/erp/user/info`
**请求头:**
```
X-Auth-Token: {token}
Authorization: Bearer {token}
```
### Token验证方法
### 5. 修改密码
**POST** `/api/erp/user/change-password`
在需要认证的接口中可以通过以下方式验证token
**请求头:**
```
Authorization: Bearer {token}
```
```java
@Autowired
private ErpUserService erpUserService;
// 验证token
ErpUser user = erpUserService.validateToken(token);
if (user == null) {
// Token无效或已过期
return Result.fail(ResultCode.TOKEN_INVALID);
**请求体:**
```json
{
"oldPassword": "123456",
"newPassword": "654321"
}
```
---
## 认证方式
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 0000 | 操作成功 |
| 4000 | 参数错误 |
| 4001 | 参数验证失败 |
| 4002 | 未授权(账号或密码错误) |
| 4003 | 禁止访问(账号被禁用) |
| 6000 | 业务错误(账号已存在等) |
| 7001 | 用户不存在 |
| 7002 | 用户已存在 |
| 7003 | 密码错误 |
| 7004 | Token无效或已过期 |
---
## 安全说明
1. **密码加密**密码使用MD5加密存储生产环境建议使用BCrypt或Argon2等更安全的加密方式。
2. **Token安全**
- 当前实现使用简单的MD5+Base64编码生产环境建议使用JWTJSON Web Token
- Token包含用户ID、用户名、时间戳和签名
- Token有效期为7天过期后需要重新登录
3. **IP记录**系统会记录用户最后登录的IP地址可用于安全审计。
4. **账号状态**:支持账号禁用功能,禁用后的账号无法登录。
---
## 后续扩展建议
1. **JWT Token**将当前简单的Token实现替换为标准的JWT
2. **密码策略**:添加密码复杂度要求、密码过期策略
3. **登录限制**添加登录失败次数限制、IP白名单等功能
4. **权限管理**:添加角色和权限管理功能
5. **操作日志**:记录用户的操作日志,便于审计
---
## 测试示例
### 使用curl测试注册接口
```bash
curl -X POST http://localhost:8080/api/erp/user/register \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "123456",
"storeCode": "STORE001",
"nickName": "测试用户",
"phone": "13800138000",
"email": "test@example.com"
}'
所有需要认证的接口都需要在请求头中携带Token
```
Authorization: Bearer {token}
```
### 使用curl测试登录接口
```bash
curl -X POST http://localhost:8080/api/erp/user/login \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"password": "123456"
}'
```
---
## 注意事项
1. 执行数据库表创建SQL后才能使用注册和登录功能
2. 生产环境建议修改`TokenUtils`中的`TOKEN_SECRET`,使用更复杂的密钥
3. 建议在生产环境使用HTTPS协议保护密码和Token传输安全
Token通过登录接口获取有效期由后端配置决定。

View File

@@ -1,82 +0,0 @@
# PayPal订单生命周期和字段填充说明
## 订单状态流转
PayPal订单会经历以下状态
1. **CREATED** - 订单已创建(初始状态)
2. **PAYER_ACTION_REQUIRED** - 需要付款人操作跳转到PayPal登录页
3. **APPROVED** - 订单已批准用户在PayPal批准支付
4. **COMPLETED** - 订单已完成(订单被捕获后)
## 字段填充时机
### 创建订单时CREATED状态
以下字段在创建订单时通常为 **null**,这是**正常现象**
- `payer_email` - null用户尚未登录PayPal
- `payer_name` - null用户尚未登录PayPal
- `payer_id` - null用户尚未登录PayPal
- `payment_status` - null尚未支付
- `capture_id` - null尚未捕获订单
**原因**创建订单时用户还没有在PayPal登录和批准支付所以PayPal不会返回这些信息。
### 用户批准订单后APPROVED状态
以下字段会被填充:
- `payer_email` - 用户PayPal邮箱
- `payer_name` - 用户PayPal姓名
- `payer_id` - 用户PayPal账户ID
- `payment_status` - 仍为null尚未捕获
- `capture_id` - 仍为null尚未捕获
**触发时机**
- 用户点击"立即支付"后跳转到PayPal
- 用户在PayPal登录并批准支付
- 系统通过Webhook事件 `CHECKOUT.ORDER.APPROVED` 或查询订单详情获取
### 订单被捕获后COMPLETED状态
以下字段会被填充:
- `payment_status` - 支付状态COMPLETED
- `capture_id` - 支付捕获ID
**触发时机**
- 调用 `/api/paypal/orders/{orderId}/capture` 接口捕获订单
- 系统通过Webhook事件 `PAYMENT.CAPTURE.COMPLETED` 获取
## 与沙箱测试的关系
**这些字段为null与沙箱测试环境无关**这是PayPal订单的正常生命周期
- 在**生产环境**中如果订单状态为CREATED这些字段同样为null
- 只有在用户完成PayPal登录、批准支付、订单被捕获后这些字段才会被填充
- 沙箱环境和生产环境的行为是一致的
## 如何查看完整信息
1. **查看 `order_data` 字段**该字段存储了PayPal返回的完整JSON响应包含所有信息
2. **查询订单详情**:调用 `/api/paypal/orders/{orderId}` 接口,系统会自动更新订单信息
3. **等待Webhook事件**PayPal会发送Webhook事件系统会自动更新订单信息
## 代码更新逻辑
系统会在以下时机自动更新订单信息:
1. **创建订单时**:调用 `createPaymentOrder` 方法
2. **查询订单时**:调用 `getOrder` 接口,自动调用 `updateOrderFromPayPal` 方法
3. **捕获订单时**:调用 `captureOrder` 接口,自动调用 `updateOrderFromPayPal` 方法
4. **Webhook事件**处理Webhook事件时自动调用 `updateOrderFromPayPal` 方法
## 建议
如果需要在订单创建后立即获取payer信息可以
1. 在用户从PayPal返回后调用 `getOrder` 接口查询订单详情
2. 配置Webhook监听 `CHECKOUT.ORDER.APPROVED` 事件
3. 在订单确认页面加载时主动查询一次PayPal订单详情

View File

@@ -1,101 +1,44 @@
# PayPal Webhook 配置说明
# PayPal Webhook 配置
## 内网穿透配置
当前使用内网穿透服务cpolar将本地服务暴露到公网
- **内网地址**: `http://localhost:8082`
- **公网地址**: `https://2646b437.r33.cpolar.top`
- **Webhook URL**: `https://2646b437.r33.cpolar.top/api/paypal/webhook`
## 配置文件
Webhook URL 已配置在 `application-dev.yml` 中:
```yaml
paypal:
webhook-url: https://2646b437.r33.cpolar.top/api/paypal/webhook
```
## PayPal 控制台配置步骤
## 配置步骤
1. **登录 PayPal 开发者控制台**
- 沙箱环境https://developer.paypal.com/dashboard/
- 使用你的 PayPal 开发者账号登录
- 选择你的沙箱应用
2. **选择应用**
- 进入 "My Apps & Credentials"
- 选择你的沙箱应用Sandbox App
3. **配置 Webhook**
- 在应用详情页面,找到 "Webhooks" 部分
- 点击 "Add Webhook" 或 "Edit Webhook"
- 输入 Webhook URL: `https://2646b437.r33.cpolar.top/api/paypal/webhook`
- 选择要监听的事件类型:
2. **配置 Webhook**
- 进入应用详情页面,找到 "Webhooks" 部分
- 点击 "Add Webhook"
- 输入 Webhook URL: `https://你的域名/api/paypal/webhook`
- 选择事件类型:
- `PAYMENT.CAPTURE.COMPLETED` - 支付捕获完成
- `PAYMENT.CAPTURE.DENIED` - 支付捕获被拒绝
- `PAYMENT.CAPTURE.REFUNDED` - 支付退款
- `CHECKOUT.ORDER.APPROVED` - 订单已批准
- `CHECKOUT.ORDER.COMPLETED` - 订单已完成
- `CHECKOUT.ORDER.CANCELLED` - 订单已取消
- 保存配置
- `CHECKOUT.ORDER.DECLINED` - 订单被拒绝
4. **获取 Webhook ID**
- 配置完成后,PayPal 会生成一个 Webhook ID
- 将此 Webhook ID 配置到 `application-dev.yml` 中的 `paypal.webhook-id`
3. **获取 Webhook ID**
- 配置完成后,获取 Webhook ID
- 配置到 `application-dev.yml` 中的 `paypal.webhook-id`
## 配置文件
```yaml
paypal:
webhook-url: https://你的域名/api/paypal/webhook
webhook-id: YOUR_WEBHOOK_ID
```
## 注意事项
1. **内网穿透地址变化**
- 免费版 cpolar 的地址可能会变化
- 如果地址变化需要
- 更新 `application-dev.yml` 中的 `webhook-url`
- 在 PayPal 控制台更新 Webhook URL
- **生产环境必须启用签名验证**
- **必须使用 HTTPS**
- 内网穿透地址变化需要更新配置
2. **HTTPS 要求**
- PayPal Webhook 要求使用 HTTPS
- cpolar 提供的地址默认支持 HTTPS
3. **测试 Webhook**
- 在 PayPal 控制台可以测试 Webhook
- 选择 "Send test event" 发送测试事件
- 检查后端日志确认是否收到事件
4. **生产环境**
- 生产环境需要使用固定的域名
- 建议使用自己的域名配置 SSL 证书
- 更新 `application-prod.yml` 中的 `webhook-url`
## 验证配置
启动应用后,查看日志中的 PayPal 配置信息:
## 本地测试
使用内网穿透工具(如 ngrok 或 cpolar
```bash
ngrok http 8082
```
═══════════════════════════════════════════════════════════
PayPal配置加载验证:
- Client ID: ✅ 已配置 (...)
- Client Secret: ✅ 已配置 (...)
- Mode: sandbox
- Enabled: true
- Base URL: https://api-m.sandbox.paypal.com
- Webhook URL: ✅ https://2646b437.r33.cpolar.top/api/paypal/webhook
═══════════════════════════════════════════════════════════
```
如果 Webhook URL 显示为 "❌ 未配置",请检查配置文件。
## 后端接口
Webhook 回调接口路径:`/api/paypal/webhook`
- **方法**: POST
- **路径**: `/api/paypal/webhook`
- **完整URL**: `https://2646b437.r33.cpolar.top/api/paypal/webhook`
## 前端配置
前端不需要修改,因为:
- 前端使用 `window.location.origin` 自动获取当前域名
- 用户访问的是前端地址,不是后端地址
- 支付成功/取消回调使用前端路由,不涉及 Webhook
将生成的HTTPS地址配置到PayPal Webhook URL。

View File

@@ -1,41 +1,15 @@
# MT Pay - 支付系统
## 项目简介
面向东南亚地区的电商支付系统支持PayPal支付、商品管理、订单管理、货币转换等功能。
## 功能特性
- ✅ PayPal支付集成创建订单、捕获支付、Webhook处理
- ✅ 商品管理商品、SKU、商品链接
- ✅ 客户订单管理(支持东南亚地址格式)
- ✅ 货币转换(实时汇率,支持多币种)
- ✅ 百度翻译集成商品名称、SKU名称自动翻译
- ✅ 订单状态管理
- ✅ 支付记录管理
## 技术栈
- Spring Boot 4.0.0
- MyBatis-Plus
- MySQL 5.7+
- Jackson (JSON处理)
- Lombok
- RestTemplate
## 快速开始
### 1. 数据库配置
执行数据库脚本(按顺序):
```sql
-- 1. 商品相关表
source database/customer_order_schema.sql;
-- 2. 货币转换字段
source database/customer_order_currency_update.sql;
-- 3. 地址字段(混合方案)
source database/customer_order_address_optimized.sql;
```
@@ -53,13 +27,6 @@ source database/customer_order_address_optimized.sql;
mvn spring-boot:run
```
```bash
mvn clean package
java -jar target/mt-pay-0.0.1-SNAPSHOT.jar
```
### 4. 访问地址
- 后端API: http://localhost:8082/api
@@ -69,89 +36,35 @@ java -jar target/mt-pay-0.0.1-SNAPSHOT.jar
## 核心API
### 商品管理
- `POST /api/product` - 创建商品
- `GET /api/product/{id}` - 获取商品详情
- `GET /api/product/link/{linkCode}` - 通过链接码获取商品
- `POST /api/product` - 创建商品
- `POST /api/product/query` - 查询商品列表(分页)
- `PUT /api/product/{id}/off-shelf` - 下架商品
### 订单管理
- `POST /api/order` - 创建客户订单
- `GET /api/order/{orderNo}` - 获取订单详情
- `POST /api/order/calculate-currency-conversion` - 计算货币转换
- `POST /api/order/query` - 查询订单列表(分页)
### PayPal支付
- `POST /api/paypal/order` - 创建PayPal订单
- `POST /api/paypal/capture` - 捕获支付
- `POST /api/paypal/orders` - 创建PayPal订单
- `POST /api/paypal/orders/{orderId}/capture` - 捕获支付
- `POST /api/paypal/webhook` - Webhook回调
## 项目结构
### ERP用户
- `POST /api/erp/user/register` - 用户注册
- `POST /api/erp/user/login` - 用户登录
- `GET /api/erp/user/info` - 获取用户信息
```
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**: 百度翻译工具
## 配置说明
### PayPal配置
```yaml
paypal:
client-id: your-client-id
client-secret: your-client-secret
mode: sandbox # sandbox 或 production
enabled: true
```
### 百度翻译配置
```yaml
baidu:
translator:
app-id: your-app-id
securityKey: your-security-key
transApiHost: https://fanyi-api.baidu.com/api/trans/vip/translate
```
## 数据库表
- `mt_product` - 商品表
- `mt_product_sku` - SKU表
- `mt_product_link` - 商品链接表
- `customer_order` - 客户订单表
- `payment_order` - 支付订单表
- `payment_record` - 支付记录表
## 地址字段设计
采用混合方案:
- **基础字段**(独立列):国家、城市、州/省、邮编、详细地址1/2
- **特殊字段**JSON存储各国特殊字段组屋号、Barangay、泰文地址等
详见:`database/customer_order_address_optimized.sql`
- Spring Boot 4.0.0
- MyBatis-Plus
- MySQL 5.7+
- Vue 3 + Element Plus
## 相关文档
- `SYSTEM_ARCHITECTURE.md` - 系统架构文档
- `../PAYPAL_WEBHOOK_GUIDE.md` - PayPal Webhook配置指南
- `../PAYPAL_TEST_ACCOUNT.md` - PayPal测试账号说明
## 许可证
MIT
- `PAYPAL_WEBHOOK_SETUP.md` - PayPal Webhook配置
- `ERP_USER_API.md` - ERP用户API文档

View File

@@ -1,162 +0,0 @@
# 系统架构完整性说明
## 架构检查清单
### ✅ 后端架构完整性
#### 1. 分层架构
-**Controller层**: 接口控制器处理HTTP请求
-**Service层**: 业务逻辑层(接口+实现分离)
-**Mapper层**: 数据访问层MyBatis-Plus
-**Entity层**: 实体类(数据库映射)
#### 2. 通用组件
-**Result<T>**: 统一响应结果类
-**ResultCode**: 响应码枚举
-**BusinessException**: 业务异常类
-**GlobalExceptionHandler**: 全局异常处理器
#### 3. 枚举类
-**OrderStatus**: 订单状态枚举
-**PaymentType**: 支付类型枚举
-**RecordType**: 记录类型枚举
#### 4. 常量类
-**PaymentConstants**: 支付相关常量
#### 5. 工具类
-**DateUtils**: 日期时间工具类
-**StringUtils**: 字符串工具类
-**OrderIdGenerator**: 订单号生成器
#### 6. 配置类
-**DruidDataSourceConfig**: 数据源配置
-**MyBatisPlusConfig**: MyBatis-Plus配置
-**MyMetaObjectHandler**: 自动填充处理器
-**PingPongProperties**: PingPong配置属性
-**RestClientConfig**: HTTP客户端配置
-**WebConfig**: Web配置跨域等
#### 7. DTO类
-**请求DTO**: CheckoutRequestDTO
-**响应DTO**: CheckoutResponseDTO
-**风控DTO**: RiskInfoDTO及其子DTO
### ✅ 前端架构完整性
#### 1. 目录结构
-**api/**: API接口封装
-**components/**: 通用组件
-**config/**: 配置文件
-**router/**: 路由配置
-**store/**: 状态管理
-**utils/**: 工具函数
-**views/**: 页面组件
#### 2. 工具类
-**constants.js**: 常量定义
-**helpers.js**: 工具函数
-**request.js**: 请求工具
#### 3. 通用组件
-**PageHeader.vue**: 页面头部组件
-**Loading.vue**: 加载组件
#### 4. 配置管理
-**config/index.js**: 统一配置
-**.env.development**: 开发环境配置
-**.env.production**: 生产环境配置
#### 5. 状态管理
-**store/index.js**: 简单状态管理
## 架构特点
### 1. 统一规范
- ✅ 统一响应格式Result<T>
- ✅ 统一异常处理BusinessException + GlobalExceptionHandler
- ✅ 统一响应码ResultCode枚举
- ✅ 统一命名规范
### 2. 代码复用
- ✅ 工具类封装通用功能
- ✅ 枚举类替代魔法字符串
- ✅ 常量类集中管理配置
- ✅ 通用组件可复用
### 3. 可扩展性
- ✅ 接口与实现分离
- ✅ 枚举类易于扩展
- ✅ 配置统一管理
- ✅ 模块化设计
### 4. 可维护性
- ✅ 代码结构清晰
- ✅ 注释完整
- ✅ 职责单一
- ✅ 依赖注入
## 包结构总览
### 后端mt-pay
```
com.mtkj.mtpay/
├── common/ # 通用组件
│ ├── Result.java
│ ├── ResultCode.java
│ ├── constants/
│ └── enums/
├── config/ # 配置类
├── controller/ # 控制器
├── dto/ # 数据传输对象
├── entity/ # 实体类
├── exception/ # 异常处理
├── mapper/ # 数据访问层
├── service/ # 服务接口
├── service/impl/ # 服务实现
└── util/ # 工具类
```
### 前端MTKJPAY-FRONT
```
src/
├── api/ # API接口
├── components/ # 通用组件
├── config/ # 配置
├── router/ # 路由
├── store/ # 状态管理
├── utils/ # 工具函数
└── views/ # 页面组件
```
## 最佳实践
1.**统一响应格式**: 所有接口返回Result<T>
2.**异常处理**: 使用BusinessException抛出业务异常
3.**枚举使用**: 使用枚举替代魔法字符串
4.**工具类**: 通用功能封装为工具类
5.**配置管理**: 配置统一管理,支持多环境
6.**接口分离**: Service接口与实现分离
7.**跨域配置**: WebConfig统一配置跨域
8.**常量管理**: 常量集中管理
## 系统完整性评分
- **后端架构**: ⭐⭐⭐⭐⭐ (5/5)
- **前端架构**: ⭐⭐⭐⭐⭐ (5/5)
- **代码规范**: ⭐⭐⭐⭐⭐ (5/5)
- **可维护性**: ⭐⭐⭐⭐⭐ (5/5)
- **可扩展性**: ⭐⭐⭐⭐⭐ (5/5)
## 总结
系统架构已完整,包含:
- ✅ 完整的分层架构
- ✅ 统一的响应格式和异常处理
- ✅ 完善的工具类和枚举类
- ✅ 规范的代码结构
- ✅ 可扩展的设计
- ✅ 完整的配置管理
系统已具备生产环境使用的基础架构!

View File

@@ -15,12 +15,16 @@ public class WebConfig implements WebMvcConfigurer {
/**
* 配置跨域
* 注意:生产环境建议限制为具体的前端域名
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
log.info("配置跨域访问,路径: /api/**, 允许所有来源");
// 开发环境允许所有来源,生产环境建议限制为具体域名
String allowedOrigins = System.getProperty("cors.allowed.origins", "*");
log.info("配置跨域访问,路径: /api/**, 允许来源: {}", allowedOrigins);
registry.addMapping("/api/**")
.allowedOriginPatterns("*")
.allowedOriginPatterns(allowedOrigins)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)

View File

@@ -3,8 +3,10 @@ package com.mtkj.mtpay.controller;
import com.mtkj.mtpay.common.Result;
import com.mtkj.mtpay.dto.request.CalculateCurrencyConversionRequestDTO;
import com.mtkj.mtpay.dto.request.CreateCustomerOrderRequestDTO;
import com.mtkj.mtpay.dto.request.OrderQueryRequestDTO;
import com.mtkj.mtpay.dto.response.CurrencyConversionDTO;
import com.mtkj.mtpay.dto.response.CustomerOrderResponseDTO;
import com.mtkj.mtpay.dto.response.PageResult;
import com.mtkj.mtpay.service.CustomerOrderService;
import com.mtkj.mtpay.service.ExchangeRateService;
import jakarta.validation.Valid;
@@ -134,6 +136,18 @@ public class CustomerOrderController {
return Result.success(order);
}
/**
* 查询订单列表(支持多条件查询和分页)
*/
@PostMapping("/query")
public Result<PageResult<com.mtkj.mtpay.dto.response.OrderListResponseDTO>> queryOrders(
@RequestBody OrderQueryRequestDTO query) {
log.info("查询订单列表,查询条件:{}", query);
PageResult<com.mtkj.mtpay.dto.response.OrderListResponseDTO> pageResult =
customerOrderService.queryOrders(query);
return Result.success(pageResult);
}
/**
* 构建汇率说明文本
*/

View File

@@ -0,0 +1,96 @@
package com.mtkj.mtpay.dto.request;
import lombok.Data;
/**
* 订单列表查询请求DTO
*/
@Data
public class OrderQueryRequestDTO {
/**
* 订单号(精确查询)
*/
private String orderNo;
/**
* PayPal订单ID精确查询
*/
private String paypalOrderId;
/**
* 商户订单号精确查询等同于orderNo
*/
private String merchantOrderNo;
/**
* 订单状态PENDING-待支付PAID-已支付SHIPPED-已发货COMPLETED-已完成CANCELLED-已取消)
*/
private String status;
/**
* 支付状态UNPAID-未支付PAID-已支付FAILED-支付失败)
*/
private String paymentStatus;
/**
* PayPal订单状态CREATED-已创建APPROVED-已批准COMPLETED-已完成VOIDED-已取消等)
*/
private String paypalStatus;
/**
* PayPal支付状态COMPLETED-已完成DENIED-被拒绝PENDING-待处理等)
*/
private String paypalPaymentStatus;
/**
* 客户姓名(模糊查询)
*/
private String customerName;
/**
* 客户电话(模糊查询)
*/
private String customerPhone;
/**
* 客户邮箱(模糊查询)
*/
private String customerEmail;
/**
* 商品名称(模糊查询)
*/
private String productName;
/**
* 支付者邮箱PayPal模糊查询
*/
private String payerEmail;
/**
* 支付者姓名PayPal模糊查询
*/
private String payerName;
/**
* 开始时间(订单创建时间)
*/
private String startTime;
/**
* 结束时间(订单创建时间)
*/
private String endTime;
/**
* 当前页码从1开始默认1
*/
private Integer pageNum = 1;
/**
* 每页大小默认10
*/
private Integer pageSize = 10;
}

View File

@@ -0,0 +1,155 @@
package com.mtkj.mtpay.dto.response;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单列表响应DTO包含客户订单和PayPal订单信息
*/
@Data
public class OrderListResponseDTO implements Serializable {
/**
* 客户订单ID
*/
private Long id;
/**
* 订单号
*/
private String orderNo;
/**
* 商品ID
*/
private Long productId;
/**
* 商品名称
*/
private String productName;
/**
* SKU名称
*/
private String skuName;
/**
* 购买数量
*/
private Integer quantity;
/**
* 订单总金额(原始货币)
*/
private BigDecimal totalAmount;
/**
* 原始货币代码
*/
private String originalCurrency;
/**
* 原始订单金额
*/
private BigDecimal originalAmount;
/**
* 支付货币代码
*/
private String paymentCurrency;
/**
* 支付金额
*/
private BigDecimal paymentAmount;
/**
* 订单状态PENDING-待支付PAID-已支付SHIPPED-已发货COMPLETED-已完成CANCELLED-已取消)
*/
private String status;
/**
* 支付状态UNPAID-未支付PAID-已支付FAILED-支付失败)
*/
private String paymentStatus;
/**
* 客户姓名
*/
private String customerName;
/**
* 客户电话
*/
private String customerPhone;
/**
* 客户邮箱
*/
private String customerEmail;
/**
* 收货国家
*/
private String shippingCountry;
/**
* 收货城市
*/
private String shippingCity;
/**
* PayPal订单ID
*/
private String paypalOrderId;
/**
* PayPal订单状态CREATED-已创建APPROVED-已批准COMPLETED-已完成VOIDED-已取消等)
*/
private String paypalStatus;
/**
* PayPal支付状态COMPLETED-已完成DENIED-被拒绝PENDING-待处理等)
*/
private String paypalPaymentStatus;
/**
* 支付者邮箱PayPal
*/
private String payerEmail;
/**
* 支付者姓名PayPal
*/
private String payerName;
/**
* 支付捕获IDPayPal
*/
private String captureId;
/**
* 订单创建时间
*/
private LocalDateTime createTime;
/**
* 订单更新时间
*/
private LocalDateTime updateTime;
/**
* PayPal订单创建时间
*/
private LocalDateTime paypalCreateTime;
/**
* PayPal订单更新时间
*/
private LocalDateTime paypalUpdateTime;
}

View File

@@ -5,6 +5,8 @@ import com.mtkj.mtpay.entity.MtProductSku;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
@@ -31,5 +33,38 @@ public interface MtProductSkuMapper extends BaseMapper<MtProductSku> {
"</foreach>" +
"</script>")
int insertBatch(@Param("skuList") List<MtProductSku> skuList);
/**
* 扣减库存使用SELECT FOR UPDATE锁防止超卖
* 使用悲观锁确保并发安全
*
* @param skuId SKU ID
* @param quantity 扣减数量
* @return 更新行数1表示成功0表示库存不足
*/
@Update("UPDATE mt_product_sku SET stock = stock - #{quantity}, update_time = NOW() " +
"WHERE id = #{skuId} AND stock >= #{quantity} AND status = 'ACTIVE'")
int deductStock(@Param("skuId") Long skuId, @Param("quantity") Integer quantity);
/**
* 恢复库存(订单取消时使用)
*
* @param skuId SKU ID
* @param quantity 恢复数量
* @return 更新行数
*/
@Update("UPDATE mt_product_sku SET stock = stock + #{quantity}, update_time = NOW() " +
"WHERE id = #{skuId}")
int restoreStock(@Param("skuId") Long skuId, @Param("quantity") Integer quantity);
/**
* 查询并锁定SKU使用SELECT FOR UPDATE
* 用于在事务中锁定SKU记录防止并发问题
*
* @param skuId SKU ID
* @return SKU实体
*/
@Select("SELECT * FROM mt_product_sku WHERE id = #{skuId} FOR UPDATE")
MtProductSku selectByIdForUpdate(@Param("skuId") Long skuId);
}

View File

@@ -1,7 +1,9 @@
package com.mtkj.mtpay.service;
import com.mtkj.mtpay.dto.request.CreateCustomerOrderRequestDTO;
import com.mtkj.mtpay.dto.request.OrderQueryRequestDTO;
import com.mtkj.mtpay.dto.response.CustomerOrderResponseDTO;
import com.mtkj.mtpay.dto.response.PageResult;
/**
* 客户订单服务接口
@@ -58,5 +60,18 @@ public interface CustomerOrderService {
String paymentCurrency,
java.math.BigDecimal paymentAmount,
java.math.BigDecimal exchangeRate);
/**
* 查询订单列表(支持多条件查询和分页)
* @param query 查询条件(包含分页参数)
* @return 分页结果
*/
PageResult<com.mtkj.mtpay.dto.response.OrderListResponseDTO> queryOrders(OrderQueryRequestDTO query);
/**
* 取消订单(恢复库存)
* @param orderNo 订单号
*/
void cancelOrder(String orderNo);
}

View File

@@ -3,8 +3,14 @@ package com.mtkj.mtpay.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mtkj.mtpay.common.ResultCode;
import com.mtkj.mtpay.common.enums.ProductStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mtkj.mtpay.dto.request.CreateCustomerOrderRequestDTO;
import com.mtkj.mtpay.dto.request.OrderQueryRequestDTO;
import com.mtkj.mtpay.dto.response.CustomerOrderResponseDTO;
import com.mtkj.mtpay.dto.response.OrderListResponseDTO;
import com.mtkj.mtpay.dto.response.PageResult;
import com.mtkj.mtpay.entity.PayPalPaymentOrder;
import com.mtkj.mtpay.entity.CustomerOrder;
import com.mtkj.mtpay.entity.MtProduct;
import com.mtkj.mtpay.entity.MtProductSku;
@@ -12,6 +18,7 @@ import com.mtkj.mtpay.exception.BusinessException;
import com.mtkj.mtpay.mapper.CustomerOrderMapper;
import com.mtkj.mtpay.mapper.MtProductMapper;
import com.mtkj.mtpay.mapper.MtProductSkuMapper;
import com.mtkj.mtpay.mapper.PayPalPaymentOrderMapper;
import com.mtkj.mtpay.service.CustomerOrderService;
import com.mtkj.mtpay.util.BaiduTranslatorUtils;
import com.mtkj.mtpay.util.OrderIdGenerator;
@@ -38,47 +45,54 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
private final CustomerOrderMapper customerOrderMapper;
private final MtProductMapper productMapper;
private final MtProductSkuMapper productSkuMapper;
private final PayPalPaymentOrderMapper payPalPaymentOrderMapper;
private final BaiduTranslatorUtils baiduTranslatorUtils;
@Override
@Transactional(rollbackFor = Exception.class)
public CustomerOrderResponseDTO createOrder(CreateCustomerOrderRequestDTO request) {
log.info("创建客户订单商品ID: {}, SKU ID: {}, 数量: {}",
request.getProductId(), request.getSkuId(), request.getQuantity());
// 验证商品是否存在
MtProduct product = productMapper.selectById(request.getProductId());
if (product == null) {
log.warn("商品不存在商品ID: {}", request.getProductId());
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "商品不存在");
}
// 验证商品状态:下架商品不能创建订单
if (ProductStatus.INACTIVE.getCode().equals(product.getStatus())) {
log.warn("商品已下架无法创建订单商品ID: {}", request.getProductId());
throw new BusinessException(ResultCode.BUSINESS_ERROR, "商品已下架,无法创建订单");
}
// 验证SKU是否存在
MtProductSku sku = productSkuMapper.selectById(request.getSkuId());
if (sku == null || !sku.getProductId().equals(request.getProductId())) {
log.warn("SKU不存在或不属于该商品SKU ID: {}, 商品ID: {}",
request.getSkuId(), request.getProductId());
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "SKU不存在");
}
// 验证库存库存为0或不足时不能创建订单
if (sku.getStock() == null || sku.getStock() <= 0) {
log.warn("库存为0无法创建订单SKU ID: {}, 库存: {}",
request.getSkuId(), sku.getStock());
throw new BusinessException(ResultCode.BUSINESS_ERROR, "商品库存为0无法创建订单");
}
if (sku.getStock() < request.getQuantity()) {
log.warn("库存不足SKU ID: {}, 库存: {}, 需要: {}",
request.getSkuId(), sku.getStock(), request.getQuantity());
throw new BusinessException(ResultCode.BUSINESS_ERROR, "库存不足");
}
// 关键使用SELECT FOR UPDATE锁定SKU记录防止并发超卖
MtProductSku lockedSku = productSkuMapper.selectByIdForUpdate(request.getSkuId());
if (lockedSku == null) {
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "SKU不存在");
}
// 再次验证库存(锁定后的最新库存)
if (lockedSku.getStock() == null || lockedSku.getStock() < request.getQuantity()) {
throw new BusinessException(ResultCode.BUSINESS_ERROR, "库存不足");
}
// 扣减库存(使用原子操作,确保并发安全)
int deductResult = productSkuMapper.deductStock(request.getSkuId(), request.getQuantity());
if (deductResult <= 0) {
throw new BusinessException(ResultCode.BUSINESS_ERROR, "库存扣减失败,可能库存不足");
}
// 创建订单
CustomerOrder order = new CustomerOrder();
order.setOrderNo(OrderIdGenerator.generateMerchantTransactionId());
@@ -90,17 +104,12 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
if (currency != null && !currency.trim().isEmpty()) {
try {
String targetLanguage = baiduTranslatorUtils.getLanguageByCurrency(currency);
log.debug("订单货币: {}, 推断目标语言: {}, 原始商品名称: {}",
currency, targetLanguage, product.getName());
String translated = baiduTranslatorUtils.getTransResult(product.getName(), targetLanguage);
if (translated != null && !translated.equals(product.getName())) {
translatedProductName = translated;
log.info("商品名称翻译: {} -> {} (货币: {}, 语言: {})",
product.getName(), translatedProductName, currency, targetLanguage);
}
} catch (Exception e) {
log.warn("翻译商品名称失败使用原始名称,商品ID: {}, 货币: {}",
request.getProductId(), currency, e);
// 翻译失败使用原始名称,静默处理
}
}
order.setProductName(translatedProductName);
@@ -182,11 +191,12 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
// 保存订单
int result = customerOrderMapper.insert(order);
if (result <= 0) {
log.error("创建订单失败商品ID: {}", request.getProductId());
throw new BusinessException(ResultCode.SYSTEM_ERROR, "创建订单失败");
}
log.info("客户订单创建成功,订单ID: {}, 订单号: {}", order.getId(), order.getOrderNo());
log.info("订单创建成功,订单号: {}, 商品ID: {}, SKU ID: {}, 数量: {}, 库存扣减: {}",
order.getOrderNo(), request.getProductId(), request.getSkuId(),
request.getQuantity(), request.getQuantity());
// 转换为响应DTO
CustomerOrderResponseDTO response = new CustomerOrderResponseDTO();
@@ -202,13 +212,11 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
@Override
public CustomerOrderResponseDTO getOrderByOrderNo(String orderNo) {
log.debug("查询订单,订单号: {}", orderNo);
LambdaQueryWrapper<CustomerOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CustomerOrder::getOrderNo, orderNo);
CustomerOrder order = customerOrderMapper.selectOne(queryWrapper);
if (order == null) {
log.warn("订单不存在,订单号: {}", orderNo);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在");
}
@@ -225,11 +233,9 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
@Override
public CustomerOrderResponseDTO getOrderById(Long id) {
log.debug("查询订单订单ID: {}", id);
CustomerOrder order = customerOrderMapper.selectById(id);
if (order == null) {
log.warn("订单不存在订单ID: {}", id);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在");
}
@@ -246,14 +252,11 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
@Override
public void updatePaymentStatus(String orderNo, String paymentStatus, Long paymentOrderId) {
log.info("更新订单支付状态,订单号: {}, 支付状态: {}, 支付订单ID: {}",
orderNo, paymentStatus, paymentOrderId);
LambdaQueryWrapper<CustomerOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CustomerOrder::getOrderNo, orderNo);
CustomerOrder order = customerOrderMapper.selectOne(queryWrapper);
if (order == null) {
log.warn("订单不存在,订单号: {}", orderNo);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在");
}
@@ -265,10 +268,10 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
// 如果支付成功,更新订单状态为已支付
if ("PAID".equals(paymentStatus)) {
order.setStatus("PAID");
log.info("订单支付成功,订单号: {}", orderNo);
}
customerOrderMapper.updateById(order);
log.info("订单支付状态更新成功,订单号: {}", orderNo);
}
@Override
@@ -277,16 +280,12 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
String paymentCurrency,
BigDecimal paymentAmount,
BigDecimal exchangeRate) {
log.info("更新订单货币转换信息,订单号: {}, 原始: {} {}, 支付: {} {} (汇率: {})",
orderNo, originalAmount, originalCurrency, paymentAmount, paymentCurrency, exchangeRate);
CustomerOrder order = customerOrderMapper.selectOne(
new LambdaQueryWrapper<CustomerOrder>()
.eq(CustomerOrder::getOrderNo, orderNo)
);
if (order == null) {
log.warn("订单不存在,订单号: {}", orderNo);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在");
}
@@ -299,24 +298,247 @@ public class CustomerOrderServiceImpl implements CustomerOrderService {
order.setRateLockedAt(java.time.LocalDateTime.now());
customerOrderMapper.updateById(order);
log.info("订单货币转换信息更新成功,订单号: {}", orderNo);
}
@Override
public void updateOrderStatus(String orderNo, String status) {
log.info("更新订单状态,订单号: {}, 状态: {}", orderNo, status);
LambdaQueryWrapper<CustomerOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CustomerOrder::getOrderNo, orderNo);
CustomerOrder order = customerOrderMapper.selectOne(queryWrapper);
if (order == null) {
log.warn("订单不存在,订单号: {}", orderNo);
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在");
}
order.setStatus(status);
customerOrderMapper.updateById(order);
log.info("订单状态更新成功,订单号: {}", orderNo);
}
@Override
public PageResult<OrderListResponseDTO> queryOrders(OrderQueryRequestDTO query) {
// 处理分页参数
int pageNum = query.getPageNum() != null && query.getPageNum() > 0 ? query.getPageNum() : 1;
int pageSize = query.getPageSize() != null && query.getPageSize() > 0 ? query.getPageSize() : 10;
// 限制每页最大数量
if (pageSize > 100) {
pageSize = 100;
}
// 1. 构建客户订单查询条件
LambdaQueryWrapper<CustomerOrder> orderWrapper = new LambdaQueryWrapper<>();
// 订单号查询
if (query.getOrderNo() != null && !query.getOrderNo().trim().isEmpty()) {
orderWrapper.eq(CustomerOrder::getOrderNo, query.getOrderNo().trim());
}
// 订单状态查询
if (query.getStatus() != null && !query.getStatus().trim().isEmpty()) {
orderWrapper.eq(CustomerOrder::getStatus, query.getStatus().trim());
}
// 支付状态查询
if (query.getPaymentStatus() != null && !query.getPaymentStatus().trim().isEmpty()) {
orderWrapper.eq(CustomerOrder::getPaymentStatus, query.getPaymentStatus().trim());
}
// 客户姓名模糊查询
if (query.getCustomerName() != null && !query.getCustomerName().trim().isEmpty()) {
orderWrapper.like(CustomerOrder::getCustomerName, query.getCustomerName().trim());
}
// 客户电话模糊查询
if (query.getCustomerPhone() != null && !query.getCustomerPhone().trim().isEmpty()) {
orderWrapper.like(CustomerOrder::getCustomerPhone, query.getCustomerPhone().trim());
}
// 客户邮箱模糊查询
if (query.getCustomerEmail() != null && !query.getCustomerEmail().trim().isEmpty()) {
orderWrapper.like(CustomerOrder::getCustomerEmail, query.getCustomerEmail().trim());
}
// 商品名称模糊查询
if (query.getProductName() != null && !query.getProductName().trim().isEmpty()) {
orderWrapper.like(CustomerOrder::getProductName, query.getProductName().trim());
}
// 时间范围查询
if (query.getStartTime() != null && !query.getStartTime().trim().isEmpty()) {
try {
java.time.LocalDateTime start = java.time.LocalDateTime.parse(query.getStartTime().trim());
orderWrapper.ge(CustomerOrder::getCreateTime, start);
} catch (Exception e) {
log.warn("开始时间格式错误: {}", query.getStartTime(), e);
}
}
if (query.getEndTime() != null && !query.getEndTime().trim().isEmpty()) {
try {
java.time.LocalDateTime end = java.time.LocalDateTime.parse(query.getEndTime().trim());
orderWrapper.le(CustomerOrder::getCreateTime, end);
} catch (Exception e) {
log.warn("结束时间格式错误: {}", query.getEndTime(), e);
}
}
// 如果指定了PayPal相关查询条件需要先查询PayPal订单获取对应的商户订单号
java.util.List<String> merchantOrderNos = null;
if (query.getPaypalOrderId() != null && !query.getPaypalOrderId().trim().isEmpty() ||
query.getMerchantOrderNo() != null && !query.getMerchantOrderNo().trim().isEmpty() ||
query.getPaypalStatus() != null && !query.getPaypalStatus().trim().isEmpty() ||
query.getPaypalPaymentStatus() != null && !query.getPaypalPaymentStatus().trim().isEmpty() ||
query.getPayerEmail() != null && !query.getPayerEmail().trim().isEmpty() ||
query.getPayerName() != null && !query.getPayerName().trim().isEmpty()) {
// 构建PayPal订单查询条件
LambdaQueryWrapper<PayPalPaymentOrder> paypalWrapper = new LambdaQueryWrapper<>();
if (query.getPaypalOrderId() != null && !query.getPaypalOrderId().trim().isEmpty()) {
paypalWrapper.eq(PayPalPaymentOrder::getPaypalOrderId, query.getPaypalOrderId().trim());
}
if (query.getMerchantOrderNo() != null && !query.getMerchantOrderNo().trim().isEmpty()) {
paypalWrapper.eq(PayPalPaymentOrder::getMerchantOrderNo, query.getMerchantOrderNo().trim());
}
if (query.getPaypalStatus() != null && !query.getPaypalStatus().trim().isEmpty()) {
paypalWrapper.eq(PayPalPaymentOrder::getStatus, query.getPaypalStatus().trim());
}
if (query.getPaypalPaymentStatus() != null && !query.getPaypalPaymentStatus().trim().isEmpty()) {
paypalWrapper.eq(PayPalPaymentOrder::getPaymentStatus, query.getPaypalPaymentStatus().trim());
}
if (query.getPayerEmail() != null && !query.getPayerEmail().trim().isEmpty()) {
paypalWrapper.like(PayPalPaymentOrder::getPayerEmail, query.getPayerEmail().trim());
}
if (query.getPayerName() != null && !query.getPayerName().trim().isEmpty()) {
paypalWrapper.like(PayPalPaymentOrder::getPayerName, query.getPayerName().trim());
}
// 查询PayPal订单获取商户订单号列表
java.util.List<PayPalPaymentOrder> paypalOrders = payPalPaymentOrderMapper.selectList(paypalWrapper);
merchantOrderNos = paypalOrders.stream()
.map(PayPalPaymentOrder::getMerchantOrderNo)
.filter(java.util.Objects::nonNull)
.distinct()
.collect(java.util.stream.Collectors.toList());
if (merchantOrderNos.isEmpty()) {
// 如果PayPal订单查询结果为空直接返回空分页结果
log.debug("PayPal订单查询结果为空返回空列表");
return new PageResult<>((long) pageNum, (long) pageSize, 0L, new java.util.ArrayList<>());
}
// 将商户订单号作为客户订单的查询条件
orderWrapper.in(CustomerOrder::getOrderNo, merchantOrderNos);
}
// 按创建时间降序排序
orderWrapper.orderByDesc(CustomerOrder::getCreateTime);
// 使用MyBatis-Plus分页查询
Page<CustomerOrder> page = new Page<>(pageNum, pageSize);
IPage<CustomerOrder> orderPage = customerOrderMapper.selectPage(page, orderWrapper);
java.util.List<CustomerOrder> orders = orderPage.getRecords();
if (orders.isEmpty()) {
return new PageResult<>((long) pageNum, (long) pageSize, orderPage.getTotal(), new java.util.ArrayList<>());
}
// 2. 批量查询PayPal订单信息优化N+1问题
java.util.List<String> orderNos = orders.stream()
.map(CustomerOrder::getOrderNo)
.filter(java.util.Objects::nonNull)
.collect(java.util.stream.Collectors.toList());
java.util.Map<String, PayPalPaymentOrder> paypalOrderMap = new java.util.HashMap<>();
if (!orderNos.isEmpty()) {
LambdaQueryWrapper<PayPalPaymentOrder> paypalWrapper = new LambdaQueryWrapper<>();
paypalWrapper.in(PayPalPaymentOrder::getMerchantOrderNo, orderNos);
java.util.List<PayPalPaymentOrder> paypalOrders = payPalPaymentOrderMapper.selectList(paypalWrapper);
paypalOrderMap = paypalOrders.stream()
.collect(java.util.stream.Collectors.toMap(
PayPalPaymentOrder::getMerchantOrderNo,
order -> order,
(existing, replacement) -> existing // 如果有重复,保留第一个
));
}
// 3. 组装响应数据
java.util.List<OrderListResponseDTO> result = new java.util.ArrayList<>();
for (CustomerOrder order : orders) {
OrderListResponseDTO dto = new OrderListResponseDTO();
BeanUtils.copyProperties(order, dto);
// 设置PayPal订单信息
PayPalPaymentOrder paypalOrder = paypalOrderMap.get(order.getOrderNo());
if (paypalOrder != null) {
dto.setPaypalOrderId(paypalOrder.getPaypalOrderId());
dto.setPaypalStatus(paypalOrder.getStatus());
dto.setPaypalPaymentStatus(paypalOrder.getPaymentStatus());
dto.setPayerEmail(paypalOrder.getPayerEmail());
dto.setPayerName(paypalOrder.getPayerName());
dto.setCaptureId(paypalOrder.getCaptureId());
dto.setPaypalCreateTime(paypalOrder.getCreateTime());
dto.setPaypalUpdateTime(paypalOrder.getUpdateTime());
}
result.add(dto);
}
// 构建分页结果
return new PageResult<>(
orderPage.getCurrent(),
orderPage.getSize(),
orderPage.getTotal(),
result
);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelOrder(String orderNo) {
// 查询订单
LambdaQueryWrapper<CustomerOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CustomerOrder::getOrderNo, orderNo);
CustomerOrder order = customerOrderMapper.selectOne(queryWrapper);
if (order == null) {
throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在");
}
// 检查订单状态,只有未完成或未取消的订单才能取消
String currentStatus = order.getStatus();
if ("CANCELLED".equals(currentStatus)) {
return; // 已取消,无需重复操作
}
if ("COMPLETED".equals(currentStatus)) {
throw new BusinessException(ResultCode.BUSINESS_ERROR, "订单已完成,无法取消");
}
// 恢复库存只有在订单状态为PENDING或PAID时才恢复库存
if (("PENDING".equals(currentStatus) || "PAID".equals(currentStatus))
&& order.getSkuId() != null && order.getQuantity() != null) {
try {
productSkuMapper.restoreStock(order.getSkuId(), order.getQuantity());
} catch (Exception e) {
// 库存恢复失败不影响订单取消,继续执行
}
}
// 更新订单状态为已取消
order.setStatus("CANCELLED");
if ("UNPAID".equals(order.getPaymentStatus())) {
order.setPaymentStatus("CANCELLED");
}
customerOrderMapper.updateById(order);
log.info("订单取消成功,订单号: {}, 原状态: {}, 库存已恢复", orderNo, currentStatus);
}
}

View File

@@ -76,26 +76,17 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
*/
@Async("paypalWebhookExecutor")
public void processWebhookEventAsync(PayPalWebhookEventDTO event) {
log.info("开始异步处理PayPal Webhook事件事件ID: {}, 事件类型: {}", event.getId(), event.getEventType());
long startTime = System.currentTimeMillis();
try {
processWebhookEvent(event);
long elapsedTime = System.currentTimeMillis() - startTime;
log.info("✅ Webhook事件异步处理完成事件ID: {}, 事件类型: {}, 处理时间: {}ms",
event.getId(), event.getEventType(), elapsedTime);
} catch (Exception e) {
long elapsedTime = System.currentTimeMillis() - startTime;
log.error("❌ Webhook事件异步处理异常事件ID: {}, 事件类型: {}, 处理时间: {}ms",
event.getId(), event.getEventType(), elapsedTime, e);
log.error("Webhook事件处理异常事件ID: {}, 事件类型: {}",
event.getId(), event.getEventType(), e);
// 异步处理中的异常不会影响HTTP响应已在上层捕获
}
}
@Override
public void processWebhookEvent(PayPalWebhookEventDTO event) {
log.info("处理PayPal Webhook事件事件ID: {}, 事件类型: {}", event.getId(), event.getEventType());
try {
String eventType = event.getEventType();
@@ -119,13 +110,10 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
Map<String, Object> relatedIds = (Map<String, Object>) supplementaryData.get("related_ids");
if (relatedIds != null) {
paypalOrderId = (String) relatedIds.get("order_id");
log.debug("从supplementary_data提取PayPal订单ID: {}", paypalOrderId);
}
}
}
log.debug("提取PayPal订单ID: {}, 事件类型: {}", paypalOrderId, eventType);
// 尝试获取商户订单号
if (paypalOrderId != null) {
try {
@@ -133,10 +121,9 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
if (paypalOrder != null && paypalOrder.getPurchaseUnits() != null
&& !paypalOrder.getPurchaseUnits().isEmpty()) {
merchantOrderNo = paypalOrder.getPurchaseUnits().get(0).getReferenceId();
log.debug("提取商户订单号: {}", merchantOrderNo);
}
} catch (Exception e) {
log.debug("无法获取PayPal订单详情PayPal订单ID: {}", paypalOrderId, e);
// 静默处理
}
}
}
@@ -165,23 +152,15 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
}
// 重要更新PayPal支付订单信息无论事件类型只要获取到订单ID就更新
// 这样可以确保所有字段都被正确填充
if (paypalOrderId != null) {
try {
log.info("Webhook事件触发订单信息更新PayPal订单ID: {}, 事件类型: {}", paypalOrderId, eventType);
var paypalOrder = payPalService.getOrder(paypalOrderId);
if (paypalOrder != null) {
payPalPaymentOrderService.updateOrderFromPayPal(paypalOrderId, paypalOrder);
log.info("Webhook事件订单信息更新成功PayPal订单ID: {}, 状态: {}, 事件类型: {}",
paypalOrderId, paypalOrder.getStatus(), eventType);
} else {
log.warn("查询PayPal订单详情返回nullPayPal订单ID: {}", paypalOrderId);
}
} catch (Exception e) {
log.warn("更新PayPal支付订单信息失败PayPal订单ID: {}, 事件类型: {}", paypalOrderId, eventType, e);
// 静默处理,不影响主流程
}
} else {
log.warn("无法提取PayPal订单ID无法更新订单信息事件类型: {}, 事件ID: {}", eventType, event.getId());
}
// 根据事件类型处理不同的业务逻辑
@@ -211,10 +190,10 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
handleOrderDeclined(event);
break;
default:
log.info("未处理的事件类型: {}已记录到数据库,但未执行特定业务逻辑)", eventType);
// 未处理的事件类型已记录到数据库
}
} catch (Exception e) {
log.error("处理Webhook事件异常事件ID: {}", event.getId(), e);
log.error("处理Webhook事件异常事件ID: {}, 事件类型: {}", event.getId(), event.getEventType(), e);
throw new RuntimeException("处理Webhook事件失败: " + e.getMessage(), e);
}
}
@@ -319,6 +298,9 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
try {
customerOrderService.updatePaymentStatus(referenceId, "PAID", null);
log.info("ERP订单支付状态已更新订单号: {}", referenceId);
// 注意:库存已在创建订单时扣减,支付成功时不需要再次扣减
// 这里只记录日志,确认库存扣减已完成
log.info("订单支付成功,库存扣减已确认,订单号: {}", referenceId);
} catch (Exception e) {
log.error("更新ERP订单状态失败订单号: {}", referenceId, e);
}
@@ -461,8 +443,6 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
* 处理订单取消事件
*/
private void handleOrderCancelled(PayPalWebhookEventDTO event) {
log.info("处理订单取消事件事件ID: {}", event.getId());
try {
Map<String, Object> resource = event.getResource();
if (resource == null) {
@@ -470,9 +450,6 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
}
String orderId = (String) resource.get("id");
String status = (String) resource.get("status");
log.info("订单已取消/作废Order ID: {}, 状态: {}", orderId, status);
// 更新PayPal支付订单信息
if (orderId != null) {
@@ -480,15 +457,29 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
var paypalOrder = payPalService.getOrder(orderId);
if (paypalOrder != null) {
payPalPaymentOrderService.updateOrderFromPayPal(orderId, paypalOrder);
log.info("PayPal支付订单信息已更新订单取消/作废后PayPal订单ID: {}, 状态: {}",
orderId, paypalOrder.getStatus());
}
} catch (Exception e) {
log.warn("更新PayPal支付订单信息失败PayPal订单ID: {}", orderId, e);
// 静默处理
}
}
// TODO: 根据业务需求处理订单取消逻辑(如更新ERP订单状态为已取消
// 处理订单取消逻辑更新ERP订单状态为已取消,并恢复库存
if (orderId != null) {
try {
var paypalOrder = payPalService.getOrder(orderId);
if (paypalOrder != null && paypalOrder.getPurchaseUnits() != null
&& !paypalOrder.getPurchaseUnits().isEmpty()) {
String merchantOrderNo = paypalOrder.getPurchaseUnits().get(0).getReferenceId();
if (merchantOrderNo != null) {
customerOrderService.cancelOrder(merchantOrderNo);
log.info("订单已取消,订单号: {}", merchantOrderNo);
}
}
} catch (Exception e) {
log.error("处理订单取消失败PayPal订单ID: {}", orderId, e);
}
}
} catch (Exception e) {
log.error("处理订单取消事件异常事件ID: {}", event.getId(), e);
@@ -500,8 +491,6 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
* 当用户拒绝支付或支付失败时触发
*/
private void handleOrderDeclined(PayPalWebhookEventDTO event) {
log.info("处理订单拒绝事件事件ID: {}", event.getId());
try {
Map<String, Object> resource = event.getResource();
if (resource == null) {
@@ -509,9 +498,6 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
}
String orderId = (String) resource.get("id");
String status = (String) resource.get("status");
log.info("订单已被拒绝Order ID: {}, 状态: {}", orderId, status);
// 更新PayPal支付订单信息
if (orderId != null) {
@@ -519,15 +505,29 @@ public class PayPalWebhookServiceImpl implements PayPalWebhookService {
var paypalOrder = payPalService.getOrder(orderId);
if (paypalOrder != null) {
payPalPaymentOrderService.updateOrderFromPayPal(orderId, paypalOrder);
log.info("PayPal支付订单信息已更新订单拒绝后PayPal订单ID: {}, 状态: {}",
orderId, paypalOrder.getStatus());
}
} catch (Exception e) {
log.warn("更新PayPal支付订单信息失败PayPal订单ID: {}", orderId, e);
// 静默处理
}
}
// TODO: 根据业务需求处理订单拒绝逻辑(如更新ERP订单状态为支付失败
// 处理订单拒绝逻辑更新ERP订单状态为支付失败,并恢复库存
if (orderId != null) {
try {
var paypalOrder = payPalService.getOrder(orderId);
if (paypalOrder != null && paypalOrder.getPurchaseUnits() != null
&& !paypalOrder.getPurchaseUnits().isEmpty()) {
String merchantOrderNo = paypalOrder.getPurchaseUnits().get(0).getReferenceId();
if (merchantOrderNo != null) {
customerOrderService.cancelOrder(merchantOrderNo);
log.info("订单已拒绝,订单号: {}", merchantOrderNo);
}
}
} catch (Exception e) {
log.error("处理订单拒绝失败PayPal订单ID: {}", orderId, e);
}
}
} catch (Exception e) {
log.error("处理订单拒绝事件异常事件ID: {}", event.getId(), e);

View File

@@ -63,7 +63,7 @@ public class ProductServiceImpl implements ProductService {
@Autowired
private BaiduTranslatorUtils baiduTranslatorUtils;
@Value("${server.port:8082}")
@Value("${server.port:18082}")
private String serverPort;
@Value("${server.servlet.context-path:}")
@@ -128,9 +128,6 @@ public class ProductServiceImpl implements ProductService {
// 创建SKU优化并行翻译 + 批量插入)
if (request.getSkus() != null && !request.getSkus().isEmpty()) {
long skuStartTime = System.currentTimeMillis();
log.debug("开始创建SKU数量: {}", request.getSkus().size());
// 第一步并行翻译所有SKU名称
List<CompletableFuture<SkuTranslationResult>> translationFutures = new ArrayList<>();
for (CreateProductRequestDTO.CreateProductSkuDTO skuDTO : request.getSkus()) {
@@ -145,12 +142,9 @@ public class ProductServiceImpl implements ProductService {
translatedSkuName = baiduTranslatorUtils.getTransResult(originalSkuName, targetLanguage);
if (translatedSkuName == null || translatedSkuName.equals(originalSkuName)) {
translatedSkuName = originalSkuName; // 翻译失败或无需翻译,使用原文
} else {
log.debug("SKU名称翻译: {} -> {} (货币: {}, 语言: {})",
originalSkuName, translatedSkuName, currency, targetLanguage);
}
} catch (Exception e) {
log.warn("翻译SKU名称失败使用原文,SKU: {}, 货币: {}", originalSkuName, currency, e);
// 翻译失败使用原文,静默处理
translatedSkuName = originalSkuName;
}
}
@@ -166,7 +160,6 @@ public class ProductServiceImpl implements ProductService {
CompletableFuture.allOf(translationFutures.toArray(new CompletableFuture[0]))
.get(30, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("等待SKU翻译完成超时或失败", e);
// 继续执行,使用原文
}
@@ -195,7 +188,6 @@ public class ProductServiceImpl implements ProductService {
skuList.add(sku);
} catch (Exception e) {
log.warn("获取SKU翻译结果失败使用原文SKU: {}", skuDTO.getSku(), e);
// 使用原始SKU信息
MtProductSku sku = new MtProductSku();
sku.setProductId(product.getId());
@@ -222,9 +214,6 @@ public class ProductServiceImpl implements ProductService {
throw new BusinessException(ResultCode.SYSTEM_ERROR,
String.format("批量创建SKU失败期望插入%d个实际插入%d个", skuList.size(), insertCount));
}
long skuEndTime = System.currentTimeMillis();
log.info("SKU批量创建成功商品ID: {}, SKU数量: {}, 耗时: {}ms",
product.getId(), skuList.size(), skuEndTime - skuStartTime);
}
}

View File

@@ -63,7 +63,7 @@ spring:
# 服务器配置(所有环境通用)
server:
port: 8082
port: 18082
servlet:
context-path: /
# 文件上传配置
@@ -79,7 +79,7 @@ server:
app:
# 前端访问地址用于生成商品详情页URL等
frontend:
url: http://localhost:3000
url: http://175.178.252.59:3000
# 阿里云OSS相关配置所有环境通用
aliyun:
@@ -111,9 +111,9 @@ paypal:
mode: sandbox
# 是否启用PayPal支付
enabled: true
# Webhook URL内网穿透公网地址 + 回调接口路径
# Webhook URL服务器公网地址
# 注意需要在PayPal控制台配置此URL
webhook-url: https://2646b437.r33.cpolar.top/api/paypal/webhook
webhook-url: http://175.178.252.59:18082/api/paypal/webhook
# Webhook ID从PayPal控制台获取用于验证Webhook签名
webhook-id: 0SX6117212808615P

View File

@@ -60,6 +60,22 @@ spring:
config:
multi-statement-allow: true
# 服务器配置
server:
port: ${server.port:18082}
servlet:
context-path: /
multipart:
max-file-size: 10MB
max-request-size: 50MB
file-size-threshold: 2MB
# 应用配置
app:
# 前端访问地址
frontend:
url: ${app.frontend.url:http://175.178.252.59:3000}
# PingPong支付配置生产环境
pingpong:
client-id: ${pingpong.client-id}
@@ -71,13 +87,20 @@ pingpong:
enabled: false
# PayPal支付配置生产环境
# 注意:当前为测试环境,使用沙箱凭证
# 正式环境需要替换为生产环境的Client ID和Secret
paypal:
# PayPal Client IDAPI密钥- 从环境变量或配置中心获取
client-id: AdGYUZpvLuHR30dybOApvM-RNB1pVKtd74SVfh-6TK52xV-1JEBddHVMCWuDdyyHri4DXd4kABBi7Icb
# PayPal Client Secret密钥- 从环境变量或配置中心获取
client-secret: ENblspyRmwsOU_PWFurlhEYUF5Da6aYKl0pjK4ehm7p3R5aSqvbpaF_YsIIs8v0ty1c9WJu15XP-Fe_1
# 环境模式sandbox沙箱或 production生产
# 当前为测试环境使用sandbox
mode: sandbox
# 是否启用PayPal支付
enabled: true
# Webhook URL部署时请修改为服务器的公网地址
webhook-url: ${paypal.webhook-url:https://your-domain.com/api/paypal/webhook}
# Webhook ID从PayPal控制台获取
webhook-id: ${paypal.webhook-id:}

5965
replay_pid15540.log Normal file

File diff suppressed because one or more lines are too long