Procházet zdrojové kódy

!542 同步支付中心、商城最新实现
Merge pull request !542 from 芋道源码/feature/mall_product

芋道源码 před 1 rokem
rodič
revize
999dfd7c4a
100 změnil soubory, kde provedl 2927 přidání a 2544 odebrání
  1. 2 3
      README.md
  2. 106 21
      sql/dm/ruoyi-vue-pro-dm8.sql
  3. 0 43
      sql/mysql/optional/go-view.sql
  4. 0 357
      sql/mysql/optional/jimureport.mysql5.7.create.sql
  5. 0 118
      sql/mysql/optional/mp.sql
  6. 135 575
      sql/mysql/ruoyi-vue-pro.sql
  7. 5 0
      yudao-dependencies/pom.xml
  8. 4 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java
  9. 5 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/util/ServiceExceptionUtil.java
  10. 0 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
  11. 15 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
  12. 53 2
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java
  13. 6 12
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
  14. 8 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java
  15. 4 0
      yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java
  16. 5 0
      yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java
  17. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml
  18. 0 35
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java
  19. 0 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java
  20. 44 17
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
  21. 3 15
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java
  22. 0 29
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java
  23. 0 48
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java
  24. 0 58
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java
  25. 141 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java
  26. 8 8
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java
  27. 0 23
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java
  28. 115 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java
  29. 21 25
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java
  30. 0 25
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java
  31. 17 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/exception/PayException.java
  32. 102 50
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
  33. 9 10
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
  34. 0 115
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java
  35. 213 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
  36. 17 14
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
  37. 20 12
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
  38. 7 15
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java
  39. 18 39
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
  40. 14 11
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
  41. 14 15
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
  42. 470 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
  43. 63 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java
  44. 107 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java
  45. 22 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxLitePayClient.java
  46. 58 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
  47. 22 23
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java
  48. 80 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java
  49. 0 198
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java
  50. 0 181
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java
  51. 0 196
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java
  52. 0 23
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelRefundRespEnum.java
  53. 0 29
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java
  54. 0 22
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayNotifyRefundStatusEnum.java
  55. 9 9
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/channel/PayChannelEnum.java
  56. 4 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderDisplayModeEnum.java
  57. 56 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java
  58. 32 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java
  59. 15 16
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java
  60. 5 5
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
  61. 123 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java
  62. 83 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java
  63. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
  64. 39 0
      yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java
  65. 30 0
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
  66. 0 1
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java
  67. 56 0
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/IntegerListTypeHandler.java
  68. 0 2
      yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java
  69. 9 1
      yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
  70. 12 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
  71. 11 11
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
  72. 20 0
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/comment/ProductCommentApi.java
  73. 59 0
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/comment/dto/ProductCommentCreateReqDTO.java
  74. 10 1
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
  75. 3 35
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
  76. 35 32
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java
  77. 13 0
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/DictTypeConstants.java
  78. 14 2
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
  79. 15 0
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ProductConstants.java
  80. 41 0
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentScoresEnum.java
  81. 0 37
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java
  82. 1 1
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java
  83. 15 0
      yudao-module-mall/yudao-module-product-biz/pom.xml
  84. 27 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/comment/ProductCommentApiImpl.java
  85. 13 3
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java
  86. 4 4
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java
  87. 10 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java
  88. 1 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandBaseVO.java
  89. 1 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandListReqVO.java
  90. 1 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandPageReqVO.java
  91. 20 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandSimpleRespVO.java
  92. 5 5
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryBaseVO.java
  93. 5 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryCreateReqVO.java
  94. 6 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryListReqVO.java
  95. 4 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryUpdateReqVO.java
  96. 0 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.http
  97. 62 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java
  98. 62 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java
  99. 14 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentCreateReqVO.java
  100. 46 0
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java

+ 2 - 3
README.md

@@ -160,12 +160,11 @@
 
 |     | 功能   | 描述                        |
 |-----|------|---------------------------|
-| 🚀  | 商户信息 | 管理商户信息,支持 Saas 场景下的多商户功能  |
 | 🚀  | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 |
 | 🚀  | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单     |
 | 🚀  | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单     |
-
-ps:核心功能已经实现,正在对接微信小程序中...
+| 🚀  | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果    |
+| 🚀  | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战    |
 
 ### 基础设施
 

+ 106 - 21
sql/dm/ruoyi-vue-pro-dm8.sql

@@ -1797,27 +1797,112 @@ INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT"
 INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2046,'满减送活动关闭','promotion:reward-activity:close',3,5,2041,'','','',null,0,1,1,1,'1','2022-11-05 10:42:53','1','2022-11-05 10:42:53',0);
 INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2047,'限时折扣活动','',2,7,2030,'discount-activity','time','mall/promotion/discountActivity/index','PromotionDiscountActivity',0,1,1,1,'','2022-11-05 17:12:15','1','2023-04-08 11:45:44',0);
 INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2048,'限时折扣活动查询','promotion:discount-activity:query',3,1,2047,'','','',null,0,1,1,1,'','2022-11-05 17:12:15','','2022-11-05 17:12:15',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2049,'限时折扣活动创建','promotion:discount-activity:create',3,2,2047,'','','',null,0,1,1,1,'','2022-11-05 17:12:15','','2022-11-05 17:12:15',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2050,'限时折扣活动更新','promotion:discount-activity:update',3,3,2047,'','','',null,0,1,1,1,'','2022-11-05 17:12:16','','2022-11-05 17:12:16',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2051,'限时折扣活动删除','promotion:discount-activity:delete',3,4,2047,'','','',null,0,1,1,1,'','2022-11-05 17:12:16','','2022-11-05 17:12:16',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2052,'限时折扣活动关闭','promotion:discount-activity:close',3,5,2047,'','','',null,0,1,1,1,'','2022-11-05 17:12:16','','2022-11-05 17:12:16',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2059,'秒杀活动管理','',2,0,2030,'seckill-activity','time-range','mall/promotion/seckill/seckillActivity/index','PromotionSeckillActivity',0,1,1,1,'','2022-11-06 22:24:49','1','2023-04-08 11:46:02',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2060,'秒杀活动查询','promotion:seckill-activity:query',3,1,2059,'','','',null,0,1,1,1,'','2022-11-06 22:24:49','','2022-11-06 22:24:49',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2061,'秒杀活动创建','promotion:seckill-activity:create',3,2,2059,'','','',null,0,1,1,1,'','2022-11-06 22:24:49','','2022-11-06 22:24:49',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2062,'秒杀活动更新','promotion:seckill-activity:update',3,3,2059,'','','',null,0,1,1,1,'','2022-11-06 22:24:49','','2022-11-06 22:24:49',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2063,'秒杀活动删除','promotion:seckill-activity:delete',3,4,2059,'','','',null,0,1,1,1,'','2022-11-06 22:24:49','','2022-11-06 22:24:49',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2064,'秒杀活动导出','promotion:seckill-activity:export',3,5,2059,'','','',null,0,1,1,1,'','2022-11-06 22:24:49','','2022-11-06 22:24:49',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2066,'秒杀时段管理','',2,0,2030,'seckill-time','','mall/promotion/seckill/seckillTime/index','PromotionSeckillTime',0,0,1,1,'','2022-11-15 19:46:50','1','2023-04-08 11:46:17',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2067,'秒杀时段查询','promotion:seckill-time:query',3,1,2066,'','','',null,0,1,1,1,'','2022-11-15 19:46:51','','2022-11-15 19:46:51',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2068,'秒杀时段创建','promotion:seckill-time:create',3,2,2066,'','','',null,0,1,1,1,'','2022-11-15 19:46:51','','2022-11-15 19:46:51',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2069,'秒杀时段更新','promotion:seckill-time:update',3,3,2066,'','','',null,0,1,1,1,'','2022-11-15 19:46:51','','2022-11-15 19:46:51',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2070,'秒杀时段删除','promotion:seckill-time:delete',3,4,2066,'','','',null,0,1,1,1,'','2022-11-15 19:46:51','','2022-11-15 19:46:51',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2071,'秒杀时段导出','promotion:seckill-time:export',3,5,2066,'','','',null,0,1,1,1,'','2022-11-15 19:46:51','','2022-11-15 19:46:51',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2072,'订单中心','',1,65,0,'/trade','order',null,null,0,1,1,1,'1','2022-11-19 18:57:19','1','2022-12-10 16:32:57',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2073,'售后退款','',2,1,2072,'trade/after-sale','education','mall/trade/afterSale/index','TradeAfterSale',0,1,1,1,'','2022-11-19 20:15:32','1','2023-04-08 11:43:19',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2074,'售后查询','trade:after-sale:query',3,1,2073,'','','',null,0,1,1,1,'','2022-11-19 20:15:33','1','2022-12-10 21:04:29',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2075,'秒杀活动关闭','promotion:sekill-activity:close',3,6,2059,'','','',null,0,1,1,1,'1','2022-11-28 20:20:15','1','2022-11-28 20:20:15',0);
-INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2076,'订单列表','',2,0,2072,'trade/order','list','mall/trade/order/index','TradeOrder',0,1,1,1,'1','2022-12-10 21:05:44','1','2023-04-08 11:42:23',0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2049, '限时折扣活动创建', 'promotion:discount-activity:create', 3, 2, 2047, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-05 17:12:15', '', '2022-11-05 17:12:15', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2059, '秒杀活动管理', '', 2, 0, 2030, 'seckill-activity', 'time-range',
+        'mall/promotion/seckill/seckillActivity/index', 'PromotionSeckillActivity', 0, 1, 1, 1, '',
+        '2022-11-06 22:24:49', '1', '2023-04-08 11:46:02', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2064, '秒杀活动导出', 'promotion:seckill-activity:export', 3, 5, 2059, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2066, '秒杀时段管理', '', 2, 0, 2030, 'seckill-time', '', 'mall/promotion/seckill/SeckillConfig/index',
+        'PromotionSeckillConfig', 0, 0, 1, 1, '', '2022-11-15 19:46:50', '1', '2023-04-08 11:46:17', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2067, '秒杀时段查询', 'promotion:seckill-time:query', 3, 1, 2066, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2068, '秒杀时段创建', 'promotion:seckill-time:create', 3, 2, 2066, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2069, '秒杀时段更新', 'promotion:seckill-time:update', 3, 3, 2066, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2070, '秒杀时段删除', 'promotion:seckill-time:delete', 3, 4, 2066, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2071, '秒杀时段导出', 'promotion:seckill-time:export', 3, 5, 2066, '', '', '', null, 0, 1, 1, 1, '',
+        '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2072, '订单中心', '', 1, 65, 0, '/trade', 'order', null, null, 0, 1, 1, 1, '1', '2022-11-19 18:57:19', '1',
+        '2022-12-10 16:32:57', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2073, '售后退款', '', 2, 1, 2072, 'trade/after-sale', 'education', 'mall/trade/afterSale/index',
+        'TradeAfterSale', 0, 1, 1, 1, '', '2022-11-19 20:15:32', '1', '2023-04-08 11:43:19', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', null, 0, 1, 1, 1, '', '2022-11-19 20:15:33',
+        '1', '2022-12-10 21:04:29', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2075, '秒杀活动关闭', 'promotion:sekill-activity:close', 3, 6, 2059, '', '', '', null, 0, 1, 1, 1, '1',
+        '2022-11-28 20:20:15', '1', '2022-11-28 20:20:15', 0);
+INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID", "NAME", "PERMISSION", "TYPE", "SORT", "PARENT_ID", "PATH", "ICON",
+                                          "COMPONENT", "COMPONENT_NAME", "STATUS", "VISIBLE", "KEEP_ALIVE",
+                                          "ALWAYS_SHOW", "CREATOR", "CREATE_TIME", "UPDATER", "UPDATE_TIME", "DELETED")
+VALUES (2076, '订单列表', '', 2, 0, 2072, 'trade/order', 'list', 'mall/trade/order/index', 'TradeOrder', 0, 1, 1, 1,
+        '1', '2022-12-10 21:05:44', '1', '2023-04-08 11:42:23', 0);
 INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2077,'物流公司管理管理','',2,0,2072,'express-company','','mall/trade/expressCompany/index',null,0,1,1,1,'','2022-12-20 23:27:55','1','2022-12-20 23:36:20',0);
 INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2078,'物流公司管理查询','trade:express-company:query',3,1,2077,'','','',null,0,1,1,1,'','2022-12-20 23:27:55','','2022-12-20 23:27:55',0);
 INSERT INTO "RUOYI_VUE_PRO"."SYSTEM_MENU"("ID","NAME","PERMISSION","TYPE","SORT","PARENT_ID","PATH","ICON","COMPONENT","COMPONENT_NAME","STATUS","VISIBLE","KEEP_ALIVE","ALWAYS_SHOW","CREATOR","CREATE_TIME","UPDATER","UPDATE_TIME","DELETED") VALUES(2079,'物流公司管理创建','trade:express-company:create',3,2,2077,'','','',null,0,1,1,1,'','2022-12-20 23:27:55','','2022-12-20 23:27:55',0);

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 43
sql/mysql/optional/go-view.sql


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 357
sql/mysql/optional/jimureport.mysql5.7.create.sql


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 118
sql/mysql/optional/mp.sql


+ 135 - 575
sql/mysql/ruoyi-vue-pro.sql

@@ -11,7 +11,7 @@
  Target Server Version : 80026
  File Encoding         : 65001
 
- Date: 13/04/2023 23:37:01
+ Date: 24/07/2023 08:51:31
 */
 
 SET NAMES utf8mb4;
@@ -73,6 +73,9 @@ CREATE TABLE `QRTZ_CRON_TRIGGERS`  (
 -- ----------------------------
 BEGIN;
 INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', '* * * * * ?', 'Asia/Shanghai');
+INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
+INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
+INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
 COMMIT;
 
 -- ----------------------------
@@ -133,6 +136,9 @@ CREATE TABLE `QRTZ_JOB_DETAILS`  (
 -- ----------------------------
 BEGIN;
 INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000057400104A4F425F48414E444C45525F4E414D4574000C7061794E6F746966794A6F627800);
+INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000127400104A4F425F48414E444C45525F4E414D457400117061794F726465724578706972654A6F627800);
+INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000117400104A4F425F48414E444C45525F4E414D4574000F7061794F7264657253796E634A6F627800);
+INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000137400104A4F425F48414E444C45525F4E414D45740010706179526566756E6453796E634A6F627800);
 COMMIT;
 
 -- ----------------------------
@@ -185,7 +191,7 @@ CREATE TABLE `QRTZ_SCHEDULER_STATE`  (
 -- Records of QRTZ_SCHEDULER_STATE
 -- ----------------------------
 BEGIN;
-INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'Yunai1677076619095', 1677076631456, 15000);
+INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'Yunai1690117495401', 1690119854263, 15000);
 COMMIT;
 
 -- ----------------------------
@@ -279,202 +285,10 @@ CREATE TABLE `QRTZ_TRIGGERS`  (
 -- Records of QRTZ_TRIGGERS
 -- ----------------------------
 BEGIN;
-INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1677076638000, 1677076637000, 5, 'WAITING', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
-COMMIT;
-
--- ----------------------------
--- Table structure for bpm_form
--- ----------------------------
-DROP TABLE IF EXISTS `bpm_form`;
-CREATE TABLE `bpm_form`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
-  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '表单名',
-  `status` tinyint NOT NULL COMMENT '开启状态',
-  `conf` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '表单的配置',
-  `fields` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '表单项的数组',
-  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的表单定义';
-
--- ----------------------------
--- Records of bpm_form
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for bpm_oa_leave
--- ----------------------------
-DROP TABLE IF EXISTS `bpm_oa_leave`;
-CREATE TABLE `bpm_oa_leave`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '请假表单主键',
-  `user_id` bigint NOT NULL COMMENT '申请人的用户编号',
-  `type` tinyint NOT NULL COMMENT '请假类型',
-  `reason` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请假原因',
-  `start_time` datetime NOT NULL COMMENT '开始时间',
-  `end_time` datetime NOT NULL COMMENT '结束时间',
-  `day` tinyint NOT NULL COMMENT '请假天数',
-  `result` tinyint NOT NULL COMMENT '请假结果',
-  `process_instance_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '流程实例的编号',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OA 请假申请表';
-
--- ----------------------------
--- Records of bpm_oa_leave
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for bpm_process_definition_ext
--- ----------------------------
-DROP TABLE IF EXISTS `bpm_process_definition_ext`;
-CREATE TABLE `bpm_process_definition_ext`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
-  `process_definition_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程定义的编号',
-  `model_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程模型的编号',
-  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '描述',
-  `form_type` tinyint NOT NULL COMMENT '表单类型',
-  `form_id` bigint NULL DEFAULT NULL COMMENT '表单编号',
-  `form_conf` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '表单的配置',
-  `form_fields` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '表单项的数组',
-  `form_custom_create_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '自定义表单的提交路径',
-  `form_custom_view_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '自定义表单的查看路径',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 141 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Bpm 流程定义的拓展表\n';
-
--- ----------------------------
--- Records of bpm_process_definition_ext
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for bpm_process_instance_ext
--- ----------------------------
-DROP TABLE IF EXISTS `bpm_process_instance_ext`;
-CREATE TABLE `bpm_process_instance_ext`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
-  `start_user_id` bigint NOT NULL COMMENT '发起流程的用户编号',
-  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '流程实例的名字',
-  `process_instance_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程实例的编号',
-  `process_definition_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程定义的编号',
-  `category` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '流程分类',
-  `status` tinyint NOT NULL COMMENT '流程实例的状态',
-  `result` tinyint NOT NULL COMMENT '流程实例的结果',
-  `end_time` datetime NULL DEFAULT NULL COMMENT '结束时间',
-  `form_variables` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '表单值',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 296 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的流程实例的拓展';
-
--- ----------------------------
--- Records of bpm_process_instance_ext
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for bpm_task_assign_rule
--- ----------------------------
-DROP TABLE IF EXISTS `bpm_task_assign_rule`;
-CREATE TABLE `bpm_task_assign_rule`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
-  `model_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程模型的编号',
-  `process_definition_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程定义的编号',
-  `task_definition_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程任务定义的 key',
-  `type` tinyint NOT NULL COMMENT '规则类型',
-  `options` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '规则值,JSON 数组',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 275 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Bpm 任务规则表';
-
--- ----------------------------
--- Records of bpm_task_assign_rule
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for bpm_task_ext
--- ----------------------------
-DROP TABLE IF EXISTS `bpm_task_ext`;
-CREATE TABLE `bpm_task_ext`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
-  `assignee_user_id` bigint NULL DEFAULT NULL COMMENT '任务的审批人',
-  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '任务的名字',
-  `task_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务的编号',
-  `result` tinyint NOT NULL COMMENT '任务的结果',
-  `reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '审批建议',
-  `end_time` datetime NULL DEFAULT NULL COMMENT '任务的结束时间',
-  `process_instance_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程实例的编号',
-  `process_definition_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流程定义的编号',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 351 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的流程任务的拓展表';
-
--- ----------------------------
--- Records of bpm_task_ext
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for bpm_user_group
--- ----------------------------
-DROP TABLE IF EXISTS `bpm_user_group`;
-CREATE TABLE `bpm_user_group`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
-  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '组名',
-  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '描述',
-  `member_user_ids` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '成员编号数组',
-  `status` tinyint NOT NULL COMMENT '状态(0正常 1停用)',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 113 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户组';
-
--- ----------------------------
--- Records of bpm_user_group
--- ----------------------------
-BEGIN;
+INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1688907102000, 1688907101000, 5, 'PAUSED', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
+INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', 'payOrderExpireJob', 'DEFAULT', NULL, 1690011600000, -1, 5, 'PAUSED', 'CRON', 1690011553000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
+INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', 'payOrderSyncJob', 'DEFAULT', NULL, 1690011600000, 1690011540000, 5, 'PAUSED', 'CRON', 1690007785000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
+INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payRefundSyncJob', 'DEFAULT', 'payRefundSyncJob', 'DEFAULT', NULL, 1690117560000, 1690117500000, 5, 'PAUSED', 'CRON', 1690117424000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
 COMMIT;
 
 -- ----------------------------
@@ -546,7 +360,7 @@ CREATE TABLE `infra_api_error_log`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1110 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
+) ENGINE = InnoDB AUTO_INCREMENT = 1391 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
 
 -- ----------------------------
 -- Records of infra_api_error_log
@@ -584,7 +398,7 @@ CREATE TABLE `infra_codegen_column`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1688 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
+) ENGINE = InnoDB AUTO_INCREMENT = 1715 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
 
 -- ----------------------------
 -- Records of infra_codegen_column
@@ -617,7 +431,7 @@ CREATE TABLE `infra_codegen_table`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 131 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
+) ENGINE = InnoDB AUTO_INCREMENT = 132 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
 
 -- ----------------------------
 -- Records of infra_codegen_table
@@ -644,7 +458,7 @@ CREATE TABLE `infra_config`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '参数配置表';
+) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '参数配置表';
 
 -- ----------------------------
 -- Records of infra_config
@@ -655,6 +469,7 @@ INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `val
 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', b'0');
 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', b'0');
 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (10, 'url', 2, 'Swagger 接口文档的地址', 'url.swagger', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:59:00', b'0');
+INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (11, 'ui', 2, '腾讯地图 key', 'tencent.lbs.key', 'TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E', b'1', '腾讯地图 key', '1', '2023-06-03 19:16:27', '1', '2023-06-03 19:16:27', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -699,7 +514,7 @@ CREATE TABLE `infra_file`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 912 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
+) ENGINE = InnoDB AUTO_INCREMENT = 953 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
 
 -- ----------------------------
 -- Records of infra_file
@@ -753,7 +568,7 @@ CREATE TABLE `infra_file_content`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
+) ENGINE = InnoDB AUTO_INCREMENT = 44 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
 
 -- ----------------------------
 -- Records of infra_file_content
@@ -781,14 +596,17 @@ CREATE TABLE `infra_job`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
+) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
 
 -- ----------------------------
 -- Records of infra_job
 -- ----------------------------
 BEGIN;
-INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '支付通知 Job', 1, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2022-11-24 23:01:35', b'0');
+INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2023-07-09 20:51:41', b'0');
 INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 'Job 示例', 1, 'demoJob', NULL, '* * * L * ?', 1, 1, 0, '1', '2022-09-24 22:31:41', '1', '2022-09-24 22:31:42', b'0');
+INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', b'0');
+INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', b'0');
+INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -812,7 +630,7 @@ CREATE TABLE `infra_job_log`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 168767 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
+) ENGINE = InnoDB AUTO_INCREMENT = 161 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
 
 -- ----------------------------
 -- Records of infra_job_log
@@ -867,7 +685,7 @@ CREATE TABLE `member_user`  (
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE INDEX `uk_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
-) ENGINE = InnoDB AUTO_INCREMENT = 247 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户';
+) ENGINE = InnoDB AUTO_INCREMENT = 248 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户';
 
 -- ----------------------------
 -- Records of member_user
