diff --git a/DEPLOYMENT_README.md b/DEPLOYMENT_README.md new file mode 100644 index 0000000..a6bb994 --- /dev/null +++ b/DEPLOYMENT_README.md @@ -0,0 +1,71 @@ +# 部署说明 + +## 快速部署指南 + +### 后端部署 + +1. **配置文件已更新** + - ✅ `app.frontend.url` 已设置为 `http://175.178.252.59:3000` + - ✅ `paypal.webhook-url` 已设置为 `http://175.178.252.59:8082/api/paypal/webhook` + - ✅ 前端 `.env.production` 已创建,API地址为 `http://175.178.252.59:8082/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地址已设置为 `http://175.178.252.59:8082/api` + - 如果没有该文件,构建时会使用默认值 `/api`(相对路径) + +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:8082/api/paypal/webhook` +- [x] 前端API地址已配置为 `http://175.178.252.59:8082/api` +- [ ] 数据库连接信息正确 +- [ ] 服务器防火墙已开放必要端口(8082、3000等) +- [ ] PayPal控制台Webhook URL已更新为 `http://175.178.252.59:8082/api/paypal/webhook` + + diff --git a/PAYPAL_TEST_ACCOUNT.md b/PAYPAL_TEST_ACCOUNT.md deleted file mode 100644 index 0ae0664..0000000 --- a/PAYPAL_TEST_ACCOUNT.md +++ /dev/null @@ -1,46 +0,0 @@ -# PayPal沙箱测试账号说明 - -## API凭证(已配置到 application-dev.yml) - -- **Client ID(API密钥)**: `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等 - diff --git a/PAYPAL_WEBHOOK_GUIDE.md b/PAYPAL_WEBHOOK_GUIDE.md deleted file mode 100644 index 788a001..0000000 --- a/PAYPAL_WEBHOOK_GUIDE.md +++ /dev/null @@ -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` - diff --git a/PROJECT_IMPROVEMENT_PLAN.md b/PROJECT_IMPROVEMENT_PLAN.md index d7ea53a..16f6add 100644 --- a/PROJECT_IMPROVEMENT_PLAN.md +++ b/PROJECT_IMPROVEMENT_PLAN.md @@ -1,373 +1,39 @@ -# 项目完善、优化、补充计划 - -## 📋 目录 -1. [功能完善](#功能完善) -2. [性能优化](#性能优化) -3. [安全性增强](#安全性增强) -4. [代码质量](#代码质量) -5. [数据完整性](#数据完整性) -6. [监控和日志](#监控和日志) -7. [部署和运维](#部署和运维) -8. [用户体验](#用户体验) -9. [文档完善](#文档完善) - ---- - -## 1. 功能完善 - -### 1.1 商品管理功能 -- [ ] **商品编辑功能** ⚠️ 高优先级 - - 当前状态:`ProductManage.vue` 中有 TODO 标记,点击编辑只显示提示 - - 需要实现: - - 后端:`PUT /api/product/{id}` 更新商品接口 - - 后端:`ProductService.updateProduct()` 方法 - - 前端:`/manage/product/edit/:id` 编辑页面 - - 支持修改商品名称、价格、主图、SKU信息等 - -- [ ] **商品上架功能** - - 当前状态:只有下架功能,没有上架功能 - - 需要实现: - - 后端:`PUT /api/product/{id}/on-shelf` 上架接口 - - 上架时恢复SKU库存(需要记录下架前的库存) - -- [ ] **商品批量操作** - - 批量上架/下架 - - 批量删除(软删除) - -### 1.2 订单管理功能 -- [ ] **订单列表查询** ⚠️ 高优先级 - - 当前状态:只有单个订单查询(`OrderQuery.vue`),没有列表 - - 需要实现: - - 后端:`GET /api/order/list` 订单列表接口(支持分页、筛选) - - 前端:订单管理页面,支持按状态、时间、订单号等查询 - - 支持订单状态筛选(待支付、已支付、已发货、已完成、已取消) - -- [ ] **订单状态管理** - - 订单发货功能(更新状态为 SHIPPED) - - 订单完成功能(更新状态为 COMPLETED) - - 订单取消功能(更新状态为 CANCELLED,恢复库存) - -- [ ] **订单详情查看** - - 在订单列表中点击查看详情 - - 显示完整的订单信息、支付信息、收货地址等 - -### 1.3 库存管理 -- [ ] **库存扣减** ⚠️ 高优先级 - - 当前状态:创建订单时只验证库存,没有扣减 - - 需要实现: - - 创建订单时扣减库存(使用数据库锁防止超卖) - - 支付成功时确认扣减(如果创建时是预扣,支付时确认) - - 订单取消时恢复库存 - -- [ ] **库存预警** - - 库存低于阈值时提醒 - - 库存为0时自动下架商品(可选) - -- [ ] **库存历史记录** - - 记录库存变动历史(入库、出库、调整等) - -### 1.4 数据统计 -- [ ] **销售统计** - - 按时间段统计销售额 - - 按商品统计销量 - - 按地区统计销售情况 - -- [ ] **订单统计** - - 订单数量统计 - - 订单状态分布 - - 支付成功率统计 - ---- - -## 2. 性能优化 - -### 2.1 缓存机制 -- [ ] **引入Redis缓存** ⚠️ 中优先级 - - 当前状态:PayPal token和汇率使用内存缓存(单机可用,集群有问题) - - 需要实现: - - 集成Redis - - PayPal token缓存到Redis - - 汇率缓存到Redis - - 商品详情缓存(热点商品) - - 商品列表缓存(带过期时间) - -- [ ] **数据库查询优化** - - 检查慢查询日志 - - 优化复杂查询 - - 添加必要的数据库索引 - -### 2.2 前端优化 -- [ ] **移除console.log** ⚠️ 低优先级 - - 当前状态:前端代码中有多处 `console.log`、`console.error` - - 需要实现: - - 使用日志库(如 `winston` 或自定义日志工具) - - 生产环境禁用console输出 - - 统一错误处理 - -- [ ] **代码分割和懒加载** - - 路由懒加载(已部分实现) - - 组件懒加载 - - 减少首屏加载时间 - -- [ ] **图片优化** - - 图片懒加载 - - 图片压缩 - - CDN加速 - -### 2.3 数据库优化 -- [ ] **连接池优化** - - 检查当前连接池配置是否合理 - - 根据实际负载调整参数 - -- [ ] **批量操作优化** - - 商品创建已优化(批量插入SKU) - - 其他批量操作也需要优化 - ---- - -## 3. 安全性增强 - -### 3.1 密码安全 -- [ ] **密码加密升级** ⚠️ 高优先级 - - 当前状态:使用MD5加密(不安全) - - 需要实现: - - 使用BCrypt或Argon2加密 - - 密码强度验证 - - 密码历史记录(防止重复使用) - -- [ ] **Token安全** - - 当前状态:使用MD5+Base64(注释说生产环境建议使用JWT) - - 需要实现: - - 集成JWT(JSON Web Token) - - Token刷新机制 - - Token黑名单(登出时) - -### 3.2 权限控制 -- [ ] **角色权限系统** ⚠️ 中优先级 - - 当前状态:只有登录验证,没有角色权限 - - 需要实现: - - 角色管理(管理员、普通用户等) - - 权限管理(商品管理、订单管理、用户管理等) - - 接口权限控制(`@PreAuthorize` 或自定义拦截器) - -- [ ] **API访问控制** - - 接口访问频率限制(防止暴力攻击) - - IP白名单(可选,用于管理接口) - -### 3.3 数据安全 -- [ ] **敏感数据加密** - - 客户手机号、邮箱加密存储 - - 支付信息加密 - -- [ ] **SQL注入防护** - - 检查是否所有查询都使用参数化查询(MyBatis-Plus已提供) - -- [ ] **XSS防护** - - 前端输入验证和转义 - - 后端输出转义 - ---- - -## 4. 代码质量 - -### 4.1 测试覆盖 -- [ ] **单元测试** ⚠️ 中优先级 - - 当前状态:没有单元测试 - - 需要实现: - - Service层单元测试 - - Controller层单元测试 - - 工具类单元测试 - - 测试覆盖率目标:>70% - -- [ ] **集成测试** - - API接口测试 - - 数据库操作测试 - -- [ ] **前端测试** - - 组件测试 - - E2E测试 - -### 4.2 代码规范 -- [ ] **代码审查** - - 统一代码风格 - - 移除TODO标记(实现或记录到任务列表) - - 代码注释完善 - -- [ ] **静态代码分析** - - 集成SonarQube或类似工具 - - 修复代码质量问题 - -### 4.3 API文档 -- [ ] **Swagger/OpenAPI文档** ⚠️ 中优先级 - - 当前状态:只有部分API有Markdown文档 - - 需要实现: - - 集成Swagger UI - - 自动生成API文档 - - 接口参数和响应说明 - ---- - -## 5. 数据完整性 - -### 5.1 库存管理 -- [ ] **库存扣减逻辑** ⚠️ 高优先级 - - 创建订单时扣减库存 - - 支付成功时确认库存 - - 订单取消时恢复库存 - - 使用数据库锁(SELECT FOR UPDATE)防止超卖 - -- [ ] **库存一致性检查** - - 定期检查库存数据一致性 - - 库存异常告警 - -### 5.2 订单状态流转 -- [ ] **订单状态机** ⚠️ 中优先级 - - 定义订单状态流转规则 - - 防止非法状态转换 - - 状态变更日志 - -### 5.3 数据备份 -- [ ] **数据库备份策略** - - 定期自动备份 - - 备份恢复测试 - - 备份文件管理 - ---- - -## 6. 监控和日志 - -### 6.1 操作日志 -- [ ] **操作审计日志** ⚠️ 中优先级 - - 记录用户操作(商品创建、编辑、下架等) - - 记录订单状态变更 - - 记录支付操作 - -- [ ] **日志查询功能** - - 前端日志查询页面 - - 按时间、用户、操作类型筛选 - -### 6.2 性能监控 -- [ ] **性能指标监控** - - API响应时间监控 - - 数据库查询性能监控 - - 系统资源监控(CPU、内存、磁盘) - -- [ ] **告警机制** - - 异常告警(错误率、响应时间) - - 系统资源告警 - -### 6.3 错误追踪 -- [ ] **错误日志聚合** - - 集成错误追踪服务(如Sentry) - - 错误分类和统计 - - 错误通知 - ---- - -## 7. 部署和运维 - -### 7.1 数据库迁移 -- [ ] **数据库版本管理** ⚠️ 中优先级 - - 当前状态:只有SQL脚本,没有版本管理 - - 需要实现: - - 集成Flyway或Liquibase - - 数据库版本控制 - - 自动迁移脚本 - -### 7.2 配置管理 -- [ ] **环境配置优化** - - 敏感信息加密(密码、密钥等) - - 配置中心(可选) - - 环境变量管理 - -### 7.3 部署文档 -- [ ] **部署指南** - - 生产环境部署步骤 - - 环境要求说明 - - 故障排查指南 - -### 7.4 容器化 -- [ ] **Docker支持** ⚠️ 低优先级 - - Dockerfile编写 - - Docker Compose配置 - - 容器化部署文档 - ---- - -## 8. 用户体验 - -### 8.1 前端优化 -- [ ] **加载状态优化** - - 骨架屏(Skeleton) - - 加载动画优化 - -- [ ] **错误提示优化** - - 友好的错误提示 - - 错误恢复建议 - -- [ ] **移动端优化** - - 响应式设计检查 - - 移动端交互优化 - -### 8.2 功能完善 -- [ ] **订单导出** - - 订单列表导出Excel - - 支持筛选条件导出 - -- [ ] **数据统计可视化** - - 销售图表 - - 订单趋势图 - ---- - -## 9. 文档完善 - -### 9.1 技术文档 -- [ ] **API文档** - - 完整的API文档(Swagger) - - 接口调用示例 - -- [ ] **数据库设计文档** - - ER图 - - 表结构说明 - - 索引说明 - -### 9.2 用户文档 -- [ ] **用户手册** - - 商品管理操作指南 - - 订单管理操作指南 - - 常见问题解答 - ---- - -## 优先级说明 - -- ⚠️ **高优先级**:影响核心功能或安全性,需要尽快实现 -- ⚠️ **中优先级**:提升系统质量和可维护性,建议实现 -- ⚠️ **低优先级**:优化和增强功能,可选择性实现 - ---- +# 项目改进计划 + +## 已完成功能 + +- ✅ 商品管理(创建、查询、下架、分页) +- ✅ 订单管理(创建、查询、分页、状态管理) +- ✅ 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. **第一阶段(核心功能)**: - - 商品编辑功能 - - 订单列表查询 - - 库存扣减逻辑 - - 密码加密升级 +**第一阶段(1-2周)**:商品编辑、订单详情、订单状态管理、密码加密升级 -2. **第二阶段(安全性)**: - - JWT Token - - 角色权限系统 - - API访问控制 - -3. **第三阶段(优化)**: - - Redis缓存 - - 性能监控 - - 单元测试 - -4. **第四阶段(完善)**: - - 操作日志 - - 数据统计 - - 文档完善 +**第二阶段(2-3周)**:JWT Token、角色权限系统 +**第三阶段(3-4周)**:Redis缓存、操作日志、性能优化 diff --git a/logs/mt-pay-error.2025-12-26.log b/logs/mt-pay-error.2025-12-26.log new file mode 100644 index 0000000..78af64f --- /dev/null +++ b/logs/mt-pay-error.2025-12-26.log @@ -0,0 +1,327 @@ +2025-12-26 10:31:23.874 [main] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null + at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92) + at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179) + at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163) + at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532) + at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98) + at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51) + at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178) + at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171) + at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149) + at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137) + at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136) + at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75) + at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54) + at java.base/java.lang.Iterable.forEach(Iterable.java:75) + at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118) + at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) +2025-12-26 10:48:58.194 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:48:58.197 [restartedMain] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:53:11.107 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:53:11.107 [restartedMain] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:53:20.855 [restartedMain] ERROR o.s.b.diagnostics.LoggingFailureAnalysisReporter - + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Field ossProperties in com.mtkj.mtpay.service.impl.OssServiceImpl required a bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' that could not be found. + +The injection point has the following annotations: + - @org.springframework.beans.factory.annotation.Autowired(required=true) + + +Action: + +Consider defining a bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' in your configuration. + +2025-12-26 10:53:20.856 [restartedMain] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productController': Unsatisfied dependency expressed through field 'ossService': Error creating bean with name 'ossServiceImpl': Unsatisfied dependency expressed through field 'ossProperties': No qualifying bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:772) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:752) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ossServiceImpl': Unsatisfied dependency expressed through field 'ossProperties': No qualifying bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:772) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:752) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:769) + ... 23 common frames omitted +Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} + at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1878) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1404) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:769) + ... 37 common frames omitted diff --git a/logs/mt-pay.2025-12-26.log b/logs/mt-pay.2025-12-26.log new file mode 100644 index 0000000..d7da0ad --- /dev/null +++ b/logs/mt-pay.2025-12-26.log @@ -0,0 +1,684 @@ +2025-12-26 10:31:20.773 [background-preinit] INFO org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 8.0.1.Final +2025-12-26 10:31:20.830 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:31:20.831 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:31:22.207 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8082"] +2025-12-26 10:31:22.210 [restartedMain] INFO org.apache.catalina.core.StandardService - Starting service [Tomcat] +2025-12-26 10:31:22.210 [restartedMain] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] +2025-12-26 10:31:22.266 [restartedMain] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext +2025-12-26 10:31:22.333 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:31:22.333 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - PayPal配置加载验证: +2025-12-26 10:31:22.333 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client ID: ✅ 已配置 (AdGYUZpvLuHR30dybOAp...) +2025-12-26 10:31:22.335 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client Secret: ✅ 已配置 (ENblspyRmwsOU_PWFurl...) +2025-12-26 10:31:22.335 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Mode: sandbox +2025-12-26 10:31:22.335 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Enabled: true +2025-12-26 10:31:22.335 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Base URL: https://api-m.sandbox.paypal.com +2025-12-26 10:31:22.335 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Webhook URL: ✅ https://2646b437.r33.cpolar.top/api/paypal/webhook +2025-12-26 10:31:22.335 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:31:22.351 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - 初始化MyBatis-Plus分页插件,数据库类型: MYSQL +2025-12-26 10:31:22.354 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - MyBatis-Plus分页插件配置完成 +2025-12-26 10:31:22.358 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 初始化Druid主数据源 +2025-12-26 10:31:22.358 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 配置属性 - URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai, Username: mtkj2025 +2025-12-26 10:31:22.383 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - Druid主数据源配置完成,URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai +2025-12-26 10:31:23.201 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - 初始化RestClient,配置JSON消息转换器 +2025-12-26 10:31:23.433 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - RestClient配置完成 +2025-12-26 10:31:23.440 [restartedMain] INFO com.mtkj.mtpay.config.AsyncConfig - PayPal Webhook异步处理线程池初始化完成 +2025-12-26 10:31:23.509 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 配置跨域访问,路径: /api/**, 允许来源: * +2025-12-26 10:31:23.512 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 跨域配置完成 +2025-12-26 10:31:23.848 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8082"] +2025-12-26 10:31:23.868 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Started MtkjpayApplication in 3.79 seconds (process running for 5.566) +2025-12-26 10:31:23.870 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ✅ MTKJ PAY 支付系统启动成功! ✅ ║ +║ ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用信息 ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用名称: {:<45} ║ +║ 运行环境: {:<45} ║ +║ 服务端口: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 访问地址 ║ +╠══════════════════════════════════════════════════════════╣ +║ 后端服务: {:<45} ║ +║ API接口: {:<45} ║ +║ Druid监控: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 状态: 🟢 服务运行中,可以接收请求 ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:31:23.870 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +📌 提示: + - 前端代理地址: http://localhost:3000 + - 后端API地址: http://localhost:8082//api + - 图片上传接口: http://localhost:8082//api/product/upload/image + - 商品管理接口: http://localhost:8082//api/product + +2025-12-26 10:31:23.874 [main] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException: null + at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:92) + at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:179) + at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:163) + at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:532) + at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:98) + at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:51) + at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178) + at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171) + at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:149) + at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:137) + at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136) + at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75) + at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54) + at java.base/java.lang.Iterable.forEach(Iterable.java:75) + at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118) + at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) +2025-12-26 10:48:57.710 [Thread-8] INFO org.apache.coyote.http11.Http11NioProtocol - Stopping ProtocolHandler ["http-nio-8082"] +2025-12-26 10:48:57.839 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ MTKJ PAY 支付系统正在启动... ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:48:57.905 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:48:57.905 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:48:58.125 [restartedMain] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] +2025-12-26 10:48:58.194 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:48:58.197 [restartedMain] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:49:02.948 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ MTKJ PAY 支付系统正在启动... ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:49:02.997 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:49:02.997 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:49:03.398 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8082"] +2025-12-26 10:49:03.398 [restartedMain] INFO org.apache.catalina.core.StandardService - Starting service [Tomcat] +2025-12-26 10:49:03.400 [restartedMain] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] +2025-12-26 10:49:03.407 [restartedMain] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - PayPal配置加载验证: +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client ID: ✅ 已配置 (AdGYUZpvLuHR30dybOAp...) +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client Secret: ✅ 已配置 (ENblspyRmwsOU_PWFurl...) +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Mode: sandbox +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Enabled: true +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Base URL: https://api-m.sandbox.paypal.com +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Webhook URL: ✅ https://2646b437.r33.cpolar.top/api/paypal/webhook +2025-12-26 10:49:03.427 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:49:03.439 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - 初始化MyBatis-Plus分页插件,数据库类型: MYSQL +2025-12-26 10:49:03.439 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - MyBatis-Plus分页插件配置完成 +2025-12-26 10:49:03.442 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 初始化Druid主数据源 +2025-12-26 10:49:03.442 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 配置属性 - URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai, Username: mtkj2025 +2025-12-26 10:49:03.442 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - Druid主数据源配置完成,URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai +2025-12-26 10:49:03.889 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - 初始化RestClient,配置JSON消息转换器 +2025-12-26 10:49:03.889 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - RestClient配置完成 +2025-12-26 10:49:03.896 [restartedMain] INFO com.mtkj.mtpay.config.AsyncConfig - PayPal Webhook异步处理线程池初始化完成 +2025-12-26 10:49:03.923 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 配置跨域访问,路径: /api/**, 允许来源: * +2025-12-26 10:49:03.930 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 跨域配置完成 +2025-12-26 10:49:04.028 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8082"] +2025-12-26 10:49:04.034 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Started MtkjpayApplication in 1.081 seconds (process running for 1065.736) +2025-12-26 10:49:04.034 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ✅ MTKJ PAY 支付系统启动成功! ✅ ║ +║ ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用信息 ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用名称: {:<45} ║ +║ 运行环境: {:<45} ║ +║ 服务端口: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 访问地址 ║ +╠══════════════════════════════════════════════════════════╣ +║ 后端服务: {:<45} ║ +║ API接口: {:<45} ║ +║ Druid监控: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 状态: 🟢 服务运行中,可以接收请求 ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:49:04.034 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +📌 提示: + - 前端代理地址: http://localhost:3000 + - 后端API地址: http://localhost:8082//api + - 图片上传接口: http://localhost:8082//api/product/upload/image + - 商品管理接口: http://localhost:8082//api/product + +2025-12-26 10:49:05.465 [Thread-10] INFO org.apache.coyote.http11.Http11NioProtocol - Stopping ProtocolHandler ["http-nio-8082"] +2025-12-26 10:49:05.544 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ MTKJ PAY 支付系统正在启动... ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:49:05.576 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:49:05.577 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:49:05.895 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8082"] +2025-12-26 10:49:05.895 [restartedMain] INFO org.apache.catalina.core.StandardService - Starting service [Tomcat] +2025-12-26 10:49:05.895 [restartedMain] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] +2025-12-26 10:49:05.916 [restartedMain] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext +2025-12-26 10:49:05.929 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:49:05.929 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - PayPal配置加载验证: +2025-12-26 10:49:05.930 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client ID: ✅ 已配置 (AdGYUZpvLuHR30dybOAp...) +2025-12-26 10:49:05.930 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client Secret: ✅ 已配置 (ENblspyRmwsOU_PWFurl...) +2025-12-26 10:49:05.930 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Mode: sandbox +2025-12-26 10:49:05.930 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Enabled: true +2025-12-26 10:49:05.930 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Base URL: https://api-m.sandbox.paypal.com +2025-12-26 10:49:05.930 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Webhook URL: ✅ https://2646b437.r33.cpolar.top/api/paypal/webhook +2025-12-26 10:49:05.930 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:49:05.937 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - 初始化MyBatis-Plus分页插件,数据库类型: MYSQL +2025-12-26 10:49:05.937 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - MyBatis-Plus分页插件配置完成 +2025-12-26 10:49:05.940 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 初始化Druid主数据源 +2025-12-26 10:49:05.940 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 配置属性 - URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai, Username: mtkj2025 +2025-12-26 10:49:05.940 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - Druid主数据源配置完成,URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai +2025-12-26 10:49:06.367 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - 初始化RestClient,配置JSON消息转换器 +2025-12-26 10:49:06.368 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - RestClient配置完成 +2025-12-26 10:49:06.374 [restartedMain] INFO com.mtkj.mtpay.config.AsyncConfig - PayPal Webhook异步处理线程池初始化完成 +2025-12-26 10:49:06.402 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 配置跨域访问,路径: /api/**, 允许来源: * +2025-12-26 10:49:06.402 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 跨域配置完成 +2025-12-26 10:49:06.492 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8082"] +2025-12-26 10:49:06.500 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Started MtkjpayApplication in 0.951 seconds (process running for 1068.198) +2025-12-26 10:49:06.500 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ✅ MTKJ PAY 支付系统启动成功! ✅ ║ +║ ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用信息 ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用名称: {:<45} ║ +║ 运行环境: {:<45} ║ +║ 服务端口: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 访问地址 ║ +╠══════════════════════════════════════════════════════════╣ +║ 后端服务: {:<45} ║ +║ API接口: {:<45} ║ +║ Druid监控: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 状态: 🟢 服务运行中,可以接收请求 ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:49:06.500 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +📌 提示: + - 前端代理地址: http://localhost:3000 + - 后端API地址: http://localhost:8082//api + - 图片上传接口: http://localhost:8082//api/product/upload/image + - 商品管理接口: http://localhost:8082//api/product + +2025-12-26 10:53:05.742 [Thread-22] INFO org.apache.coyote.http11.Http11NioProtocol - Stopping ProtocolHandler ["http-nio-8082"] +2025-12-26 10:53:05.833 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ MTKJ PAY 支付系统正在启动... ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:53:05.894 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:53:05.894 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:53:06.435 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8082"] +2025-12-26 10:53:06.435 [restartedMain] INFO org.apache.catalina.core.StandardService - Starting service [Tomcat] +2025-12-26 10:53:06.435 [restartedMain] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] +2025-12-26 10:53:06.464 [restartedMain] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext +2025-12-26 10:53:06.490 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:53:06.491 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - PayPal配置加载验证: +2025-12-26 10:53:06.491 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client ID: ✅ 已配置 (AdGYUZpvLuHR30dybOAp...) +2025-12-26 10:53:06.491 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client Secret: ✅ 已配置 (ENblspyRmwsOU_PWFurl...) +2025-12-26 10:53:06.492 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Mode: sandbox +2025-12-26 10:53:06.492 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Enabled: true +2025-12-26 10:53:06.492 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Base URL: https://api-m.sandbox.paypal.com +2025-12-26 10:53:06.492 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Webhook URL: ✅ https://2646b437.r33.cpolar.top/api/paypal/webhook +2025-12-26 10:53:06.492 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:53:06.505 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - 初始化MyBatis-Plus分页插件,数据库类型: MYSQL +2025-12-26 10:53:06.506 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - MyBatis-Plus分页插件配置完成 +2025-12-26 10:53:06.514 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 初始化Druid主数据源 +2025-12-26 10:53:06.515 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 配置属性 - URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai, Username: mtkj2025 +2025-12-26 10:53:06.516 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - Druid主数据源配置完成,URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai +2025-12-26 10:53:08.301 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - 初始化RestClient,配置JSON消息转换器 +2025-12-26 10:53:08.316 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - RestClient配置完成 +2025-12-26 10:53:08.327 [restartedMain] INFO com.mtkj.mtpay.config.AsyncConfig - PayPal Webhook异步处理线程池初始化完成 +2025-12-26 10:53:08.395 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 配置跨域访问,路径: /api/**, 允许来源: * +2025-12-26 10:53:08.396 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 跨域配置完成 +2025-12-26 10:53:08.873 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8082"] +2025-12-26 10:53:08.925 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Started MtkjpayApplication in 3.083 seconds (process running for 1310.623) +2025-12-26 10:53:08.934 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ✅ MTKJ PAY 支付系统启动成功! ✅ ║ +║ ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用信息 ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用名称: {:<45} ║ +║ 运行环境: {:<45} ║ +║ 服务端口: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 访问地址 ║ +╠══════════════════════════════════════════════════════════╣ +║ 后端服务: {:<45} ║ +║ API接口: {:<45} ║ +║ Druid监控: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 状态: 🟢 服务运行中,可以接收请求 ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:53:08.934 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +📌 提示: + - 前端代理地址: http://localhost:3000 + - 后端API地址: http://localhost:8082//api + - 图片上传接口: http://localhost:8082//api/product/upload/image + - 商品管理接口: http://localhost:8082//api/product + +2025-12-26 10:53:10.305 [Thread-29] INFO org.apache.coyote.http11.Http11NioProtocol - Stopping ProtocolHandler ["http-nio-8082"] +2025-12-26 10:53:10.624 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ MTKJ PAY 支付系统正在启动... ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:53:10.705 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:53:10.705 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:53:11.095 [restartedMain] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] +2025-12-26 10:53:11.107 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:53:11.107 [restartedMain] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +java.lang.IllegalArgumentException: Could not find class [com.mtkj.mtpay.config.PingPongProperties] + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:355) + at org.springframework.core.annotation.TypeMappedAnnotation.adapt(TypeMappedAnnotation.java:465) + at org.springframework.core.annotation.TypeMappedAnnotation.getValue(TypeMappedAnnotation.java:390) + at org.springframework.core.annotation.TypeMappedAnnotation.getAttributeValue(TypeMappedAnnotation.java:371) + at org.springframework.core.annotation.AbstractMergedAnnotation.getRequiredAttributeValue(AbstractMergedAnnotation.java:215) + at org.springframework.core.annotation.AbstractMergedAnnotation.getClassArray(AbstractMergedAnnotation.java:153) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.lambda$getTypes$0(EnableConfigurationPropertiesRegistrar.java:55) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) + at org.springframework.core.annotation.MergedAnnotationsCollection$AnnotationsSpliterator.tryAdvance(MergedAnnotationsCollection.java:254) + at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.getTypes(EnableConfigurationPropertiesRegistrar.java:57) + at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49) + at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376) + at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148) + at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428) + at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) + at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) + at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: java.lang.ClassNotFoundException: com.mtkj.mtpay.config.PingPongProperties + at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) + at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:121) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:467) + at org.springframework.util.ClassUtils.forName(ClassUtils.java:304) + at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:345) + ... 38 common frames omitted +2025-12-26 10:53:19.292 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ MTKJ PAY 支付系统正在启动... ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:53:19.365 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:53:19.365 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:53:20.117 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8082"] +2025-12-26 10:53:20.120 [restartedMain] INFO org.apache.catalina.core.StandardService - Starting service [Tomcat] +2025-12-26 10:53:20.122 [restartedMain] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] +2025-12-26 10:53:20.188 [restartedMain] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext +2025-12-26 10:53:20.209 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:53:20.209 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - PayPal配置加载验证: +2025-12-26 10:53:20.210 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client ID: ✅ 已配置 (AdGYUZpvLuHR30dybOAp...) +2025-12-26 10:53:20.210 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client Secret: ✅ 已配置 (ENblspyRmwsOU_PWFurl...) +2025-12-26 10:53:20.210 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Mode: sandbox +2025-12-26 10:53:20.210 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Enabled: true +2025-12-26 10:53:20.210 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Base URL: https://api-m.sandbox.paypal.com +2025-12-26 10:53:20.210 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Webhook URL: ✅ https://2646b437.r33.cpolar.top/api/paypal/webhook +2025-12-26 10:53:20.210 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:53:20.225 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - 初始化MyBatis-Plus分页插件,数据库类型: MYSQL +2025-12-26 10:53:20.225 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - MyBatis-Plus分页插件配置完成 +2025-12-26 10:53:20.230 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 初始化Druid主数据源 +2025-12-26 10:53:20.230 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 配置属性 - URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai, Username: mtkj2025 +2025-12-26 10:53:20.230 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - Druid主数据源配置完成,URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai +2025-12-26 10:53:20.842 [restartedMain] WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productController': Unsatisfied dependency expressed through field 'ossService': Error creating bean with name 'ossServiceImpl': Unsatisfied dependency expressed through field 'ossProperties': No qualifying bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} +2025-12-26 10:53:20.843 [restartedMain] INFO org.apache.catalina.core.StandardService - Stopping service [Tomcat] +2025-12-26 10:53:20.855 [restartedMain] ERROR o.s.b.diagnostics.LoggingFailureAnalysisReporter - + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Field ossProperties in com.mtkj.mtpay.service.impl.OssServiceImpl required a bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' that could not be found. + +The injection point has the following annotations: + - @org.springframework.beans.factory.annotation.Autowired(required=true) + + +Action: + +Consider defining a bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' in your configuration. + +2025-12-26 10:53:20.856 [restartedMain] ERROR com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ❌ MTKJ PAY 支付系统启动失败! ❌ ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productController': Unsatisfied dependency expressed through field 'ossService': Error creating bean with name 'ossServiceImpl': Unsatisfied dependency expressed through field 'ossProperties': No qualifying bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:772) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:752) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:616) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) + at com.mtkj.mtkjpay.MtkjpayApplication.main(MtkjpayApplication.java:42) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:568) + at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) +Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ossServiceImpl': Unsatisfied dependency expressed through field 'ossProperties': No qualifying bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:772) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:752) + at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:769) + ... 23 common frames omitted +Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mtkj.mtpay.config.AliyunOSSProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} + at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1878) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1404) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) + at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:769) + ... 37 common frames omitted +2025-12-26 10:53:22.355 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ MTKJ PAY 支付系统正在启动... ║ +║ ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:53:22.390 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Starting MtkjpayApplication using Java 17.0.12 with PID 23252 (E:\MTKJPAY\mt-startup\target\classes started by 18969 in E:\MTKJPAY) +2025-12-26 10:53:22.390 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - The following 1 profile is active: "dev" +2025-12-26 10:53:22.733 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8082"] +2025-12-26 10:53:22.733 [restartedMain] INFO org.apache.catalina.core.StandardService - Starting service [Tomcat] +2025-12-26 10:53:22.733 [restartedMain] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] +2025-12-26 10:53:22.748 [restartedMain] INFO o.a.c.c.ContainerBase.[Tomcat-1].[localhost].[/] - Initializing Spring embedded WebApplicationContext +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - PayPal配置加载验证: +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client ID: ✅ 已配置 (AdGYUZpvLuHR30dybOAp...) +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Client Secret: ✅ 已配置 (ENblspyRmwsOU_PWFurl...) +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Mode: sandbox +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Enabled: true +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Base URL: https://api-m.sandbox.paypal.com +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - - Webhook URL: ✅ https://2646b437.r33.cpolar.top/api/paypal/webhook +2025-12-26 10:53:22.760 [restartedMain] INFO com.mtkj.mtpay.config.PayPalProperties - ═══════════════════════════════════════════════════════════ +2025-12-26 10:53:22.767 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - 初始化MyBatis-Plus分页插件,数据库类型: MYSQL +2025-12-26 10:53:22.767 [restartedMain] INFO com.mtkj.mtpay.config.MyBatisPlusConfig - MyBatis-Plus分页插件配置完成 +2025-12-26 10:53:22.769 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 初始化Druid主数据源 +2025-12-26 10:53:22.769 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - 配置属性 - URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai, Username: mtkj2025 +2025-12-26 10:53:22.770 [restartedMain] INFO com.mtkj.mtpay.config.DruidDataSourceConfig - Druid主数据源配置完成,URL: jdbc:mysql://rm-j6c3u06k2afwn8hxw6o.mysql.rds.aliyuncs.com:3306/mtpay?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai +2025-12-26 10:53:23.166 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - 初始化RestClient,配置JSON消息转换器 +2025-12-26 10:53:23.169 [restartedMain] INFO com.mtkj.mtpay.config.RestClientConfig - RestClient配置完成 +2025-12-26 10:53:23.174 [restartedMain] INFO com.mtkj.mtpay.config.AsyncConfig - PayPal Webhook异步处理线程池初始化完成 +2025-12-26 10:53:23.205 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 配置跨域访问,路径: /api/**, 允许来源: * +2025-12-26 10:53:23.206 [restartedMain] INFO com.mtkj.mtpay.config.WebConfig - 跨域配置完成 +2025-12-26 10:53:23.302 [restartedMain] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8082"] +2025-12-26 10:53:23.309 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - Started MtkjpayApplication in 0.95 seconds (process running for 1325.008) +2025-12-26 10:53:23.310 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +╔══════════════════════════════════════════════════════════╗ +║ ║ +║ ✅ MTKJ PAY 支付系统启动成功! ✅ ║ +║ ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用信息 ║ +╠══════════════════════════════════════════════════════════╣ +║ 应用名称: {:<45} ║ +║ 运行环境: {:<45} ║ +║ 服务端口: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 访问地址 ║ +╠══════════════════════════════════════════════════════════╣ +║ 后端服务: {:<45} ║ +║ API接口: {:<45} ║ +║ Druid监控: {:<45} ║ +╠══════════════════════════════════════════════════════════╣ +║ 状态: 🟢 服务运行中,可以接收请求 ║ +╚══════════════════════════════════════════════════════════╝ + +2025-12-26 10:53:23.310 [restartedMain] INFO com.mtkj.mtkjpay.MtkjpayApplication - +📌 提示: + - 前端代理地址: http://localhost:3000 + - 后端API地址: http://localhost:8082//api + - 图片上传接口: http://localhost:8082//api/product/upload/image + - 商品管理接口: http://localhost:8082//api/product + diff --git a/mt-pay/ERP_USER_API.md b/mt-pay/ERP_USER_API.md index 2801440..81c32f9 100644 --- a/mt-pay/ERP_USER_API.md +++ b/mt-pay/ERP_USER_API.md @@ -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编码,生产环境建议使用JWT(JSON 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通过登录接口获取,有效期由后端配置决定。 diff --git a/mt-pay/PAYPAL_ORDER_LIFECYCLE.md b/mt-pay/PAYPAL_ORDER_LIFECYCLE.md deleted file mode 100644 index 03ebb35..0000000 --- a/mt-pay/PAYPAL_ORDER_LIFECYCLE.md +++ /dev/null @@ -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订单详情 - diff --git a/mt-pay/PAYPAL_WEBHOOK_SETUP.md b/mt-pay/PAYPAL_WEBHOOK_SETUP.md index e077e58..fd89fdc 100644 --- a/mt-pay/PAYPAL_WEBHOOK_SETUP.md +++ b/mt-pay/PAYPAL_WEBHOOK_SETUP.md @@ -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。 diff --git a/mt-pay/README.md b/mt-pay/README.md index 07caa1a..3d99d82 100644 --- a/mt-pay/README.md +++ b/mt-pay/README.md @@ -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文档 diff --git a/mt-pay/SYSTEM_ARCHITECTURE.md b/mt-pay/SYSTEM_ARCHITECTURE.md deleted file mode 100644 index 13fb400..0000000 --- a/mt-pay/SYSTEM_ARCHITECTURE.md +++ /dev/null @@ -1,162 +0,0 @@ -# 系统架构完整性说明 - -## 架构检查清单 - -### ✅ 后端架构完整性 - -#### 1. 分层架构 -- ✅ **Controller层**: 接口控制器,处理HTTP请求 -- ✅ **Service层**: 业务逻辑层(接口+实现分离) -- ✅ **Mapper层**: 数据访问层(MyBatis-Plus) -- ✅ **Entity层**: 实体类(数据库映射) - -#### 2. 通用组件 -- ✅ **Result**: 统一响应结果类 -- ✅ **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) -- ✅ 统一异常处理(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 -2. ✅ **异常处理**: 使用BusinessException抛出业务异常 -3. ✅ **枚举使用**: 使用枚举替代魔法字符串 -4. ✅ **工具类**: 通用功能封装为工具类 -5. ✅ **配置管理**: 配置统一管理,支持多环境 -6. ✅ **接口分离**: Service接口与实现分离 -7. ✅ **跨域配置**: WebConfig统一配置跨域 -8. ✅ **常量管理**: 常量集中管理 - -## 系统完整性评分 - -- **后端架构**: ⭐⭐⭐⭐⭐ (5/5) -- **前端架构**: ⭐⭐⭐⭐⭐ (5/5) -- **代码规范**: ⭐⭐⭐⭐⭐ (5/5) -- **可维护性**: ⭐⭐⭐⭐⭐ (5/5) -- **可扩展性**: ⭐⭐⭐⭐⭐ (5/5) - -## 总结 - -系统架构已完整,包含: -- ✅ 完整的分层架构 -- ✅ 统一的响应格式和异常处理 -- ✅ 完善的工具类和枚举类 -- ✅ 规范的代码结构 -- ✅ 可扩展的设计 -- ✅ 完整的配置管理 - -系统已具备生产环境使用的基础架构! - diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/config/WebConfig.java b/mt-pay/src/main/java/com/mtkj/mtpay/config/WebConfig.java index 1eefb47..4f3cccb 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/config/WebConfig.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/config/WebConfig.java @@ -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) diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/mapper/MtProductSkuMapper.java b/mt-pay/src/main/java/com/mtkj/mtpay/mapper/MtProductSkuMapper.java index 2429664..d01b8a6 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/mapper/MtProductSkuMapper.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/mapper/MtProductSkuMapper.java @@ -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 { "" + "") int insertBatch(@Param("skuList") List 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); } diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/CustomerOrderService.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/CustomerOrderService.java index 0a8b633..41c9685 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/service/CustomerOrderService.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/CustomerOrderService.java @@ -67,5 +67,11 @@ public interface CustomerOrderService { * @return 分页结果 */ PageResult queryOrders(OrderQueryRequestDTO query); + + /** + * 取消订单(恢复库存) + * @param orderNo 订单号 + */ + void cancelOrder(String orderNo); } diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java index 3640a28..795d24f 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/CustomerOrderServiceImpl.java @@ -51,42 +51,48 @@ public class CustomerOrderServiceImpl implements CustomerOrderService { @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()); @@ -98,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); @@ -190,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(); @@ -210,13 +212,11 @@ public class CustomerOrderServiceImpl implements CustomerOrderService { @Override public CustomerOrderResponseDTO getOrderByOrderNo(String orderNo) { - log.debug("查询订单,订单号: {}", orderNo); LambdaQueryWrapper 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, "订单不存在"); } @@ -233,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, "订单不存在"); } @@ -254,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 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, "订单不存在"); } @@ -273,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 @@ -285,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() .eq(CustomerOrder::getOrderNo, orderNo) ); if (order == null) { - log.warn("订单不存在,订单号: {}", orderNo); throw new BusinessException(ResultCode.DATA_NOT_FOUND, "订单不存在"); } @@ -307,30 +298,24 @@ 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 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 queryOrders(OrderQueryRequestDTO query) { - long startTime = System.currentTimeMillis(); - // 处理分页参数 int pageNum = query.getPageNum() != null && query.getPageNum() > 0 ? query.getPageNum() : 1; int pageSize = query.getPageSize() != null && query.getPageSize() > 0 ? query.getPageSize() : 10; @@ -338,11 +323,8 @@ public class CustomerOrderServiceImpl implements CustomerOrderService { // 限制每页最大数量 if (pageSize > 100) { pageSize = 100; - log.warn("每页大小超过限制,已调整为100,原始值: {}", query.getPageSize()); } - log.info("查询订单列表,查询条件: {}, 页码: {}, 每页大小: {}", query, pageNum, pageSize); - // 1. 构建客户订单查询条件 LambdaQueryWrapper orderWrapper = new LambdaQueryWrapper<>(); @@ -461,10 +443,8 @@ public class CustomerOrderServiceImpl implements CustomerOrderService { IPage orderPage = customerOrderMapper.selectPage(page, orderWrapper); java.util.List orders = orderPage.getRecords(); - log.debug("查询到订单数量: {}/{}, 总记录数: {}", orders.size(), pageSize, orderPage.getTotal()); if (orders.isEmpty()) { - log.info("订单列表为空,耗时: {}ms", System.currentTimeMillis() - startTime); return new PageResult<>((long) pageNum, (long) pageSize, orderPage.getTotal(), new java.util.ArrayList<>()); } @@ -486,8 +466,6 @@ public class CustomerOrderServiceImpl implements CustomerOrderService { order -> order, (existing, replacement) -> existing // 如果有重复,保留第一个 )); - - log.debug("批量查询到PayPal订单数量: {}", paypalOrderMap.size()); } // 3. 组装响应数据 @@ -512,10 +490,6 @@ public class CustomerOrderServiceImpl implements CustomerOrderService { result.add(dto); } - long endTime = System.currentTimeMillis(); - log.info("查询订单列表完成,查询条件: {}, 结果数量: {}/{}, 总记录数: {}, 耗时: {}ms", - query, result.size(), pageSize, orderPage.getTotal(), endTime - startTime); - // 构建分页结果 return new PageResult<>( orderPage.getCurrent(), @@ -525,5 +499,47 @@ public class CustomerOrderServiceImpl implements CustomerOrderService { ); } + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelOrder(String orderNo) { + // 查询订单 + LambdaQueryWrapper 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); + } + } diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/PayPalWebhookServiceImpl.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/PayPalWebhookServiceImpl.java index 0b20bad..6341c5b 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/PayPalWebhookServiceImpl.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/PayPalWebhookServiceImpl.java @@ -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 relatedIds = (Map) 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订单详情返回null,PayPal订单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 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 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); diff --git a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java index 2888c5f..3126b58 100644 --- a/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java +++ b/mt-pay/src/main/java/com/mtkj/mtpay/service/impl/ProductServiceImpl.java @@ -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> 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); } } diff --git a/mt-pay/src/main/resources/application-dev.yml b/mt-pay/src/main/resources/application-dev.yml index a2e388a..299a832 100644 --- a/mt-pay/src/main/resources/application-dev.yml +++ b/mt-pay/src/main/resources/application-dev.yml @@ -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:8082/api/paypal/webhook # Webhook ID(从PayPal控制台获取,用于验证Webhook签名) webhook-id: 0SX6117212808615P diff --git a/mt-pay/src/main/resources/application-prod.yml b/mt-pay/src/main/resources/application-prod.yml index 21c222d..a858632 100644 --- a/mt-pay/src/main/resources/application-prod.yml +++ b/mt-pay/src/main/resources/application-prod.yml @@ -60,6 +60,22 @@ spring: config: multi-statement-allow: true +# 服务器配置 +server: + port: ${server.port:8082} + 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 ID(API密钥)- 从环境变量或配置中心获取 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:}