@@ -875,294 +693,6 @@ CREATE TABLE `member_user`  (
 BEGIN;
 COMMIT;
 
--- ----------------------------
--- Table structure for pay_app
--- ----------------------------
-DROP TABLE IF EXISTS `pay_app`;
-CREATE TABLE `pay_app`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '应用编号',
-  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',
-  `status` tinyint NOT NULL COMMENT '开启状态',
-  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
-  `pay_notify_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付结果的回调地址',
-  `refund_notify_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '退款结果的回调地址',
-  `merchant_id` bigint NOT NULL COMMENT '商户编号',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '支付应用信息';
-
--- ----------------------------
--- Records of pay_app
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_channel
--- ----------------------------
-DROP TABLE IF EXISTS `pay_channel`;
-CREATE TABLE `pay_channel`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商户编号',
-  `code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道编码',
-  `status` tinyint NOT NULL COMMENT '开启状态',
-  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
-  `fee_rate` double NOT NULL DEFAULT 0 COMMENT '渠道费率,单位:百分比',
-  `merchant_id` bigint NOT NULL COMMENT '商户编号',
-  `app_id` bigint NOT NULL COMMENT '应用编号',
-  `config` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付渠道配置',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '支付渠道\n';
-
--- ----------------------------
--- Records of pay_channel
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_demo_order
--- ----------------------------
-DROP TABLE IF EXISTS `pay_demo_order`;
-CREATE TABLE `pay_demo_order`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单编号',
-  `user_id` bigint UNSIGNED NOT NULL COMMENT '用户编号',
-  `spu_id` bigint NOT NULL COMMENT '商品编号',
-  `spu_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '商品名字',
-  `price` int NOT NULL COMMENT '价格,单位:分',
-  `payed` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已支付:[0:未支付 1:已经支付过]',
-  `pay_order_id` bigint NULL DEFAULT NULL COMMENT '支付订单编号',
-  `pay_channel_code` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '支付成功的支付渠道',
-  `pay_time` datetime NULL DEFAULT NULL COMMENT '订单支付时间',
-  `pay_refund_id` bigint NULL DEFAULT NULL COMMENT '退款订单编号',
-  `refund_price` int NOT NULL DEFAULT 0 COMMENT '退款金额,单位:分',
-  `refund_time` datetime NULL DEFAULT NULL COMMENT '退款时间',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 72 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '示例订单\n';
-
--- ----------------------------
--- Records of pay_demo_order
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_merchant
--- ----------------------------
-DROP TABLE IF EXISTS `pay_merchant`;
-CREATE TABLE `pay_merchant`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商户编号',
-  `no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户号',
-  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户全称',
-  `short_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户简称',
-  `status` tinyint NOT NULL COMMENT '开启状态',
-  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '支付商户信息';
-
--- ----------------------------
--- Records of pay_merchant
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_notify_log
--- ----------------------------
-DROP TABLE IF EXISTS `pay_notify_log`;
-CREATE TABLE `pay_notify_log`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志编号',
-  `task_id` bigint NOT NULL COMMENT '通知任务编号',
-  `notify_times` tinyint NOT NULL COMMENT '第几次被通知',
-  `response` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求参数',
-  `status` tinyint NOT NULL COMMENT '通知状态',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 371964 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '支付通知 App 的日志';
-
--- ----------------------------
--- Records of pay_notify_log
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_notify_task
--- ----------------------------
-DROP TABLE IF EXISTS `pay_notify_task`;
-CREATE TABLE `pay_notify_task`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务编号',
-  `merchant_id` bigint NOT NULL COMMENT '商户编号',
-  `app_id` bigint NOT NULL COMMENT '应用编号',
-  `type` tinyint NOT NULL COMMENT '通知类型',
-  `data_id` bigint NOT NULL COMMENT '数据编号',
-  `status` tinyint NOT NULL COMMENT '通知状态',
-  `merchant_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户订单编号',
-  `next_notify_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '下一次通知时间',
-  `last_execute_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后一次执行时间',
-  `notify_times` tinyint NOT NULL COMMENT '当前通知次数',
-  `max_notify_times` tinyint NOT NULL COMMENT '最大可通知次数',
-  `notify_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异步通知地址',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 151 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商户支付、退款等的通知\n';
-
--- ----------------------------
--- Records of pay_notify_task
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_order
--- ----------------------------
-DROP TABLE IF EXISTS `pay_order`;
-CREATE TABLE `pay_order`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '支付订单编号',
-  `merchant_id` bigint NOT NULL COMMENT '商户编号',
-  `app_id` bigint NOT NULL COMMENT '应用编号',
-  `channel_id` bigint NULL DEFAULT NULL COMMENT '渠道编号',
-  `channel_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '渠道编码',
-  `merchant_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户订单编号',
-  `subject` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品标题',
-  `body` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品描述',
-  `notify_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异步通知地址',
-  `notify_status` tinyint NOT NULL COMMENT '通知商户支付结果的回调状态',
-  `amount` bigint NOT NULL COMMENT '支付金额,单位:分',
-  `channel_fee_rate` double NULL DEFAULT 0 COMMENT '渠道手续费,单位:百分比',
-  `channel_fee_amount` bigint NULL DEFAULT 0 COMMENT '渠道手续金额,单位:分',
-  `status` tinyint NOT NULL COMMENT '支付状态',
-  `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP',
-  `expire_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订单失效时间',
-  `success_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订单支付成功时间',
-  `notify_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订单支付通知时间',
-  `success_extension_id` bigint NULL DEFAULT NULL COMMENT '支付成功的订单拓展单编号',
-  `refund_status` tinyint NOT NULL COMMENT '退款状态',
-  `refund_times` tinyint NOT NULL COMMENT '退款次数',
-  `refund_amount` bigint NOT NULL COMMENT '退款总金额,单位:分',
-  `channel_user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '渠道用户编号',
-  `channel_order_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '渠道订单号',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 171 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '支付订单\n';
-
--- ----------------------------
--- Records of pay_order
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_order_extension
--- ----------------------------
-DROP TABLE IF EXISTS `pay_order_extension`;
-CREATE TABLE `pay_order_extension`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '支付订单编号',
-  `no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付订单号',
-  `order_id` bigint NOT NULL COMMENT '支付订单编号',
-  `channel_id` bigint NOT NULL COMMENT '渠道编号',
-  `channel_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道编码',
-  `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP',
-  `status` tinyint NOT NULL COMMENT '支付状态',
-  `channel_extras` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '支付渠道的额外参数',
-  `channel_notify_data` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '支付渠道异步通知的内容',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 383 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '支付订单\n';
-
--- ----------------------------
--- Records of pay_order_extension
--- ----------------------------
-BEGIN;
-COMMIT;
-
--- ----------------------------
--- Table structure for pay_refund
--- ----------------------------
-DROP TABLE IF EXISTS `pay_refund`;
-CREATE TABLE `pay_refund`  (
-  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '支付退款编号',
-  `merchant_id` bigint NOT NULL COMMENT '商户编号',
-  `app_id` bigint NOT NULL COMMENT '应用编号',
-  `channel_id` bigint NOT NULL COMMENT '渠道编号',
-  `channel_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道编码',
-  `order_id` bigint NOT NULL COMMENT '支付订单编号 pay_order 表id',
-  `trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '交易订单号 pay_extension 表no 字段',
-  `merchant_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户订单编号(商户系统生成)',
-  `merchant_refund_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户退款订单号(商户系统生成)',
-  `notify_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '异步通知商户地址',
-  `notify_status` tinyint NOT NULL COMMENT '通知商户退款结果的回调状态',
-  `status` tinyint NOT NULL COMMENT '退款状态',
-  `type` tinyint NOT NULL COMMENT '退款类型(部分退款,全部退款)',
-  `pay_amount` bigint NOT NULL COMMENT '支付金额,单位分',
-  `refund_amount` bigint NOT NULL COMMENT '退款金额,单位分',
-  `reason` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '退款原因',
-  `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户 IP',
-  `channel_order_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道订单号,pay_order 中的channel_order_no 对应',
-  `channel_refund_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '渠道退款单号,渠道返回',
-  `channel_error_code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '渠道调用报错时,错误码',
-  `channel_error_msg` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '渠道调用报错时,错误信息',
-  `channel_extras` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '支付渠道的额外参数',
-  `expire_time` datetime NULL DEFAULT NULL COMMENT '退款失效时间',
-  `success_time` datetime NULL DEFAULT NULL COMMENT '退款成功时间',
-  `notify_time` datetime NULL DEFAULT NULL COMMENT '退款通知时间',
-  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
-  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
-  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
-  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '退款订单';
-
--- ----------------------------
--- Records of pay_refund
--- ----------------------------
-BEGIN;
-COMMIT;
-
 -- ----------------------------
 -- Table structure for system_dept
 -- ----------------------------
@@ -1223,7 +753,7 @@ CREATE TABLE `system_dict_data`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1234 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
+) ENGINE = InnoDB AUTO_INCREMENT = 1348 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
 
 -- ----------------------------
 -- Records of system_dict_data
@@ -1299,33 +829,22 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (86, 0, '病假', '1', 'bpm_oa_leave_type', 0, 'primary', '', NULL, '1', '2021-09-21 22:35:28', '1', '2022-02-16 10:00:41', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (87, 1, '事假', '2', 'bpm_oa_leave_type', 0, 'info', '', NULL, '1', '2021-09-21 22:36:11', '1', '2022-02-16 10:00:49', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (88, 2, '婚假', '3', 'bpm_oa_leave_type', 0, 'warning', '', NULL, '1', '2021-09-21 22:36:38', '1', '2022-02-16 10:00:53', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (98, 1, 'v2', 'v2', 'pay_channel_wechat_version', 0, '', '', 'v2版本', '1', '2021-11-08 17:00:58', '1', '2021-11-08 17:00:58', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (99, 2, 'v3', 'v3', 'pay_channel_wechat_version', 0, '', '', 'v3版本', '1', '2021-11-08 17:01:07', '1', '2021-11-08 17:01:07', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (108, 1, 'RSA2', 'RSA2', 'pay_channel_alipay_sign_type', 0, '', '', 'RSA2', '1', '2021-11-18 15:39:29', '1', '2021-11-18 15:39:29', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (109, 1, '公钥模式', '1', 'pay_channel_alipay_mode', 0, '', '', '公钥模式:privateKey + alipayPublicKey', '1', '2021-11-18 15:45:23', '1', '2021-11-18 15:45:23', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (110, 2, '证书模式', '2', 'pay_channel_alipay_mode', 0, '', '', '证书模式:appCertContent + alipayPublicCertContent + rootCertContent', '1', '2021-11-18 15:45:40', '1', '2021-11-18 15:45:40', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, 1, '线上', 'https://openapi.alipay.com/gateway.do', 'pay_channel_alipay_server_type', 0, '', '', '网关地址 - 线上', '1', '2021-11-18 16:59:32', '1', '2021-11-21 17:37:29', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (112, 2, '沙箱', 'https://openapi.alipaydev.com/gateway.do', 'pay_channel_alipay_server_type', 0, '', '', '网关地址 - 沙箱', '1', '2021-11-18 16:59:48', '1', '2021-11-21 17:37:39', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (113, 1, '微信 JSAPI 支付', 'wx_pub', 'pay_channel_code_type', 0, '', '', '微信 JSAPI(公众号) 支付', '1', '2021-12-03 10:40:24', '1', '2021-12-04 16:41:00', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code_type', 0, '', '', '微信小程序支付', '1', '2021-12-03 10:41:06', '1', '2021-12-03 10:41:06', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code_type', 0, '', '', '微信 App 支付', '1', '2021-12-03 10:41:20', '1', '2021-12-03 10:41:20', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (116, 4, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code_type', 0, '', '', '支付宝 PC 网站支付', '1', '2021-12-03 10:42:09', '1', '2021-12-03 10:42:09', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (117, 5, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code_type', 0, '', '', '支付宝 Wap 网站支付', '1', '2021-12-03 10:42:26', '1', '2021-12-03 10:42:26', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (118, 6, '支付宝App 支付', 'alipay_app', 'pay_channel_code_type', 0, '', '', '支付宝App 支付', '1', '2021-12-03 10:42:55', '1', '2021-12-03 10:42:55', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (119, 7, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code_type', 0, '', '', '支付宝扫码支付', '1', '2021-12-03 10:43:10', '1', '2021-12-03 10:43:10', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (120, 1, '通知成功', '10', 'pay_order_notify_status', 0, 'success', '', '通知成功', '1', '2021-12-03 11:02:41', '1', '2022-02-16 13:59:13', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, 2, '通知失败', '20', 'pay_order_notify_status', 0, 'danger', '', '通知失败', '1', '2021-12-03 11:02:59', '1', '2022-02-16 13:59:17', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, 3, '未通知', '0', 'pay_order_notify_status', 0, 'info', '', '未通知', '1', '2021-12-03 11:03:10', '1', '2022-02-16 13:59:23', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (123, 1, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', '2021-12-03 11:18:29', '1', '2022-02-16 15:24:25', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (124, 2, '支付关闭', '20', 'pay_order_status', 0, 'danger', '', '支付关闭', '1', '2021-12-03 11:18:42', '1', '2022-02-16 15:24:31', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (125, 3, '未支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', '2021-12-03 11:18:18', '1', '2022-02-16 15:24:35', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (126, 1, '未退款', '0', 'pay_order_refund_status', 0, '', '', '未退款', '1', '2021-12-03 11:30:35', '1', '2021-12-03 11:34:05', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (127, 2, '部分退款', '10', 'pay_order_refund_status', 0, '', '', '部分退款', '1', '2021-12-03 11:30:44', '1', '2021-12-03 11:34:10', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (128, 3, '全部退款', '20', 'pay_order_refund_status', 0, '', '', '全部退款', '1', '2021-12-03 11:30:52', '1', '2021-12-03 11:34:14', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1117, 1, '退款订单生成', '0', 'pay_refund_order_status', 0, 'primary', '', '退款订单生成', '1', '2021-12-10 16:44:44', '1', '2022-02-16 14:05:24', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1118, 2, '退款成功', '1', 'pay_refund_order_status', 0, 'success', '', '退款成功', '1', '2021-12-10 16:44:59', '1', '2022-02-16 14:05:28', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1119, 3, '退款失败', '2', 'pay_refund_order_status', 0, 'danger', '', '退款失败', '1', '2021-12-10 16:45:10', '1', '2022-02-16 14:05:34', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1124, 8, '退款关闭', '99', 'pay_refund_order_status', 0, 'info', '', '退款关闭', '1', '2021-12-10 16:46:26', '1', '2022-02-16 14:05:40', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (113, 1, '微信公众号支付', 'wx_pub', 'pay_channel_code', 0, 'success', '', '微信公众号支付', '1', '2021-12-03 10:40:24', '1', '2023-07-19 20:08:47', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (114, 2, '微信小程序支付', 'wx_lite', 'pay_channel_code', 0, 'success', '', '微信小程序支付', '1', '2021-12-03 10:41:06', '1', '2023-07-19 20:08:50', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (115, 3, '微信 App 支付', 'wx_app', 'pay_channel_code', 0, 'success', '', '微信 App 支付', '1', '2021-12-03 10:41:20', '1', '2023-07-19 20:08:56', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (116, 10, '支付宝 PC 网站支付', 'alipay_pc', 'pay_channel_code', 0, 'primary', '', '支付宝 PC 网站支付', '1', '2021-12-03 10:42:09', '1', '2023-07-19 20:09:12', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (117, 11, '支付宝 Wap 网站支付', 'alipay_wap', 'pay_channel_code', 0, 'primary', '', '支付宝 Wap 网站支付', '1', '2021-12-03 10:42:26', '1', '2023-07-19 20:09:16', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (118, 12, '支付宝 App 支付', 'alipay_app', 'pay_channel_code', 0, 'primary', '', '支付宝 App 支付', '1', '2021-12-03 10:42:55', '1', '2023-07-19 20:09:20', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (119, 14, '支付宝扫码支付', 'alipay_qr', 'pay_channel_code', 0, 'primary', '', '支付宝扫码支付', '1', '2021-12-03 10:43:10', '1', '2023-07-19 20:09:28', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (120, 10, '通知成功', '10', 'pay_notify_status', 0, 'success', '', '通知成功', '1', '2021-12-03 11:02:41', '1', '2023-07-19 10:08:19', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, 20, '通知失败', '20', 'pay_notify_status', 0, 'danger', '', '通知失败', '1', '2021-12-03 11:02:59', '1', '2023-07-19 10:08:21', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, 0, '等待通知', '0', 'pay_notify_status', 0, 'info', '', '未通知', '1', '2021-12-03 11:03:10', '1', '2023-07-19 10:08:24', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (123, 10, '支付成功', '10', 'pay_order_status', 0, 'success', '', '支付成功', '1', '2021-12-03 11:18:29', '1', '2023-07-19 18:04:28', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (124, 30, '支付关闭', '30', 'pay_order_status', 0, 'info', '', '支付关闭', '1', '2021-12-03 11:18:42', '1', '2023-07-19 18:05:07', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (125, 0, '等待支付', '0', 'pay_order_status', 0, 'info', '', '未支付', '1', '2021-12-03 11:18:18', '1', '2023-07-19 18:04:15', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1118, 0, '等待退款', '0', 'pay_refund_status', 0, 'info', '', '等待退款', '1', '2021-12-10 16:44:59', '1', '2023-07-19 10:14:39', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1119, 20, '退款失败', '20', 'pay_refund_status', 0, 'danger', '', '退款失败', '1', '2021-12-10 16:45:10', '1', '2023-07-19 10:15:10', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1124, 10, '退款成功', '10', 'pay_refund_status', 0, 'success', '', '退款成功', '1', '2021-12-10 16:46:26', '1', '2023-07-19 10:15:00', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1125, 0, '默认', '1', 'bpm_model_category', 0, 'primary', '', '流程分类 - 默认', '1', '2022-01-02 08:41:11', '1', '2022-02-16 20:01:42', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1126, 0, 'OA', '2', 'bpm_model_category', 0, 'success', '', '流程分类 - OA', '1', '2022-01-02 08:41:22', '1', '2022-02-16 20:01:50', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1127, 0, '进行中', '1', 'bpm_process_instance_status', 0, 'primary', '', '流程实例的状态 - 进行中', '1', '2022-01-07 23:47:22', '1', '2022-02-16 20:07:49', b'0');
@@ -1348,9 +867,6 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1144, 21, '流程发起人的二级领导', '21', 'bpm_task_assign_script', 0, '', '', '任务分配自定义脚本 - 流程发起人的二级领导', '103', '2022-01-15 21:24:46', '103', '2022-01-15 21:24:57', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1145, 1, '管理后台', '1', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 管理后台', '1', '2022-02-02 13:15:06', '1', '2022-03-10 16:32:59', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1146, 2, '用户 APP', '2', 'infra_codegen_scene', 0, '', '', '代码生成的场景枚举 - 用户 APP', '1', '2022-02-02 13:15:19', '1', '2022-03-10 16:33:03', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1147, 0, '未退款', '0', 'pay_refund_order_type', 0, 'info', '', '退款类型 - 未退款', '1', '2022-02-16 14:09:01', '1', '2022-02-16 14:09:01', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1148, 10, '部分退款', '10', 'pay_refund_order_type', 0, 'success', '', '退款类型 - 部分退款', '1', '2022-02-16 14:09:25', '1', '2022-02-16 14:11:38', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1149, 20, '全部退款', '20', 'pay_refund_order_type', 0, 'warning', '', '退款类型 - 全部退款', '1', '2022-02-16 14:11:33', '1', '2022-02-16 14:11:33', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1150, 1, '数据库', '1', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:28', '1', '2022-03-15 00:25:28', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1151, 10, '本地磁盘', '10', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:25:41', '1', '2022-03-15 00:25:56', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1152, 11, 'FTP 服务器', '11', 'infra_file_storage', 0, 'default', '', NULL, '1', '2022-03-15 00:26:06', '1', '2022-03-15 00:26:10', b'0');
@@ -1429,12 +945,37 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 -  不发送', '1', '2023-01-26 09:55:06', '1', '2023-01-26 16:36:36', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1227, 1, '通知公告', '1', 'system_notify_template_type', 0, 'primary', '', '站内信模版的类型 - 通知公告', '1', '2023-01-28 10:35:59', '1', '2023-01-28 10:35:59', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1228, 2, '系统消息', '2', 'system_notify_template_type', 0, 'success', '', '站内信模版的类型 - 系统消息', '1', '2023-01-28 10:36:20', '1', '2023-01-28 10:36:25', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1229, 0, '模拟支付', 'mock', 'pay_channel_code_type', 0, 'default', '', NULL, '1', '2023-02-12 21:50:22', '1', '2023-02-12 21:50:22', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1230, 8, '支付宝条码支付', 'alipay_bar', 'pay_channel_code_type', 0, 'default', '', NULL, '1', '2023-02-18 23:32:24', '1', '2023-02-18 23:32:32', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1229, 0, '模拟支付', 'mock', 'pay_channel_code', 0, 'default', '', '模拟支付', '1', '2023-02-12 21:50:22', '1', '2023-07-10 10:11:02', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1230, 13, '支付宝条码支付', 'alipay_bar', 'pay_channel_code', 0, 'primary', '', '支付宝条码支付', '1', '2023-02-18 23:32:24', '1', '2023-07-19 20:09:23', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1231, 10, 'Vue2 Element UI 标准模版', '10', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:03:55', '1', '2023-04-13 00:03:55', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1232, 20, 'Vue3 Element Plus 标准模版', '20', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:08', '1', '2023-04-13 00:04:08', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1233, 21, 'Vue3 Element Plus Schema 模版', '21', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:26', '1', '2023-04-13 00:04:26', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1234, 30, 'Vue3 vben 模版', '30', 'infra_codegen_front_type', 0, '', '', '', '1', '2023-04-13 00:04:26', '1', '2023-04-13 00:04:26', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1235, 1, '个', '1', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1236, 1, '件', '2', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1237, 1, '盒', '3', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1238, 1, '袋', '4', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1239, 1, '箱', '5', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1240, 1, '套', '6', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1241, 1, '包', '7', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1242, 1, '双', '8', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1243, 1, '卷', '9', 'product_unit', 0, '', '', '', '1', '2023-05-23 14:38:38', '1', '2023-05-23 14:38:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1244, 0, '按件', '1', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:40', '1', '2023-05-21 22:46:40', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1245, 1, '按重量', '2', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:46:58', '1', '2023-05-21 22:46:58', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1246, 2, '按体积', '3', 'trade_delivery_express_charge_mode', 0, '', '', '', '1', '2023-05-21 22:47:18', '1', '2023-05-21 22:47:18', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1335, 1, '购物', '1', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-06-28 13:48:28', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1336, 2, '签到', '2', 'member_point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-06-28 13:48:31', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1337, 1, '订单创建', '1', 'member_point_status', 0, '', '', '', '1', '2023-06-10 12:16:42', '1', '2023-06-28 13:48:34', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1338, 2, '冻结期', '2', 'member_point_status', 0, '', '', '', '1', '2023-06-10 12:16:58', '1', '2023-06-28 13:48:36', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1339, 3, '完成', '3', 'member_point_status', 0, '', '', '', '1', '2023-06-10 12:17:07', '1', '2023-06-28 13:48:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1340, 4, '失效(订单退款)', '4', 'member_point_status', 0, '', '', '', '1', '2023-06-10 12:17:21', '1', '2023-06-28 13:48:42', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1341, 20, '已退款', '20', 'pay_order_status', 0, 'danger', '', '已退款', '1', '2023-07-19 18:05:37', '1', '2023-07-19 18:05:37', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1342, 21, '请求成功,但是结果失败', '21', 'pay_notify_status', 0, 'warning', '', '请求成功,但是结果失败', '1', '2023-07-19 18:10:47', '1', '2023-07-19 18:11:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1343, 22, '请求失败', '22', 'pay_notify_status', 0, 'warning', '', NULL, '1', '2023-07-19 18:11:05', '1', '2023-07-19 18:11:27', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1344, 4, '微信扫码支付', 'wx_native', 'pay_channel_code', 0, 'success', '', '微信扫码支付', '1', '2023-07-19 20:07:47', '1', '2023-07-19 20:09:03', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1345, 5, '微信条码支付', 'wx_bar', 'pay_channel_code', 0, 'success', '', '微信条码支付\n', '1', '2023-07-19 20:08:06', '1', '2023-07-19 20:09:08', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1346, 1, '支付单', '1', 'pay_notify_type', 0, 'primary', '', '支付单', '1', '2023-07-20 12:23:17', '1', '2023-07-20 12:23:17', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1347, 2, '退款单', '2', 'pay_notify_type', 0, 'danger', '', NULL, '1', '2023-07-20 12:23:26', '1', '2023-07-20 12:23:26', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -1455,7 +996,7 @@ CREATE TABLE `system_dict_type`  (
   `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 169 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
+) ENGINE = InnoDB AUTO_INCREMENT = 174 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
 
 -- ----------------------------
 -- Records of system_dict_type
@@ -1481,16 +1022,10 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (115, '错误码的类型', 'system_error_code_type', 0, NULL, '1', '2021-04-21 00:06:30', '1', '2022-02-01 16:36:49', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (116, '登陆日志的类型', 'system_login_type', 0, '登陆日志的类型', '1', '2021-10-06 00:50:46', '1', '2022-02-01 16:35:56', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (117, 'OA 请假类型', 'bpm_oa_leave_type', 0, NULL, '1', '2021-09-21 22:34:33', '1', '2022-01-22 10:41:37', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (122, '支付渠道微信版本', 'pay_channel_wechat_version', 0, '支付渠道微信版本', '1', '2021-11-08 17:00:26', '1', '2021-11-08 17:00:26', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (127, '支付渠道支付宝算法类型', 'pay_channel_alipay_sign_type', 0, '支付渠道支付宝算法类型', '1', '2021-11-18 15:39:09', '1', '2021-11-18 15:39:09', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (128, '支付渠道支付宝公钥类型', 'pay_channel_alipay_mode', 0, '支付渠道支付宝公钥类型', '1', '2021-11-18 15:44:28', '1', '2021-11-18 15:44:28', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (129, '支付宝网关地址', 'pay_channel_alipay_server_type', 0, '支付宝网关地址', '1', '2021-11-18 16:58:55', '1', '2021-11-18 17:01:34', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (130, '支付渠道编码类型', 'pay_channel_code_type', 0, '支付渠道的编码', '1', '2021-12-03 10:35:08', '1', '2021-12-03 10:35:08', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (131, '支付订单回调状态', 'pay_order_notify_status', 0, '支付订单回调状态', '1', '2021-12-03 10:53:29', '1', '2021-12-03 10:53:29', b'0', NULL);
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (130, '支付渠道编码类型', 'pay_channel_code', 0, '支付渠道的编码', '1', '2021-12-03 10:35:08', '1', '2023-07-10 10:11:39', b'0', NULL);
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (131, '支付回调状态', 'pay_notify_status', 0, '支付回调状态(包括退款回调)', '1', '2021-12-03 10:53:29', '1', '2023-07-19 18:09:43', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (132, '支付订单状态', 'pay_order_status', 0, '支付订单状态', '1', '2021-12-03 11:17:50', '1', '2021-12-03 11:17:50', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (133, '支付订单退款状态', 'pay_order_refund_status', 0, '支付订单退款状态', '1', '2021-12-03 11:27:31', '1', '2021-12-03 11:27:31', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (134, '退款订单状态', 'pay_refund_order_status', 0, '退款订单状态', '1', '2021-12-10 16:42:50', '1', '2021-12-10 16:42:50', b'0', NULL);
-INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (135, '退款订单类别', 'pay_refund_order_type', 0, '退款订单类别', '1', '2021-12-10 17:14:53', '1', '2021-12-10 17:14:53', b'0', NULL);
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (134, '退款订单状态', 'pay_refund_status', 0, '退款订单状态', '1', '2021-12-10 16:42:50', '1', '2023-07-19 10:13:17', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (138, '流程分类', 'bpm_model_category', 0, '流程分类', '1', '2022-01-02 08:40:45', '1', '2022-01-02 08:40:45', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (139, '流程实例的状态', 'bpm_process_instance_status', 0, '流程实例的状态', '1', '2022-01-07 23:46:42', '1', '2022-01-07 23:46:42', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (140, '流程实例的结果', 'bpm_process_instance_result', 0, '流程实例的结果', '1', '2022-01-07 23:48:10', '1', '2022-01-07 23:48:10', b'0', NULL);
@@ -1521,6 +1056,11 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', '2023-01-26 09:53:13', '1', '2023-01-26 09:53:13', b'0', '1970-01-01 00:00:00');
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (167, '站内信模版的类型', 'system_notify_template_type', 0, '站内信模版的类型', '1', '2023-01-28 10:35:10', '1', '2023-01-28 10:35:10', b'0', '1970-01-01 00:00:00');
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (168, '代码生成的前端类型', 'infra_codegen_front_type', 0, '', '1', '2023-04-12 23:57:52', '1', '2023-04-12 23:57:52', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (169, '商品的单位', 'product_unit', 0, '商品的单位', '1', '2023-05-24 21:23:59', '1', '2023-05-24 21:23:59', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (170, '快递计费方式', 'trade_delivery_express_charge_mode', 0, '用于商城交易模块配送管理', '1', '2023-05-21 22:45:03', '1', '2023-05-21 22:45:03', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (171, '积分业务类型', 'member_point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-28 13:48:20', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (172, '积分订单状态', 'member_point_status', 0, '', '1', '2023-06-10 12:16:27', '1', '2023-06-28 13:48:17', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (173, '支付通知类型', 'pay_notify_type', 0, NULL, '1', '2023-07-20 12:23:03', '1', '2023-07-20 12:23:03', b'0', '1970-01-01 00:00:00');
 COMMIT;
 
 -- ----------------------------
@@ -1540,7 +1080,7 @@ CREATE TABLE `system_error_code`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 5832 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
+) ENGINE = InnoDB AUTO_INCREMENT = 5833 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
 
 -- ----------------------------
 -- Records of system_error_code
@@ -1569,7 +1109,7 @@ CREATE TABLE `system_login_log`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2163 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 2243 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
 
 -- ----------------------------
 -- Records of system_login_log
@@ -1699,7 +1239,7 @@ CREATE TABLE `system_menu`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2162 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
+) ENGINE = InnoDB AUTO_INCREMENT = 2303 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
 
 -- ----------------------------
 -- Records of system_menu
@@ -1833,12 +1373,11 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1118, '请假查询', '', 2, 0, 5, 'leave', 'user', 'bpm/oa/leave/index', 'BpmOALeave', 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2023-04-08 11:30:40', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1119, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 1118, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1120, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 1118, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'table', 'pay/app/index', 'PayMerchant', 0, b'1', b'1', b'1', '', '2021-11-10 01:13:30', '1', '2023-04-08 10:43:14', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1126, '应用信息', '', 2, 1, 1117, 'app', 'table', 'pay/app/index', 'PayApp', 0, b'1', b'1', b'1', '', '2021-11-10 01:13:30', '1', '2023-07-20 12:13:32', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1127, '支付应用信息查询', 'pay:app:query', 3, 1, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1128, '支付应用信息创建', 'pay:app:create', 3, 2, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1129, '支付应用信息更新', 'pay:app:update', 3, 3, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1130, '支付应用信息删除', 'pay:app:delete', 3, 4, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1131, '支付应用信息导出', 'pay:app:export', 3, 5, 1126, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:31', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1132, '秘钥解析', 'pay:channel:parsing', 3, 6, 1129, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2021-11-08 15:15:47', '1', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1133, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1132, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1134, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1132, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-11-10 01:13:41', '', '2022-04-20 17:03:10', b'0');
@@ -1864,12 +1403,6 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1176, '支付订单更新', 'pay:order:update', 3, 3, 1173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1177, '支付订单删除', 'pay:order:delete', 3, 4, 1173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1178, '支付订单导出', 'pay:order:export', 3, 5, 1173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1179, '商户信息', '', 2, 0, 1117, 'merchant', 'merchant', 'pay/merchant/index', 'PayApp', 0, b'1', b'1', b'1', '', '2021-12-25 09:01:44', '1', '2023-04-08 10:42:32', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1180, '支付商户信息查询', 'pay:merchant:query', 3, 1, 1179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 09:01:44', '', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1181, '支付商户信息创建', 'pay:merchant:create', 3, 2, 1179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 09:01:44', '', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1182, '支付商户信息更新', 'pay:merchant:update', 3, 3, 1179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 09:01:44', '', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1183, '支付商户信息删除', '', 3, 4, 1179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 09:01:44', '', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1184, '支付商户信息导出', 'pay:merchant:export', 3, 5, 1179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 09:01:44', '', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1185, '工作流程', '', 1, 50, 0, '/bpm', 'tool', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-30 20:26:36', '103', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1186, '流程管理', '', 1, 10, 1185, 'manager', 'nested', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-30 20:28:30', '1', '2022-04-20 17:03:10', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1187, '流程表单', '', 2, 0, 1186, 'form', 'form', 'bpm/form/index', 'BpmForm', 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2023-04-08 10:50:37', b'0');
@@ -1984,29 +1517,22 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2050, '限时折扣活动更新', 'promotion:discount-activity:update', 3, 3, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2051, '限时折扣活动删除', 'promotion:discount-activity:delete', 3, 4, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2052, '限时折扣活动关闭', 'promotion:discount-activity:close', 3, 5, 2047, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-05 17:12:16', '', '2022-11-05 17:12:16', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2059, '秒杀活动管理', '', 2, 0, 2030, 'seckill-activity', 'time-range', 'mall/promotion/seckill/seckillActivity/index', 'PromotionSeckillActivity', 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '1', '2023-04-08 11:46:02', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2059, '秒杀商品', '', 2, 2, 2209, 'activity', 'ep:basketball', 'mall/promotion/seckill/activity/index', 'PromotionSeckillActivity', 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '1', '2023-06-24 18:57:25', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2060, '秒杀活动查询', 'promotion:seckill-activity:query', 3, 1, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2061, '秒杀活动创建', 'promotion:seckill-activity:create', 3, 2, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2062, '秒杀活动更新', 'promotion:seckill-activity:update', 3, 3, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2063, '秒杀活动删除', 'promotion:seckill-activity:delete', 3, 4, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2064, '秒杀活动导出', 'promotion:seckill-activity:export', 3, 5, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-06 22:24:49', '', '2022-11-06 22:24:49', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2066, '秒杀时段管理', '', 2, 0, 2030, 'seckill-time', '', 'mall/promotion/seckill/seckillTime/index', 'PromotionSeckillTime', 0, b'0', b'1', b'1', '', '2022-11-15 19:46:50', '1', '2023-04-08 11:46:17', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2067, '秒杀时段查询', 'promotion:seckill-time:query', 3, 1, 2066, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2068, '秒杀时段创建', 'promotion:seckill-time:create', 3, 2, 2066, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2069, '秒杀时段更新', 'promotion:seckill-time:update', 3, 3, 2066, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2070, '秒杀时段删除', 'promotion:seckill-time:delete', 3, 4, 2066, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2071, '秒杀时段导出', 'promotion:seckill-time:export', 3, 5, 2066, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '', '2022-11-15 19:46:51', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2066, '秒杀时段', '', 2, 1, 2209, 'config', 'ep:baseball', 'mall/promotion/seckill/config/index', 'PromotionSeckillConfig', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:50', '1', '2023-06-24 18:57:14', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2067, '秒杀时段查询', 'promotion:seckill-config:query', 3, 1, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:25', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2068, '秒杀时段创建', 'promotion:seckill-config:create', 3, 2, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:48:39', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2069, '秒杀时段更新', 'promotion:seckill-config:update', 3, 3, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2070, '秒杀时段删除', 'promotion:seckill-config:delete', 3, 4, 2066, '', '', '', '', 0, b'1', b'1', b'1', '', '2022-11-15 19:46:51', '1', '2023-06-24 17:50:32', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2072, '订单中心', '', 1, 65, 0, '/trade', 'order', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-11-19 18:57:19', '1', '2022-12-10 16:32:57', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2073, '售后退款', '', 2, 1, 2072, 'trade/after-sale', 'education', 'mall/trade/afterSale/index', 'TradeAfterSale', 0, b'1', b'1', b'1', '', '2022-11-19 20:15:32', '1', '2023-04-08 11:43:19', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2074, '售后查询', 'trade:after-sale:query', 3, 1, 2073, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-11-19 20:15:33', '1', '2022-12-10 21:04:29', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2075, '秒杀活动关闭', 'promotion:sekill-activity:close', 3, 6, 2059, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2022-11-28 20:20:15', '1', '2022-11-28 20:20:15', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2076, '订单列表', '', 2, 0, 2072, 'trade/order', 'list', 'mall/trade/order/index', 'TradeOrder', 0, b'1', b'1', b'1', '1', '2022-12-10 21:05:44', '1', '2023-04-08 11:42:23', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2077, '物流公司管理管理', '', 2, 0, 2072, 'express-company', '', 'mall/trade/expressCompany/index', NULL, 0, b'1', b'1', b'1', '', '2022-12-20 23:27:55', '1', '2022-12-20 23:36:20', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2078, '物流公司管理查询', 'trade:express-company:query', 3, 1, 2077, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-12-20 23:27:55', '', '2022-12-20 23:27:55', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2079, '物流公司管理创建', 'trade:express-company:create', 3, 2, 2077, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-12-20 23:27:55', '', '2022-12-20 23:27:55', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2080, '物流公司管理更新', 'trade:express-company:update', 3, 3, 2077, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-12-20 23:27:55', '', '2022-12-20 23:27:55', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2081, '物流公司管理删除', 'trade:express-company:delete', 3, 4, 2077, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-12-20 23:27:55', '', '2022-12-20 23:27:55', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2082, '物流公司管理导出', 'trade:express-company:export', 3, 5, 2077, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-12-20 23:27:55', '', '2022-12-20 23:27:55', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2083, '地区管理', '', 2, 14, 1, 'area', 'row', 'system/area/index', 'SystemArea', 0, b'1', b'1', b'1', '1', '2022-12-23 17:35:05', '1', '2023-04-08 09:01:37', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2084, '公众号管理', '', 1, 100, 0, '/mp', 'wechat', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-01-01 20:11:04', '1', '2023-01-15 11:28:57', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2085, '账号管理', '', 2, 1, 2084, 'account', 'phone', 'mp/account/index', 'MpAccount', 0, b'1', b'1', b'1', '1', '2023-01-01 20:13:31', '1', '2023-02-09 23:56:39', b'0');
@@ -2086,6 +1612,47 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2159, 'Boot 开发文档', '', 1, 1, 0, 'https://doc.iocoder.cn/', 'education', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:46:28', '1', '2023-02-10 22:46:28', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2160, 'Cloud 开发文档', '', 1, 2, 0, 'https://cloud.iocoder.cn', 'documentation', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:47:07', '1', '2023-02-10 22:47:07', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2161, '接入示例', '', 2, 99, 1117, 'demo-order', 'drag', 'pay/demo/index', NULL, 0, b'1', b'1', b'1', '', '2023-02-11 14:21:42', '1', '2023-02-11 22:26:35', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2162, '商品导出', 'product:spu:export', 3, 5, 2014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2164, '配送管理', '', 1, 2, 2072, 'delivery', '', '', '', 0, b'1', b'1', b'1', '1', '2023-05-18 09:18:02', '1', '2023-05-24 23:24:13', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2165, '快递发货', '', 1, 0, 2164, 'express', '', '', '', 0, b'1', b'1', b'1', '1', '2023-05-18 09:22:06', '1', '2023-05-18 09:22:06', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2166, '门店自提', '', 1, 1, 2164, 'pick-up-store', '', '', '', 0, b'1', b'1', b'1', '1', '2023-05-18 09:23:14', '1', '2023-05-18 09:23:14', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2167, '快递公司', '', 2, 0, 2165, 'express', '', 'mall/trade/delivery/express/index', 'Express', 0, b'1', b'1', b'1', '1', '2023-05-18 09:27:21', '1', '2023-05-18 22:11:14', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2168, '快递公司查询', 'trade:delivery:express:query', 3, 1, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2169, '快递公司创建', 'trade:delivery:express:create', 3, 2, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2170, '快递公司更新', 'trade:delivery:express:update', 3, 3, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2171, '快递公司删除', 'trade:delivery:express:delete', 3, 4, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2172, '快递公司导出', 'trade:delivery:express:export', 3, 5, 2167, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-18 09:37:53', '', '2023-05-18 09:37:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2173, '运费模版', 'trade:delivery:express-template:query', 2, 1, 2165, 'express-template', '', 'mall/trade/delivery/expressTemplate/index', 'ExpressTemplate', 0, b'1', b'1', b'1', '1', '2023-05-20 06:48:10', '1', '2023-05-20 06:48:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2174, '快递运费模板查询', 'trade:delivery:express-template:query', 3, 1, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2175, '快递运费模板创建', 'trade:delivery:express-template:create', 3, 2, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2176, '快递运费模板更新', 'trade:delivery:express-template:update', 3, 3, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2177, '快递运费模板删除', 'trade:delivery:express-template:delete', 3, 4, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2178, '快递运费模板导出', 'trade:delivery:express-template:export', 3, 5, 2173, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-20 06:49:53', '', '2023-05-20 06:49:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2179, '门店管理', '', 2, 1, 2166, 'pick-up-store', '', 'mall/trade/delivery/pickUpStore/index', 'PickUpStore', 0, b'1', b'1', b'1', '1', '2023-05-25 10:50:00', '1', '2023-05-25 10:50:00', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2180, '自提门店查询', 'trade:delivery:pick-up-store:query', 3, 1, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2181, '自提门店创建', 'trade:delivery:pick-up-store:create', 3, 2, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2182, '自提门店更新', 'trade:delivery:pick-up-store:update', 3, 3, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2183, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2184, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 2179, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2023-05-25 10:53:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2209, '秒杀活动', '', 2, 3, 2030, 'seckill', 'ep:place', '', '', 0, b'1', b'1', b'1', '1', '2023-06-24 17:39:13', '1', '2023-06-24 18:55:15', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2262, '会员中心', '', 1, 55, 0, '/member', 'date-range', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-06-10 00:42:03', '1', '2023-06-28 22:52:34', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2275, '积分配置', '', 2, 0, 2299, 'config', '', 'member/point/config/index', 'PointConfig', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2023-06-27 22:50:59', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2276, '积分设置查询', 'point:config:query', 3, 1, 2275, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '', '2023-06-10 02:07:44', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2277, '积分设置创建', 'point:config:save', 3, 2, 2275, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2023-06-27 20:32:31', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2281, '签到配置', '', 2, 2, 2300, 'sign-in-config', '', 'member/signin/config/index', 'SignInConfig', 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '1', '2023-07-02 15:04:15', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2282, '积分签到规则查询', 'point:sign-in-config:query', 3, 1, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2283, '积分签到规则创建', 'point:sign-in-config:create', 3, 2, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2284, '积分签到规则更新', 'point:sign-in-config:update', 3, 3, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2285, '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, 2281, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 03:26:12', '', '2023-06-10 03:26:12', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2287, '积分记录', '', 2, 1, 2299, 'record', '', 'member/point/record/index', 'PointRecord', 0, b'1', b'1', b'1', '', '2023-06-10 04:18:50', '1', '2023-06-27 22:51:07', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2288, '用户积分记录查询', 'point:record:query', 3, 1, 2287, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 04:18:50', '', '2023-06-10 04:18:50', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2293, '签到记录', '', 2, 3, 2300, 'sign-in-record', '', 'member/signin/record/index', 'SignInRecord', 0, b'1', b'1', b'1', '', '2023-06-10 04:48:22', '1', '2023-07-02 15:04:10', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2294, '用户签到积分查询', 'point:sign-in-record:query', 3, 1, 2293, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2297, '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, 2293, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-06-10 04:48:22', '', '2023-06-10 04:48:22', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2299, '会员积分', '', 1, 1, 2262, 'point', '', '', '', 0, b'1', b'1', b'1', '1', '2023-06-27 22:48:51', '1', '2023-06-27 22:48:51', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2300, '会员签到', '', 1, 2, 2262, 'signin', '', '', '', 0, b'1', b'1', b'1', '1', '2023-06-27 22:49:53', '1', '2023-06-27 22:49:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2301, '回调通知', '', 2, 4, 1117, 'notify', 'example', 'pay/notify/index', 'PayNotify', 0, b'1', b'1', b'1', '', '2023-07-20 04:41:32', '1', '2023-07-20 13:45:08', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2302, '支付通知查询', 'pay:notify:query', 3, 1, 2301, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-07-20 04:41:32', '', '2023-07-20 04:41:32', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -2202,7 +1769,7 @@ CREATE TABLE `system_oauth2_access_token`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1785 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
+) ENGINE = InnoDB AUTO_INCREMENT = 2231 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
 
 -- ----------------------------
 -- Records of system_oauth2_access_token
@@ -2324,7 +1891,7 @@ CREATE TABLE `system_oauth2_refresh_token`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 738 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
+) ENGINE = InnoDB AUTO_INCREMENT = 804 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
 
 -- ----------------------------
 -- Records of system_oauth2_refresh_token
@@ -2364,7 +1931,7 @@ CREATE TABLE `system_operate_log`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 6440 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 7134 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
 
 -- ----------------------------
 -- Records of system_operate_log
@@ -2946,7 +2513,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2062, 2, 1128, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2063, 2, 1129, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2064, 2, 1130, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2065, 2, 1131, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2066, 2, 1132, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2067, 2, 1133, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2068, 2, 1134, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
@@ -2974,12 +2540,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2090, 2, 1176, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2091, 2, 1177, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2092, 2, 1178, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2093, 2, 1179, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2094, 2, 1180, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2095, 2, 1181, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2096, 2, 1182, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2097, 2, 1183, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2098, 2, 1184, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2099, 2, 1226, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2100, 2, 1227, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2101, 2, 1228, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
@@ -3524,7 +3084,7 @@ CREATE TABLE `system_tenant`  (
 BEGIN;
 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'https://www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2022-02-23 12:15:11', b'0');
 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'http://www.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2022-05-17 10:03:59', b'0');
-INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'https://www.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0');
+INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'https://www.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-04-15 09:17:54', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -3662,7 +3222,7 @@ CREATE TABLE `system_users`  (
 -- Records of system_users
 -- ----------------------------
 BEGIN;
-INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-04-13 23:09:16', 'admin', '2021-01-05 17:03:47', NULL, '2023-04-13 23:09:16', b'0', 1);
+INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '127.0.0.1', '2023-07-24 08:41:23', 'admin', '2021-01-05 17:03:47', NULL, '2023-07-24 08:41:23', b'0', 1);
 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1);
 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '127.0.0.1', '2022-05-28 15:43:17', '', '2021-01-21 02:13:53', NULL, '2022-07-09 09:00:33', b'0', 1);

+ 5 - 0
yudao-dependencies/pom.xml

@@ -96,6 +96,11 @@
                 <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
                 <version>${revision}</version>
             </dependency>
+            <dependency>
+                <groupId>cn.iocoder.boot</groupId>
+                <artifactId>yudao-spring-boot-starter-biz-trade</artifactId>
+                <version>${revision}</version>
+            </dependency>
             <dependency>
                 <groupId>cn.iocoder.boot</groupId>
                 <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>

+ 4 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java

@@ -33,7 +33,11 @@ public class ServiceErrorCodeRange {
     // 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000)
     // 模块 report 错误码区间 [1-003-000-000 ~ 1-004-000-000)
     // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
+    // 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
     // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
+    // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
     // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
+    // 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
+    // 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
 
 }

+ 5 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/util/ServiceExceptionUtil.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.exception.util;
 
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 
@@ -80,6 +81,10 @@ public class ServiceExceptionUtil {
         return new ServiceException(code, message);
     }
 
+    public static ServiceException invalidParamException(String messagePattern, Object... params) {
+        return exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), messagePattern, params);
+    }
+
     // ========== 格式化方法 ==========
 
     /**

+ 0 - 1
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java

@@ -55,7 +55,6 @@ public class DateUtils {
         return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
     }
 
-    @Deprecated
     public static Date addTime(Duration duration) {
         return new Date(System.currentTimeMillis() + duration.toMillis());
     }

+ 15 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java

@@ -4,6 +4,7 @@ import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
@@ -13,6 +14,7 @@ import lombok.experimental.UtilityClass;
 import lombok.extern.slf4j.Slf4j;
 
 import java.io.IOException;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,6 +31,7 @@ public class JsonUtils {
 
     static {
         objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
         objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
     }
 
@@ -70,6 +73,18 @@ public class JsonUtils {
         }
     }
 
+    public static <T> T parseObject(String text, Type type) {
+        if (StrUtil.isEmpty(text)) {
+            return null;
+        }
+        try {
+            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
+        } catch (IOException e) {
+            log.error("json parse err,json:{}", text, e);
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * 将字符串解析成指定类型的对象
      * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,

+ 53 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.spring;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ArrayUtil;
+import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.springframework.core.DefaultParameterNameDiscoverer;
@@ -24,7 +25,13 @@ import java.util.Map;
  */
 public class SpringExpressionUtils {
 
+    /**
+     * spel表达式解析器
+     */
     private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
+    /**
+     * 参数名发现器
+     */
     private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
 
     private SpringExpressionUtils() {
@@ -33,7 +40,7 @@ public class SpringExpressionUtils {
     /**
      * 从切面中,单个解析 EL 表达式的结果
      *
-     * @param joinPoint  切面点
+     * @param joinPoint        切面点
      * @param expressionString EL 表达式数组
      * @return 执行界面
      */
@@ -45,7 +52,7 @@ public class SpringExpressionUtils {
     /**
      * 从切面中,批量解析 EL 表达式的结果
      *
-     * @param joinPoint   切面点
+     * @param joinPoint         切面点
      * @param expressionStrings EL 表达式数组
      * @return 结果,key 为表达式,value 为对应值
      */
@@ -79,4 +86,48 @@ public class SpringExpressionUtils {
         });
         return result;
     }
+
+    /**
+     * JoinPoint 切面 批量解析 EL 表达式,转换 jspl参数
+     *
+     * @param joinPoint         切面点
+     * @param info              返回值
+     * @param expressionStrings EL 表达式数组
+     * @return Map<String, Object> 结果
+     * @author 陈賝
+     * @since 2023/6/18 11:20
+     */
+    // TODO @chenchen: 这个方法,和 parseExpressions 比较接近,是不是可以合并下;
+    public static Map<String, Object> parseExpression(JoinPoint joinPoint, Object info, List<String> expressionStrings) {
+        // 如果为空,则不进行解析
+        if (CollUtil.isEmpty(expressionStrings)) {
+            return MapUtil.newHashMap();
+        }
+
+        // 第一步,构建解析的上下文 EvaluationContext
+        // 通过 joinPoint 获取被注解方法
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        // 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组
+        String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
+        // Spring 的表达式上下文对象
+        EvaluationContext context = new StandardEvaluationContext();
+        if (ArrayUtil.isNotEmpty(parameterNames)) {
+            //获取方法参数值
+            Object[] args = joinPoint.getArgs();
+            for (int i = 0; i < args.length; i++) {
+                // 替换 SP EL 里的变量值为实际值, 比如 #user --> user对象
+                context.setVariable(parameterNames[i], args[i]);
+            }
+            context.setVariable("info", info);
+        }
+        // 第二步,逐个参数解析
+        Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true);
+        expressionStrings.forEach(key -> {
+            Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context);
+            result.put(key, value);
+        });
+        return result;
+    }
+
 }

+ 6 - 12
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.framework.common.util.string;
 
-import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.StrUtil;
 
@@ -17,14 +16,7 @@ import java.util.stream.Collectors;
 public class StrUtils {
 
     public static String maxLength(CharSequence str, int maxLength) {
-        Assert.isTrue(maxLength > 0);
-        if (null == str) {
-            return null;
-        }
-        if (str.length() <= maxLength) {
-            return str.toString();
-        }
-        return StrUtil.sub(str, 0, maxLength - 3) + "..."; // -3 的原因,是该方法会补充 ... 恰好
+        return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
     }
 
     /**
@@ -48,12 +40,14 @@ public class StrUtils {
         return false;
     }
 
-    public static List<Long> splitToLong(String value,  CharSequence separator) {
+    public static List<Long> splitToLong(String value, CharSequence separator) {
         long[] longs = StrUtil.splitToLong(value, separator);
         return Arrays.stream(longs).boxed().collect(Collectors.toList());
     }
 
-    public static void main(String[] args) {
-        System.out.println(maxLength("aaaaa", 4));
+    public static List<Integer> splitToInteger(String value, CharSequence separator) {
+        int[] integers = StrUtil.splitToInt(value, separator);
+        return Arrays.stream(integers).boxed().collect(Collectors.toList());
     }
+
 }

+ 8 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java

@@ -1,10 +1,12 @@
 package cn.iocoder.yudao.framework.common.util.validation;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
 import org.springframework.util.StringUtils;
 
 import javax.validation.ConstraintViolation;
 import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
 import javax.validation.Validator;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -37,6 +39,12 @@ public class ValidationUtils {
                 && PATTERN_XML_NCNAME.matcher(str).matches();
     }
 
+    public static void validate(Object object, Class<?>... groups) {
+        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+        Assert.notNull(validator);
+        validate(validator, object, groups);
+    }
+
     public static void validate(Validator validator, Object object, Class<?>... groups) {
         Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
         if (CollUtil.isNotEmpty(constraintViolations)) {

+ 4 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java

@@ -48,6 +48,10 @@ public class BannerApplicationRunner implements ApplicationRunner {
             if (isNotPresent("cn.iocoder.yudao.module.trade.framework.web.config.TradeWebConfiguration")) {
                 System.out.println("[商城系统 yudao-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]");
             }
+            // 支付
+            if (isNotPresent("cn.iocoder.yudao.module.pay.framework.pay.config.PayConfiguration")) {
+                System.out.println("[支付系统 yudao-module-pay - 已禁用][参考 https://doc.iocoder.cn/pay/build/ 开启]");
+            }
         });
     }
 

+ 5 - 0
yudao-framework/yudao-spring-boot-starter-biz-dict/src/main/java/cn/iocoder/yudao/framework/dict/core/util/DictFrameworkUtils.java

@@ -57,6 +57,11 @@ public class DictFrameworkUtils {
         log.info("[init][初始化 DictFrameworkUtils 成功]");
     }
 
+    @SneakyThrows
+    public static String getDictDataLabel(String dictType, Integer value) {
+        return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, String.valueOf(value))).getLabel();
+    }
+
     @SneakyThrows
     public static String getDictDataLabel(String dictType, String value) {
         return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, value)).getLabel();

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml

@@ -37,7 +37,7 @@
         <!-- 业务组件 -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
+            <artifactId>yudao-module-system-api</artifactId>
             <version>${revision}</version>
         </dependency>
 

+ 0 - 35
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java

@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.framework.pay.config;
-
-import lombok.Data;
-import org.hibernate.validator.constraints.URL;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.validation.annotation.Validated;
-
-import javax.validation.constraints.NotEmpty;
-
-@ConfigurationProperties(prefix = "yudao.pay")
-@Validated
-@Data
-public class PayProperties {
-
-    /**
-     * 回调地址
-     *
-     * 实际上,对应的 PayNotifyController 的 notifyCallback 方法的 URL
-     *
-     * 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址
-     */
-    @NotEmpty(message = "回调地址不能为空")
-    @URL(message = "回调地址的格式必须是 URL")
-    private String callbackUrl;
-
-    /**
-     * 回跳地址
-     *
-     * 实际上,对应的 PayNotifyController 的 returnCallback 方法的 URL
-     */
-    @URL(message = "回跳地址的格式必须是 URL")
-    @NotEmpty(message = "回跳地址不能为空")
-    private String returnUrl;
-
-}

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java

@@ -12,7 +12,6 @@ import org.springframework.context.annotation.Bean;
  * @author 芋道源码
  */
 @AutoConfiguration
-@EnableConfigurationProperties(PayProperties.class)
 public class YudaoPayAutoConfiguration {
 
     @Bean

+ 44 - 17
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java

@@ -1,12 +1,11 @@
 package cn.iocoder.yudao.framework.pay.core.client;
 
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
+
+import java.util.Map;
 
 /**
  * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能
@@ -22,31 +21,59 @@ public interface PayClient {
      */
     Long getId();
 
+    // ============ 支付相关 ==========
+
     /**
      * 调用支付渠道,统一下单
      *
      * @param reqDTO 下单信息
-     * @return 各支付渠道的返回结果
+     * @return 支付订单信息
+     */
+    PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
+
+    /**
+     * 解析 order 回调数据
+     *
+     * @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
+     * @param body HTTP 回调接口的 request body
+     * @return 支付订单信息
+     */
+    PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body);
+
+    /**
+     * 获得支付订单信息
+     *
+     * @param outTradeNo 外部订单号
+     * @return 支付订单信息
      */
-    PayOrderUnifiedRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
+    PayOrderRespDTO getOrder(String outTradeNo);
+
+    // ============ 退款相关 ==========
 
     /**
      * 调用支付渠道,进行退款
+     *
      * @param reqDTO  统一退款请求信息
-     * @return 各支付渠道的统一返回结果
+     * @return 退款信息
+     */
+    PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
+
+    /**
+     * 解析 refund 回调数据
+     *
+     * @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
+     * @param body HTTP 回调接口的 request body
+     * @return 支付订单信息
      */
-    PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
+    PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body);
 
     /**
-     * 解析回调数据
+     * 获得退款订单信息
      *
-     * @param rawNotify 通知内容
-     * @return 回调对象
-     *         1. {@link PayRefundNotifyRespDTO} 退款通知
-     *         2. {@link PayOrderNotifyRespDTO} 支付通知
+     * @param outTradeNo 外部订单号
+     * @param outRefundNo 外部退款号
+     * @return 退款订单信息
      */
-    default Object parseNotify(PayNotifyReqDTO rawNotify) {
-        throw new UnsupportedOperationException("未实现 parseNotify 方法!");
-    }
+    PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo);
 
 }

+ 3 - 15
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client;
 
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 import javax.validation.ConstraintViolation;
@@ -19,24 +20,11 @@ import java.util.Set;
 // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型
 public interface PayClientConfig {
 
-    /**
-     * 配置验证参数是
-     *
-     * @param validator 校验对象
-     * @return 配置好的验证参数
-     */
-    Set<ConstraintViolation<PayClientConfig>> verifyParam(Validator validator);
-
-    // TODO @aquan:貌似抽象一个 validation group 就好了!
     /**
      * 参数校验
      *
      * @param validator 校验对象
      */
-    default void validate(Validator validator) {
-        Set<ConstraintViolation<PayClientConfig>> violations = verifyParam(validator);
-        if (!violations.isEmpty()) {
-            throw new ConstraintViolationException(violations);
-        }
-    }
+    void validate(Validator validator);
+
 }

+ 0 - 29
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java

@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.dto.notify;
-
-import lombok.Builder;
-import lombok.Data;
-import lombok.ToString;
-
-import java.util.Map;
-
-
-/**
- * 支付订单,退款订单回调,渠道的统一通知请求数据
- */
-@Data
-@ToString
-@Builder
-public class PayNotifyReqDTO {
-
-
-    /**
-     * HTTP 回调接口的 request body
-     */
-    private String body;
-
-    /**
-     * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
-     */
-    private Map<String,String> params;
-
-}

+ 0 - 48
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java

@@ -1,48 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.dto.notify;
-
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.time.LocalDateTime;
-
-/**
- * 支付通知 Response DTO
- *
- * @author 芋道源码
- */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class PayOrderNotifyRespDTO {
-
-    /**
-     * 支付订单号(支付模块的)
-     */
-    private String orderExtensionNo;
-    /**
-     * 支付渠道编号
-     */
-    private String channelOrderNo;
-    /**
-     * 支付渠道用户编号
-     */
-    private String channelUserId;
-    /**
-     * 支付成功时间
-     */
-    private LocalDateTime successTime;
-
-    /**
-     * TODO @jason 结合其他的渠道定义成枚举,
-     *
-     * alipay
-     * TRADE_CLOSED,未付款交易超时关闭,或支付完成后全额退款。
-     * TRADE_SUCCESS, 交易支付成功
-     * TRADE_FINISHED 	交易结束,不可退款。
-     */
-    private String tradeStatus;
-
-}

+ 0 - 58
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java

@@ -1,58 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.dto.notify;
-
-import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum;
-import lombok.Builder;
-import lombok.Data;
-import lombok.ToString;
-
-import java.time.LocalDateTime;
-
-/**
- * 从渠道返回数据中解析得到的支付退款通知的Notify DTO
- *
- * @author jason
- */
-@Data
-@ToString
-@Builder
-public class PayRefundNotifyRespDTO {
-
-    /**
-     * 支付渠道编号
-     */
-    private String channelOrderNo;
-
-    /**
-     * 交易订单号,根据规则生成
-     * 调用支付渠道时,使用该字段作为对接的订单号。
-     * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no
-     * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no
-     *  这里对应 pay_extension 里面的 no
-     * 例如说,P202110132239124200055
-     */
-    private String tradeNo;
-
-    /**
-     * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no
-     * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no
-     * 退款请求号。
-     * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。
-     * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更,
-     * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。
-     * 退款单请求号,根据规则生成
-     *
-     * 例如说,RR202109181134287570000
-     */
-    private String reqNo;
-
-    /**
-     * 退款是否成功
-     */
-    private PayNotifyRefundStatusEnum status;
-
-    /**
-     * 退款成功时间
-     */
-    private LocalDateTime refundSuccessTime;
-
-}

+ 141 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java

@@ -0,0 +1,141 @@
+package cn.iocoder.yudao.framework.pay.core.client.dto.order;
+
+import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 渠道支付订单 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class PayOrderRespDTO {
+
+    /**
+     * 支付状态
+     *
+     * 枚举:{@link PayOrderStatusRespEnum}
+     */
+    private Integer status;
+
+    /**
+     * 外部订单号
+     *
+     * 对应 PayOrderExtensionDO 的 no 字段
+     */
+    private String outTradeNo;
+
+    /**
+     * 支付渠道编号
+     */
+    private String channelOrderNo;
+    /**
+     * 支付渠道用户编号
+     */
+    private String channelUserId;
+
+    /**
+     * 支付成功时间
+     */
+    private LocalDateTime successTime;
+
+    /**
+     * 原始的同步/异步通知结果
+     */
+    private Object rawData;
+
+    // ========== 主动发起支付时,会返回的字段 ==========
+
+    /**
+     * 展示模式
+     *
+     * 枚举 {@link PayOrderDisplayModeEnum} 类
+     */
+    private String displayMode;
+    /**
+     * 展示内容
+     */
+    private String displayContent;
+
+    /**
+     * 调用渠道的错误码
+     *
+     * 注意:这里返回的是业务异常,而是不系统异常。
+     * 如果是系统异常,则会抛出 {@link PayException}
+     */
+    private String channelErrorCode;
+    /**
+     * 调用渠道报错时,错误信息
+     */
+    private String channelErrorMsg;
+
+    public PayOrderRespDTO() {
+    }
+
+    /**
+     * 创建【WAITING】状态的订单返回
+     */
+    public static PayOrderRespDTO waitingOf(String displayMode, String displayContent,
+                                            String outTradeNo, Object rawData) {
+        PayOrderRespDTO respDTO = new PayOrderRespDTO();
+        respDTO.status = PayOrderStatusRespEnum.WAITING.getStatus();
+        respDTO.displayMode = displayMode;
+        respDTO.displayContent = displayContent;
+        // 相对通用的字段
+        respDTO.outTradeNo = outTradeNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
+    /**
+     * 创建【SUCCESS】状态的订单返回
+     */
+    public static PayOrderRespDTO successOf(String channelOrderNo, String channelUserId, LocalDateTime successTime,
+                                            String outTradeNo, Object rawData) {
+        PayOrderRespDTO respDTO = new PayOrderRespDTO();
+        respDTO.status = PayOrderStatusRespEnum.SUCCESS.getStatus();
+        respDTO.channelOrderNo = channelOrderNo;
+        respDTO.channelUserId = channelUserId;
+        respDTO.successTime = successTime;
+        // 相对通用的字段
+        respDTO.outTradeNo = outTradeNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
+    /**
+     * 创建指定状态的订单返回,适合支付渠道回调时
+     */
+    public static PayOrderRespDTO of(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime,
+                                     String outTradeNo, Object rawData) {
+        PayOrderRespDTO respDTO = new PayOrderRespDTO();
+        respDTO.status = status;
+        respDTO.channelOrderNo = channelOrderNo;
+        respDTO.channelUserId = channelUserId;
+        respDTO.successTime = successTime;
+        // 相对通用的字段
+        respDTO.outTradeNo = outTradeNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
+    /**
+     * 创建【CLOSED】状态的订单返回,适合调用支付渠道失败时
+     */
+    public static PayOrderRespDTO closedOf(String channelErrorCode, String channelErrorMsg,
+                                           String outTradeNo, Object rawData) {
+        PayOrderRespDTO respDTO = new PayOrderRespDTO();
+        respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus();
+        respDTO.channelErrorCode = channelErrorCode;
+        respDTO.channelErrorMsg = channelErrorMsg;
+        // 相对通用的字段
+        respDTO.outTradeNo = outTradeNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
+}

+ 8 - 8
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client.dto.order;
 
-import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import lombok.Data;
 import org.hibernate.validator.constraints.Length;
 import org.hibernate.validator.constraints.URL;
@@ -8,7 +8,6 @@ import org.hibernate.validator.constraints.URL;
 import javax.validation.constraints.DecimalMin;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
-import java.awt.*;
 import java.time.LocalDateTime;
 import java.util.Map;
 
@@ -29,10 +28,12 @@ public class PayOrderUnifiedReqDTO {
     // ========== 商户相关字段 ==========
 
     /**
-     * 商户订单编号
+     * 外部订单号
+     *
+     * 对应 PayOrderExtensionDO 的 no 字段
      */
-    @NotEmpty(message = "商户订单编号不能为空")
-    private String merchantOrderId;
+    @NotEmpty(message = "外部订单编号不能为空")
+    private String outTradeNo;
     /**
      * 商品标题
      */
@@ -42,7 +43,6 @@ public class PayOrderUnifiedReqDTO {
     /**
      * 商品描述信息
      */
-    @NotEmpty(message = "商品描述信息不能为空")
     @Length(max = 128, message = "商品描述信息长度不能超过128")
     private String body;
     /**
@@ -64,7 +64,7 @@ public class PayOrderUnifiedReqDTO {
      */
     @NotNull(message = "支付金额不能为空")
     @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
-    private Integer amount;
+    private Integer price;
 
     /**
      * 支付过期时间
@@ -85,7 +85,7 @@ public class PayOrderUnifiedReqDTO {
      *
      * 如果不传递,则每个支付渠道使用默认的方式
      *
-     * 枚举 {@link PayDisplayModeEnum}
+     * 枚举 {@link PayOrderDisplayModeEnum}
      */
     private String displayMode;
 

+ 0 - 23
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java

@@ -1,23 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.dto.order;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
-/**
- * 统一下单 Response DTO
- *
- * @author 芋道源码
- */
-@Data
-public class PayOrderUnifiedRespDTO {
-
-    /**
-     * 展示模式
-     */
-    private String displayMode;
-    /**
-     * 展示内容
-     */
-    private String displayContent;
-
-}

+ 115 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java

@@ -0,0 +1,115 @@
+package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
+
+import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
+import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 渠道退款订单 Response DTO
+ *
+ * @author jason
+ */
+@Data
+public class PayRefundRespDTO {
+
+    /**
+     * 退款状态
+     *
+     * 枚举 {@link PayRefundStatusRespEnum}
+     */
+    private Integer status;
+
+    /**
+     * 外部退款号
+     *
+     * 对应 PayRefundDO 的 no 字段
+     */
+    private String outRefundNo;
+
+    /**
+     * 渠道退款单号
+     *
+     * 对应 PayRefundDO.channelRefundNo 字段
+     */
+    private String channelRefundNo;
+
+    /**
+     * 退款成功时间
+     */
+    private LocalDateTime successTime;
+
+    /**
+     * 原始的异步通知结果
+     */
+    private Object rawData;
+
+    /**
+     * 调用渠道的错误码
+     *
+     * 注意:这里返回的是业务异常,而是不系统异常。
+     * 如果是系统异常,则会抛出 {@link PayException}
+     */
+    private String channelErrorCode;
+    /**
+     * 调用渠道报错时,错误信息
+     */
+    private String channelErrorMsg;
+
+    private PayRefundRespDTO() {
+    }
+
+    /**
+     * 创建【WAITING】状态的退款返回
+     */
+    public static PayRefundRespDTO waitingOf(String channelRefundNo,
+                                             String outRefundNo, Object rawData) {
+        PayRefundRespDTO respDTO = new PayRefundRespDTO();
+        respDTO.status = PayRefundStatusRespEnum.WAITING.getStatus();
+        respDTO.channelRefundNo = channelRefundNo;
+        // 相对通用的字段
+        respDTO.outRefundNo = outRefundNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
+    /**
+     * 创建【SUCCESS】状态的退款返回
+     */
+    public static PayRefundRespDTO successOf(String channelRefundNo, LocalDateTime successTime,
+                                             String outRefundNo, Object rawData) {
+        PayRefundRespDTO respDTO = new PayRefundRespDTO();
+        respDTO.status = PayRefundStatusRespEnum.SUCCESS.getStatus();
+        respDTO.channelRefundNo = channelRefundNo;
+        respDTO.successTime = successTime;
+        // 相对通用的字段
+        respDTO.outRefundNo = outRefundNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
+    /**
+     * 创建【FAILURE】状态的退款返回
+     */
+    public static PayRefundRespDTO failureOf(String outRefundNo, Object rawData) {
+        return failureOf(null, null,
+                outRefundNo, rawData);
+    }
+
+    /**
+     * 创建【FAILURE】状态的退款返回
+     */
+    public static PayRefundRespDTO failureOf(String channelErrorCode, String channelErrorMsg,
+                                             String outRefundNo, Object rawData) {
+        PayRefundRespDTO respDTO = new PayRefundRespDTO();
+        respDTO.status = PayRefundStatusRespEnum.FAILURE.getStatus();
+        respDTO.channelErrorCode = channelErrorCode;
+        respDTO.channelErrorMsg = channelErrorMsg;
+        // 相对通用的字段
+        respDTO.outRefundNo = outRefundNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
+}

+ 21 - 25
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java

@@ -24,33 +24,20 @@ import javax.validation.constraints.NotNull;
 public class PayRefundUnifiedReqDTO {
 
     /**
-     * 用户 IP
+     * 外部订单号
+     *
+     * 对应 PayOrderExtensionDO 的 no 字段
      */
-    private String userIp;
+    @NotEmpty(message = "外部订单编号不能为空")
+    private String outTradeNo;
 
-    // TODO @jason:这个是否为非必传字段呀,只需要传递 payTradeNo 字段即可。尽可能精简
     /**
-     * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id
-     * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no
-     * 渠道订单号
+     * 外部退款号
+     *
+     * 对应 PayRefundDO 的 no 字段
      */
-    private String channelOrderNo;
-
-    /**
-     * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no
-     * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no
-     * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空
-     */
-    private String payTradeNo;
-
-    /**
-     * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no
-     * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no
-     * 退款请求单号  同一退款请求单号多次请求只退一笔。
-     * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo}
-     */
-    @NotEmpty(message = "退款请求单号")
-    private String merchantRefundId;
+    @NotEmpty(message = "退款请求单号不能为空")
+    private String outRefundNo;
 
     /**
      * 退款原因
@@ -58,16 +45,25 @@ public class PayRefundUnifiedReqDTO {
     @NotEmpty(message = "退款原因不能为空")
     private String reason;
 
+    /**
+     * 支付金额,单位:分
+     *
+     * 目前微信支付在退款的时候,必须传递该字段
+     */
+    @NotNull(message = "支付金额不能为空")
+    @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
+    private Integer payPrice;
     /**
      * 退款金额,单位:分
      */
     @NotNull(message = "退款金额不能为空")
     @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
-    private Integer amount;
+    private Integer refundPrice;
 
     /**
-     * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要
+     * 退款结果 notify 回调地址
      */
+    @NotEmpty(message = "支付结果的回调地址不能为空")
     @URL(message = "支付结果的 notify 回调地址必须是 URL 格式")
     private String notifyUrl;
 

+ 0 - 25
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java

@@ -1,25 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
-
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.Accessors;
-/**
- * 统一退款 Response DTO
- *
- * @author jason
- */
-@Accessors(chain = true)
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@Data
-public class PayRefundUnifiedRespDTO {
-
-    /**
-     * 渠道退款单编号
-     */
-    private String channelRefundId;
-}

+ 17 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/exception/PayException.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.framework.pay.core.client.exception;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 支付系统异常 Exception
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class PayException extends RuntimeException {
+
+    public PayException(Throwable cause) {
+        super(cause);
+    }
+
+}

+ 102 - 50
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java

@@ -1,23 +1,19 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl;
 
-import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
-import com.alipay.api.AlipayResponse;import lombok.extern.slf4j.Slf4j;
+import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
+import lombok.extern.slf4j.Slf4j;
 
-import javax.validation.Validation;
-import java.time.LocalDateTime;
+import java.util.Map;
 
-import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
-import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MS_FORMATTER;
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
-import static cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants.PAY_EXCEPTION;
 
 /**
  * 支付客户端的抽象类,提供模板方法,减少子类的冗余代码
@@ -34,6 +30,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
     /**
      * 渠道编码
      */
+    @SuppressWarnings("FieldCanBeLocal")
     private final String channelCode;
     /**
      * 支付配置
@@ -51,7 +48,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
      */
     public final void init() {
         doInit();
-        log.info("[init][配置({}) 初始化完成]", config);
+        log.info("[init][客户端({}) 初始化完成]", getId());
     }
 
     /**
@@ -64,7 +61,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
         if (config.equals(this.config)) {
             return;
         }
-        log.info("[refresh][配置({})发生变化,重新初始化]", config);
+        log.info("[refresh][客户端({})发生变化,重新初始化]", getId());
         this.config = config;
         // 初始化
         this.init();
@@ -75,67 +72,122 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
         return channelId;
     }
 
+    // ============ 支付相关 ==========
+
     @Override
-    public final PayOrderUnifiedRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
-        Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO);
-        // 执行短信发送
-        PayOrderUnifiedRespDTO result;
+    public final PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
+        ValidationUtils.validate(reqDTO);
+        // 执行统一下单
+        PayOrderRespDTO resp;
         try {
-            result = doUnifiedOrder(reqDTO);
+            resp = doUnifiedOrder(reqDTO);
+        } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
+            throw ex;
         } catch (Throwable ex) {
-            // 打印异常日志
-            log.error("[unifiedOrder][request({}) 发起支付失败]", toJsonString(reqDTO), ex);
-            throw buildException(ex);
+            // 系统异常,则包装成 PayException 异常抛出
+            log.error("[unifiedOrder][客户端({}) request({}) 发起支付异常]",
+                    getId(), toJsonString(reqDTO), ex);
+            throw buildPayException(ex);
         }
-        return result;
+        return resp;
     }
 
-    protected abstract PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO)
+    protected abstract PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO)
             throws Throwable;
 
     @Override
-    public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
-        PayRefundUnifiedRespDTO resp;
+    public final PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {
         try {
-            resp = doUnifiedRefund(reqDTO);
-        }  catch (Throwable ex) {
-            // 记录异常日志
-            log.error("[unifiedRefund][request({}) 发起退款失败]", toJsonString(reqDTO), ex);
-            throw buildException(ex);
+            return doParseOrderNotify(params, body);
+        } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
+            throw ex;
+        } catch (Throwable ex) {
+            log.error("[parseOrderNotify][客户端({}) params({}) body({}) 解析失败]",
+                    getId(), params, body, ex);
+            throw buildPayException(ex);
         }
-        return resp;
     }
 
-    protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
-
-    // ========== 各种工具方法 ==========
+    protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body)
+            throws Throwable;
 
-    private RuntimeException buildException(Throwable ex) {
-        if (ex instanceof RuntimeException) {
-            return (RuntimeException) ex;
+    @Override
+    public final PayOrderRespDTO getOrder(String outTradeNo) {
+        try {
+            return doGetOrder(outTradeNo);
+        } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
+            throw ex;
+        } catch (Throwable ex) {
+            log.error("[getOrder][客户端({}) outTradeNo({}) 查询支付单异常]",
+                    getId(), outTradeNo, ex);
+            throw buildPayException(ex);
         }
-        throw new RuntimeException(ex);
     }
 
-    protected void validateSuccess(AlipayResponse response) {
-        if (response.isSuccess()) {
-            return;
+    protected abstract PayOrderRespDTO doGetOrder(String outTradeNo)
+            throws Throwable;
+
+    // ============ 退款相关 ==========
+
+    @Override
+    public final PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
+        ValidationUtils.validate(reqDTO);
+        // 执行统一退款
+        PayRefundRespDTO resp;
+        try {
+            resp = doUnifiedRefund(reqDTO);
+        } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
+            throw ex;
+        } catch (Throwable ex) {
+            // 系统异常,则包装成 PayException 异常抛出
+            log.error("[unifiedRefund][客户端({}) request({}) 发起退款异常]",
+                    getId(), toJsonString(reqDTO), ex);
+            throw buildPayException(ex);
         }
-        throw exception0(PAY_EXCEPTION.getCode(), response.getSubMsg());
+        return resp;
     }
 
-    protected String formatAmount(Integer amount) {
-        return String.valueOf(amount / 100.0);
+    protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
+
+    @Override
+    public final PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
+        try {
+            return doParseRefundNotify(params, body);
+        } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
+            throw ex;
+        } catch (Throwable ex) {
+            log.error("[parseRefundNotify][客户端({}) params({}) body({}) 解析失败]",
+                    getId(), params, body, ex);
+            throw buildPayException(ex);
+        }
     }
 
-    protected String formatTime(LocalDateTime time) {
-        // "yyyy-MM-dd HH:mm:ss"
-        return LocalDateTimeUtil.format(time, NORM_DATETIME_FORMATTER);
+    protected abstract PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body)
+            throws Throwable;
+
+    @Override
+    public final PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo) {
+        try {
+            return doGetRefund(outTradeNo, outRefundNo);
+        } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
+            throw ex;
+        } catch (Throwable ex) {
+            log.error("[getRefund][客户端({}) outTradeNo({}) outRefundNo({}) 查询退款单异常]",
+                    getId(), outTradeNo, outRefundNo, ex);
+            throw buildPayException(ex);
+        }
     }
 
-    protected LocalDateTime parseTime(String str) {
-        // "yyyy-MM-dd HH:mm:ss"
-        return LocalDateTimeUtil.parse(str, NORM_DATETIME_FORMATTER);
+    protected abstract PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo)
+            throws Throwable;
+
+    // ========== 各种工具方法 ==========
+
+    private PayException buildPayException(Throwable ex) {
+        if (ex instanceof PayException) {
+            return (PayException) ex;
+        }
+        throw new PayException(ex);
     }
 
 }

+ 9 - 10
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java

@@ -5,11 +5,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.*;
-import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXLitePayClient;
-import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXNativePayClient;
-import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
-import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.*;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.concurrent.ConcurrentHashMap;
@@ -58,12 +55,14 @@ public class PayClientFactoryImpl implements PayClientFactory {
         PayChannelEnum channelEnum = PayChannelEnum.getByCode(channelCode);
         Assert.notNull(channelEnum, String.format("支付渠道(%s) 为空", channelEnum));
         // 创建客户端
-        // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配
         switch (channelEnum) {
-            case WX_PUB: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
-            case WX_LITE: return (AbstractPayClient<Config>) new WXLitePayClient(channelId, (WXPayClientConfig) config); //微信小程序请求支付
-            case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
-            case WX_NATIVE: return (AbstractPayClient<Config>) new WXNativePayClient(channelId, (WXPayClientConfig) config);
+            // 微信支付
+            case WX_PUB: return (AbstractPayClient<Config>) new WxPubPayClient(channelId, (WxPayClientConfig) config);
+            case WX_LITE: return (AbstractPayClient<Config>) new WxLitePayClient(channelId, (WxPayClientConfig) config);
+            case WX_APP: return (AbstractPayClient<Config>) new WxAppPayClient(channelId, (WxPayClientConfig) config);
+            case WX_BAR: return (AbstractPayClient<Config>) new WxBarPayClient(channelId, (WxPayClientConfig) config);
+            case WX_NATIVE: return (AbstractPayClient<Config>) new WxNativePayClient(channelId, (WxPayClientConfig) config);
+            // 支付宝支付
             case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config);
             case ALIPAY_QR: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
             case ALIPAY_APP: return (AbstractPayClient<Config>) new AlipayAppPayClient(channelId, (AlipayPayClientConfig) config);

+ 0 - 115
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java

@@ -1,115 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
-
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.http.HttpUtil;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
-import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum;
-import com.alipay.api.AlipayApiException;
-import com.alipay.api.AlipayConfig;
-import com.alipay.api.DefaultAlipayClient;
-import com.alipay.api.domain.AlipayTradeRefundModel;
-import com.alipay.api.internal.util.AlipaySignature;
-import com.alipay.api.request.AlipayTradeRefundRequest;
-import com.alipay.api.response.AlipayTradeRefundResponse;
-import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-
-import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
-
-/**
- * 支付宝抽象类, 实现支付宝统一的接口。如退款
- *
- * @author  jason
- */
-@Slf4j
-public abstract class AbstractAlipayClient extends AbstractPayClient<AlipayPayClientConfig> {
-
-    protected DefaultAlipayClient client;
-
-    public AbstractAlipayClient(Long channelId, String channelCode, AlipayPayClientConfig config) {
-        super(channelId, channelCode, config);
-    }
-
-    @Override
-    @SneakyThrows
-    protected void doInit() {
-        AlipayConfig alipayConfig = new AlipayConfig();
-        BeanUtil.copyProperties(config, alipayConfig, false);
-        this.client = new DefaultAlipayClient(alipayConfig);
-    }
-
-    /**
-     * 支付宝统一的退款接口 alipay.trade.refund
-     * @param reqDTO 退款请求 request DTO
-     * @return 退款请求 Response
-     */
-    @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO)  {
-        AlipayTradeRefundModel model=new AlipayTradeRefundModel();
-        model.setTradeNo(reqDTO.getChannelOrderNo());
-        model.setOutTradeNo(reqDTO.getPayTradeNo());
-
-        model.setOutRequestNo(reqDTO.getMerchantRefundId());
-        model.setRefundAmount(formatAmount(reqDTO.getAmount()).toString());
-        model.setRefundReason(reqDTO.getReason());
-
-        AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
-        refundRequest.setBizModel(model);
-        refundRequest.setNotifyUrl(reqDTO.getNotifyUrl());
-        refundRequest.setReturnUrl(reqDTO.getNotifyUrl());
-        try {
-            AlipayTradeRefundResponse response =  client.execute(refundRequest);
-            log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response));
-            if (response.isSuccess()) {
-                //退款导致触发的异步通知是发送到支付接口中设置的notify_url
-                //支付宝不返回退款单号,设置为空
-                PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO();
-                respDTO.setChannelRefundId("");
-//                return PayCommonResult.build(response.getCode(), response.getMsg(), respDTO, codeMapping); TODO
-                return null;
-            }
-            // 失败。需要抛出异常
-//            return PayCommonResult.build(response.getCode(), response.getMsg(), null, codeMapping); TODO
-            return null;
-        } catch (AlipayApiException e) {
-            // TODO 记录异常日志
-            log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e);
-//            return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); TODO
-            return null;
-        }
-    }
-
-    @Override
-    @SneakyThrows
-    public Object parseNotify(PayNotifyReqDTO rawNotify) {
-        // 1. 校验回调数据
-        String body = rawNotify.getBody();
-        Map<String, String> params = rawNotify.getParams();
-        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
-        AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
-                StandardCharsets.UTF_8.name(), "RSA2");
-
-        // 2.1 退款的情况
-        if (bodyObj.containsKey("refund_fee")) {
-            return PayRefundNotifyRespDTO.builder().channelOrderNo(bodyObj.get("trade_no"))
-                    .tradeNo(bodyObj.get("out_trade_no")).reqNo(bodyObj.get("out_biz_no"))
-                    .status(PayNotifyRefundStatusEnum.SUCCESS)
-                    .refundSuccessTime(parseTime(params.get("gmt_refund")))
-                    .build();
-        }
-        // 2.2 支付的情况
-        return PayOrderNotifyRespDTO.builder().orderExtensionNo(bodyObj.get("out_trade_no"))
-                .channelOrderNo(bodyObj.get("trade_no")).channelUserId(bodyObj.get("seller_id"))
-                .tradeStatus(bodyObj.get("trade_status")).successTime(parseTime(params.get("notify_time")))
-                .build();
-    }
-
-}

+ 213 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java

@@ -0,0 +1,213 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayConfig;
+import com.alipay.api.AlipayResponse;
+import com.alipay.api.DefaultAlipayClient;
+import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel;
+import com.alipay.api.domain.AlipayTradeQueryModel;
+import com.alipay.api.domain.AlipayTradeRefundModel;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
+import com.alipay.api.request.AlipayTradeQueryRequest;
+import com.alipay.api.request.AlipayTradeRefundRequest;
+import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
+import com.alipay.api.response.AlipayTradeQueryResponse;
+import com.alipay.api.response.AlipayTradeRefundResponse;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
+
+/**
+ * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
+ *
+ * @author jason
+ */
+@Slf4j
+public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPayClientConfig> {
+
+    protected DefaultAlipayClient client;
+
+    public AbstractAlipayPayClient(Long channelId, String channelCode, AlipayPayClientConfig config) {
+        super(channelId, channelCode, config);
+    }
+
+    @Override
+    @SneakyThrows
+    protected void doInit() {
+        AlipayConfig alipayConfig = new AlipayConfig();
+        BeanUtil.copyProperties(config, alipayConfig, false);
+        this.client = new DefaultAlipayClient(alipayConfig);
+    }
+
+    // ============ 支付相关 ==========
+
+    /**
+     * 构造支付关闭的 {@link PayOrderRespDTO} 对象
+     *
+     * @return 支付关闭的 {@link PayOrderRespDTO} 对象
+     */
+    protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) {
+        Assert.isFalse(response.isSuccess());
+        return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
+                reqDTO.getOutTradeNo(), response);
+    }
+
+    @Override
+    public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws Throwable {
+        // 1. 校验回调数据
+        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
+        AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
+                StandardCharsets.UTF_8.name(), config.getSignType());
+
+        // 2. 解析订单的状态
+        // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂
+        Integer status = parseStatus(bodyObj.get("trade_status"));
+        // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功
+        if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
+            status = PayOrderStatusRespEnum.REFUND.getStatus();
+        }
+        Assert.notNull(status, (Supplier<Throwable>) () -> {
+            throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
+        });
+        return PayOrderRespDTO.of(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")),
+                bodyObj.get("out_trade_no"), body);
+    }
+
+    @Override
+    protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
+        // 1.1 构建 AlipayTradeRefundModel 请求
+        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
+        model.setOutTradeNo(outTradeNo);
+        // 1.2 构建 AlipayTradeQueryRequest 请求
+        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+        request.setBizModel(model);
+
+        // 2.1 执行请求
+        AlipayTradeQueryResponse response =  client.execute(request);
+        if (!response.isSuccess()) { // 不成功,例如说订单不存在
+            return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
+                    outTradeNo, response);
+        }
+        // 2.2 解析订单的状态
+        Integer status = parseStatus(response.getTradeStatus());
+        Assert.notNull(status, (Supplier<Throwable>) () -> {
+            throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody()));
+        });
+        return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()),
+                outTradeNo, response);
+    }
+
+    private static Integer parseStatus(String tradeStatus) {
+        return Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
+                : ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
+                : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
+    }
+
+    // ============ 退款相关 ==========
+
+    /**
+     * 支付宝统一的退款接口 alipay.trade.refund
+     *
+     * @param reqDTO 退款请求 request DTO
+     * @return 退款请求 Response
+     */
+    @Override
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws AlipayApiException {
+        // 1.1 构建 AlipayTradeRefundModel 请求
+        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+        model.setOutTradeNo(reqDTO.getOutTradeNo());
+        model.setOutRequestNo(reqDTO.getOutRefundNo());
+        model.setRefundAmount(formatAmount(reqDTO.getRefundPrice()));
+        model.setRefundReason(reqDTO.getReason());
+        // 1.2 构建 AlipayTradePayRequest 请求
+        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+        request.setBizModel(model);
+
+        // 2.1 执行请求
+        AlipayTradeRefundResponse response = client.execute(request);
+        if (!response.isSuccess()) {
+            return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
+        }
+        // 2.2 创建返回结果
+        // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
+        // 另外,支付宝没有退款单号,所以不用设置
+        return PayRefundRespDTO.successOf(null, LocalDateTimeUtil.of(response.getGmtRefundPay()),
+                reqDTO.getOutRefundNo(), response);
+    }
+
+    @Override
+    public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) {
+        // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
+        // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
+        // ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
+        // 所以,这里在解析时,即使是退款导致的订单状态同步,我们也忽略不做为“退款同步”,而是订单的回调。
+        // 实际上,支付宝退款只要发起成功,就可以认为退款成功,不需要等待回调。
+        throw new UnsupportedOperationException("支付宝无退款回调");
+    }
+
+    @Override
+    protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws AlipayApiException {
+        // 1.1 构建 AlipayTradeFastpayRefundQueryModel 请求
+        AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
+        model.setOutTradeNo(outTradeNo);
+        model.setOutRequestNo(outRefundNo);
+        model.setQueryOptions(Collections.singletonList("gmt_refund_pay"));
+        // 1.2 构建 AlipayTradeFastpayRefundQueryRequest 请求
+        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
+        request.setBizModel(model);
+
+        // 2.1 执行请求
+        AlipayTradeFastpayRefundQueryResponse response = client.execute(request);
+        if (!response.isSuccess()) {
+            // 明确不存在的情况,应该就是失败,可进行关闭
+            if (ObjectUtils.equalsAny(response.getSubCode(), "TRADE_NOT_EXIST", "ACQ.TRADE_NOT_EXIST")) {
+                return PayRefundRespDTO.failureOf(outRefundNo, response);
+            }
+            // 可能存在“ACQ.SYSTEM_ERROR”系统错误等情况,所以返回 WAIT 继续等待
+            return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
+        }
+        // 2.2 创建返回结果
+        if (Objects.equals(response.getRefundStatus(), "REFUND_SUCCESS")) {
+            return PayRefundRespDTO.successOf(null, LocalDateTimeUtil.of(response.getGmtRefundPay()),
+                    outRefundNo, response);
+        }
+        return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
+    }
+
+    // ========== 各种工具方法 ==========
+
+    protected String formatAmount(Integer amount) {
+        return String.valueOf(amount / 100.0);
+    }
+
+    protected String formatTime(LocalDateTime time) {
+        return LocalDateTimeUtil.format(time, NORM_DATETIME_FORMATTER);
+    }
+
+    protected LocalDateTime parseTime(String str) {
+        return LocalDateTimeUtil.parse(str, NORM_DATETIME_FORMATTER);
+    }
+
+}

+ 17 - 14
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java

@@ -1,9 +1,9 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.domain.AlipayTradeAppPayModel;
 import com.alipay.api.request.AlipayTradeAppPayRequest;
@@ -20,25 +20,26 @@ import lombok.extern.slf4j.Slf4j;
  * @author 芋道源码
  */
 @Slf4j
-public class AlipayAppPayClient extends AbstractAlipayClient {
+public class AlipayAppPayClient extends AbstractAlipayPayClient {
 
     public AlipayAppPayClient(Long channelId, AlipayPayClientConfig config) {
         super(channelId, PayChannelEnum.ALIPAY_APP.getCode(), config);
     }
 
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradeAppPayModel 请求
         AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
         // ① 通用的参数
-        model.setOutTradeNo(reqDTO.getMerchantOrderId());
+        model.setOutTradeNo(reqDTO.getOutTradeNo());
         model.setSubject(reqDTO.getSubject());
-        model.setBody(reqDTO.getBody());
-        model.setTotalAmount(formatAmount(reqDTO.getAmount()));
-        model.setProductCode(" QUICK_MSECURITY_PAY"); // 销售产品码:无线快捷支付产品
+        model.setBody(reqDTO.getBody() + "test");
+        model.setTotalAmount(formatAmount(reqDTO.getPrice()));
+        model.setTimeExpire(formatTime(reqDTO.getExpireTime()));
+        model.setProductCode("QUICK_MSECURITY_PAY"); // 销售产品码:无线快捷支付产品
         // ② 个性化的参数【无】
         // ③ 支付宝扫码支付只有一种展示
-        String displayMode = PayDisplayModeEnum.APP.getMode();
+        String displayMode = PayOrderDisplayModeEnum.APP.getMode();
 
         // 1.2 构建 AlipayTradePrecreateRequest 请求
         AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
@@ -47,11 +48,13 @@ public class AlipayAppPayClient extends AbstractAlipayClient {
         request.setReturnUrl(reqDTO.getReturnUrl());
 
         // 2.1 执行请求
-        AlipayTradeAppPayResponse response = client.execute(request);
+        AlipayTradeAppPayResponse response = client.sdkExecute(request);
         // 2.2 处理结果
-        validateSuccess(response);
-        return new PayOrderUnifiedRespDTO()
-                .setDisplayMode(displayMode).setDisplayContent("");
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
+                reqDTO.getOutTradeNo(), response);
     }
 
 }

+ 20 - 12
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java

@@ -1,11 +1,12 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
+import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.domain.AlipayTradePayModel;
 import com.alipay.api.request.AlipayTradePayRequest;
@@ -13,7 +14,6 @@ import com.alipay.api.response.AlipayTradePayResponse;
 import lombok.extern.slf4j.Slf4j;
 
 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
 
 /**
@@ -24,14 +24,14 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
  * @author 芋道源码
  */
 @Slf4j
-public class AlipayBarPayClient extends AbstractAlipayClient {
+public class AlipayBarPayClient extends AbstractAlipayPayClient {
 
     public AlipayBarPayClient(Long channelId, AlipayPayClientConfig config) {
         super(channelId, PayChannelEnum.ALIPAY_BAR.getCode(), config);
     }
 
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         String authCode = MapUtil.getStr(reqDTO.getChannelExtras(), "auth_code");
         if (StrUtil.isEmpty(authCode)) {
             throw exception0(BAD_REQUEST.getCode(), "条形码不能为空");
@@ -40,15 +40,15 @@ public class AlipayBarPayClient extends AbstractAlipayClient {
         // 1.1 构建 AlipayTradePayModel 请求
         AlipayTradePayModel model = new AlipayTradePayModel();
         // ① 通用的参数
-        model.setOutTradeNo(reqDTO.getMerchantOrderId());
+        model.setOutTradeNo(reqDTO.getOutTradeNo());
         model.setSubject(reqDTO.getSubject());
         model.setBody(reqDTO.getBody());
-        model.setTotalAmount(formatAmount(reqDTO.getAmount()));
+        model.setTotalAmount(formatAmount(reqDTO.getPrice()));
         model.setScene("bar_code"); // 当面付条码支付场景
         // ② 个性化的参数
         model.setAuthCode(authCode);
         // ③ 支付宝条码支付只有一种展示
-        String displayMode = PayDisplayModeEnum.BAR_CODE.getMode();
+        String displayMode = PayOrderDisplayModeEnum.BAR_CODE.getMode();
 
         // 1.2 构建 AlipayTradePayRequest 请求
         AlipayTradePayRequest request = new AlipayTradePayRequest();
@@ -59,8 +59,16 @@ public class AlipayBarPayClient extends AbstractAlipayClient {
         // 2.1 执行请求
         AlipayTradePayResponse response = client.execute(request);
         // 2.2 处理结果
-        validateSuccess(response);
-        return new PayOrderUnifiedRespDTO()
-                .setDisplayMode(displayMode).setDisplayContent("");
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        if ("10000".equals(response.getCode())) { // 免密支付
+            return PayOrderRespDTO.successOf(response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getGmtPayment()),
+                    response.getOutTradeNo(), response);
+        }
+        // 大额支付,需要用户输入密码,所以返回 waiting。此时,前端一般会进行轮询
+        return PayOrderRespDTO.waitingOf(displayMode, "",
+                reqDTO.getOutTradeNo(), response);
     }
+
 }

+ 7 - 15
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
 import lombok.Data;
 
@@ -9,8 +10,6 @@ import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import java.util.Set;
 
-// TODO 芋艿:参数校验
-
 /**
  * 支付宝的 PayClientConfig 实现类
  * 属性主要来自 {@link com.alipay.api.AlipayConfig} 的必要属性
@@ -20,15 +19,6 @@ import java.util.Set;
 @Data
 public class AlipayPayClientConfig implements PayClientConfig {
 
-    /**
-     * 网关地址 - 线上
-     */
-    public static final String SERVER_URL_PROD = "https://openapi.alipay.com/gateway.do";
-    /**
-     * 网关地址 - 沙箱
-     */
-    public static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do";
-
     /**
      * 公钥类型 - 公钥模式
      */
@@ -45,8 +35,9 @@ public class AlipayPayClientConfig implements PayClientConfig {
 
     /**
      * 网关地址
-     * 1. {@link #SERVER_URL_PROD}
-     * 2. {@link #SERVER_URL_SANDBOX}
+     *
+     * 1. <a href="https://openapi.alipay.com/gateway.do">生产环境</a>
+     * 2. <a href="https://openapi-sandbox.dl.alipaydev.com/gateway.do">沙箱环境</a>
      */
     @NotBlank(message = "网关地址不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
     private String serverUrl;
@@ -110,8 +101,9 @@ public class AlipayPayClientConfig implements PayClientConfig {
     }
 
     @Override
-    public Set<ConstraintViolation<PayClientConfig>> verifyParam(Validator validator) {
-        return validator.validate(this,
+    public void validate(Validator validator) {
+        ValidationUtils.validate(validator, this,
                 MODE_PUBLIC_KEY.equals(this.getMode()) ? ModePublicKey.class : ModeCertificate.class);
     }
+
 }

+ 18 - 39
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java

@@ -1,12 +1,11 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.Method;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.domain.AlipayTradePagePayModel;
 import com.alipay.api.request.AlipayTradePagePayRequest;
@@ -23,29 +22,29 @@ import java.util.Objects;
  * @author XGD
  */
 @Slf4j
-public class AlipayPcPayClient extends AbstractAlipayClient {
+public class AlipayPcPayClient extends AbstractAlipayPayClient {
 
     public AlipayPcPayClient(Long channelId, AlipayPayClientConfig config) {
         super(channelId, PayChannelEnum.ALIPAY_PC.getCode(), config);
     }
 
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradePagePayModel 请求
         AlipayTradePagePayModel model = new AlipayTradePagePayModel();
         // ① 通用的参数
-        model.setOutTradeNo(reqDTO.getMerchantOrderId());
+        model.setOutTradeNo(reqDTO.getOutTradeNo());
         model.setSubject(reqDTO.getSubject());
         model.setBody(reqDTO.getBody());
-        model.setTotalAmount(formatAmount(reqDTO.getAmount()));
+        model.setTotalAmount(formatAmount(reqDTO.getPrice()));
         model.setTimeExpire(formatTime(reqDTO.getExpireTime()));
         model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY
         // ② 个性化的参数
-        // 参考 https://www.pingxx.com/api/支付渠道 extra 参数说明.html 的 alipay_pc_direct 部分
-        model.setQrPayMode(MapUtil.getStr(reqDTO.getChannelExtras(), "qr_pay_mode"));
-        model.setQrcodeWidth(MapUtil.getLong(reqDTO.getChannelExtras(), "qr_code_width"));
-        // ③ 支付宝 PC 支付有多种展示模式,因此这里需要计算
-        String displayMode = getDisplayMode(reqDTO.getDisplayMode(), model.getQrPayMode());
+        // 如果想弄更多个性化的参数,可参考 https://www.pingxx.com/api/支付渠道 extra 参数说明.html 的 alipay_pc_direct 部分进行拓展
+        model.setQrPayMode("2"); // 跳转模式 - 订单码,效果参见:https://help.pingxx.com/article/1137360/
+        // ③ 支付宝 PC 支付有两种展示模式:FORM、URL
+        String displayMode = ObjectUtil.defaultIfNull(reqDTO.getDisplayMode(),
+                PayOrderDisplayModeEnum.URL.getMode());
 
         // 1.2 构建 AlipayTradePagePayRequest 请求
         AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
@@ -55,37 +54,17 @@ public class AlipayPcPayClient extends AbstractAlipayClient {
 
         // 2.1 执行请求
         AlipayTradePagePayResponse response;
-        if (Objects.equals(displayMode, PayDisplayModeEnum.FORM.getMode())) {
+        if (Objects.equals(displayMode, PayOrderDisplayModeEnum.FORM.getMode())) {
             response = client.pageExecute(request, Method.POST.name()); // 需要特殊使用 POST 请求
         } else {
             response = client.pageExecute(request, Method.GET.name());
         }
-
         // 2.2 处理结果
-        validateSuccess(response);
-        return new PayOrderUnifiedRespDTO().setDisplayMode(displayMode)
-                .setDisplayContent(response.getBody());
-    }
-
-    /**
-     * 获得最终的支付 UI 展示模式
-     *
-     * @param displayMode 前端传递的 UI 展示模式
-     * @param qrPayMode 前端传递的二维码模式
-     * @return 最终的支付 UI 展示模式
-     */
-    private String getDisplayMode(String displayMode, String qrPayMode) {
-        // 1.1 支付宝二维码的前置模式
-        if (StrUtil.equalsAny(qrPayMode, "0", "1", "3", "4")) {
-            return PayDisplayModeEnum.IFRAME.getMode();
-        }
-        // 1.2 支付宝二维码的跳转模式
-        if (StrUtil.equals(qrPayMode, "2")) {
-            return PayDisplayModeEnum.URL.getMode();
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
         }
-        // 2. 前端传递了 UI 展示模式
-        return displayMode != null ? displayMode :
-                PayDisplayModeEnum.URL.getMode(); // 模式使用 URL 跳转
+        return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
+                reqDTO.getOutTradeNo(), response);
     }
 
 }

+ 14 - 11
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java

@@ -1,9 +1,9 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.domain.AlipayTradePrecreateModel;
 import com.alipay.api.request.AlipayTradePrecreateRequest;
@@ -18,25 +18,25 @@ import lombok.extern.slf4j.Slf4j;
  * @author 芋道源码
  */
 @Slf4j
-public class AlipayQrPayClient extends AbstractAlipayClient {
+public class AlipayQrPayClient extends AbstractAlipayPayClient {
 
     public AlipayQrPayClient(Long channelId, AlipayPayClientConfig config) {
         super(channelId, PayChannelEnum.ALIPAY_QR.getCode(), config);
     }
 
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradePrecreateModel 请求
         AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
         // ① 通用的参数
-        model.setOutTradeNo(reqDTO.getMerchantOrderId());
+        model.setOutTradeNo(reqDTO.getOutTradeNo());
         model.setSubject(reqDTO.getSubject());
         model.setBody(reqDTO.getBody());
-        model.setTotalAmount(formatAmount(reqDTO.getAmount()));
+        model.setTotalAmount(formatAmount(reqDTO.getPrice()));
         model.setProductCode("FACE_TO_FACE_PAYMENT"); // 销售产品码. 目前扫码支付场景下仅支持 FACE_TO_FACE_PAYMENT
         // ② 个性化的参数【无】
         // ③ 支付宝扫码支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开
-        String displayMode = PayDisplayModeEnum.QR_CODE.getMode();
+        String displayMode = PayOrderDisplayModeEnum.QR_CODE.getMode();
 
         // 1.2 构建 AlipayTradePrecreateRequest 请求
         AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
@@ -47,8 +47,11 @@ public class AlipayQrPayClient extends AbstractAlipayClient {
         // 2.1 执行请求
         AlipayTradePrecreateResponse response = client.execute(request);
         // 2.2 处理结果
-        validateSuccess(response);
-        return new PayOrderUnifiedRespDTO()
-                .setDisplayMode(displayMode).setDisplayContent(response.getQrCode());
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return PayOrderRespDTO.waitingOf(displayMode, response.getQrCode(),
+                reqDTO.getOutTradeNo(), response);
     }
+
 }

+ 14 - 15
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java

@@ -1,11 +1,10 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.Method;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.domain.AlipayTradeWapPayModel;
 import com.alipay.api.request.AlipayTradeWapPayRequest;
@@ -20,26 +19,25 @@ import lombok.extern.slf4j.Slf4j;
  * @author 芋道源码
  */
 @Slf4j
-public class AlipayWapPayClient extends AbstractAlipayClient {
+public class AlipayWapPayClient extends AbstractAlipayPayClient {
 
     public AlipayWapPayClient(Long channelId, AlipayPayClientConfig config) {
         super(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), config);
     }
 
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradeWapPayModel 请求
         AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
         // ① 通用的参数
-        model.setOutTradeNo(reqDTO.getMerchantOrderId());
+        model.setOutTradeNo(reqDTO.getOutTradeNo());
         model.setSubject(reqDTO.getSubject());
         model.setBody(reqDTO.getBody());
-        model.setTotalAmount(formatAmount(reqDTO.getAmount()));
+        model.setTotalAmount(formatAmount(reqDTO.getPrice()));
         model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY
         // ② 个性化的参数【无】
-        // ③ 支付宝 Wap 支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开
-        String displayMode = ObjectUtil.defaultIfNull(reqDTO.getDisplayMode(),
-                PayDisplayModeEnum.URL.getMode());
+        // ③ 支付宝 Wap 支付只有一种展示:URL
+        String displayMode = PayOrderDisplayModeEnum.URL.getMode();
 
         // 1.2 构建 AlipayTradeWapPayRequest 请求
         AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
@@ -50,11 +48,12 @@ public class AlipayWapPayClient extends AbstractAlipayClient {
 
         // 2.1 执行请求
         AlipayTradeWapPayResponse response = client.pageExecute(request, Method.GET.name());
-
         // 2.2 处理结果
-        validateSuccess(response);
-        return new PayOrderUnifiedRespDTO()
-                .setDisplayMode(displayMode).setDisplayContent(response.getBody());
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
+                reqDTO.getOutTradeNo(), response);
     }
 
 }

+ 470 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java

@@ -0,0 +1,470 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.date.TemporalAccessorUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.io.FileUtils;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
+import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
+import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
+import com.github.binarywang.wxpay.bean.request.*;
+import com.github.binarywang.wxpay.bean.result.*;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Map;
+import java.util.Objects;
+
+import static cn.hutool.core.date.DatePattern.*;
+import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2;
+
+/**
+ * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款)
+ *
+ * @author 遇到源码
+ */
+@Slf4j
+public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientConfig> {
+
+    protected WxPayService client;
+
+    public AbstractWxPayClient(Long channelId, String channelCode, WxPayClientConfig config) {
+        super(channelId, channelCode, config);
+    }
+
+    /**
+     * 初始化 client 客户端
+     *
+     * @param tradeType 交易类型
+     */
+    protected void doInit(String tradeType) {
+        // 创建 config 配置
+        WxPayConfig payConfig = new WxPayConfig();
+        BeanUtil.copyProperties(config, payConfig, "keyContent");
+        payConfig.setTradeType(tradeType);
+        // weixin-pay-java 无法设置内容,只允许读取文件,所以这里要创建临时文件来解决
+        if (Base64.isBase64(config.getKeyContent())) {
+            payConfig.setKeyPath(FileUtils.createTempFile(Base64.decode(config.getKeyContent())).getPath());
+        }
+        if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
+            payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
+        }
+        if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
+            payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
+        }
+
+        // 创建 client 客户端
+        client = new WxPayServiceImpl();
+        client.setConfig(payConfig);
+    }
+
+    // ============ 支付相关 ==========
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception {
+        try {
+            switch (config.getApiVersion()) {
+                case API_VERSION_V2:
+                    return doUnifiedOrderV2(reqDTO);
+                case WxPayClientConfig.API_VERSION_V3:
+                    return doUnifiedOrderV3(reqDTO);
+                default:
+                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+            }
+        } catch (WxPayException e) {
+            String errorCode = getErrorCode(e);
+            String errorMessage = getErrorMessage(e);
+            return PayOrderRespDTO.closedOf(errorCode, errorMessage,
+                    reqDTO.getOutTradeNo(), e.getXmlString());
+        }
+    }
+
+    /**
+     * 【V2】调用支付渠道,统一下单
+     *
+     * @param reqDTO 下单信息
+     * @return 各支付渠道的返回结果
+     */
+    protected abstract PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO)
+            throws Exception;
+
+    /**
+     * 【V3】调用支付渠道,统一下单
+     *
+     * @param reqDTO 下单信息
+     * @return 各支付渠道的返回结果
+     */
+    protected abstract PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO)
+            throws WxPayException;
+
+    /**
+     * 【V2】创建微信下单请求
+     *
+     * @param reqDTO 下信息
+     * @return 下单请求
+     */
+    protected WxPayUnifiedOrderRequest buildPayUnifiedOrderRequestV2(PayOrderUnifiedReqDTO reqDTO) {
+        return WxPayUnifiedOrderRequest.newBuilder()
+                .outTradeNo(reqDTO.getOutTradeNo())
+                .body(reqDTO.getSubject())
+                .detail(reqDTO.getBody())
+                .totalFee(reqDTO.getPrice()) // 单位分
+                .timeExpire(formatDateV2(reqDTO.getExpireTime()))
+                .spbillCreateIp(reqDTO.getUserIp())
+                .notifyUrl(reqDTO.getNotifyUrl())
+                .build();
+    }
+
+    /**
+     * 【V3】创建微信下单请求
+     *
+     * @param reqDTO 下信息
+     * @return 下单请求
+     */
+    protected WxPayUnifiedOrderV3Request buildPayUnifiedOrderRequestV3(PayOrderUnifiedReqDTO reqDTO) {
+        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
+        request.setOutTradeNo(reqDTO.getOutTradeNo());
+        request.setDescription(reqDTO.getSubject());
+        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
+        request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
+        request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
+        request.setNotifyUrl(reqDTO.getNotifyUrl());
+        return request;
+    }
+
+    @Override
+    public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws WxPayException {
+        switch (config.getApiVersion()) {
+            case API_VERSION_V2:
+                return doParseOrderNotifyV2(body);
+            case WxPayClientConfig.API_VERSION_V3:
+                return doParseOrderNotifyV3(body);
+            default:
+                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+        }
+    }
+
+    private PayOrderRespDTO doParseOrderNotifyV2(String body) throws WxPayException {
+        // 1. 解析回调
+        WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body);
+        // 2. 构建结果
+        // V2 微信支付的回调,只有 SUCCESS 支付成功、CLOSED 支付失败两种情况,无需像支付宝一样解析的比较复杂
+        Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
+                PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
+        return PayOrderRespDTO.of(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
+                response.getOutTradeNo(), body);
+    }
+
+    private PayOrderRespDTO doParseOrderNotifyV3(String body) throws WxPayException {
+        // 1. 解析回调
+        WxPayOrderNotifyV3Result response = client.parseOrderNotifyV3Result(body, null);
+        WxPayOrderNotifyV3Result.DecryptNotifyResult result = response.getResult();
+        // 2. 构建结果
+        Integer status = parseStatus(result.getTradeState());
+        String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;
+        return PayOrderRespDTO.of(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()),
+                result.getOutTradeNo(), body);
+    }
+
+    @Override
+    protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
+        try {
+            switch (config.getApiVersion()) {
+                case API_VERSION_V2:
+                    return doGetOrderV2(outTradeNo);
+                case WxPayClientConfig.API_VERSION_V3:
+                    return doGetOrderV3(outTradeNo);
+                default:
+                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+            }
+        } catch (WxPayException e) {
+            if (ObjectUtils.equalsAny(e.getErrCode(), "ORDERNOTEXIST", "ORDER_NOT_EXIST")) {
+                String errorCode = getErrorCode(e);
+                String errorMessage = getErrorMessage(e);
+                return PayOrderRespDTO.closedOf(errorCode, errorMessage,
+                        outTradeNo, e.getXmlString());
+            }
+            throw e;
+        }
+    }
+
+    private PayOrderRespDTO doGetOrderV2(String outTradeNo) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayOrderQueryRequest request = WxPayOrderQueryRequest.newBuilder()
+                .outTradeNo(outTradeNo).build();
+        // 执行请求
+        WxPayOrderQueryResult response = client.queryOrder(request);
+
+        // 转换结果
+        Integer status = parseStatus(response.getTradeState());
+        return PayOrderRespDTO.of(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
+                outTradeNo, response);
+    }
+
+    private PayOrderRespDTO doGetOrderV3(String outTradeNo) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request()
+                .setOutTradeNo(outTradeNo);
+        // 执行请求
+        WxPayOrderQueryV3Result response = client.queryOrderV3(request);
+
+        // 转换结果
+        Integer status = parseStatus(response.getTradeState());
+        String openid = response.getPayer() != null ? response.getPayer().getOpenid() : null;
+        return PayOrderRespDTO.of(status, response.getTransactionId(), openid, parseDateV3(response.getSuccessTime()),
+                outTradeNo, response);
+    }
+
+    private static Integer parseStatus(String tradeState) {
+        switch (tradeState) {
+            case "NOTPAY":
+            case "USERPAYING": // 支付中,等待用户输入密码(条码支付独有)
+                return PayOrderStatusRespEnum.WAITING.getStatus();
+            case "SUCCESS":
+                return PayOrderStatusRespEnum.SUCCESS.getStatus();
+            case "REFUND":
+                return PayOrderStatusRespEnum.REFUND.getStatus();
+            case "CLOSED":
+            case "REVOKED": // 已撤销(刷卡支付独有)
+            case "PAYERROR": // 支付失败(其它原因,如银行返回失败)
+                return PayOrderStatusRespEnum.CLOSED.getStatus();
+            default:
+                throw new IllegalArgumentException(StrUtil.format("未知的支付状态({})", tradeState));
+        }
+    }
+
+    // ============ 退款相关 ==========
+
+    @Override
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
+        try {
+            switch (config.getApiVersion()) {
+                case API_VERSION_V2:
+                    return doUnifiedRefundV2(reqDTO);
+                case WxPayClientConfig.API_VERSION_V3:
+                    return doUnifiedRefundV3(reqDTO);
+                default:
+                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+            }
+        } catch (WxPayException e) {
+            String errorCode = getErrorCode(e);
+            String errorMessage = getErrorMessage(e);
+            return PayRefundRespDTO.failureOf(errorCode, errorMessage,
+                    reqDTO.getOutTradeNo(), e.getXmlString());
+        }
+    }
+
+    private PayRefundRespDTO doUnifiedRefundV2(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
+        // 1. 构建 WxPayRefundRequest 请求
+        WxPayRefundRequest request = new WxPayRefundRequest()
+                .setOutTradeNo(reqDTO.getOutTradeNo())
+                .setOutRefundNo(reqDTO.getOutRefundNo())
+                .setRefundFee(reqDTO.getRefundPrice())
+                .setRefundDesc(reqDTO.getReason())
+                .setTotalFee(reqDTO.getPayPrice())
+                .setNotifyUrl(reqDTO.getNotifyUrl());
+        // 2.1 执行请求
+        WxPayRefundResult response = client.refundV2(request);
+        // 2.2 创建返回结果
+        if (Objects.equals("SUCCESS", response.getResultCode())) { // V2 情况下,不直接返回退款成功,而是等待异步通知
+            return PayRefundRespDTO.waitingOf(response.getRefundId(),
+                    reqDTO.getOutRefundNo(), response);
+        }
+        return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
+    }
+
+    private PayRefundRespDTO doUnifiedRefundV3(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
+        // 1. 构建 WxPayRefundRequest 请求
+        WxPayRefundV3Request request = new WxPayRefundV3Request()
+                .setOutTradeNo(reqDTO.getOutTradeNo())
+                .setOutRefundNo(reqDTO.getOutRefundNo())
+                .setAmount(new WxPayRefundV3Request.Amount().setRefund(reqDTO.getRefundPrice())
+                        .setTotal(reqDTO.getPayPrice()).setCurrency("CNY"))
+                .setReason(reqDTO.getReason())
+                .setNotifyUrl(reqDTO.getNotifyUrl());
+        // 2.1 执行请求
+        WxPayRefundV3Result response = client.refundV3(request);
+        // 2.2 创建返回结果
+        if (Objects.equals("SUCCESS", response.getStatus())) {
+            return PayRefundRespDTO.successOf(response.getRefundId(), parseDateV3(response.getSuccessTime()),
+                    reqDTO.getOutRefundNo(), response);
+        }
+        if (Objects.equals("PROCESSING", response.getStatus())) {
+            return PayRefundRespDTO.waitingOf(response.getRefundId(),
+                    reqDTO.getOutRefundNo(), response);
+        }
+        return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
+    }
+
+    @Override
+    public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) throws WxPayException {
+        switch (config.getApiVersion()) {
+            case API_VERSION_V2:
+                return doParseRefundNotifyV2(body);
+            case WxPayClientConfig.API_VERSION_V3:
+                return parseRefundNotifyV3(body);
+            default:
+                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+        }
+    }
+
+    private PayRefundRespDTO doParseRefundNotifyV2(String body) throws WxPayException {
+        // 1. 解析回调
+        WxPayRefundNotifyResult response = client.parseRefundNotifyResult(body);
+        WxPayRefundNotifyResult.ReqInfo result = response.getReqInfo();
+        // 2. 构建结果
+        if (Objects.equals("SUCCESS", result.getRefundStatus())) {
+            return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV2B(result.getSuccessTime()),
+                    result.getOutRefundNo(), response);
+        }
+        return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
+    }
+
+    private PayRefundRespDTO parseRefundNotifyV3(String body) throws WxPayException {
+        // 1. 解析回调
+        WxPayRefundNotifyV3Result response = client.parseRefundNotifyV3Result(body, null);
+        WxPayRefundNotifyV3Result.DecryptNotifyResult result = response.getResult();
+        // 2. 构建结果
+        if (Objects.equals("SUCCESS", result.getRefundStatus())) {
+            return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV3(result.getSuccessTime()),
+                    result.getOutRefundNo(), response);
+        }
+        return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
+    }
+
+    @Override
+    protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws WxPayException {
+        try {
+            switch (config.getApiVersion()) {
+                case API_VERSION_V2:
+                    return doGetRefundV2(outTradeNo, outRefundNo);
+                case WxPayClientConfig.API_VERSION_V3:
+                    return doGetRefundV3(outTradeNo, outRefundNo);
+                default:
+                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
+            }
+        } catch (WxPayException e) {
+            if (ObjectUtils.equalsAny(e.getErrCode(), "REFUNDNOTEXIST", "RESOURCE_NOT_EXISTS")) {
+                String errorCode = getErrorCode(e);
+                String errorMessage = getErrorMessage(e);
+                return PayRefundRespDTO.failureOf(errorCode, errorMessage,
+                        outRefundNo, e.getXmlString());
+            }
+            throw e;
+        }
+    }
+
+    private PayRefundRespDTO doGetRefundV2(String outTradeNo, String outRefundNo) throws WxPayException {
+        // 1. 构建 WxPayRefundRequest 请求
+        WxPayRefundQueryRequest request = WxPayRefundQueryRequest.newBuilder()
+                .outTradeNo(outTradeNo)
+                .outRefundNo(outRefundNo)
+                .build();
+        // 2.1 执行请求
+        WxPayRefundQueryResult response = client.refundQuery(request);
+        // 2.2 创建返回结果
+        if (!Objects.equals("SUCCESS", response.getResultCode())) {
+            return PayRefundRespDTO.waitingOf(null,
+                    outRefundNo, response);
+        }
+        WxPayRefundQueryResult.RefundRecord refund = CollUtil.findOne(response.getRefundRecords(),
+                record -> record.getOutRefundNo().equals(outRefundNo));
+        if (refund == null) {
+            return PayRefundRespDTO.failureOf(outRefundNo, response);
+        }
+        switch (refund.getRefundStatus()) {
+            case "SUCCESS":
+                return PayRefundRespDTO.successOf(refund.getRefundId(), parseDateV2B(refund.getRefundSuccessTime()),
+                        outRefundNo, response);
+            case "PROCESSING":
+                return PayRefundRespDTO.waitingOf(refund.getRefundId(),
+                        outRefundNo, response);
+            case "CHANGE": // 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转账的方式进行退款
+            case "FAIL":
+                return PayRefundRespDTO.failureOf(outRefundNo, response);
+            default:
+                throw new IllegalArgumentException(String.format("未知的退款状态(%s)", refund.getRefundStatus()));
+        }
+    }
+
+    private PayRefundRespDTO doGetRefundV3(String outTradeNo, String outRefundNo) throws WxPayException {
+        // 1. 构建 WxPayRefundRequest 请求
+        WxPayRefundQueryV3Request request = new WxPayRefundQueryV3Request();
+        request.setOutRefundNo(outRefundNo);
+        // 2.1 执行请求
+        WxPayRefundQueryV3Result response = client.refundQueryV3(request);
+        // 2.2 创建返回结果
+        switch (response.getStatus()) {
+            case "SUCCESS":
+                return PayRefundRespDTO.successOf(response.getRefundId(), parseDateV3(response.getSuccessTime()),
+                        outRefundNo, response);
+            case "PROCESSING":
+                return PayRefundRespDTO.waitingOf(response.getRefundId(),
+                        outRefundNo, response);
+            case "ABNORMAL": // 退款异常
+            case "CLOSED":
+                return PayRefundRespDTO.failureOf(outRefundNo, response);
+            default:
+                throw new IllegalArgumentException(String.format("未知的退款状态(%s)", response.getStatus()));
+        }
+    }
+
+    // ========== 各种工具方法 ==========
+
+    static String formatDateV2(LocalDateTime time) {
+        return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
+    }
+
+    static LocalDateTime parseDateV2(String time) {
+        return LocalDateTimeUtil.parse(time, PURE_DATETIME_PATTERN);
+    }
+
+    static LocalDateTime parseDateV2B(String time) {
+        return LocalDateTimeUtil.parse(time, NORM_DATETIME_PATTERN);
+    }
+
+    static String formatDateV3(LocalDateTime time) {
+        return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), UTC_WITH_XXX_OFFSET_PATTERN);
+    }
+
+    static LocalDateTime parseDateV3(String time) {
+        return LocalDateTimeUtil.parse(time, UTC_WITH_XXX_OFFSET_PATTERN);
+    }
+
+    static String getErrorCode(WxPayException e) {
+        if (StrUtil.isNotEmpty(e.getErrCode())) {
+            return e.getErrCode();
+        }
+        if (StrUtil.isNotEmpty(e.getCustomErrorMsg())) {
+            return "CUSTOM_ERROR";
+        }
+        return e.getReturnCode();
+    }
+
+    static String getErrorMessage(WxPayException e) {
+        if (StrUtil.isNotEmpty(e.getErrCode())) {
+            return e.getErrCodeDes();
+        }
+        if (StrUtil.isNotEmpty(e.getCustomErrorMsg())) {
+            return e.getCustomErrorMsg();
+        }
+        return e.getReturnMsg();
+    }
+
+}

+ 63 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java

@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import lombok.extern.slf4j.Slf4j;
+
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+
+/**
+ * 微信支付【App 支付】的 PayClient 实现类
+ *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_5_3.shtml">App 支付</a>
+ *
+ * // TODO 芋艿:未详细测试,因为手头没 App
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class WxAppPayClient extends AbstractWxPayClient {
+
+    public WxAppPayClient(Long channelId, WxPayClientConfig config) {
+        super(channelId, PayChannelEnum.WX_APP.getCode(), config);
+    }
+
+    @Override
+    protected void doInit() {
+        super.doInit(WxPayConstants.TradeType.APP);
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO);
+        // 执行请求
+        WxPayMpOrderResult response = client.createOrder(request);
+
+        // 转换结果
+        return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
+                reqDTO.getOutTradeNo(), response);
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderV3Request 对象
+        WxPayUnifiedOrderV3Request request = buildPayUnifiedOrderRequestV3(reqDTO);
+        // 执行请求
+        WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.APP, request);
+
+        // 转换结果
+        return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
+                reqDTO.getOutTradeNo(), response);
+    }
+
+}

+ 107 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java

@@ -0,0 +1,107 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
+import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.concurrent.TimeUnit;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+
+/**
+ * 微信支付【付款码支付】的 PayClient 实现类
+ *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1">付款码支付</a>
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class WxBarPayClient extends AbstractWxPayClient {
+
+    /**
+     * 微信付款码的过期时间
+     */
+    private static final Duration AUTH_CODE_EXPIRE = Duration.ofMinutes(3);
+
+    public WxBarPayClient(Long channelId, WxPayClientConfig config) {
+        super(channelId, PayChannelEnum.WX_BAR.getCode(), config);
+    }
+
+    @Override
+    protected void doInit() {
+        super.doInit(WxPayConstants.TradeType.MICROPAY);
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 由于付款码需要不断轮询,所以需要在较短的时间完成支付
+        LocalDateTime expireTime = LocalDateTimeUtils.addTime(AUTH_CODE_EXPIRE);
+        if (expireTime.isAfter(reqDTO.getExpireTime())) {
+            expireTime = reqDTO.getExpireTime();
+        }
+        // 构建 WxPayMicropayRequest 对象
+        WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
+                .outTradeNo(reqDTO.getOutTradeNo())
+                .body(reqDTO.getSubject())
+                .detail(reqDTO.getBody())
+                .totalFee(reqDTO.getPrice()) // 单位分
+                .timeExpire(formatDateV2(expireTime))
+                .spbillCreateIp(reqDTO.getUserIp())
+                .authCode(getAuthCode(reqDTO))
+                .build();
+        // 执行请求,重试直到失败(过期),或者成功
+        WxPayException lastWxPayException = null;
+        for (int i = 1; i < Byte.MAX_VALUE; i++) {
+            try {
+                WxPayMicropayResult response = client.micropay(request);
+                // 支付成功,例如说:1)用户输入了密码;2)用户免密支付
+                return PayOrderRespDTO.successOf(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
+                        response.getOutTradeNo(), response)
+                        .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode());
+            } catch (WxPayException ex) {
+                lastWxPayException = ex;
+                // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
+                // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
+                // 2. USERPAYING:用户支付中,需要输入密码:等待 5 秒,然后调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。
+                // 3. BANKERROR:银行系统异常:请立即调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。
+                if (!StrUtil.equalsAny(ex.getErrCode(), "SYSTEMERROR", "USERPAYING", "BANKERROR")) {
+                    throw ex;
+                }
+                // 等待 5 秒,继续下一轮重新发起支付
+                log.info("[doUnifiedOrderV2][发起微信 Bar 支付第({})失败,等待下一轮重试,请求({}),响应({})]", i,
+                        toJsonString(request), ex.getMessage());
+                ThreadUtil.sleep(5, TimeUnit.SECONDS);
+            }
+        }
+        throw lastWxPayException;
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        return doUnifiedOrderV2(reqDTO);
+    }
+
+    // ========== 各种工具方法 ==========
+
+    static String getAuthCode(PayOrderUnifiedReqDTO reqDTO) {
+        String authCode = MapUtil.getStr(reqDTO.getChannelExtras(), "authCode");
+        if (StrUtil.isEmpty(authCode)) {
+            throw invalidParamException("支付请求的 authCode 不能为空!");
+        }
+        return authCode;
+    }
+
+}

+ 22 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxLitePayClient.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 微信支付【小程序】的 PayClient 实现类
+ *
+ * 由于公众号和小程序的微信支付逻辑一致,所以直接进行继承
+ *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml">JSAPI 下单</>
+ *
+ * @author zwy
+ */
+@Slf4j
+public class WxLitePayClient extends WxPubPayClient {
+
+    public WxLitePayClient(Long channelId, WxPayClientConfig config) {
+        super(channelId, PayChannelEnum.WX_LITE.getCode(), config);
+    }
+
+}

+ 58 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java

@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 微信支付【Native 二维码】的 PayClient 实现类
+ *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml">Native 下单</a>
+ *
+ * @author zwy
+ */
+@Slf4j
+public class WxNativePayClient extends AbstractWxPayClient {
+
+    public WxNativePayClient(Long channelId, WxPayClientConfig config) {
+        super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config);
+    }
+
+    @Override
+    protected void doInit() {
+        super.doInit(WxPayConstants.TradeType.NATIVE);
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO);
+        // 执行请求
+        WxPayNativeOrderResult response = client.createOrder(request);
+
+        // 转换结果
+        return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl(),
+                reqDTO.getOutTradeNo(), response);
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderV3Request 对象
+        WxPayUnifiedOrderV3Request request = buildPayUnifiedOrderRequestV3(reqDTO);
+        // 执行请求
+        String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
+
+        // 转换结果
+        return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.QR_CODE.getMode(), response,
+                reqDTO.getOutTradeNo(), response);
+    }
+
+}

+ 22 - 23
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java → yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java

@@ -1,15 +1,14 @@
-package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 import cn.hutool.core.io.IoUtil;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
 import lombok.Data;
 
-import javax.validation.ConstraintViolation;
 import javax.validation.Validator;
 import javax.validation.constraints.NotBlank;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.util.Set;
 
 /**
  * 微信支付的 PayClientConfig 实现类
@@ -18,33 +17,37 @@ import java.util.Set;
  * @author 芋道源码
  */
 @Data
-public class WXPayClientConfig implements PayClientConfig {
+public class WxPayClientConfig implements PayClientConfig {
 
     /**
      * API 版本 - V2
-     * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1
+     *
+     * <a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1">V2 协议说明</a>
      */
     public static final String API_VERSION_V2 = "v2";
     /**
      * API 版本 - V3
-     * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml
+     *
+     * <a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml">V3 协议说明</a>
      */
     public static final String API_VERSION_V3 = "v3";
 
     /**
      * 公众号或者小程序的 appid
+     *
+     * 只有公众号或小程序需要该字段
      */
     @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class})
     private String appId;
     /**
      * 商户号
      */
-    @NotBlank(message = "商户号 不能为空", groups = {V2.class, V3.class})
+    @NotBlank(message = "商户号不能为空", groups = {V2.class, V3.class})
     private String mchId;
     /**
      * API 版本
      */
-    @NotBlank(message = "API 版本 不能为空", groups = {V2.class, V3.class})
+    @NotBlank(message = "API 版本不能为空", groups = {V2.class, V3.class})
     private String apiVersion;
 
     // ========== V2 版本的参数 ==========
@@ -52,36 +55,31 @@ public class WXPayClientConfig implements PayClientConfig {
     /**
      * 商户密钥
      */
-    @NotBlank(message = "商户密钥 不能为空", groups = V2.class)
+    @NotBlank(message = "商户密钥不能为空", groups = V2.class)
     private String mchKey;
     /**
-     * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径.
-     * 对应的字符串
+     * apiclient_cert.p12 证书文件的对应字符串【base64 格式】
      *
-     * 注意,可通过 {@link #main(String[])} 读取
+     * 为什么采用 base64 格式?因为 p12 读取后是二进制,需要转换成 base64 格式才好传输和存储
      */
-    /// private String keyContent;
+    @NotBlank(message = "apiclient_cert.p12 不能为空", groups = V2.class)
+    private String keyContent;
 
     // ========== V3 版本的参数 ==========
     /**
-     * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
-     * 对应的字符串
-     * 注意,可通过 {@link #main(String[])} 读取
+     * apiclient_key.pem 证书文件的对应字符串
      */
     @NotBlank(message = "apiclient_key 不能为空", groups = V3.class)
     private String privateKeyContent;
     /**
-     * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
-     * 对应的字符串
-     * <p>
-     * 注意,可通过 {@link #main(String[])} 读取
+     * apiclient_cert.pem 证书文件的对应的字符串
      */
     @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class)
     private String privateCertContent;
     /**
      * apiV3 密钥值
      */
-    @NotBlank(message = "apiV3 密钥值 不能为空", groups = V3.class)
+    @NotBlank(message = "apiV3 密钥值不能为空", groups = V3.class)
     private String apiV3Key;
 
     /**
@@ -97,8 +95,9 @@ public class WXPayClientConfig implements PayClientConfig {
     }
 
     @Override
-    public Set<ConstraintViolation<PayClientConfig>> verifyParam(Validator validator) {
-        return validator.validate(this, this.getApiVersion().equals(API_VERSION_V2) ? V2.class : V3.class);
+    public void validate(Validator validator) {
+        ValidationUtils.validate(validator, this,
+                API_VERSION_V2.equals(this.getApiVersion()) ? V2.class : V3.class);
     }
 
     public static void main(String[] args) throws FileNotFoundException {

+ 80 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java

@@ -0,0 +1,80 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import lombok.extern.slf4j.Slf4j;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+
+/**
+ * 微信支付(公众号)的 PayClient 实现类
+ *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml">JSAPI 下单</>
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class WxPubPayClient extends AbstractWxPayClient {
+
+    public WxPubPayClient(Long channelId, WxPayClientConfig config) {
+        super(channelId, PayChannelEnum.WX_PUB.getCode(), config);
+    }
+
+    protected WxPubPayClient(Long channelId, String channelCode, WxPayClientConfig config) {
+        super(channelId, channelCode, config);
+    }
+
+    @Override
+    protected void doInit() {
+        super.doInit(WxPayConstants.TradeType.JSAPI);
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO)
+                .setOpenid(getOpenid(reqDTO));
+        // 执行请求
+        WxPayMpOrderResult response = client.createOrder(request);
+
+        // 转换结果
+        return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
+                reqDTO.getOutTradeNo(), response);
+    }
+
+    @Override
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 构建 WxPayUnifiedOrderRequest 对象
+        WxPayUnifiedOrderV3Request request = buildPayUnifiedOrderRequestV3(reqDTO)
+                .setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
+        // 执行请求
+        WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request);
+
+        // 转换结果
+        return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
+                reqDTO.getOutTradeNo(), response);
+    }
+
+    // ========== 各种工具方法 ==========
+
+    static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
+        String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
+        if (StrUtil.isEmpty(openid)) {
+            throw invalidParamException("支付请求的 openid 不能为空!");
+        }
+        return openid;
+    }
+
+}

+ 0 - 198
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java

@@ -1,198 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
-
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.date.LocalDateTimeUtil;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.util.io.FileUtils;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
-import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
-import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
-import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
-import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
-import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
-import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
-import com.github.binarywang.wxpay.config.WxPayConfig;
-import com.github.binarywang.wxpay.constant.WxPayConstants;
-import com.github.binarywang.wxpay.exception.WxPayException;
-import com.github.binarywang.wxpay.service.WxPayService;
-import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
-import lombok.extern.slf4j.Slf4j;
-
-import java.time.ZoneId;
-import java.util.Date;
-import java.util.Objects;
-
-/**
- * 微信小程序下支付
- *
- * @author zwy
- */
-@Slf4j
-public class WXLitePayClient extends AbstractPayClient<WXPayClientConfig> {
-
-    private WxPayService client;
-
-    public WXLitePayClient(Long channelId, WXPayClientConfig config) {
-        super(channelId, PayChannelEnum.WX_LITE.getCode(), config);
-    }
-
-    @Override
-    protected void doInit() {
-        WxPayConfig payConfig = new WxPayConfig();
-        BeanUtil.copyProperties(config, payConfig, "privateKeyContent","privateCertContent");
-        payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式
-//        if (StrUtil.isNotEmpty(config.getKeyContent())) {
-//            payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));
-//        }
-        if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
-            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
-            payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
-        }
-        if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
-            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
-            payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
-        }
-        // 真实客户端
-        this.client = new WxPayServiceImpl();
-        client.setConfig(payConfig);
-    }
-
-    @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException();
-//        WxPayMpOrderResult response;
-//        try {
-//            switch (config.getApiVersion()) {
-//                case WXPayClientConfig.API_VERSION_V2:
-//                    response = this.unifiedOrderV2(reqDTO);
-//                    break;
-//                case WXPayClientConfig.API_VERSION_V3:
-//                    WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO);
-//                    // 将 V3 的结果,统一转换成 V2。返回的字段是一致的
-//                    response = new WxPayMpOrderResult();
-//                    BeanUtil.copyProperties(responseV3, response, true);
-//                    break;
-//                default:
-//                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
-//            }
-//        } catch (WxPayException e) {
-//            log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);
-//            return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),
-//                    ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping);
-//        }
-//        return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping);
-    }
-
-    private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
-        // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
-                .outTradeNo(reqDTO.getMerchantOrderId())
-                .body(reqDTO.getBody())
-                .totalFee(reqDTO.getAmount().intValue()) // 单位分
-                .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式
-                .spbillCreateIp(reqDTO.getUserIp())
-                .openid(getOpenid(reqDTO))
-                .notifyUrl(reqDTO.getNotifyUrl())
-                .build();
-        // 执行请求
-        return client.createOrder(request);
-    }
-
-    private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
-        // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
-        request.setOutTradeNo(reqDTO.getMerchantOrderId());
-
-        request.setDescription(reqDTO.getBody());
-        request.setAmount(new WxPayUnifiedOrderV3Request
-                .Amount()
-                .setTotal(reqDTO
-                        .getAmount()
-                        .intValue())); // 单位分
-        request.setTimeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式
-        request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
-        request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
-        request.setNotifyUrl(reqDTO.getNotifyUrl());
-        // 执行请求
-        return client.createOrderV3(TradeTypeEnum.JSAPI, request);
-    }
-
-    private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
-        String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
-        if (StrUtil.isEmpty(openid)) {
-            throw new IllegalArgumentException("支付请求的 openid 不能为空!");
-        }
-        return openid;
-    }
-
-    /**
-     *
-     * 微信支付回调 分 v2 和v3 的处理方式
-     *
-     * @param data 通知结果
-     * @return 支付回调对象
-     * @throws WxPayException 微信异常类
-     */
-//    @Override
-    public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException {
-        log.info("[parseOrderNotify][微信支付回调data数据:{}]", data.getBody());
-        // 微信支付 v2 回调结果处理
-        switch (config.getApiVersion()) {
-            case WXPayClientConfig.API_VERSION_V2:
-                return parseOrderNotifyV2(data);
-            case WXPayClientConfig.API_VERSION_V3:
-                return parseOrderNotifyV3(data);
-            default:
-                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
-        }
-    }
-
-    private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {
-        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
-        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
-        // 转换结果
-        Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
-                "支付结果非 SUCCESS");
-
-        return PayOrderNotifyRespDTO
-                .builder()
-                .orderExtensionNo(result.getOutTradeNo())
-                .channelOrderNo(result.getTransactionId())
-                .channelUserId(result.getPayer().getOpenid())
-                .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
-                .build();
-    }
-
-    private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {
-        WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
-        Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
-        // 转换结果
-        return PayOrderNotifyRespDTO
-                .builder()
-                .orderExtensionNo(notifyResult.getOutTradeNo())
-                .channelOrderNo(notifyResult.getTransactionId())
-                .channelUserId(notifyResult.getOpenid())
-                .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
-                .build();
-
-    }
-
-    @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO)  {
-        //TODO 需要实现
-        throw new UnsupportedOperationException();
-    }
-
-}

+ 0 - 181
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java

@@ -1,181 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
-
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.date.LocalDateTimeUtil;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.util.io.FileUtils;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
-import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
-import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
-import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
-import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
-import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
-import com.github.binarywang.wxpay.config.WxPayConfig;
-import com.github.binarywang.wxpay.constant.WxPayConstants;
-import com.github.binarywang.wxpay.exception.WxPayException;
-import com.github.binarywang.wxpay.service.WxPayService;
-import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
-import lombok.extern.slf4j.Slf4j;
-
-import java.time.ZoneId;
-import java.util.Date;
-import java.util.Objects;
-
-/**
- * 微信 App 支付
- *
- * @author zwy
- */
-@Slf4j
-public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
-
-    private WxPayService client;
-
-    public WXNativePayClient(Long channelId, WXPayClientConfig config) {
-        super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config);
-    }
-
-    @Override
-    protected void doInit() {
-        WxPayConfig payConfig = new WxPayConfig();
-        BeanUtil.copyProperties(config, payConfig, "keyContent");
-        payConfig.setTradeType(WxPayConstants.TradeType.NATIVE); // 设置使用 native 支付方式
-//        if (StrUtil.isNotEmpty(config.getKeyContent())) {
-//            payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));
-//        }
-        if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
-            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
-            payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
-        }
-        if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
-            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
-            payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
-        }
-        // 真实客户端
-        this.client = new WxPayServiceImpl();
-        client.setConfig(payConfig);
-    }
-
-    @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException();
-//        // 这里原生的返回的是支付的 url 所以直接使用string接收
-//        // "invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz"
-//        String responseV3;
-//        try {
-//            switch (config.getApiVersion()) {
-//                case WXPayClientConfig.API_VERSION_V2:
-//                    responseV3 = unifiedOrderV2(reqDTO).getCodeUrl();
-//                    break;
-//                case WXPayClientConfig.API_VERSION_V3:
-//                  responseV3 = this.unifiedOrderV3(reqDTO);
-//                    break;
-//                default:
-//                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
-//            }
-//        } catch (WxPayException e) {
-//            log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);
-//            return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),
-//                    ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping);
-//        }
-//        return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, responseV3, codeMapping);
-    }
-
-    private WxPayNativeOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
-        //前端
-        String tradeType = reqDTO.getChannelExtras().get("trade_type");
-        // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest
-                .newBuilder()
-                .outTradeNo(reqDTO.getMerchantOrderId())
-                .body(reqDTO.getBody())
-                .totalFee(reqDTO.getAmount().intValue()) // 单位分
-                .timeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX"))
-                .spbillCreateIp(reqDTO.getUserIp())
-                .notifyUrl(reqDTO.getNotifyUrl())
-                .productId(tradeType)
-                .build();
-        // 执行请求
-        return client.createOrder(request);
-    }
-
-    private String unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
-        // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
-        request.setOutTradeNo(reqDTO.getMerchantOrderId());
-        request.setDescription(reqDTO.getBody());
-        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分
-        request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
-        request.setNotifyUrl(reqDTO.getNotifyUrl());
-        // 执行请求
-        return client.createOrderV3(TradeTypeEnum.NATIVE, request);
-    }
-
-    /**
-     *
-     * 微信支付回调 分v2 和v3 的处理方式
-     *
-     * @param data 通知结果
-     * @return 支付回调对象
-     * @throws WxPayException 微信异常类
-     */
-//    @Override
-    public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException {
-        log.info("微信支付回调data数据:{}", data.getBody());
-        // 微信支付 v2 回调结果处理
-        switch (config.getApiVersion()) {
-            case WXPayClientConfig.API_VERSION_V2:
-                return parseOrderNotifyV2(data);
-            case WXPayClientConfig.API_VERSION_V3:
-                return parseOrderNotifyV3(data);
-            default:
-                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
-        }
-    }
-
-    private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {
-        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
-        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
-        // 转换结果
-        Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
-                "支付结果非 SUCCESS");
-        return PayOrderNotifyRespDTO
-                .builder()
-                .orderExtensionNo(result.getOutTradeNo())
-                .channelOrderNo(result.getTradeState())
-                .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
-                .build();
-    }
-
-    private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {
-        WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
-        Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
-        // 转换结果
-        return PayOrderNotifyRespDTO
-                .builder()
-                .orderExtensionNo(notifyResult.getOutTradeNo())
-                .channelOrderNo(notifyResult.getTransactionId())
-                .channelUserId(notifyResult.getOpenid())
-                .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
-                .build();
-
-    }
-
-    @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO)  {
-        // TODO 需要实现
-        throw new UnsupportedOperationException();
-    }
-
-}

+ 0 - 196
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java

@@ -1,196 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
-
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.date.LocalDateTimeUtil;
-import cn.hutool.core.date.TemporalAccessorUtil;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.util.io.FileUtils;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
-import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
-import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
-import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
-import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
-import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
-import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
-import com.github.binarywang.wxpay.config.WxPayConfig;
-import com.github.binarywang.wxpay.constant.WxPayConstants;
-import com.github.binarywang.wxpay.exception.WxPayException;
-import com.github.binarywang.wxpay.service.WxPayService;
-import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
-import lombok.extern.slf4j.Slf4j;
-
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.Objects;
-
-/**
- * 微信支付(公众号)的 PayClient 实现类
- *
- * @author 芋道源码
- */
-@Slf4j
-public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
-
-    private WxPayService client;
-
-    public WXPubPayClient(Long channelId, WXPayClientConfig config) {
-        super(channelId, PayChannelEnum.WX_PUB.getCode(), config);
-    }
-
-    @Override
-    protected void doInit() {
-        WxPayConfig payConfig = new WxPayConfig();
-        BeanUtil.copyProperties(config, payConfig, "keyContent");
-        payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式
-//        if (StrUtil.isNotEmpty(config.getKeyContent())) {
-//            payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));
-//        }
-        if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
-            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
-            payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
-        }
-        if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
-            // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
-            payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
-        }
-        // 真实客户端
-        this.client = new WxPayServiceImpl();
-        client.setConfig(payConfig);
-    }
-
-    @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException();
-//
-//        WxPayMpOrderResult response;
-//        try {
-//            switch (config.getApiVersion()) {
-//                case WXPayClientConfig.API_VERSION_V2:
-//                    response = this.unifiedOrderV2(reqDTO);
-//                    break;
-//                case WXPayClientConfig.API_VERSION_V3:
-//                    WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO);
-//                    // 将 V3 的结果,统一转换成 V2。返回的字段是一致的
-//                    response = new WxPayMpOrderResult();
-//                    BeanUtil.copyProperties(responseV3, response, true);
-//                    break;
-//                default:
-//                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
-//            }
-//        } catch (WxPayException e) {
-//            log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);
-//            return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),
-//                    ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()),null, codeMapping);
-//        }
-//        return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping);
-    }
-
-
-    private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
-        // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
-                .outTradeNo(reqDTO.getMerchantOrderId())
-                .body(reqDTO.getBody())
-                .totalFee(reqDTO.getAmount()) // 单位分
-                .timeExpire(formatDate(reqDTO.getExpireTime()))
-                .spbillCreateIp(reqDTO.getUserIp())
-                .openid(getOpenid(reqDTO))
-                .notifyUrl(reqDTO.getNotifyUrl())
-                .build();
-        // 执行请求
-        return client.createOrder(request);
-    }
-
-    private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
-        // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
-        request.setOutTradeNo(reqDTO.getMerchantOrderId());
-        request.setDescription(reqDTO.getBody());
-        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
-        request.setTimeExpire(formatDate(reqDTO.getExpireTime()));
-        request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
-        request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
-        request.setNotifyUrl(reqDTO.getNotifyUrl());
-        // 执行请求
-        return client.createOrderV3(TradeTypeEnum.JSAPI, request);
-    }
-
-    private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
-        String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
-        if (StrUtil.isEmpty(openid)) {
-            throw new IllegalArgumentException("支付请求的 openid 不能为空!");
-        }
-        return openid;
-    }
-
-    /**
-     *
-     * 微信支付回调 分v2 和v3 的处理方式
-     *
-     * @param data 通知结果
-     * @return 支付回调对象
-     * @throws WxPayException 微信异常类
-     */
-//    @Override
-    public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException {
-        log.info("[parseOrderNotify][微信支付回调data数据: {}]", data.getBody());
-        // 微信支付 v2 回调结果处理
-        switch (config.getApiVersion()) {
-            case WXPayClientConfig.API_VERSION_V2:
-                return parseOrderNotifyV2(data);
-            case WXPayClientConfig.API_VERSION_V3:
-                return parseOrderNotifyV3(data);
-            default:
-                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
-        }
-    }
-
-    private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {
-        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
-        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
-        // 转换结果
-        Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
-                "支付结果非 SUCCESS");
-        return PayOrderNotifyRespDTO
-                .builder()
-                .orderExtensionNo(result.getOutTradeNo())
-                .channelOrderNo(result.getTradeState())
-                .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
-                .build();
-    }
-
-    private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {
-        WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
-        Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
-        // 转换结果
-        return PayOrderNotifyRespDTO
-                .builder()
-                .orderExtensionNo(notifyResult.getOutTradeNo())
-                .channelOrderNo(notifyResult.getTransactionId())
-                .channelUserId(notifyResult.getOpenid())
-                .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
-                .build();
-
-    }
-
-    @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
-        // TODO 需要实现
-        throw new UnsupportedOperationException();
-    }
-
-    private static String formatDate(LocalDateTime time) {
-        return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyy-MM-dd'T'HH:mm:ssXXX");
-    }
-
-}

+ 0 - 23
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelRefundRespEnum.java

@@ -1,23 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 渠道统一的退款返回结果
- *
- * @author  jason
- */
-@Getter
-@AllArgsConstructor
-public enum PayChannelRefundRespEnum {
-
-    SUCCESS(1, "退款成功"),
-    FAILURE(2, "退款失败"),
-    PROCESSING(3,"退款处理中"),
-    CLOSED(4, "退款关闭");
-
-    private final Integer status;
-    private final String name;
-
-}

+ 0 - 29
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java

@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.enums;
-
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-
-/**
- * 支付框架的错误码枚举
- *
- * 支付框架,使用 2-002-000-000 段
- *
- * @author 芋道源码
- */
-public interface PayFrameworkErrorCodeConstants {
-
-    ErrorCode PAY_UNKNOWN = new ErrorCode(2002000000, "未知错误,需要解析");
-
-    // ========== 配置相关相关 2002000100 ==========
-    // todo 芋艿:如下的错误码,怎么处理掉
-    ErrorCode PAY_CONFIG_APP_ID_ERROR = new ErrorCode(2002000100, "支付渠道 AppId 不正确");
-    ErrorCode PAY_CONFIG_SIGN_ERROR = new ErrorCode(2002000100, "签名错误"); // 例如说,微信支付,配置错了 mchId 或者 mchKey
-
-
-    // ========== 其它相关 2002000900 开头 ==========
-    // todo 芋艿:如下的错误码,怎么处理掉
-    ErrorCode PAY_OPENID_ERROR = new ErrorCode(2002000900, "无效的 openid"); // 例如说,微信 openid 未授权过
-    ErrorCode PAY_PARAM_MISSING = new ErrorCode(2002000901, "请求参数缺失"); // 例如说,支付少传了金额
-
-    ErrorCode PAY_EXCEPTION = new ErrorCode(2002000999, "调用异常");
-
-}

+ 0 - 22
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayNotifyRefundStatusEnum.java

@@ -1,22 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.enums;
-
-/**
- * 退款通知, 统一的渠道退款状态
- *
- * @author  jason
- */
-public enum PayNotifyRefundStatusEnum {
-
-    /**
-     * 支付宝 中 全额退款 trade_status=TRADE_CLOSED, 部分退款 trade_status=TRADE_SUCCESS
-     * 退款成功
-     */
-    SUCCESS,
-
-    /**
-     * 支付宝退款通知没有这个状态
-     * 退款异常
-     */
-    ABNORMAL;
-
-}

+ 9 - 9
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java → yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/channel/PayChannelEnum.java

@@ -1,15 +1,14 @@
-package cn.iocoder.yudao.framework.pay.core.enums;
+package cn.iocoder.yudao.framework.pay.core.enums.channel;
 
 import cn.hutool.core.util.ArrayUtil;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
-import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
+import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
 /**
  * 支付渠道的编码的枚举
- * 枚举值
  *
  * @author 芋道源码
  */
@@ -17,10 +16,11 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum PayChannelEnum {
 
-    WX_PUB("wx_pub", "微信 JSAPI 支付", WXPayClientConfig.class), // 公众号网页
-    WX_LITE("wx_lite", "微信小程序支付", WXPayClientConfig.class),
-    WX_APP("wx_app", "微信 App 支付", WXPayClientConfig.class),
-    WX_NATIVE("wx_native", "微信 native 支付", WXPayClientConfig.class),
+    WX_PUB("wx_pub", "微信 JSAPI 支付", WxPayClientConfig.class), // 公众号网页
+    WX_LITE("wx_lite", "微信小程序支付", WxPayClientConfig.class),
+    WX_APP("wx_app", "微信 App 支付", WxPayClientConfig.class),
+    WX_NATIVE("wx_native", "微信 Native 支付", WxPayClientConfig.class),
+    WX_BAR("wx_bar", "微信付款码支付", WxPayClientConfig.class),
 
     ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付", AlipayPayClientConfig.class),
     ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付", AlipayPayClientConfig.class),
@@ -30,8 +30,8 @@ public enum PayChannelEnum {
 
     /**
      * 编码
-     * <p>
-     * 参考 https://www.pingxx.com/api/支付渠道属性值.html
+     *
+     * 参考 <a href="https://www.pingxx.com/api/支付渠道属性值.html">支付渠道属性值</a>
      */
     private final String code;
     /**

+ 4 - 4
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayDisplayModeEnum.java → yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderDisplayModeEnum.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.pay.core.enums;
+package cn.iocoder.yudao.framework.pay.core.enums.order;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -10,15 +10,15 @@ import lombok.Getter;
  */
 @Getter
 @AllArgsConstructor
-public enum PayDisplayModeEnum {
+public enum PayOrderDisplayModeEnum {
 
     URL("url"), // Redirect 跳转链接的方式
-    IFRAME("iframe"), // IFrame 内嵌链接的方式
+    IFRAME("iframe"), // IFrame 内嵌链接的方式【目前暂时用不到】
     FORM("form"), // HTML 表单提交
     QR_CODE("qr_code"), // 二维码的文字内容
     QR_CODE_URL("qr_code_url"), // 二维码的图片链接
     BAR_CODE("bar_code"), // 条形码
-    APP("app"), // 应用
+    APP("app"), // 应用:Android、iOS、微信小程序、微信公众号等,需要做自定义处理的
     ;
 
     /**

+ 56 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java

@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.framework.pay.core.enums.order;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * 渠道的支付状态枚举
+ *
+ * @author 芋道源码
+ */
+@Getter
+@AllArgsConstructor
+public enum PayOrderStatusRespEnum {
+
+    WAITING(0, "未支付"),
+    SUCCESS(10, "支付成功"),
+    REFUND(20, "已退款"),
+    CLOSED(30, "支付关闭"),
+    ;
+
+    private final Integer status;
+    private final String name;
+
+    /**
+     * 判断是否支付成功
+     *
+     * @param status 状态
+     * @return 是否支付成功
+     */
+    public static boolean isSuccess(Integer status) {
+        return Objects.equals(status, SUCCESS.getStatus());
+    }
+
+    /**
+     * 判断是否已退款
+     *
+     * @param status 状态
+     * @return 是否支付成功
+     */
+    public static boolean isRefund(Integer status) {
+        return Objects.equals(status, REFUND.getStatus());
+    }
+
+    /**
+     * 判断是否支付关闭
+     *
+     * @param status 状态
+     * @return 是否支付关闭
+     */
+    public static boolean isClosed(Integer status) {
+        return Objects.equals(status, CLOSED.getStatus());
+    }
+
+}

+ 32 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.framework.pay.core.enums.refund;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * 渠道的退款状态枚举
+ *
+ * @author jason
+ */
+@Getter
+@AllArgsConstructor
+public enum PayRefundStatusRespEnum {
+
+    WAITING(0, "等待退款"),
+    SUCCESS(10, "退款成功"),
+    FAILURE(20, "退款失败");
+
+    private final Integer status;
+    private final String name;
+
+    public static boolean isSuccess(Integer status) {
+        return Objects.equals(status, SUCCESS.getStatus());
+    }
+
+    public static boolean isFailure(Integer status) {
+        return Objects.equals(status, FAILURE.getStatus());
+    }
+
+}

+ 15 - 16
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java

@@ -2,17 +2,14 @@ package cn.iocoder.yudao.framework.pay.core.client.impl;
 
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.RandomUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayQrPayClient;
 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayWapPayClient;
-import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
-import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import com.alipay.api.response.AlipayTradePrecreateResponse;
+import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
+import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPubPayClient;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -27,18 +24,20 @@ import java.io.FileNotFoundException;
 @Disabled
 public class PayClientFactoryImplIntegrationTest {
 
+    private static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do";
+
     private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl();
 
     /**
-     * {@link WXPubPayClient} 的 V2 版本
+     * {@link WxPubPayClient} 的 V2 版本
      */
     @Test
     public void testCreatePayClient_WX_PUB_V2() {
         // 创建配置
-        WXPayClientConfig config = new WXPayClientConfig();
+        WxPayClientConfig config = new WxPayClientConfig();
         config.setAppId("wx041349c6f39b268b");
         config.setMchId("1545083881");
-        config.setApiVersion(WXPayClientConfig.API_VERSION_V2);
+        config.setApiVersion(WxPayClientConfig.API_VERSION_V2);
         config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
         // 创建客户端
         Long channelId = RandomUtil.randomLong();
@@ -51,15 +50,15 @@ public class PayClientFactoryImplIntegrationTest {
     }
 
     /**
-     * {@link WXPubPayClient} 的 V3 版本
+     * {@link WxPubPayClient} 的 V3 版本
      */
     @Test
     public void testCreatePayClient_WX_PUB_V3() throws FileNotFoundException {
         // 创建配置
-        WXPayClientConfig config = new WXPayClientConfig();
+        WxPayClientConfig config = new WxPayClientConfig();
         config.setAppId("wx041349c6f39b268b");
         config.setMchId("1545083881");
-        config.setApiVersion(WXPayClientConfig.API_VERSION_V3);
+        config.setApiVersion(WxPayClientConfig.API_VERSION_V3);
         config.setPrivateKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_key.pem")));
         config.setPrivateCertContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem")));
         config.setApiV3Key("joerVi8y5DJ3o4ttA0o1uH47Xz1u2Ase");
@@ -82,7 +81,7 @@ public class PayClientFactoryImplIntegrationTest {
         // 创建配置
         AlipayPayClientConfig config = new AlipayPayClientConfig();
         config.setAppId("2021000118634035");
-        config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX);
+        config.setServerUrl(SERVER_URL_SANDBOX);
         config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
         config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
         config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
@@ -106,7 +105,7 @@ public class PayClientFactoryImplIntegrationTest {
         // 创建配置
         AlipayPayClientConfig config = new AlipayPayClientConfig();
         config.setAppId("2021000118634035");
-        config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX);
+        config.setServerUrl(SERVER_URL_SANDBOX);
         config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
         config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
         config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
@@ -122,10 +121,10 @@ public class PayClientFactoryImplIntegrationTest {
 
     private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
         PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
-        reqDTO.setAmount(123);
+        reqDTO.setPrice(123);
         reqDTO.setSubject("IPhone 13");
         reqDTO.setBody("biubiubiu");
-        reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
+        reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
         reqDTO.setUserIp("127.0.0.1");
         reqDTO.setNotifyUrl("http://127.0.0.1:8080");
         return reqDTO;

+ 5 - 5
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java

@@ -1,8 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 import cn.hutool.core.util.ReflectUtil;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.DefaultAlipayClient;
@@ -22,9 +20,11 @@ import static org.mockito.Mockito.when;
 
 public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
 
+    private static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do";
+
     private final AlipayPayClientConfig config = new AlipayPayClientConfig()
         .setAppId("2021000118634035")
-        .setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX)
+        .setServerUrl(SERVER_URL_SANDBOX)
         .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
         // TODO @tina:key 可以随机就好,简洁一点哈。
         .setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJ" +
@@ -72,8 +72,8 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
         // 这里,设置可以直接随机整个对象。
         Long shopOrderId = System.currentTimeMillis();
         PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO();
-        reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
-        reqDTO.setAmount(1);
+        reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
+        reqDTO.setPrice(1);
         reqDTO.setBody("内容:" + shopOrderId);
         reqDTO.setSubject("标题:"+shopOrderId);
         String notify="http://niubi.natapp1.cc/api/pay/order/notify";

+ 123 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java

@@ -0,0 +1,123 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
+import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
+import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
+import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
+import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+
+import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.AbstractWxPayClient.formatDateV2;
+
+/**
+ * {@link WxBarPayClient} 的集成测试,用于快速调试微信条码支付
+ *
+ * @author 芋道源码
+ */
+@Disabled
+public class WxBarPayClientIntegrationTest {
+
+    @Test
+    public void testPayV2() throws WxPayException {
+        // 创建 config 配置
+        WxPayConfig config = buildWxPayConfigV2();
+        // 创建 WxPayService 客户端
+        WxPayService client = new WxPayServiceImpl();
+        client.setConfig(config);
+
+        // 执行发起支付
+        WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
+                .outTradeNo(String.valueOf(System.currentTimeMillis()))
+                .body("测试支付-body")
+                .detail("测试支付-detail")
+                .totalFee(1) // 单位分
+                .timeExpire(formatDateV2(LocalDateTimeUtils.addTime(Duration.ofMinutes(2))))
+                .spbillCreateIp("127.0.0.1")
+                .authCode("134298744426278497")
+                .build();
+        System.out.println("========= request ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(request));
+        WxPayMicropayResult response = client.micropay(request);
+        System.out.println("========= response ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(response));
+    }
+
+    @Test
+    public void testParseRefundNotifyV2() throws WxPayException {
+        // 创建 config 配置
+        WxPayConfig config = buildWxPayConfigV2();
+        // 创建 WxPayService 客户端
+        WxPayService client = new WxPayServiceImpl();
+        client.setConfig(config);
+
+        // 执行解析
+        String xml = "<xml><return_code>SUCCESS</return_code><appid><![CDATA[wx62056c0d5e8db250]]></appid><mch_id><![CDATA[1545083881]]></mch_id><nonce_str><![CDATA[ed8f02c21d15635cede114a42d0525a0]]></nonce_str><req_info><![CDATA[bGp+wB9DAHjoOO9Nw1iSmmIFdN2zZDhsoRWZBYdf/8bcpjowr4T8i2qjLsbMtvKQeVC5kBZOL/Agal3be6UPwnoantil+L+ojZgvLch7dXFKs/AcoxIYcVYyGka+wmnRJfUmuFRBgzt++8HOFsmJz6e2brYv1EAz+93fP2AsJtRuw1FEzodcg8eXm52hbE0KhLNqC2OyNVkn8AbOOrwIxSYobg2jVbuJ4JllYbEGIQ/6kWzNbVmMKhGJGYBy/NbUGKoQsoe4QeTQqcqQqVp08muxaOfJGThaN3B9EEMFSrog/3yT7ykVV6WQ5+Ygt89LplOf5ucWa4Ird7VJhHWtzI92ZePj4Omy1XkT1TRlwtDegA0S5MeQpM4WZ1taMrhxgmNkTUJ0JXFncx5e2KLQvbvD/HOcccx48Xv1c16JBz6G3501k8E++LWXgZ2TeNXwGsk6FyRZb0ApLyQHIx5ZtPo/UET9z3AmJCPXkrUsZ4WK46fDtbzxVPU2r8nTOcGCPbO0LUsGT6wpsuQVC4CisXDJwoZmL6kKwHfKs6mmUL2YZYzNfgoB/KgpJYSpC96kcpQyFvw+xuwqK2SXGZbAl9lADT+a83z04feQHSSIG3PCrX4QEWzpCZZ4+ySEz1Y34aoU20X9GtX+1LSwUjmQgwHrMBSvFm3/B7+IFM8OUqDB+Uvkr9Uvy7P2/KDvfy3Ih7GFcGd0C5NXpSvVTTfu1IlK/T3/t6MR/8iq78pp/2ZTYvO6eNDRJWaXYU+x6sl2dTs9n+2Z4W4AfYTvEyuxlx+aI19SqCJh7WmaFcAxidFl/9iqDjWiplb9+C6ijZv2hJtVjSCuoptIWpGDYItH7RAqlKHrx6flJD+M/5BceMHBv2w4OWCD9vPRLo8gl9o06ip0iflzO1dixhOAgLFjsQmQHNGFtR3EvCID+iS4FUlilwK+hcKNxrr0wp9Btkl9W1R9aTo289CUiIxx45skfCYzHwb+7Hqj3uTiXnep6zhCKZBAnPsDOvISXfBgXKufcFsTNtts09jX8H5/uMc9wyJ179H1cp+At1mIK2duwfo4Q9asfEoffl6Zn1olGdtEruxHGeVU0NwJ8V7RflC/Cx5RXtJ3sPJ/sHmVnBlVyR0=]]></req_info></xml>";
+        WxPayRefundNotifyResult response = client.parseRefundNotifyResult(xml);
+        System.out.println(response.getReqInfo());
+    }
+
+    @Test
+    public void testRefundV2() throws WxPayException {
+        // 创建 config 配置
+        WxPayConfig config = buildWxPayConfigV2();
+        // 创建 WxPayService 客户端
+        WxPayService client = new WxPayServiceImpl();
+        client.setConfig(config);
+
+        // 执行发起退款
+        WxPayRefundRequest request = new WxPayRefundRequest()
+                .setOutTradeNo("1689545667276")
+                .setOutRefundNo(String.valueOf(System.currentTimeMillis()))
+                .setRefundFee(1)
+                .setRefundDesc("就是想退了")
+                .setTotalFee(1);
+        System.out.println("========= request ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(request));
+        WxPayRefundResult response = client.refund(request);
+        System.out.println("========= response ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(response));
+    }
+
+    @Test
+    public void testRefundV3() throws WxPayException {
+        // 创建 config 配置
+        WxPayConfig config = buildWxPayConfigV2();
+        // 创建 WxPayService 客户端
+        WxPayService client = new WxPayServiceImpl();
+        client.setConfig(config);
+
+        // 执行发起退款
+        WxPayRefundV3Request request = new WxPayRefundV3Request()
+                .setOutTradeNo("1689506325635")
+                .setOutRefundNo(String.valueOf(System.currentTimeMillis()))
+                .setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY"))
+                .setReason("就是想退了");
+        System.out.println("========= request ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(request));
+        WxPayRefundV3Result response = client.refundV3(request);
+        System.out.println("========= response ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(response));
+    }
+
+    private WxPayConfig buildWxPayConfigV2() {
+        WxPayConfig config = new WxPayConfig();
+        config.setAppId("wx62056c0d5e8db250");
+        config.setMchId("1545083881");
+        config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim");
+//        config.setSignType(WxPayConstants.SignType.MD5);
+        config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12");
+        return config;
+    }
+
+}

+ 83 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java

@@ -0,0 +1,83 @@
+package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
+
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
+import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
+import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+
+import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.AbstractWxPayClient.formatDateV3;
+
+/**
+ * {@link WxNativePayClient} 的集成测试,用于快速调试微信扫码支付
+ *
+ * @author 芋道源码
+ */
+@Disabled
+public class WxNativePayClientIntegrationTest {
+
+    @Test
+    public void testPayV3() throws WxPayException {
+        // 创建 config 配置
+        WxPayConfig config = buildWxPayConfigV3();
+        // 创建 WxPayService 客户端
+        WxPayService client = new WxPayServiceImpl();
+        client.setConfig(config);
+
+        // 执行发起支付
+        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request()
+                .setOutTradeNo(String.valueOf(System.currentTimeMillis()))
+                .setDescription("测试支付-body")
+                .setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(1)) // 单位分
+                .setTimeExpire(formatDateV3(LocalDateTimeUtils.addTime(Duration.ofMinutes(2))))
+                .setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp("127.0.0.1"))
+                .setNotifyUrl("http://127.0.0.1:48080");
+        System.out.println("========= request ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(request));
+        String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
+        System.out.println("========= response ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(response));
+    }
+
+    @Test
+    public void testRefundV3() throws WxPayException {
+        // 创建 config 配置
+        WxPayConfig config = buildWxPayConfigV3();
+        // 创建 WxPayService 客户端
+        WxPayService client = new WxPayServiceImpl();
+        client.setConfig(config);
+
+        // 执行发起退款
+        WxPayRefundV3Request request = new WxPayRefundV3Request()
+                .setOutTradeNo("1689545729695")
+                .setOutRefundNo(String.valueOf(System.currentTimeMillis()))
+                .setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY"))
+                .setReason("就是想退了");
+        System.out.println("========= request ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(request));
+        WxPayRefundV3Result response = client.refundV3(request);
+        System.out.println("========= response ==========");
+        System.out.println(JsonUtils.toJsonPrettyString(response));
+    }
+
+    private WxPayConfig buildWxPayConfigV3() {
+        WxPayConfig config = new WxPayConfig();
+        config.setAppId("wx62056c0d5e8db250");
+        config.setMchId("1545083881");
+        config.setApiV3Key("459arNsYHl1mgkiO6H9ZH5KkhFXSxaA4");
+//        config.setCertSerialNo(serialNo);
+        config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem");
+        config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem");
+        return config;
+    }
+
+}

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java

@@ -82,9 +82,9 @@ public class TenantUtils {
      * 将多租户编号,添加到 header 中
      *
      * @param headers HTTP 请求 headers
+     * @param tenantId 租户编号
      */
-    public static void addTenantHeader(Map<String, String> headers) {
-        Long tenantId = TenantContextHolder.getTenantId();
+    public static void addTenantHeader(Map<String, String> headers, Long tenantId) {
         if (tenantId != null) {
             headers.put(HEADER_TENANT_ID, tenantId.toString());
         }

+ 39 - 0
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.framework.excel.core.convert;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * 金额转换器
+ *
+ * 金额单位:分
+ *
+ * @author 芋道源码
+ */
+public class MoneyConvert implements Converter<Integer> {
+
+    @Override
+    public Class<?> supportJavaTypeKey() {
+        throw new UnsupportedOperationException("暂不支持,也不需要");
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        throw new UnsupportedOperationException("暂不支持,也不需要");
+    }
+
+    @Override
+    public WriteCellData<String> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
+                                                    GlobalConfiguration globalConfiguration) {
+        BigDecimal result = BigDecimal.valueOf(value)
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+        return new WriteCellData<>(result.toString());
+    }
+
+}

+ 30 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java

@@ -46,6 +46,18 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
         return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
     }
 
+    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
+                        SFunction<T, ?> field3, Object value3) {
+        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
+                .eq(field3, value3));
+    }
+
+    default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
+                        SFunction<T, ?> field3, Object value3, SFunction<T, ?> field4, Object value4) {
+        return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
+                .eq(field3, value3).eq(field4, value4));
+    }
+
     default Long selectCount() {
         return selectCount(new QueryWrapper<T>());
     }
@@ -105,8 +117,26 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
         update(update, new QueryWrapper<>());
     }
 
+    /**
+     * 根据ID 批量更新,适合大量数据更新
+     *
+     * @param entities 实体们
+     */
+    default void updateBatch(Collection<T> entities) {
+        Db.updateBatchById(entities);
+    }
+
     default void updateBatch(Collection<T> entities, int size) {
         Db.updateBatchById(entities, size);
     }
 
+    /**
+     * 批量修改插入, 会根据实体的主键是否为空,更新还是修改。默认为 1000
+     *
+     * @param entities 实体们
+     */
+    default void saveOrUpdateBatch(Collection<T> entities){
+        Db.saveOrUpdateBatch(entities);
+    }
+
 }

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.mybatis.core.query;
 
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;

+ 56 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/IntegerListTypeHandler.java

@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.framework.mybatis.core.type;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+import org.apache.ibatis.type.TypeHandler;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * List<Integer> 的类型转换器实现类,对应数据库的 varchar 类型
+ *
+ * @author jason
+ */
+@MappedJdbcTypes(JdbcType.VARCHAR)
+@MappedTypes(List.class)
+public class IntegerListTypeHandler implements TypeHandler<List<Integer>> {
+
+    private static final String COMMA = ",";
+
+    @Override
+    public void setParameter(PreparedStatement ps, int i, List<Integer> strings, JdbcType jdbcType) throws SQLException {
+        ps.setString(i, CollUtil.join(strings, COMMA));
+    }
+
+    @Override
+    public List<Integer> getResult(ResultSet rs, String columnName) throws SQLException {
+        String value = rs.getString(columnName);
+        return getResult(value);
+    }
+
+    @Override
+    public List<Integer> getResult(ResultSet rs, int columnIndex) throws SQLException {
+        String value = rs.getString(columnIndex);
+        return getResult(value);
+    }
+
+    @Override
+    public List<Integer> getResult(CallableStatement cs, int columnIndex) throws SQLException {
+        String value = cs.getString(columnIndex);
+        return getResult(value);
+    }
+
+    private List<Integer> getResult(String value) {
+        if (value == null) {
+            return null;
+        }
+        return StrUtils.splitToInteger(value, COMMA);
+    }
+}

+ 0 - 2
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java

@@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration
 import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
 import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
 import org.redisson.spring.starter.RedissonAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
 import org.springframework.boot.test.context.SpringBootTest;
@@ -41,7 +40,6 @@ public class BaseDbAndRedisUnitTest {
 
             // Redis 配置类
             RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
-            RedisAutoConfiguration.class, // Spring Redis 自动配置类
             YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
             RedissonAutoConfiguration.class, // Redisson 自动高配置类
     })

+ 9 - 1
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java

@@ -55,6 +55,9 @@ public class RandomUtils {
             }
             return RandomUtil.randomInt();
         });
+        // LocalDateTime
+        PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(LocalDateTime.class,
+                (dataProviderStrategy, attributeMetadata, map) -> randomLocalDateTime());
         // Boolean
         PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> {
             // 如果是 deleted 的字段,返回非删除
@@ -82,7 +85,8 @@ public class RandomUtils {
     }
 
     public static LocalDateTime randomLocalDateTime() {
-        return LocalDateTimeUtil.of(randomDate());
+        // 设置 Nano 为零的原因,避免 MySQL、H2 存储不到时间戳
+        return LocalDateTimeUtil.of(randomDate()).withNano(0);
     }
 
     public static Short randomShort() {
@@ -102,6 +106,10 @@ public class RandomUtils {
         return randomString() + "@qq.com";
     }
 
+    public static String randomURL() {
+        return "https://www.iocoder.cn/" + randomString();
+    }
+
     @SafeVarargs
     public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) {
         T pojo = PODAM_FACTORY.manufacturePojo(clazz);

+ 12 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java

@@ -10,11 +10,14 @@ import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.util.AntPathMatcher;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 import org.springframework.web.filter.CorsFilter;
@@ -113,4 +116,13 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
         return bean;
     }
 
+    /**
+     * 创建 RestTemplate 实例
+     *
+     * @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder}
+     */
+    @Bean
+    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
+        return restTemplateBuilder.build();
+    }
 }

+ 11 - 11
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java

@@ -3,16 +3,16 @@ package cn.iocoder.yudao.module.bpm.enums;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 
 /**
- * 工作流 错误码枚举类
+ * Bpm 错误码枚举类
  *
- * 工作流系统,使用 1-009-000-000 段
+ * bpm 系统,使用 1-009-000-000 段
  */
 public interface ErrorCodeConstants {
 
-    // ==========  通用流程处理 模块 1-009-000-000 ==========
+    // ==========  通用流程处理 模块 1009000000 ==========
     ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1009000002, "获取高亮流程图异常");
 
-    // ========== OA 流程模块 1-009-001-000 ==========
+    // ========== OA 流程模块 1009001000 ==========
     ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1009001001, "请假申请不存在");
     ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1009001002, "项目经理岗位未设置");
     ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1009001009, "部门的项目经理不存在");
@@ -21,7 +21,7 @@ public interface ErrorCodeConstants {
     ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1009001006, "HR岗位未设置");
     ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1009001007, "请假天数必须>=1");
 
-    // ========== 流程模型 1-009-002-000 ==========
+    // ========== 流程模型 1009002000 ==========
     ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程");
     ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在");
     ErrorCode MODEL_KEY_VALID = new ErrorCode(1009002002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
@@ -30,34 +30,34 @@ public interface ErrorCodeConstants {
             "原因:用户任务({})未配置分配规则,请点击【修改流程】按钮进行配置");
     ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1009003005, "流程定义部署失败,原因:信息未发生变化");
 
-    // ========== 流程定义 1-009-003-000 ==========
+    // ========== 流程定义 1009003000 ==========
     ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在");
     ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003003, "流程定义处于挂起状态");
     ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009003004, "流程定义的模型不存在");
 
-    // ========== 流程实例 1-009-004-000 ==========
+    // ========== 流程实例 1009004000 ==========
     ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在");
     ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1009004001, "流程取消失败,流程不处于运行中");
     ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1009004002, "流程取消失败,该流程不是你发起的");
 
-    // ========== 流程任务 1-009-005-000 ==========
+    // ========== 流程任务 1009005000 ==========
     ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1009005000, "审批任务失败,原因:该任务不处于未审批");
     ErrorCode TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1009005001, "审批任务失败,原因:该任务的审批人不是你");
 
-    // ========== 流程任务分配规则 1-009-006-000 ==========
+    // ========== 流程任务分配规则 1009006000 ==========
     ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则");
     ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006001, "流程任务分配规则不存在");
     ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006002, "只有流程模型的任务分配规则,才允许被修改");
     ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1009006003, "操作失败,原因:找不到任务的审批人!");
     ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1009006004, "操作失败,原因:任务分配脚本({}) 不存在");
 
-    // ========== 动态表单模块 1-009-010-000 ==========
+    // ========== 动态表单模块 1009010000 ==========
     ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在");
     ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1009010001, "表单项({}) 和 ({}) 使用了相同的字段名({})");
 
-    // ========== 用户组模块 1-009-011-000 ==========
+    // ========== 用户组模块 1009011000 ==========
     ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1009011000, "用户组不存在");
     ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1009011001, "名字为【{}】的用户组已被禁用");
 

+ 20 - 0
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/comment/ProductCommentApi.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.product.api.comment;
+
+import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
+
+/**
+ * 产品评论 API 接口
+ *
+ * @author HUIHUI
+ */
+public interface ProductCommentApi {
+
+    /**
+     * 创建评论
+     *
+     * @param createReqDTO 评论参数
+     * @return 返回评论创建后的 id
+     */
+    Long createComment(ProductCommentCreateReqDTO createReqDTO);
+
+}

+ 59 - 0
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/comment/dto/ProductCommentCreateReqDTO.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.product.api.comment.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 评论创建请求 DTO
+ *
+ * @author HUIHUI
+ */
+@Data
+public class ProductCommentCreateReqDTO {
+
+    /**
+     * 商品 SKU 编号
+     */
+    private Long skuId;
+    /**
+     * 订单编号
+     */
+    private Long orderId;
+    /**
+     * 交易订单项编号
+     */
+    private Long orderItemId;
+
+    /**
+     * 评分星级 1-5 分
+     */
+    private Integer scores;
+    /**
+     * 描述星级 1-5 分
+     */
+    private Integer descriptionScores;
+    /**
+     * 服务星级 1-5 分
+     */
+    private Integer benefitScores;
+    /**
+     * 评论内容
+     */
+    private String content;
+    /**
+     * 评论图片地址数组,以逗号分隔最多上传 9 张
+     */
+    private List<String> picUrls;
+
+    /**
+     * 是否匿名
+     */
+    private Boolean anonymous;
+    /**
+     * 评价人
+     */
+    private Long userId;
+
+
+}

+ 10 - 1
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.module.product.api.sku;
 
-import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
+import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
 
 import java.util.Collection;
 import java.util.List;
@@ -30,6 +30,15 @@ public interface ProductSkuApi {
      */
     List<ProductSkuRespDTO> getSkuList(Collection<Long> ids);
 
+    // TODO puhui999:入参用 Collection<Long> 更通用
+    /**
+     * 批量查询 SKU 数组
+     *
+     * @param spuIds SPU 编号列表
+     * @return SKU 数组
+     */
+    List<ProductSkuRespDTO> getSkuListBySpuId(List<Long> spuIds);
+
     /**
      * 更新 SKU 库存
      *

+ 3 - 35
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.product.api.sku.dto;
 
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
 import lombok.Data;
 
 import java.util.List;
@@ -22,15 +22,11 @@ public class ProductSkuRespDTO {
      * SPU 编号
      */
     private Long spuId;
-    /**
-     * SPU 名字
-     */
-    private String spuName;
 
     /**
-     * 属性数组,JSON 格式
+     * 属性数组
      */
-    private List<Property> properties;
+    private List<ProductPropertyValueDetailRespDTO> properties;
     /**
      * 销售价格,单位:分
      */
@@ -51,20 +47,10 @@ public class ProductSkuRespDTO {
      * 图片地址
      */
     private String picUrl;
-    /**
-     * SKU 状态
-     * <p>
-     * 枚举 {@link CommonStatusEnum}
-     */
-    private Integer status;
     /**
      * 库存
      */
     private Integer stock;
-    /**
-     * 预警预存
-     */
-    private Integer warnStock;
     /**
      * 商品重量,单位:kg 千克
      */
@@ -74,22 +60,4 @@ public class ProductSkuRespDTO {
      */
     private Double volume;
 
-    /**
-     * 商品属性
-     */
-    @Data
-    public static class Property {
-
-        /**
-         * 属性编号
-         */
-        private Long propertyId;
-        /**
-         * 属性值编号
-         */
-        private Long valueId;
-
-    }
-
-
 }

+ 35 - 32
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.product.api.spu.dto;
 
 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
-import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
 import lombok.Data;
 
@@ -29,17 +28,23 @@ public class ProductSpuRespDTO {
      */
     private String name;
     /**
-     * 商品编码
+     * 关键字
      */
-    private String code;
+    private String keyword;
     /**
-     * 促销语
+     * 商品简介
      */
-    private String sellPoint;
+    private String introduction;
     /**
      * 商品详情
      */
     private String description;
+    // TODO @芋艿:是不是要删除
+    /**
+     * 商品条码(一维码)
+     */
+    private String barCode;
+
     /**
      * 商品分类编号
      */
@@ -49,13 +54,13 @@ public class ProductSpuRespDTO {
      */
     private Long brandId;
     /**
-     * 商品图片的数组
-     * <p>
-     * 1. 第一张图片将作为商品主图,支持同时上传多张图;
-     * 2. 建议使用尺寸 800x800 像素以上、大小不超过 1M 的正方形图片;
-     * 3. 至少 1 张,最多上传 10 张
+     * 商品封面图
      */
-    private List<String> picUrls;
+    private String picUrl;
+    /**
+     * 商品轮播图
+     */
+    private List<String> sliderPicUrls;
     /**
      * 商品视频
      */
@@ -76,38 +81,36 @@ public class ProductSpuRespDTO {
 
     /**
      * 规格类型
-     * <p>
-     * 枚举 {@link ProductSpuSpecTypeEnum}
+     *
+     * false - 单规格
+     * true - 多规格
      */
-    private Integer specType;
+    private Boolean specType;
     /**
-     * 最小价格,单位使用:分
-     * <p>
-     * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最小值
+     * 商品价格,单位使用:分
      */
-    private Integer minPrice;
-    /**
-     * 最大价格,单位使用:分
-     * <p>
-     * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最大值
-     */
-    private Integer maxPrice;
+    private Integer price;
     /**
      * 市场价,单位使用:分
-     * <p>
-     * 基于其对应的 {@link ProductSkuRespDTO#getMarketPrice()} 最大值
      */
     private Integer marketPrice;
     /**
-     * 总库存
-     * <p>
-     * 基于其对应的 {@link ProductSkuRespDTO#getStock()} 求和
+     * 成本价,单位使用:分
      */
-    private Integer totalStock;
+    private Integer costPrice;
+    /**
+     * 库存
+     */
+    private Integer stock;
+
+    // ========== 物流相关字段 =========
+
     /**
-     * 是否展示库存
+     * 物流配置模板编号
+     *
+     * 对应 TradeDeliveryExpressTemplateDO 的 id 编号
      */
-    private Boolean showStock;
+    private Long deliveryTemplateId;
 
     // ========== 统计相关字段 =========
 

+ 13 - 0
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/DictTypeConstants.java

@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.product.enums;
+
+/**
+ * product 字典类型的枚举类
+ *
+ * @author HUIHUI
+ */
+public interface DictTypeConstants {
+
+    String PRODUCT_UNIT = "product_unit";  // 商品单位
+    String PRODUCT_SPU_STATUS = "product_spu_status"; // 商品 SPU 状态
+
+}

+ 14 - 2
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java

@@ -15,10 +15,11 @@ public interface ErrorCodeConstants {
     ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类");
     ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除");
     ErrorCode CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用");
+    ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1008001005, "类别下存在商品,无法删除");
 
     // ========== 商品品牌相关编号 1008002000 ==========
     ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在");
-    ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌不存在");
+    ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌已禁用");
     ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1008002002, "品牌名称已存在");
 
     // ========== 商品属性项 1008003000 ==========
@@ -32,8 +33,9 @@ public interface ErrorCodeConstants {
 
     // ========== 商品 SPU 1008005000 ==========
     ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品 SPU 不存在");
-    ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第三级的商品分类下");
+    ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
     ErrorCode SPU_NOT_ENABLE = new ErrorCode(1008005002, "商品 SPU 不处于上架状态");
+    ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1008005003, "商品 SPU 不处于回收站状态");
 
     // ========== 商品 SKU 1008006000 ==========
     ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品 SKU 不存在");
@@ -42,4 +44,14 @@ public interface ErrorCodeConstants {
     ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU,必须不重复");
     ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
 
+    // ========== 商品 评价 1008007000 ==========
+    ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品 评价 不存在");
+    ErrorCode ORDER_SPU_COMMENT_EXISTS = new ErrorCode(1008007001, "订单 商品评价 已存在");
+    ErrorCode COMMENT_ERROR_OPT = new ErrorCode(1008007002, "商品评价非法操作");
+    ErrorCode COMMENT_ADDITIONAL_EXISTS  = new ErrorCode(1008007003, "商品追加评价已存在");
+
+    // ========== 商品 收藏 1008008000 ==========
+    ErrorCode FAVORITE_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏");
+    ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1008008001, "商品收藏不存在");
+
 }

+ 15 - 0
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ProductConstants.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.product.enums;
+
+/**
+ * Product 常量
+ *
+ * @author HUIHUI
+ */
+public interface ProductConstants {
+
+    /**
+     * 警戒库存 TODO 警戒库存暂时为 10,后期需要使用常量或者数据库配置替换
+     */
+    int ALERT_STOCK = 10;
+
+}

+ 41 - 0
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentScoresEnum.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.product.enums.comment;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 商品评论的星级枚举
+ *
+ * @author wangzhs
+ */
+@Getter
+@AllArgsConstructor
+public enum ProductCommentScoresEnum implements IntArrayValuable {
+
+    ONE(1, "1星"),
+    TWO(2, "2星"),
+    THREE(3, "3星"),
+    FOUR(4, "4星"),
+    FIVE(5, "5星");
+
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductCommentScoresEnum::getScores).toArray();
+
+    /**
+     * 星级
+     */
+    private final Integer scores;
+
+    /**
+     * 星级名
+     */
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

+ 0 - 37
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuSpecTypeEnum.java

@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.product.enums.spu;
-
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-import java.util.Arrays;
-
-/**
- * 商品 SPU 规格类型
- *
- * @author 芋道源码
- */
-@Getter
-@AllArgsConstructor
-public enum ProductSpuSpecTypeEnum implements IntArrayValuable {
-
-    RECYCLE(1, "统一规格"),
-    DISABLE(2, "多规格");
-
-    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuSpecTypeEnum::getType).toArray();
-
-    /**
-     * 规格类型
-     */
-    private final Integer type;
-    /**
-     * 规格名称
-     */
-    private final String name;
-
-    @Override
-    public int[] array() {
-        return ARRAYS;
-    }
-
-}

+ 1 - 1
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java

@@ -17,7 +17,7 @@ public enum ProductSpuStatusEnum implements IntArrayValuable {
 
     RECYCLE(-1, "回收站"),
     DISABLE(0, "下架"),
-    ENABLE(1, "上架"),;
+    ENABLE(1, "上架");
 
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuStatusEnum::getStatus).toArray();
 

+ 15 - 0
yudao-module-mall/yudao-module-product-biz/pom.xml

@@ -23,12 +23,27 @@
             <artifactId>yudao-module-product-api</artifactId>
             <version>${revision}</version>
         </dependency>
+        <!-- TODO 芋艿:看看~~~ -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-trade-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-member-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
 
         <!-- 业务组件 -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
         </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
+        </dependency>
 
         <!-- Web 相关 -->
         <dependency>

+ 27 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/comment/ProductCommentApiImpl.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.product.api.comment;
+
+import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
+import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+/**
+ * 商品评论 API 实现类
+ *
+ * @author HUIHUI
+ */
+@Service
+@Validated
+public class ProductCommentApiImpl implements ProductCommentApi {
+
+    @Resource
+    private ProductCommentService productCommentService;
+
+    @Override
+    public Long createComment(ProductCommentCreateReqDTO createReqDTO) {
+        return productCommentService.createComment(createReqDTO);
+    }
+
+}

+ 13 - 3
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java

@@ -15,7 +15,8 @@ import java.util.Collections;
 import java.util.List;
 
 /**
- * TODO LeeYan9: 类注释;
+ * 商品 SKU API 实现类
+ *
  * @author LeeYan9
  * @since 2022-09-06
  */
@@ -28,8 +29,8 @@ public class ProductSkuApiImpl implements ProductSkuApi {
 
     @Override
     public ProductSkuRespDTO getSku(Long id) {
-        // TODO TODO LeeYan9: 需要实现
-        return null;
+        ProductSkuDO sku = productSkuService.getSku(id);
+        return ProductSkuConvert.INSTANCE.convert02(sku);
     }
 
     @Override
@@ -41,6 +42,15 @@ public class ProductSkuApiImpl implements ProductSkuApi {
         return ProductSkuConvert.INSTANCE.convertList04(skus);
     }
 
+    @Override
+    public List<ProductSkuRespDTO> getSkuListBySpuId(List<Long> spuIds) {
+        if (CollUtil.isEmpty(spuIds)) {
+            return Collections.emptyList();
+        }
+        List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spuIds);
+        return ProductSkuConvert.INSTANCE.convertList04(skus);
+    }
+
     @Override
     public void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) {
         productSkuService.updateSkuStock(updateStockReqDTO);

+ 4 - 4
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.product.api.spu;
 
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.hutool.core.collection.CollectionUtil;
 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
@@ -14,7 +14,7 @@ import java.util.Collections;
 import java.util.List;
 
 /**
- * TODO LeeYan9: 类注释;
+ * 商品 SPU API 接口实现类
  *
  * @author LeeYan9
  * @since 2022-09-06
@@ -28,11 +28,11 @@ public class ProductSpuApiImpl implements ProductSpuApi {
 
     @Override
     public List<ProductSpuRespDTO> getSpuList(Collection<Long> spuIds) {
-        // TODO TODO LeeYan9: AllEmpty?
-        if (CollectionUtils.isAnyEmpty(spuIds)) {
+        if (CollectionUtil.isEmpty(spuIds)) {
             return Collections.emptyList();
         }
         List<ProductSpuDO> productSpuDOList = productSpuMapper.selectBatchIds(spuIds);
         return ProductSpuConvert.INSTANCE.convertList2(productSpuDOList);
     }
+
 }

+ 10 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/ProductBrandController.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.product.controller.admin.brand;
 
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.product.controller.admin.brand.vo.*;
@@ -62,6 +63,15 @@ public class ProductBrandController {
         return success(ProductBrandConvert.INSTANCE.convert(brand));
     }
 
+    @GetMapping("/list-all-simple")
+    @Operation(summary = "获取品牌精简信息列表", description = "主要用于前端的下拉选项")
+    public CommonResult<List<ProductBrandSimpleRespVO>> getSimpleBrandList() {
+        // 获取品牌列表,只要开启状态的
+        List<ProductBrandDO> list = brandService.getBrandListByStatus(CommonStatusEnum.ENABLE.getStatus());
+        // 排序后,返回给前端
+        return success(ProductBrandConvert.INSTANCE.convertList1(list));
+    }
+
     @GetMapping("/page")
     @Operation(summary = "获得品牌分页")
     @PreAuthorize("@ss.hasPermission('product:brand:query')")

+ 1 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandBaseVO.java

@@ -12,7 +12,7 @@ import javax.validation.constraints.NotNull;
 @Data
 public class ProductBrandBaseVO {
 
-    @Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+    @Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "苹果")
     @NotNull(message = "品牌名称不能为空")
     private String name;
 

+ 1 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandListReqVO.java

@@ -7,7 +7,7 @@ import lombok.Data;
 @Data
 public class ProductBrandListReqVO {
 
-    @Schema(description = "品牌名称", example = "芋道")
+    @Schema(description = "品牌名称", example = "苹果")
     private String name;
 
 }

+ 1 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandPageReqVO.java

@@ -17,7 +17,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
 @ToString(callSuper = true)
 public class ProductBrandPageReqVO extends PageParam {
 
-    @Schema(description = "品牌名称", example = "芋道")
+    @Schema(description = "品牌名称", example = "苹果")
     private String name;
 
     @Schema(description = "状态", example = "0")

+ 20 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/brand/vo/ProductBrandSimpleRespVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.product.controller.admin.brand.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Schema(description = "管理后台 - 品牌精简信息 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ProductBrandSimpleRespVO {
+
+    @Schema(description = "品牌编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "品牌名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "苹果")
+    private String name;
+
+}

+ 5 - 5
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryBaseVO.java

@@ -21,16 +21,16 @@ public class ProductCategoryBaseVO {
     @NotBlank(message = "分类名称不能为空")
     private String name;
 
-    @Schema(description = "分类图", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotBlank(message = "分类图不能为空")
+    @Schema(description = "移动端分类图", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "移动端分类图不能为空")
     private String picUrl;
 
+    @Schema(description = "PC 端分类图")
+    private String bigPicUrl;
+
     @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Integer sort;
 
-    @Schema(description = "分类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "描述")
-    private String description;
-
     @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
     @NotNull(message = "开启状态不能为空")
     private Integer status;

+ 5 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryCreateReqVO.java

@@ -5,10 +5,15 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+import javax.validation.constraints.NotBlank;
+
 @Schema(description = "管理后台 - 商品分类创建 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
 public class ProductCategoryCreateReqVO extends ProductCategoryBaseVO {
 
+    @Schema(description = "分类描述", example = "描述")
+    private String description;
+
 }

+ 6 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryListReqVO.java

@@ -10,4 +10,10 @@ public class ProductCategoryListReqVO {
     @Schema(description = "分类名称", example = "办公文具")
     private String name;
 
+    @Schema(description = "开启状态", example = "0")
+    private Integer status;
+
+    @Schema(description = "父分类编号", example = "1")
+    private Long parentId;
+
 }

+ 4 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/category/vo/ProductCategoryUpdateReqVO.java

@@ -5,6 +5,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
+import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 
 @Schema(description = "管理后台 - 商品分类更新 Request VO")
@@ -17,4 +18,7 @@ public class ProductCategoryUpdateReqVO extends ProductCategoryBaseVO {
     @NotNull(message = "分类编号不能为空")
     private Long id;
 
+    @Schema(description = "分类描述", example = "描述")
+    private String description;
+
 }

+ 0 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/vo/PayNotifyOrderReqVO.java → yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.http


+ 62 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.product.controller.admin.comment;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.product.controller.admin.comment.vo.*;
+import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
+import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
+import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - 商品评价")
+@RestController
+@RequestMapping("/product/comment")
+@Validated
+public class ProductCommentController {
+
+    @Resource
+    private ProductCommentService productCommentService;
+
+    @GetMapping("/page")
+    @Operation(summary = "获得商品评价分页")
+    @PreAuthorize("@ss.hasPermission('product:comment:query')")
+    public CommonResult<PageResult<ProductCommentRespVO>> getCommentPage(@Valid ProductCommentPageReqVO pageVO) {
+        PageResult<ProductCommentDO> pageResult = productCommentService.getCommentPage(pageVO);
+        return success(ProductCommentConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @PutMapping("/update-visible")
+    @Operation(summary = "显示 / 隐藏评论")
+    @PreAuthorize("@ss.hasPermission('product:comment:update')")
+    public CommonResult<Boolean> updateCommentVisible(@Valid @RequestBody ProductCommentUpdateVisibleReqVO updateReqVO) {
+        productCommentService.updateCommentVisible(updateReqVO);
+        return success(true);
+    }
+
+    @PutMapping("/reply")
+    @Operation(summary = "商家回复")
+    @PreAuthorize("@ss.hasPermission('product:comment:update')")
+    public CommonResult<Boolean> commentReply(@Valid @RequestBody ProductCommentReplyReqVO replyVO) {
+        productCommentService.replyComment(replyVO, getLoginUserId());
+        return success(true);
+    }
+
+    @PutMapping("/create")
+    @Operation(summary = "添加自评")
+    @PreAuthorize("@ss.hasPermission('product:comment:update')")
+    public CommonResult<Boolean> createComment(@Valid @RequestBody ProductCommentCreateReqVO createReqVO) {
+        productCommentService.createComment(createReqVO);
+        return success(true);
+    }
+
+}

+ 62 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+@Data
+public class ProductCommentBaseVO {
+
+    @Schema(description = "评价人", requiredMode = Schema.RequiredMode.REQUIRED, example = "16868")
+    @NotNull(message = "评价人不能为空")
+    private Long userId;
+
+    @Schema(description = "评价订单项", requiredMode = Schema.RequiredMode.REQUIRED, example = "19292")
+    @NotNull(message = "评价订单项不能为空")
+    private Long orderItemId;
+
+    @Schema(description = "评价人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小姑凉")
+    @NotNull(message = "评价人名称不能为空")
+    private String userNickname;
+
+    @Schema(description = "评价人头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
+    @NotNull(message = "评价人头像不能为空")
+    private String userAvatar;
+
+    // TODO @puhui:spuId、spuName 是不是只有 ProductCommentRespVO 有呀。
+    @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑透气小短袖")
+    @NotNull(message = "商品 SPU 编号不能为空")
+    private Long spuId;
+
+    @Schema(description = "商品 SPU 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
+    @NotNull(message = "商品 SPU 名称不能为空")
+    private String spuName;
+
+    @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "商品 SKU 编号不能为空")
+    private Long skuId;
+
+    @Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    @NotNull(message = "评分星级不能为空")
+    private Integer scores;
+
+    @Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    @NotNull(message = "描述星级不能为空")
+    private Integer descriptionScores;
+
+    @Schema(description = "服务星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    @NotNull(message = "服务星级分不能为空")
+    private Integer benefitScores;
+
+    @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "穿起来非常丝滑凉快")
+    @NotNull(message = "评论内容不能为空")
+    private String content;
+
+    @Schema(description = "评论图片地址数组,以逗号分隔最多上传 9 张", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png]")
+    @Size(max = 9, message = "评论图片地址数组长度不能超过 9 张")
+    private List<String> picUrls;
+
+}

+ 14 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 商品评价创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ProductCommentCreateReqVO extends ProductCommentBaseVO {
+
+}

+ 46 - 0
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java

@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.product.controller.admin.comment.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 商品评价分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ProductCommentPageReqVO extends PageParam {
+
+    @Schema(description = "评价人名称", example = "王二狗")
+    private String userNickname;
+
+    @Schema(description = "交易订单编号", example = "24428")
+    private Long orderId;
+
+    @Schema(description = "商品SPU编号", example = "29502")
+    private Long spuId;
+
+    @Schema(description = "商品SPU名称", example = "感冒药")
+    private String spuName;
+
+    @Schema(description = "评分星级 1-5 分", example = "5")
+    @InEnum(ProductCommentScoresEnum.class)
+    private Integer scores;
+
+    // TODO @puhui999:replyStatus 哈
+    @Schema(description = "商家是否回复", example = "true")
+    private Boolean replied;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů