Browse Source

Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into develop

YunaiV 10 months ago
parent
commit
0e24994c92
100 changed files with 586 additions and 799 deletions
  1. 92 127
      sql/mysql/quartz.sql
  2. 88 33
      sql/mysql/ruoyi-vue-pro.sql
  3. 1 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java
  4. 1 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java
  5. 1 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java
  6. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
  7. 6 0
      yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/Area.java
  8. 7 0
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java
  9. 3 2
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
  10. 10 2
      yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java
  11. 0 7
      yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
  12. 1 1
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
  13. 5 0
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java
  14. 2 1
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
  15. 10 2
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java
  16. 4 0
      yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
  17. 1 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java
  18. 36 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java
  19. 8 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java
  20. 8 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java
  21. 3 3
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java
  22. 34 2
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
  23. 1 2
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
  24. 3 2
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java
  25. 8 5
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java
  26. 1 2
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
  27. 2 3
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
  28. 1 1
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
  29. 1 1
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
  30. 6 0
      yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
  31. 0 3
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/DictTypeConstants.java
  32. 1 1
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java
  33. 2 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java
  34. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
  35. 2 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
  36. 7 6
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java
  37. 2 0
      yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/statistics/ErpPurchaseStatisticsMapper.xml
  38. 2 0
      yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/statistics/ErpSaleStatisticsMapper.xml
  39. 18 0
      yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java
  40. 27 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApiImpl.java
  41. 1 1
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java
  42. 12 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
  43. 1 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java
  44. 1 1
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigService.java
  45. 8 1
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java
  46. 9 2
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java
  47. 1 1
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm
  48. 2 2
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm
  49. 2 2
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
  50. 2 1
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
  51. 1 1
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
  52. 8 5
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java
  53. 12 2
      yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java
  54. 2 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordReqPageVO.java
  55. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java
  56. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java
  57. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java
  58. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java
  59. 2 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
  60. 6 6
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/SeckillActivityConvert.java
  61. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/SeckillConfigConvert.java
  62. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
  63. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
  64. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillConfigServiceImpl.java
  65. 5 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http
  66. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
  67. 5 6
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java
  68. 1 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordService.java
  69. 19 17
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java
  70. 0 11
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageUserService.java
  71. 1 5
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageUserServiceImpl.java
  72. 1 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java
  73. 1 5
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java
  74. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
  75. 0 5
      yudao-module-pay/yudao-module-pay-biz/pom.xml
  76. 5 30
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletController.java
  77. 7 5
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/wallet/PayWalletPageReqVO.java
  78. 0 5
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/wallet/PayWalletRespVO.java
  79. 1 12
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletConvert.java
  80. 4 4
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java
  81. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/order/PayOrderSyncJob.java
  82. 5 43
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
  83. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
  84. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
  85. 0 29
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/dataobject/merchant/PayChannelDOTest.java
  86. 0 80
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapperIntegrationTest.java
  87. 0 51
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceIntegrationTest.java
  88. 0 1
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/service/package-info.java
  89. 0 38
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseDbAndRedisIntegrationTest.java
  90. 0 30
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseDbIntegrationTest.java
  91. 0 23
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseRedisIntegrationTest.java
  92. 0 84
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/resources/application-integration-test.yaml
  93. 3 9
      yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java
  94. 3 2
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
  95. 3 2
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
  96. 10 14
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
  97. 2 1
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
  98. 9 17
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java
  99. 7 0
      yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/jmreport/core/service/JmReportTokenServiceImpl.java
  100. 2 5
      yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/security/config/SecurityConfiguration.java

File diff suppressed because it is too large
+ 92 - 127
sql/mysql/quartz.sql


+ 88 - 33
sql/mysql/ruoyi-vue-pro.sql

@@ -11,7 +11,7 @@
  Target Server Version : 80200 (8.2.0)
  File Encoding         : 65001
 
- Date: 07/07/2024 18:29:17
+ Date: 28/07/2024 23:20:28
 */
 
 SET NAMES utf8mb4;
@@ -49,7 +49,7 @@ CREATE TABLE `infra_api_access_log`  (
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `idx_create_time`(`create_time` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 35940 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
+) ENGINE = InnoDB AUTO_INCREMENT = 35942 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
 
 -- ----------------------------
 -- Records of infra_api_access_log
@@ -91,7 +91,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 = 17761 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
+) ENGINE = InnoDB AUTO_INCREMENT = 19166 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
 
 -- ----------------------------
 -- Records of infra_api_error_log
@@ -128,7 +128,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 = 2440 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
+) ENGINE = InnoDB AUTO_INCREMENT = 2470 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
 
 -- ----------------------------
 -- Records of infra_codegen_column
@@ -166,7 +166,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 = 184 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
+) ENGINE = InnoDB AUTO_INCREMENT = 186 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
 
 -- ----------------------------
 -- Records of infra_codegen_table
@@ -199,7 +199,7 @@ CREATE TABLE `infra_config`  (
 -- Records of infra_config
 -- ----------------------------
 BEGIN;
-INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'sys.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-04-03 17:22:28', b'0');
+INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'system.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-07-20 17:22:47', b'0');
 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', b'0');
 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');
@@ -250,7 +250,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 = 1397 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
+) ENGINE = InnoDB AUTO_INCREMENT = 1447 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
 
 -- ----------------------------
 -- Records of infra_file
@@ -438,7 +438,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 = 1555 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
+) ENGINE = InnoDB AUTO_INCREMENT = 1588 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
 
 -- ----------------------------
 -- Records of system_dict_data
@@ -490,7 +490,7 @@ 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 (62, 0, '未处理', '0', 'infra_api_error_log_process_status', 0, 'primary', '', NULL, '', '2021-02-26 07:07:19', '1', '2022-02-16 20:14: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 (63, 1, '已处理', '1', 'infra_api_error_log_process_status', 0, 'success', '', NULL, '', '2021-02-26 07:07:26', '1', '2022-02-16 20:14: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 (64, 2, '已忽略', '2', 'infra_api_error_log_process_status', 0, 'danger', '', NULL, '', '2021-02-26 07:07:34', '1', '2022-02-16 20:14: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 (66, 2, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', '2021-04-05 01:05:26', '1', '2022-02-16 10:09:52', 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 (66, 1, '阿里云', 'ALIYUN', 'system_sms_channel_code', 0, 'primary', '', NULL, '1', '2021-04-05 01:05:26', '1', '2024-07-22 22:23: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 (67, 1, '验证码', '1', 'system_sms_template_type', 0, 'warning', '', NULL, '1', '2021-04-05 21:50:57', '1', '2022-02-16 12:48:30', 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 (68, 2, '通知', '2', 'system_sms_template_type', 0, 'primary', '', NULL, '1', '2021-04-05 21:51:08', '1', '2022-02-16 12:48: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 (69, 0, '营销', '3', 'system_sms_template_type', 0, 'danger', '', NULL, '1', '2021-04-05 21:51:15', '1', '2022-02-16 12:48:22', b'0');
@@ -605,8 +605,8 @@ 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 (1206, 30, '已完成', '30', 'trade_order_status', 0, 'success', '', '交易订单状态 - 已完成', '1', '2022-12-10 16:50:30', '1', '2022-12-10 16:51: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 (1207, 40, '已取消', '40', 'trade_order_status', 0, 'danger', '', '交易订单状态 - 已取消', '1', '2022-12-10 16:50:50', '1', '2022-12-10 16:51: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 (1208, 0, '未售后', '0', 'trade_order_item_after_sale_status', 0, 'info', '', '交易订单项的售后状态 - 未售后', '1', '2022-12-10 20:58:42', '1', '2022-12-10 20:59: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 (1209, 1, '售后中', '1', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', '2022-12-10 20:59:21', '1', '2022-12-10 20:59: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 (1210, 2, '已退款', '2', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', '2022-12-10 20:59:46', '1', '2022-12-10 20:59:46', 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 (1209, 10, '售后中', '10', 'trade_order_item_after_sale_status', 0, 'primary', '', '交易订单项的售后状态 - 售后中', '1', '2022-12-10 20:59:21', '1', '2024-07-21 17:01: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 (1210, 20, '已退款', '20', 'trade_order_item_after_sale_status', 0, 'success', '', '交易订单项的售后状态 - 已退款', '1', '2022-12-10 20:59:46', '1', '2024-07-21 17:01: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 (1211, 1, '完全匹配', '1', 'mp_auto_reply_request_match', 0, 'primary', '', '公众号自动回复的请求关键字匹配模式 - 完全匹配', '1', '2023-01-16 23:30:39', '1', '2023-01-16 23:31: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 (1212, 2, '半匹配', '2', 'mp_auto_reply_request_match', 0, 'success', '', '公众号自动回复的请求关键字匹配模式 - 半匹配', '1', '2023-01-16 23:30:55', '1', '2023-01-16 23:31: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 (1213, 1, '文本', 'text', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 文本', '1', '2023-01-17 22:17:32', '1', '2023-01-17 22:17:39', b'0');
@@ -741,7 +741,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 (1457, 2, '现金', '2', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54: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 (1458, 3, '邮政汇款', '3', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54: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 (1459, 4, '电汇', '4', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55: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 (1460, 5, '网上转账', '5', 'crm_receivable_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55: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 (1461, 1, '个', '1', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:26', '1', '2023-12-05 23:02: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 (1462, 2, '块', '2', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:34', '1', '2023-12-05 23:02: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 (1463, 3, '只', '3', 'crm_product_unit', 0, '', '', '', '1', '2023-12-05 23:02:57', '1', '2023-12-05 23:02:57', b'0');
@@ -827,6 +826,38 @@ 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 (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13: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 (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05: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 (1554, 10, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2024-07-06 18:00: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 (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49: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 (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', 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 (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50: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 (1558, 7, '博客文章', '7', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:23', '1', '2024-07-07 15:50: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 (1559, 8, '想法', '8', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:31', '1', '2024-07-07 15:50: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 (1560, 9, '大纲', '9', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:37', '1', '2024-07-07 15:50: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 (1561, 1, '自动', '1', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:06', '1', '2024-07-07 15:51: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 (1562, 2, '友善', '2', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:19', '1', '2024-07-07 15:51: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 (1563, 3, '随意', '3', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:27', '1', '2024-07-07 15:51: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 (1564, 4, '友好', '4', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:37', '1', '2024-07-07 15:51: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 (1565, 5, '专业', '5', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:51:49', '1', '2024-07-07 15:52: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 (1566, 6, '诙谐', '6', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:15', '1', '2024-07-07 15:52: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 (1567, 7, '有趣', '7', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:52:24', '1', '2024-07-07 15:52: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 (1568, 8, '正式', '8', 'ai_write_tone', 0, '', '', '', '1', '2024-07-07 15:54:33', '1', '2024-07-07 15:54: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 (1569, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', 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 (1570, 1, '自动', '1', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:34', '1', '2024-07-07 15:19: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 (1571, 2, '电子邮件', '2', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:19:50', '1', '2024-07-07 15:49:30', 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 (1572, 3, '消息', '3', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:01', '1', '2024-07-07 15:49: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 (1573, 4, '评论', '4', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:20:13', '1', '2024-07-07 15:49:45', 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 (1574, 1, '自动', '1', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:18', '1', '2024-07-07 15:44: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 (1575, 2, '中文', '2', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:28', '1', '2024-07-07 15:44: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 (1576, 3, '英文', '3', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:44:37', '1', '2024-07-07 15:44: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 (1577, 4, '韩语', '4', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:28', '1', '2024-07-07 15:46: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 (1578, 5, '日语', '5', 'ai_write_language', 0, '', '', '', '1', '2024-07-07 15:46:44', '1', '2024-07-07 15:46:44', 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 (1579, 1, '自动', '1', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:34', '1', '2024-07-07 15: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 (1580, 2, '短', '2', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:44', '1', '2024-07-07 15:48:44', 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 (1581, 3, '中等', '3', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:48:52', '1', '2024-07-07 15:48:52', 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 (1582, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49: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 (1584, 1, '撰写', '1', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:00', '1', '2024-07-10 21:26: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 (1585, 2, '回复', '2', 'ai_write_type', 0, '', '', '', '1', '2024-07-10 21:26:06', '1', '2024-07-10 21:26: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 (1586, 2, '腾讯云', 'TENCENT', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:16', '1', '2024-07-22 22:23: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 (1587, 3, '华为云', 'HUAWEI', 'system_sms_channel_code', 0, '', '', '', '1', '2024-07-22 22:23:46', '1', '2024-07-22 22:23:53', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -846,7 +877,7 @@ CREATE TABLE `system_dict_type`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 624 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
+) ENGINE = InnoDB AUTO_INCREMENT = 629 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
 
 -- ----------------------------
 -- Records of system_dict_type
@@ -939,6 +970,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 (621, 'AI 绘画状态', 'ai_image_status', 0, '', '1', '2024-06-26 20:51:23', '1', '2024-06-26 20:51:23', 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 (622, 'AI 音乐状态', 'ai_music_status', 0, '', '1', '2024-06-27 22:45:07', '1', '2024-06-28 00:56:27', 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 (623, 'AI 音乐生成模式', 'ai_generate_mode', 0, '', '1', '2024-06-27 22:46:21', '1', '2024-06-28 01:22:29', 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 (624, '写作语气', 'ai_write_tone', 0, '', '1', '2024-07-07 15:19:02', '1', '2024-07-07 15:19:02', 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 (625, '写作语言', 'ai_write_language', 0, '', '1', '2024-07-07 15:18:52', '1', '2024-07-07 15:18: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 (626, '写作长度', 'ai_write_length', 0, '', '1', '2024-07-07 15:18:41', '1', '2024-07-07 15:18:41', 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 (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', 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 (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00');
 COMMIT;
 
 -- ----------------------------
@@ -962,7 +998,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 = 3177 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 3261 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
 
 -- ----------------------------
 -- Records of system_login_log
@@ -995,7 +1031,7 @@ CREATE TABLE `system_mail_account`  (
 -- Records of system_mail_account
 -- ----------------------------
 BEGIN;
-INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, b'0', b'0', '1', '2023-01-25 17:39:52', '1', '2024-04-24 09:13:56', b'0');
+INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '7684413@qq.com', '7684413@qq.com', '1234576', '127.0.0.1', 8080, b'0', b'0', '1', '2023-01-25 17:39:52', '1', '2024-07-27 22:39:12', b'0');
 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, b'1', b'0', '1', '2023-01-26 01:26:03', '1', '2023-04-12 22:39:38', b'0');
 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, b'0', b'0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', b'1');
 INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `starttls_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '7685413x@qq.com', '2', '3', '4', 5, b'1', b'0', '1', '2023-04-12 23:05:06', '1', '2023-04-12 15:05:11', b'1');
@@ -1093,7 +1129,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 = 2792 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
+) ENGINE = InnoDB AUTO_INCREMENT = 2798 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
 
 -- ----------------------------
 -- Records of system_menu
@@ -1445,7 +1481,7 @@ 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 (2156, '查询项目', 'report:go-view-project:query', 3, 0, 2153, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:25:53', '1', '2023-02-07 19:25: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 (2157, '使用 SQL 查询数据', 'report:go-view-data:get-by-sql', 3, 3, 2153, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:26:15', '1', '2023-02-07 19:26: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 (2158, '使用 HTTP 查询数据', 'report:go-view-data:get-by-http', 3, 4, 2153, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:26:35', '1', '2023-02-07 19: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 (2159, 'Boot 开发文档', '', 1, 1, 0, 'https://doc.iocoder.cn/', 'ep:document', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:46:28', '1', '2023-12-02 21:32: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 (2159, 'Boot 开发文档', '', 1, 1, 0, 'https://doc.iocoder.cn/', 'ep:document', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:46:28', '1', '2024-07-28 11:36:48', 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', 'ep:document-copy', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:47:07', '1', '2023-12-02 21:32: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 (2161, '接入示例', '', 1, 99, 1117, 'demo', 'fa-solid:dragon', 'pay/demo/index', NULL, 0, b'1', b'1', b'1', '', '2023-02-11 14:21:42', '1', '2024-01-18 23: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 (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');
@@ -1921,6 +1957,12 @@ 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 (2789, '音乐查询', 'ai:music:query', 3, 1, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', 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 (2790, '音乐更新', 'ai:music:update', 3, 3, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', 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 (2791, '音乐删除', 'ai:music:delete', 3, 4, 2788, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-06-27 15:03:33', '', '2024-06-27 15:03:33', 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 (2792, 'AI 写作', '', 2, 3, 2758, 'write', 'fa-solid:book-reader', 'ai/write/index/index.vue', 'AiWrite', 0, b'1', b'1', b'1', '1', '2024-07-08 09:26:44', '1', '2024-07-16 13:03: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 (2793, '写作管理', '', 2, 13, 2760, 'write', 'fa:bookmark-o', 'ai/write/manager/index.vue', 'AiWriteManager', 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '1', '2024-07-10 21:31: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 (2794, 'AI 写作查询', 'ai:write:query', 3, 1, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24: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 (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24: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 (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiWrite', 0, b'1', b'1', b'1', '1', '2024-07-17 09:21:12', '1', '2024-07-17 09:36: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 (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, b'1', b'1', b'1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -2042,7 +2084,7 @@ CREATE TABLE `system_oauth2_access_token`  (
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
   INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 7699 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
+) ENGINE = InnoDB AUTO_INCREMENT = 8784 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
 
 -- ----------------------------
 -- Records of system_oauth2_access_token
@@ -2164,7 +2206,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 = 1539 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
+) ENGINE = InnoDB AUTO_INCREMENT = 1598 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
 
 -- ----------------------------
 -- Records of system_oauth2_refresh_token
@@ -2197,7 +2239,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 = 9038 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';
+) ENGINE = InnoDB AUTO_INCREMENT = 9053 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';
 
 -- ----------------------------
 -- Records of system_operate_log
@@ -2265,7 +2307,7 @@ BEGIN;
 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '超级管理员', 'super_admin', 1, 1, '', 0, 1, '超级管理员', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:21', b'0', 1);
 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '普通角色', 'common', 2, 2, '', 0, 1, '普通角色', 'admin', '2021-01-05 17:03:48', '', '2022-02-22 05:08:20', b'0', 1);
 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 'CRM 管理员', 'crm_admin', 2, 1, '', 0, 1, 'CRM 专属角色', '1', '2024-02-24 10:51:13', '1', '2024-02-24 02:51:32', b'0', 1);
-INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (101, '测试账号', 'test', 0, 1, '[]', 0, 2, '我想测试', '', '2021-01-06 13:49:35', '1', '2024-03-24 22:22:45', b'0', 1);
+INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (101, '测试账号', 'test', 0, 2, '[105,106,107]', 0, 2, '', '', '2021-01-06 13:49:35', '1', '2024-07-27 23:30:47', b'0', 1);
 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', b'0', 121);
 INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (111, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
 COMMIT;
@@ -2285,7 +2327,7 @@ CREATE TABLE `system_role_menu`  (
   `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 = 5779 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
+) ENGINE = InnoDB AUTO_INCREMENT = 5793 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
 
 -- ----------------------------
 -- Records of system_role_menu
@@ -2852,7 +2894,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 (3010, 109, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3011, 109, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3012, 109, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3013, 109, 1076, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3014, 109, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3015, 109, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3016, 109, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
@@ -2928,7 +2969,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 (3086, 111, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3087, 111, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3088, 111, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3089, 111, 1076, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3090, 111, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3091, 111, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3092, 111, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
@@ -3114,6 +3154,20 @@ 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 (4280, 111, 1222, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5777, 101, 2739, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', b'0', 1);
 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5778, 101, 2740, '1', '2024-04-30 09:38:37', '1', '2024-04-30 09:38:37', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5779, 2, 2739, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5780, 2, 2740, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5781, 2, 2758, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5782, 2, 2759, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5783, 2, 2362, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5784, 2, 2387, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5785, 2, 2030, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5786, 101, 2758, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5787, 101, 2759, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5788, 101, 2783, '1', '2024-07-07 20:39:55', '1', '2024-07-07 20:39:55', b'0', 1);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5789, 109, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5790, 109, 2740, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5791, 111, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 122);
+INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5792, 111, 2740, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 122);
 COMMIT;
 
 -- ----------------------------
@@ -3168,7 +3222,7 @@ CREATE TABLE `system_sms_code`  (
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
-) ENGINE = InnoDB AUTO_INCREMENT = 615 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
+) ENGINE = InnoDB AUTO_INCREMENT = 628 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
 
 -- ----------------------------
 -- Records of system_sms_code
@@ -3209,7 +3263,7 @@ CREATE TABLE `system_sms_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 = 972 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
+) ENGINE = InnoDB AUTO_INCREMENT = 987 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
 
 -- ----------------------------
 -- Records of system_sms_log
@@ -3374,8 +3428,8 @@ CREATE TABLE `system_tenant`  (
 -- ----------------------------
 BEGIN;
 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2023-11-06 11:41:41', b'0');
-INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2024-05-04 22:37:03', b'0');
-INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2024-05-04 22:36:09', b'0');
+INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn', 111, '2025-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2024-07-20 22:21:53', b'0');
+INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn', 111, '2022-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2024-07-20 15:51:18', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -3400,7 +3454,7 @@ CREATE TABLE `system_tenant_package`  (
 -- Records of system_tenant_package
 -- ----------------------------
 BEGIN;
-INSERT INTO `system_tenant_package` (`id`, `name`, `status`, `remark`, `menu_ids`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, '普通套餐', 0, '小功能', '[1,2,5,1031,1032,1033,1034,1035,1036,1037,1038,1039,1050,1051,1052,1053,1054,1056,1057,1058,1059,1060,1063,1064,1065,1066,1067,1070,1075,1076,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2484,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', '2022-02-22 00:54:00', '1', '2024-03-30 17:53:17', b'0');
+INSERT INTO `system_tenant_package` (`id`, `name`, `status`, `remark`, `menu_ids`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, '普通套餐', 0, '小功能', '[1,2,5,1031,1032,1033,1034,1035,1036,1037,1038,1039,1050,1051,1052,1053,1054,1056,1057,1058,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1118,1119,1120,100,101,102,103,106,107,110,111,112,113,1138,114,1139,115,1140,116,1141,1142,1143,2713,2714,2715,2716,2717,2718,2720,1185,2721,1186,2722,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,2472,1192,2728,1193,2729,1194,2730,1195,2731,1196,2732,1197,2733,2478,1198,2734,2479,1199,2735,2480,1200,2481,1201,2482,1202,2483,2739,2484,2740,2485,2486,2487,1207,2488,1208,2489,1209,2490,1210,2491,1211,2492,1212,2493,1213,2494,2495,1215,1216,2497,1217,1218,1219,1220,1221,1222,1224,1225,1226,1227,1228,1229,1237,1238,1239,1240,1241,1242,1243,2525,1255,1256,1001,1257,1002,1258,1003,1259,1004,1260,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020]', '1', '2022-02-22 00:54:00', '1', '2024-07-13 22:37:24', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -3418,7 +3472,7 @@ CREATE TABLE `system_user_post`  (
   `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 = 125 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户岗位表';
+) ENGINE = InnoDB AUTO_INCREMENT = 126 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户岗位表';
 
 -- ----------------------------
 -- Records of system_user_post
@@ -3432,6 +3486,7 @@ INSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_t
 INSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (119, 114, 5, '1', '2024-03-24 20:45:51', '1', '2024-03-24 20:45:51', b'0', 1);
 INSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (123, 115, 1, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', b'0', 1);
 INSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (124, 115, 2, '1', '2024-04-04 09:37:14', '1', '2024-04-04 09:37:14', b'0', 1);
+INSERT INTO `system_user_post` (`id`, `user_id`, `post_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (125, 1, 2, '1', '2024-07-13 22:31:39', '1', '2024-07-13 22:31:39', b'0', 1);
 COMMIT;
 
 -- ----------------------------
@@ -3504,14 +3559,14 @@ 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', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '0:0:0:0:0:0:0:1', '2024-07-07 17:15:51', 'admin', '2021-01-05 17:03:47', NULL, '2024-07-07 17:15:51', 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,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2024-07-28 11:35:00', 'admin', '2021-01-05 17:03:47', NULL, '2024-07-28 11:35:00', 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, '0:0:0:0:0:0:0:1', '2024-03-18 21:09:04', '', '2021-01-13 23:50:35', NULL, '2024-03-18 21:09:04', 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$04$KhExCYl7lx6eWWZYKsibKOZ8IBJRyuNuCcEOLQ11RYhJKgHmlSwK.', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-06-17 14:23:23', '', '2021-01-21 02:13:53', NULL, '2024-06-17 14:23: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 (104, 'test', '$2a$04$jDFLttgfik0QqJKAbfhMa.2A9xXoZmAIxakdFJUzkX.MgBKT6ddo6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-07-13 23:13:16', '', '2021-01-21 02:13:53', NULL, '2024-07-13 23:13: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 (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2022-02-27 08:26:51', b'0', 118);
 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 (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2022-02-27 08:26:53', b'0', 119);
 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 (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2022-02-27 08:26:56', b'0', 120);
-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 (110, 'admin110', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '小王', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '127.0.0.1', '2022-09-25 22:47:33', '1', '2022-02-22 00:56:14', NULL, '2022-09-25 22:47:33', b'0', 121);
+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 (110, 'admin110', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '小王', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-07-20 22:23:17', '1', '2022-02-22 00:56:14', NULL, '2024-07-20 22:23:17', b'0', 121);
 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 (111, 'test', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '测试用户', NULL, NULL, '[]', '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2023-12-30 11:42:17', '110', '2022-02-23 13:14:33', NULL, '2023-12-30 11:42:17', b'0', 121);
 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 (112, 'newobject', '$2a$04$dB0z8Q819fJWz0hbaLe6B.VfHCjYgWx6LFfET5lyz3JwcqlyCkQ4C', '新对象', NULL, 100, '[]', '', '15601691235', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-03-16 23:11:38', '1', '2022-02-23 19:08:03', NULL, '2024-03-16 23:11:38', 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 (113, 'aoteman', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', '芋道', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '127.0.0.1', '2022-03-19 18:38:51', '1', '2022-03-07 21:37:58', NULL, '2022-03-19 18:38:51', b'0', 122);

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/NumberSerializer.java → yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.jackson.core.databind;
+package cn.iocoder.yudao.framework.common.util.json.databind;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.SerializerProvider;

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/TimestampLocalDateTimeDeserializer.java → yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.jackson.core.databind;
+package cn.iocoder.yudao.framework.common.util.json.databind;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.DeserializationContext;

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/TimestampLocalDateTimeSerializer.java → yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeSerializer.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.jackson.core.databind;
+package cn.iocoder.yudao.framework.common.util.json.databind;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.JsonSerializer;

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java

@@ -157,7 +157,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
         // 拼接条件
         return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName),
                 // Parenthesis 的目的,是提供 (1,2,3) 的 () 左右括号
-                new Parenthesis(new ExpressionList<>(CollectionUtils.convertList(deptIds, LongValue::new))));
+                new Parenthesis(new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new))));
     }
 
     private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {

+ 6 - 0
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/Area.java

@@ -1,9 +1,12 @@
 package cn.iocoder.yudao.framework.ip.core;
 
 import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import lombok.ToString;
 
 import java.util.List;
 
@@ -17,6 +20,7 @@ import java.util.List;
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
+@ToString(exclude = {"parent"}) // 参见 https://gitee.com/yudaocode/yudao-cloud-mini/pulls/2 原因
 public class Area {
 
     /**
@@ -46,10 +50,12 @@ public class Area {
     /**
      * 父节点
      */
+    @JsonManagedReference
     private Area parent;
     /**
      * 子节点
      */
+    @JsonBackReference
     private List<Area> children;
 
 }

+ 7 - 0
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java

@@ -39,4 +39,11 @@ public class TenantProperties {
      */
     private Set<String> ignoreTables = Collections.emptySet();
 
+    /**
+     * 需要忽略多租户的 Spring Cache 缓存
+     *
+     * 即默认所有缓存都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟
+     */
+    private Set<String> ignoreCaches = Collections.emptySet();
+
 }

+ 3 - 2
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java

@@ -120,13 +120,14 @@ public class YudaoTenantAutoConfiguration {
     @Primary // 引入租户时,tenantRedisCacheManager 为主 Bean
     public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate,
                                                      RedisCacheConfiguration redisCacheConfiguration,
-                                                     YudaoCacheProperties yudaoCacheProperties) {
+                                                     YudaoCacheProperties yudaoCacheProperties,
+                                                     TenantProperties tenantProperties) {
         // 创建 RedisCacheWriter 对象
         RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
         RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
                 BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
         // 创建 TenantRedisCacheManager 对象
-        return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration);
+        return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties.getIgnoreCaches());
     }
 
 }

+ 10 - 2
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/redis/TenantRedisCacheManager.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.framework.tenant.core.redis;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import lombok.extern.slf4j.Slf4j;
@@ -8,6 +9,8 @@ import org.springframework.data.redis.cache.RedisCacheConfiguration;
 import org.springframework.data.redis.cache.RedisCacheManager;
 import org.springframework.data.redis.cache.RedisCacheWriter;
 
+import java.util.Set;
+
 /**
  * 多租户的 {@link RedisCacheManager} 实现类
  *
@@ -18,16 +21,21 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
 @Slf4j
 public class TenantRedisCacheManager extends TimeoutRedisCacheManager {
 
+    private final Set<String> ignoreCaches;
+
     public TenantRedisCacheManager(RedisCacheWriter cacheWriter,
-                                   RedisCacheConfiguration defaultCacheConfiguration) {
+                                   RedisCacheConfiguration defaultCacheConfiguration,
+                                   Set<String> ignoreCaches) {
         super(cacheWriter, defaultCacheConfiguration);
+        this.ignoreCaches = ignoreCaches;
     }
 
     @Override
     public Cache getCache(String name) {
         // 如果开启多租户,则 name 拼接租户后缀
         if (!TenantContextHolder.isIgnore()
-            && TenantContextHolder.getTenantId() != null) {
+            && TenantContextHolder.getTenantId() != null
+            && !CollUtil.contains(ignoreCaches, name)) {
             name = name + ":" + TenantContextHolder.getTenantId();
         }
 

+ 0 - 7
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.excel.core.handler;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
-import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
@@ -55,12 +54,6 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
     private final Map<Integer, List<String>> selectMap = new HashMap<>();
 
     public SelectSheetWriteHandler(Class<?> head) {
-        // 加载下拉数据获取接口
-        Map<String, ExcelColumnSelectFunction> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class);
-        if (MapUtil.isEmpty(beansMap)) {
-            return;
-        }
-
         // 解析下拉数据
         int colIndex = 0;
         for (Field field : head.getDeclaredFields()) {

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

@@ -56,7 +56,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
 
     default <D> PageResult<D> selectJoinPage(PageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {
         // 特殊:不分页,直接查询全部
-        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageNo())) {
+        if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
             List<D> list = selectJoinList(clazz, lambdaWrapper);
             return new PageResult<>(list, (long) list.size());
         }

+ 5 - 0
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.Data;
 
+import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -42,6 +43,10 @@ public class LoginUser {
      * 授权范围
      */
     private List<String> scopes;
+    /**
+     * 过期时间
+     */
+    private LocalDateTime expiresTime;
 
     // ========== 上下文 ==========
     /**

+ 2 - 1
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java

@@ -84,7 +84,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
             // 构建登录用户
             return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
                     .setInfo(accessToken.getUserInfo()) // 额外的用户信息
-                    .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes());
+                    .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
+                    .setExpiresTime(accessToken.getExpiresTime());
         } catch (ServiceException serviceException) {
             // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
             return null;

+ 10 - 2
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java

@@ -27,7 +27,11 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
 
     @Override
     public boolean hasAnyPermissions(String... permissions) {
-        return permissionApi.hasAnyPermissions(getLoginUserId(), permissions);
+        Long userId = getLoginUserId();
+        if (userId == null) {
+            return false;
+        }
+        return permissionApi.hasAnyPermissions(userId, permissions);
     }
 
     @Override
@@ -37,7 +41,11 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
 
     @Override
     public boolean hasAnyRoles(String... roles) {
-        return permissionApi.hasAnyRoles(getLoginUserId(), roles);
+        Long userId = getLoginUserId();
+        if (userId == null) {
+            return false;
+        }
+        return permissionApi.hasAnyRoles(userId, roles);
     }
 
     @Override

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

@@ -103,6 +103,10 @@ public class RandomUtils {
         return randomString() + "@qq.com";
     }
 
+    public static String randomMobile() {
+        return "13800138" + RandomUtil.randomNumbers(3);
+    }
+
     public static String randomURL() {
         return "https://www.iocoder.cn/" + randomString();
     }

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java

@@ -181,7 +181,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
     // ========== 请求和响应的脱敏逻辑,移除类似 password、token 等敏感字段 ==========
 
     private static String sanitizeMap(Map<String, ?> map, String[] sanitizeKeys) {
-        if (CollUtil.isNotEmpty(map)) {
+        if (CollUtil.isEmpty(map)) {
             return null;
         }
         if (sanitizeKeys != null) {

+ 36 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.framework.apilog.core.interceptor;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.resource.ResourceUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
@@ -11,7 +13,11 @@ import org.springframework.util.StopWatch;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerInterceptor;
 
+import java.lang.reflect.Method;
+import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+import java.util.stream.IntStream;
 
 /**
  * API 访问日志 Interceptor
@@ -49,6 +55,8 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
             StopWatch stopWatch = new StopWatch();
             stopWatch.start();
             request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
+            // 打印 Controller 路径
+            printHandlerMethodPosition(handlerMethod);
         }
         return true;
     }
@@ -64,4 +72,32 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
         }
     }
 
+    /**
+     * 打印 Controller 方法路径
+     */
+    private void printHandlerMethodPosition(HandlerMethod handlerMethod) {
+        if (handlerMethod == null) {
+            return;
+        }
+        Method method = handlerMethod.getMethod();
+        Class<?> clazz = method.getDeclaringClass();
+        try {
+            // 获取 method 的 lineNumber
+            List<String> clazzContents = FileUtil.readUtf8Lines(
+                    ResourceUtil.getResource(null, clazz).getPath().replace("/target/classes/", "/src/main/java/")
+                            + clazz.getSimpleName() + ".java");
+            Optional<Integer> lineNumber = IntStream.range(0, clazzContents.size())
+                    .filter(i -> clazzContents.get(i).contains(" " + method.getName() + "(")) // 简单匹配,不考虑方法重名
+                    .mapToObj(i -> i + 1) // 行号从 1 开始
+                    .findFirst();
+            if (!lineNumber.isPresent()) {
+                return;
+            }
+            // 打印结果
+            System.out.printf("\tController 方法路径:%s(%s.java:%d)\n", clazz.getName(), clazz.getSimpleName(), lineNumber.get());
+        } catch (Exception ignore) {
+            // 忽略异常。原因:仅仅打印,非重要逻辑
+        }
+    }
+
 }

+ 8 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service;
 import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Async;
 
 /**
@@ -13,6 +14,7 @@ import org.springframework.scheduling.annotation.Async;
  * @author 芋道源码
  */
 @RequiredArgsConstructor
+@Slf4j
 public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService {
 
     private final ApiAccessLogApi apiAccessLogApi;
@@ -20,7 +22,12 @@ public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkSe
     @Override
     @Async
     public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) {
-        apiAccessLogApi.createApiAccessLog(reqDTO);
+        try {
+            apiAccessLogApi.createApiAccessLog(reqDTO);
+        } catch (Throwable ex) {
+            // 由于 @Async 异步调用,这里打印下日志,更容易跟进
+            log.error("[createApiAccessLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex);
+        }
     }
 
 }

+ 8 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service;
 import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Async;
 
 /**
@@ -13,6 +14,7 @@ import org.springframework.scheduling.annotation.Async;
  * @author 芋道源码
  */
 @RequiredArgsConstructor
+@Slf4j
 public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService {
 
     private final ApiErrorLogApi apiErrorLogApi;
@@ -20,7 +22,12 @@ public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkServ
     @Override
     @Async
     public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) {
-        apiErrorLogApi.createApiErrorLog(reqDTO);
+        try {
+            apiErrorLogApi.createApiErrorLog(reqDTO);
+        } catch (Throwable ex) {
+            // 由于 @Async 异步调用,这里打印下日志,更容易跟进
+            log.error("[createApiErrorLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex);
+        }
     }
 
 }

+ 3 - 3
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java

@@ -2,9 +2,9 @@ package cn.iocoder.yudao.framework.jackson.config;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.jackson.core.databind.NumberSerializer;
-import cn.iocoder.yudao.framework.jackson.core.databind.TimestampLocalDateTimeDeserializer;
-import cn.iocoder.yudao.framework.jackson.core.databind.TimestampLocalDateTimeSerializer;
+import cn.iocoder.yudao.framework.common.util.json.databind.NumberSerializer;
+import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
+import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;

+ 34 - 2
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java

@@ -2,9 +2,11 @@ package cn.iocoder.yudao.framework.web.core.handler;
 
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -30,6 +32,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
 import org.springframework.web.servlet.NoHandlerFoundException;
+import org.springframework.web.servlet.resource.NoResourceFoundException;
 
 import java.time.LocalDateTime;
 import java.util.Map;
@@ -87,6 +90,9 @@ public class GlobalExceptionHandler {
         if (ex instanceof NoHandlerFoundException) {
             return noHandlerFoundExceptionHandler(request, (NoHandlerFoundException) ex);
         }
+        if (ex instanceof NoResourceFoundException) {
+            return noResourceFoundExceptionHandler(request, (NoResourceFoundException) ex);
+        }
         if (ex instanceof HttpRequestMethodNotSupportedException) {
             return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex);
         }
@@ -176,6 +182,15 @@ public class GlobalExceptionHandler {
         return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getRequestURL()));
     }
 
+    /**
+     * 处理 SpringMVC 请求地址不存在
+     */
+    @ExceptionHandler(NoResourceFoundException.class)
+    private CommonResult<?> noResourceFoundExceptionHandler(HttpServletRequest req, NoResourceFoundException ex) {
+        log.warn("[noResourceFoundExceptionHandler]", ex);
+        return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getResourcePath()));
+    }
+
     /**
      * 处理 SpringMVC 请求方法不正确
      *
@@ -206,9 +221,20 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(value = ServiceException.class)
     public CommonResult<?> serviceExceptionHandler(ServiceException ex) {
+        // 不包含的时候,才进行打印,避免 ex 堆栈过多
         if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) {
-            // 不包含的时候,才进行打印,避免 ex 堆栈过多
-            log.info("[serviceExceptionHandler]", ex);
+            // 即使打印,也只打印第一层 StackTraceElement,并且使用 warn 在控制台输出,更容易看到
+            try {
+                StackTraceElement[] stackTraces = ex.getStackTrace();
+                for (StackTraceElement stackTrace : stackTraces) {
+                    if (ObjUtil.notEqual(stackTrace.getClassName(), ServiceExceptionUtil.class.getName())) {
+                        log.warn("[serviceExceptionHandler]\n\t{}", stackTrace);
+                        break;
+                    }
+                }
+            } catch (Exception ignored) {
+                // 忽略日志,避免影响主流程
+            }
         }
         return CommonResult.error(ex.getCode(), ex.getMessage());
     }
@@ -328,6 +354,12 @@ public class GlobalExceptionHandler {
             return CommonResult.error(NOT_IMPLEMENTED.getCode(),
                     "[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]");
         }
+        // 8. AI 大模型
+        if (message.contains("ai_")) {
+            log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://doc.iocoder.cn/ai/build/ 开启]");
+            return CommonResult.error(NOT_IMPLEMENTED.getCode(),
+                    "[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://doc.iocoder.cn/ai/build/ 开启]");
+        }
         return null;
     }
 

+ 1 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.ai.controller.admin.image;
 import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
@@ -41,7 +40,7 @@ public class AiImageController {
 
     @GetMapping("/my-page")
     @Operation(summary = "获取【我的】绘图分页")
-    public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated PageParam pageReqVO) {
+    public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated AiImagePageReqVO pageReqVO) {
         PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
         return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
     }

+ 3 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java

@@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 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;
@@ -21,6 +19,9 @@ public class AiImagePageReqVO extends PageParam {
     @Schema(description = "平台", example = "OpenAI")
     private String platform;
 
+    @Schema(description = "提示词", example = "1")
+    private String prompt;
+
     @Schema(description = "绘画状态", example = "1")
     private Integer status;
 

+ 8 - 5
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.ai.dal.mysql.image;
 
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -19,7 +18,7 @@ import java.util.List;
 public interface AiImageMapper extends BaseMapperX<AiImageDO> {
 
     default AiImageDO selectByTaskId(String taskId) {
-        return this.selectOne(AiImageDO::getTaskId, taskId);
+        return selectOne(AiImageDO::getTaskId, taskId);
     }
 
     default PageResult<AiImageDO> selectPage(AiImagePageReqVO reqVO) {
@@ -32,9 +31,13 @@ public interface AiImageMapper extends BaseMapperX<AiImageDO> {
                 .orderByDesc(AiImageDO::getId));
     }
 
-    default PageResult<AiImageDO> selectPage(Long userId, PageParam pageReqVO) {
-        return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>()
-                .eq(AiImageDO::getUserId, userId)
+    default PageResult<AiImageDO> selectPageMy(Long userId, AiImagePageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()
+                .likeIfPresent(AiImageDO::getPrompt, reqVO.getPrompt())
+                // 情况一:公开
+                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiImageDO::getPublicStatus, reqVO.getPublicStatus())
+                // 情况二:私有
+                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiImageDO::getUserId, userId)
                 .orderByDesc(AiImageDO::getId));
     }
 

+ 1 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.ai.service.image;
 
 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
@@ -27,7 +26,7 @@ public interface AiImageService {
      * @param pageReqVO 分页条件
      * @return 绘图分页
      */
-    PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO);
+    PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO);
 
     /**
      * 获得绘图记录

+ 2 - 3
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java

@@ -10,7 +10,6 @@ import cn.hutool.extra.spring.SpringUtil;
 import cn.hutool.http.HttpUtil;
 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
@@ -67,8 +66,8 @@ public class AiImageServiceImpl implements AiImageService {
     private AiApiKeyService apiKeyService;
 
     @Override
-    public PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO) {
-        return imageMapper.selectPage(userId, pageReqVO);
+    public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {
+        return imageMapper.selectPageMy(userId, pageReqVO);
     }
 
     @Override

+ 1 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java

@@ -124,7 +124,7 @@ public class AiMindMapServiceImpl implements AiMindMapService {
         if (role != null && role.getModelId() != null) {
             model = chatModalService.getChatModel(role.getModelId());
         }
-        if (model != null) {
+        if (model == null) {
             model = chatModalService.getRequiredDefaultChatModel();
         }
         Assert.notNull(model, "[AI] 获取不到模型");

+ 1 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java

@@ -114,7 +114,7 @@ public class AiWriteServiceImpl implements AiWriteService {
         if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {
             model = chatModalService.getChatModel(writeRole.getModelId());
         }
-        if (Objects.isNull(model)) {
+        if (model == null) {
             model = chatModalService.getRequiredDefaultChatModel();
         }
         Assert.notNull(model, "[AI] 获取不到模型");

+ 6 - 0
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml

@@ -58,6 +58,12 @@
             <groupId>com.alibaba</groupId>
             <artifactId>dashscope-sdk-java</artifactId>
             <version>2.14.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
 
         <!-- Test 测试相关 -->

+ 0 - 3
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/DictTypeConstants.java

@@ -7,7 +7,4 @@ package cn.iocoder.yudao.module.bpm.enums;
  */
 public interface DictTypeConstants {
 
-    String TASK_ASSIGN_RULE_TYPE = "bpm_task_assign_rule_type"; // 任务分配规则类型
-    String TASK_ASSIGN_SCRIPT = "bpm_task_assign_script"; // 任务分配自定义脚本
-
 }

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java

@@ -33,7 +33,7 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
 
     @Override
     public int[] array() {
-        return new int[0];
+        return ARRAYS;
     }
 
 }

+ 2 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java

@@ -77,7 +77,6 @@ public class BpmProcessDefinitionController {
     @GetMapping ("/list")
     @Operation(summary = "获得流程定义列表")
     @Parameter(name = "suspensionState", description = "挂起状态", required = true, example = "1") // 参见 Flowable SuspensionState 枚举
-    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
     public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
             @RequestParam("suspensionState") Integer suspensionState) {
         List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState);
@@ -96,7 +95,6 @@ public class BpmProcessDefinitionController {
     @Operation(summary = "获得流程定义")
     @Parameter(name = "id", description = "流程编号", required = true, example = "1024")
     @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024")
-    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
     public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition(
             @RequestParam(value = "id", required = false) String id,
             @RequestParam(value = "key", required = false) String key) {
@@ -105,10 +103,11 @@ public class BpmProcessDefinitionController {
         if (processDefinition == null) {
             return success(null);
         }
+        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
         BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
         List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
         return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
-                processDefinition, null, null, null, null, bpmnModel, userTaskList));
+                processDefinition, null, processDefinitionInfo, null, null, bpmnModel, userTaskList));
     }
 
 }

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java

@@ -135,7 +135,7 @@ public class BpmProcessInstanceController {
                 processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()));
         AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId()));
         DeptRespDTO dept = null;
-        if (startUser != null) {
+        if (startUser != null && startUser.getDeptId() != null) {
             dept = deptApi.getDept(startUser.getDeptId());
         }
         return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance,

+ 2 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java

@@ -47,7 +47,8 @@ public interface BpmProcessInstanceConvert {
             BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i);
             respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i)));
             MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(),
-                    processDefinition -> respVO.setCategory(processDefinition.getCategory()));
+                    processDefinition -> respVO.setCategory(processDefinition.getCategory())
+                            .setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)));
             MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName()));
             respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class));
             // user

+ 7 - 6
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java

@@ -1,6 +1,9 @@
 package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
 
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
+import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -27,7 +30,8 @@ public class CrmReceivableRespVO {
     private Long planId;
 
     @Schema(description = "回款方式", example = "2")
-    @ExcelProperty("回款方式")
+    @ExcelProperty(value = "回款方式", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.CRM_RECEIVABLE_RETURN_TYPE)
     private Integer returnType;
 
     @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000")
@@ -39,7 +43,6 @@ public class CrmReceivableRespVO {
     private LocalDateTime returnTime;
 
     @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
-    @ExcelProperty("客户编号")
     private Long customerId;
     @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
     @ExcelProperty("客户名字")
@@ -49,11 +52,9 @@ public class CrmReceivableRespVO {
     @ExcelProperty("合同编号")
     private Long contractId;
     @Schema(description = "合同信息")
-    @ExcelProperty("合同信息")
     private CrmContractRespVO contract;
 
     @Schema(description = "负责人的用户编号", example = "25682")
-    @ExcelProperty("负责人的用户编号")
     private Long ownerUserId;
     @Schema(description = "负责人名字", example = "25682")
     @ExcelProperty("负责人名字")
@@ -67,7 +68,8 @@ public class CrmReceivableRespVO {
     private String processInstanceId;
 
     @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
-    @ExcelProperty("审批状态")
+    @ExcelProperty(value = "审批状态", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.CRM_AUDIT_STATUS)
     private Integer auditStatus;
 
     @Schema(description = "工作流编号", example = "备注")
@@ -83,7 +85,6 @@ public class CrmReceivableRespVO {
     private LocalDateTime updateTime;
 
     @Schema(description = "创建人", example = "25682")
-    @ExcelProperty("创建人")
     private String creator;
     @Schema(description = "创建人名字", example = "test")
     @ExcelProperty("创建人名字")

+ 2 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/statistics/ErpPurchaseStatisticsMapper.xml

@@ -10,6 +10,7 @@
                <if test="endTime != null">
                    AND in_time &lt; #{endTime}
                </if>
+               AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()}
                AND deleted = 0) -
             (SELECT IFNULL(SUM(total_price), 0)
                 FROM erp_purchase_return
@@ -17,6 +18,7 @@
                 <if test="endTime != null">
                     AND return_time &lt; #{endTime}
                 </if>
+                AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()}
                 AND deleted = 0)
     </select>
 

+ 2 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/statistics/ErpSaleStatisticsMapper.xml

@@ -10,6 +10,7 @@
                <if test="endTime != null">
                    AND out_time &lt; #{endTime}
                </if>
+               AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()}
                AND deleted = 0) -
             (SELECT IFNULL(SUM(total_price), 0)
                 FROM erp_sale_return
@@ -17,6 +18,7 @@
                 <if test="endTime != null">
                     AND return_time &lt; #{endTime}
                 </if>
+                AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()}
                 AND deleted = 0)
     </select>
 

+ 18 - 0
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.infra.api.config;
+
+/**
+ * 参数配置 API 接口
+ *
+ * @author 芋道源码
+ */
+public interface ConfigApi {
+
+    /**
+     * 根据参数键查询参数值
+     *
+     * @param key 参数键
+     * @return 参数值
+     */
+    String getConfigValueByKey(String key);
+
+}

+ 27 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApiImpl.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.infra.api.config;
+
+import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
+import cn.iocoder.yudao.module.infra.service.config.ConfigService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * 参数配置 API 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class ConfigApiImpl implements ConfigApi {
+
+    @Resource
+    private ConfigService configService;
+
+    @Override
+    public String getConfigValueByKey(String key) {
+        ConfigDO config = configService.getConfigByKey(key);
+        return config != null ? config.getValue() : null;
+    }
+
+}

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java

@@ -94,7 +94,7 @@ public class ConfigController {
     @Operation(summary = "导出参数配置")
     @PreAuthorize("@ss.hasPermission('infra:config:export')")
     @ApiAccessLog(operateType = EXPORT)
-    public void exportConfig(@Valid ConfigPageReqVO exportReqVO,
+    public void exportConfig(ConfigPageReqVO exportReqVO,
                              HttpServletResponse response) throws IOException {
         exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
         List<ConfigDO> list = configService.getConfigPage(exportReqVO).getList();

+ 12 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java

@@ -37,6 +37,7 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
                 .region(buildRegion()) // Region
                 .credentials(config.getAccessKey(), config.getAccessSecret()) // 认证密钥
                 .build();
+        enableVirtualStyleEndpoint();
     }
 
     /**
@@ -86,6 +87,17 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
         return null;
     }
 
+    /**
+     * 开启 VirtualStyle 模式
+     */
+    private void enableVirtualStyleEndpoint() {
+        if (StrUtil.containsAll(config.getEndpoint(),
+                S3FileClientConfig.ENDPOINT_TENCENT, // 腾讯云 https://cloud.tencent.com/document/product/436/41284
+                S3FileClientConfig.ENDPOINT_VOLCES)) { // 火山云 https://www.volcengine.com/docs/6349/1288493
+            client.enableVirtualStyleEndpoint();
+        }
+    }
+
     @Override
     public String upload(byte[] content, String path, String type) throws Exception {
         // 执行上传

+ 1 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java

@@ -20,6 +20,7 @@ public class S3FileClientConfig implements FileClientConfig {
     public static final String ENDPOINT_QINIU = "qiniucs.com";
     public static final String ENDPOINT_ALIYUN = "aliyuncs.com";
     public static final String ENDPOINT_TENCENT = "myqcloud.com";
+    public static final String ENDPOINT_VOLCES = "volces.com"; // 火山云(字节)
 
     /**
      * 节点地址

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigService.java

@@ -58,6 +58,6 @@ public interface ConfigService {
      * @param reqVO 分页条件
      * @return 分页列表
      */
-    PageResult<ConfigDO> getConfigPage(@Valid ConfigPageReqVO reqVO);
+    PageResult<ConfigDO> getConfigPage(ConfigPageReqVO reqVO);
 
 }

+ 8 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.infra.service.logger;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
 import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO;
@@ -35,7 +37,12 @@ public class ApiAccessLogServiceImpl implements ApiAccessLogService {
         ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class);
         apiAccessLog.setRequestParams(StrUtil.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));
         apiAccessLog.setResultMsg(StrUtil.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH));
-        apiAccessLogMapper.insert(apiAccessLog);
+        if (TenantContextHolder.getTenantId() != null) {
+            apiAccessLogMapper.insert(apiAccessLog);
+        } else {
+            // 极端情况下,上下文中没有租户时,此时忽略租户上下文,避免插入失败!
+            TenantUtils.executeIgnore(() -> apiAccessLogMapper.insert(apiAccessLog));
+        }
     }
 
     @Override

+ 9 - 2
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.infra.service.logger;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
 import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO;
@@ -25,9 +27,9 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_L
  *
  * @author 芋道源码
  */
-@Slf4j
 @Service
 @Validated
+@Slf4j
 public class ApiErrorLogServiceImpl implements ApiErrorLogService {
 
     @Resource
@@ -38,7 +40,12 @@ public class ApiErrorLogServiceImpl implements ApiErrorLogService {
         ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class)
                 .setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus());
         apiErrorLog.setRequestParams(StrUtil.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));
-        apiErrorLogMapper.insert(apiErrorLog);
+        if (TenantContextHolder.getTenantId() != null) {
+            apiErrorLogMapper.insert(apiErrorLog);
+        } else {
+            // 极端情况下,上下文中没有租户时,此时忽略租户上下文,避免插入失败!
+            TenantUtils.executeIgnore(() -> apiErrorLogMapper.insert(apiErrorLog));
+        }
     }
 
     @Override

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm

@@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" (
     "${column.columnName}" ${dataType} DEFAULT '',
     #elseif (${column.columnName} == 'deleted')
     "deleted" bit NOT NULL DEFAULT FALSE,
-    #elseif (${column.columnName} == 'tenantId')
+    #elseif (${column.columnName} == 'tenant_id')
     "tenant_id" bigint NOT NULL DEFAULT 0,
     #else
     "${column.columnName.toLowerCase()}" ${dataType}#if (${column.nullable} == false) NOT NULL#end,

+ 2 - 2
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm

@@ -306,8 +306,8 @@ export default {
       await this.#[[$modal]]#.confirm('是否确认导出所有${table.classComment}数据项?');
       try {
         this.exportLoading = true;
-        const res = await ${simpleClassName}Api.export${simpleClassName}Excel(this.queryParams);
-        this.#[[$]]#download.excel(res.data, '${table.classComment}.xls');
+        const data = await ${simpleClassName}Api.export${simpleClassName}Excel(this.queryParams);
+        this.#[[$]]#download.excel(data, '${table.classComment}.xls');
       } catch {
       } finally {
         this.exportLoading = false;

+ 2 - 2
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm

@@ -74,7 +74,7 @@
           start-placeholder="开始日期"
           end-placeholder="结束日期"
           :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-240px"
+          class="!w-220px"
         />
       </el-form-item>
       #end
@@ -181,7 +181,7 @@
         #end
       #end
     #end
-      <el-table-column label="操作" align="center">
+      <el-table-column label="操作" align="center" min-width="120px">
         <template #default="scope">
           <el-button
             link

+ 2 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm

@@ -4,7 +4,8 @@ import { columns, searchFormSchema } from './${classNameVar}.data'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useMessage } from '@/hooks/web/useMessage'
 import { useModal } from '@/components/Modal'
-import { useTable } from '@/components/Table'
+import { IconEnum } from '@/enums/appEnum'
+import { BasicTable, TableAction, useTable } from '@/components/Table'
 import { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}'
 
 defineOptions({ name: '${table.className}' })

+ 1 - 1
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java

@@ -112,7 +112,7 @@ public class AppProductSpuController {
         productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id);
 
         // 拼接返回
-        spu.setBrowseCount(spu.getBrowseCount() + spu.getVirtualSalesCount());
+        spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount());
         AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class)
                 .setSkus(BeanUtils.toBean(skus, AppProductSpuDetailRespVO.Sku.class));
         // 处理 vip 价格

+ 8 - 5
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/sku/ProductSkuMapper.java

@@ -26,7 +26,7 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
     }
 
     /**
-     * 更新 SKU 库存(增加)
+     * 更新 SKU 库存(增加)、销量(减少)
      *
      * @param id        编号
      * @param incrCount 增加库存(正数)
@@ -34,13 +34,14 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
     default void updateStockIncr(Long id, Integer incrCount) {
         Assert.isTrue(incrCount > 0);
         LambdaUpdateWrapper<ProductSkuDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()
-                .setSql(" stock = stock + " + incrCount)
+                .setSql(" stock = stock + " + incrCount
+                    + ", sales_count = sales_count - " + incrCount)
                 .eq(ProductSkuDO::getId, id);
         update(null, lambdaUpdateWrapper);
     }
 
     /**
-     * 更新 SKU 库存(减少)
+     * 更新 SKU 库存(减少)、销量(增加)
      *
      * @param id        编号
      * @param incrCount 减少库存(负数)
@@ -48,10 +49,12 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
      */
     default int updateStockDecr(Long id, Integer incrCount) {
         Assert.isTrue(incrCount < 0);
+        incrCount = - incrCount; // 取正
         LambdaUpdateWrapper<ProductSkuDO> updateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()
-                .setSql(" stock = stock + " + incrCount) // 负数,所以使用 + 号
+                .setSql(" stock = stock - " + incrCount
+                    + ", sales_count = sales_count + " + incrCount)
                 .eq(ProductSkuDO::getId, id)
-                .ge(ProductSkuDO::getStock, -incrCount); // cas 逻辑
+                .ge(ProductSkuDO::getStock, incrCount);
         return update(null, updateWrapper);
     }
 

+ 12 - 2
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java

@@ -85,9 +85,19 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
      * @param incrCount 增加的库存数量
      */
     default void updateStock(Long id, Integer incrCount) {
+        // 拼接 SQL
+        if (incrCount == 0) {
+            return;
+        }
+        String sql;
+        if (incrCount > 0) {
+            sql = " stock = stock + " + incrCount + ", sales_count = sales_count - " + incrCount;
+        } else {
+            sql = " stock = stock - " + Math.abs(incrCount) + ", sales_count = sales_count + " + Math.abs(incrCount);
+        }
+        // 执行更新
         LambdaUpdateWrapper<ProductSpuDO> updateWrapper = new LambdaUpdateWrapper<ProductSpuDO>()
-                // 负数,所以使用 + 号
-                .setSql(" stock = stock +" + incrCount)
+                .setSql(sql)
                 .eq(ProductSpuDO::getId, id);
         update(null, updateWrapper);
     }

+ 2 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordReqPageVO.java

@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod
 
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;
+import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -20,7 +20,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
 public class CombinationRecordReqPageVO extends PageParam {
 
     @Schema(description = "活动状态", example = "1")
-    @InEnum(BargainRecordStatusEnum.class)
+    @InEnum(CombinationRecordStatusEnum.class)
     private Integer status;
 
     @Schema(description = "团长编号", example = "1024")

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java

@@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.*;
-import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert;
+import cn.iocoder.yudao.module.promotion.convert.seckill.SeckillActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
 import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java

@@ -4,7 +4,7 @@ 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.promotion.controller.admin.seckill.vo.config.*;
-import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillConfigConvert;
+import cn.iocoder.yudao.module.promotion.convert.seckill.SeckillConfigConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
 import cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigService;
 import io.swagger.v3.oas.annotations.Operation;

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java

@@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppS
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityNowRespVO;
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityRespVO;
-import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert;
+import cn.iocoder.yudao.module.promotion.convert.seckill.SeckillActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.promotion.controller.app.seckill;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO;
-import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillConfigConvert;
+import cn.iocoder.yudao.module.promotion.convert.seckill.SeckillConfigConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
 import cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigService;
 import io.swagger.v3.oas.annotations.Operation;

+ 2 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java

@@ -68,10 +68,11 @@ public interface CombinationActivityConvert {
                                                                       List<ProductSpuRespDTO> spuList) {
         PageResult<CombinationActivityPageItemRespVO> pageResult = convertPage(page);
         Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
+        Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
         pageResult.getList().forEach(item -> {
             MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())
                     .setMarketPrice(spu.getMarketPrice()));
-            item.setProducts(convertList2(productList));
+            item.setProducts(convertList2(productMap.get(item.getId())));
             // 设置统计字段
             item.setGroupCount(groupCountMap.getOrDefault(item.getId(), 0))
                     .setGroupSuccessCount(groupSuccessCountMap.getOrDefault(item.getId(), 0))

+ 6 - 6
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java → yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/SeckillActivityConvert.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity;
+package cn.iocoder.yudao.module.promotion.convert.seckill;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.Sec
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityDetailRespVO;
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityNowRespVO;
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityRespVO;
-import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillConfigConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
@@ -56,10 +55,11 @@ public interface SeckillActivityConvert {
         PageResult<SeckillActivityRespVO> pageResult = convertPage(page);
         // 拼接商品
         Map<Long, ProductSpuRespDTO> spuMap = CollectionUtils.convertMap(spuList, ProductSpuRespDTO::getId);
-        pageResult.getList().forEach(item -> {
-            item.setProducts(convertList2(seckillProducts));
-            MapUtils.findAndThen(spuMap, item.getSpuId(),
-                    spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
+        Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(seckillProducts, SeckillProductDO::getActivityId);
+        pageResult.getList().forEach(activity -> {
+            activity.setProducts(convertList2(productMap.get(activity.getId())));
+            MapUtils.findAndThen(spuMap, activity.getSpuId(),
+                    spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
         });
         return pageResult;
     }

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillconfig/SeckillConfigConvert.java → yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/SeckillConfigConvert.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig;
+package cn.iocoder.yudao.module.promotion.convert.seckill;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO;

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java

@@ -215,7 +215,7 @@ public class CouponServiceImpl implements CouponService {
         int count = 0;
         for (CouponDO coupon : list) {
             try {
-                boolean success = getSelf().expireCoupon(coupon);
+                boolean success = expireCoupon(coupon);
                 if (success) {
                     count++;
                 }

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java

@@ -17,7 +17,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.Se
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;
 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
-import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert;
+import cn.iocoder.yudao.module.promotion.convert.seckill.SeckillActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillConfigServiceImpl.java

@@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigUpdateReqVO;
-import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillConfigConvert;
+import cn.iocoder.yudao.module.promotion.convert.seckill.SeckillConfigConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig.SeckillConfigMapper;
 import org.springframework.stereotype.Service;

+ 5 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http

@@ -7,3 +7,8 @@ tenant-id: {{adminTenentId}}
 GET {{baseUrl}}/trade/order/get-detail?id=21
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
+
+### 获得交易订单的物流轨迹 => 成功
+GET {{baseUrl}}/trade/order/get-express-track-list?id=21
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenentId}}

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java

@@ -176,7 +176,7 @@ public interface TradeOrderConvert {
                                                 TradeOrderProperties tradeOrderProperties,
                                                 DeliveryExpressDO express) {
         AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);
-        orderVO.setPayExpireTime(addTime(tradeOrderProperties.getPayExpireTime()));
+        orderVO.setPayExpireTime(order.getCreateTime().plus(tradeOrderProperties.getPayExpireTime()));
         if (StrUtil.isNotEmpty(order.getPayChannelCode())) {
             orderVO.setPayChannelName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CHANNEL_CODE, order.getPayChannelCode()));
         }

+ 5 - 6
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java

@@ -50,10 +50,9 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
                 .eq(BrokerageRecordDO::getStatus, status));
     }
 
-    default BrokerageRecordDO selectByBizTypeAndBizIdAndUserId(Integer bizType, String bizId, Long userId) {
-        return selectOne(BrokerageRecordDO::getBizType, bizType,
-                BrokerageRecordDO::getBizId, bizId,
-                BrokerageRecordDO::getUserId, userId);
+    default List<BrokerageRecordDO> selectListByBizTypeAndBizId(Integer bizType, String bizId) {
+        return selectList(BrokerageRecordDO::getBizType, bizType,
+                BrokerageRecordDO::getBizId, bizId);
     }
 
     default List<UserBrokerageSummaryRespBO> selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(Collection<Long> userIds,
@@ -64,7 +63,7 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
                 .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryRespBO::getCount)
                 .selectSum(BrokerageRecordDO::getPrice)
                 .in(BrokerageRecordDO::getUserId, userIds)
-                .eq(BrokerageRecordDO::getBizId, bizType)
+                .eq(BrokerageRecordDO::getBizType, bizType)
                 .eq(BrokerageRecordDO::getStatus, status)
                 .groupBy(BrokerageRecordDO::getUserId)); // 按照 userId 聚合
         return BeanUtil.copyToList(list, UserBrokerageSummaryRespBO.class);
@@ -74,7 +73,7 @@ public interface BrokerageRecordMapper extends BaseMapperX<BrokerageRecordDO> {
 //                    .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount)
 //                    .selectSum(BrokerageRecordDO::getPrice)
 //                    .in(BrokerageRecordDO::getUserId, userIds)
-//                    .eq(BrokerageRecordDO::getBizId, bizType)
+//                    .eq(BrokerageRecordDO::getBizType, bizType)
 //                    .eq(BrokerageRecordDO::getStatus, status)
 //                    .groupBy(BrokerageRecordDO::getUserId));
     }

+ 1 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordService.java

@@ -78,11 +78,10 @@ public interface BrokerageRecordService {
     /**
      * 取消佣金:将佣金记录,状态修改为已失效
      *
-     * @param userId  会员编号
      * @param bizType 业务类型
      * @param bizId   业务编号
      */
-    void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId);
+    void cancelBrokerage(BrokerageRecordBizTypeEnum bizType, String bizId);
 
     /**
      * 解冻佣金:将待结算的佣金记录,状态修改为已结算

+ 19 - 17
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java

@@ -107,27 +107,29 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void cancelBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId) {
-        BrokerageRecordDO record = brokerageRecordMapper.selectByBizTypeAndBizIdAndUserId(bizType.getType(), bizId, userId);
-        if (record == null) {
-            log.error("[cancelBrokerage][userId({})][bizId({}) 更新为已失效失败:记录不存在]", userId, bizId);
+    public void cancelBrokerage(BrokerageRecordBizTypeEnum bizType, String bizId) {
+        List<BrokerageRecordDO> records = brokerageRecordMapper.selectListByBizTypeAndBizId(bizType.getType(), bizId);
+        if (CollUtil.isEmpty(records)) {
+            log.error("[cancelBrokerage][bizId({}) bizType({}) 更新为已失效失败:记录不存在]", bizId, bizType);
             return;
         }
 
-        // 1. 更新佣金记录为已失效
-        BrokerageRecordDO updateObj = new BrokerageRecordDO().setStatus(BrokerageRecordStatusEnum.CANCEL.getStatus());
-        int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
-        if (updateRows == 0) {
-            log.error("[cancelBrokerage][record({}) 更新为已失效失败]", record.getId());
-            return;
-        }
+        records.forEach(record -> {
+            // 1. 更新佣金记录为已失效
+            BrokerageRecordDO updateObj = new BrokerageRecordDO().setStatus(BrokerageRecordStatusEnum.CANCEL.getStatus());
+            int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
+            if (updateRows == 0) {
+                log.error("[cancelBrokerage][record({}) 更新为已失效失败]", record.getId());
+                return;
+            }
 
-        // 2. 更新用户的佣金
-        if (BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus().equals(record.getStatus())) {
-            brokerageUserService.updateUserFrozenPrice(userId, -record.getPrice());
-        } else if (BrokerageRecordStatusEnum.SETTLEMENT.getStatus().equals(record.getStatus())) {
-            brokerageUserService.updateUserPrice(userId, -record.getPrice());
-        }
+            // 2. 更新用户的佣金
+            if (BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus().equals(record.getStatus())) {
+                brokerageUserService.updateUserFrozenPrice(record.getUserId(), -record.getPrice());
+            } else if (BrokerageRecordStatusEnum.SETTLEMENT.getStatus().equals(record.getStatus())) {
+                brokerageUserService.updateUserPrice(record.getUserId(), -record.getPrice());
+            }
+        });
     }
 
     /**

+ 0 - 11
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageUserService.java

@@ -9,9 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokera
 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
 import jakarta.validation.constraints.NotNull;
 
-import java.util.Collection;
-import java.util.List;
-
 /**
  * 分销用户 Service 接口
  *
@@ -27,14 +24,6 @@ public interface BrokerageUserService {
      */
     BrokerageUserDO getBrokerageUser(Long id);
 
-    /**
-     * 获得分销用户列表
-     *
-     * @param ids 编号
-     * @return 分销用户列表
-     */
-    List<BrokerageUserDO> getBrokerageUserList(Collection<Long> ids);
-
     /**
      * 获得分销用户分页
      *

+ 1 - 5
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageUserServiceImpl.java

@@ -60,11 +60,6 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
         return brokerageUserMapper.selectById(id);
     }
 
-    @Override
-    public List<BrokerageUserDO> getBrokerageUserList(Collection<Long> ids) {
-        return brokerageUserMapper.selectBatchIds(ids);
-    }
-
     @Override
     public PageResult<BrokerageUserDO> getBrokerageUserPage(BrokerageUserPageReqVO pageReqVO) {
         List<Long> childIds = getChildUserIdsByLevel(pageReqVO.getBindUserId(), pageReqVO.getLevel());
@@ -130,6 +125,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
 
     @Override
     public BrokerageUserDO getOrCreateBrokerageUser(Long id) {
+        // TODO @芋艿:这块优化下;统一到注册时处理;
         BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(id);
         // 特殊:人人分销的情况下,如果分销人为空则创建分销人
         if (brokerageUser == null && ObjUtil.equal(BrokerageEnabledConditionEnum.ALL.getCondition(),

+ 1 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java

@@ -215,14 +215,13 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
      * @return 物流轨迹
      */
     @Cacheable(cacheNames = RedisKeyConstants.EXPRESS_TRACK, key = "#code + '-' + #logisticsNo + '-' + #receiverMobile",
-            condition = "#result != null")
+            condition = "#result != null && #result.length() > 0")
     public List<ExpressTrackRespDTO> getExpressTrackList(String code, String logisticsNo, String receiverMobile) {
         return expressClientFactory.getDefaultExpressClient().getExpressTrackList(
                 new ExpressTrackQueryReqDTO().setExpressCode(code).setLogisticsNo(logisticsNo)
                         .setPhone(receiverMobile));
     }
 
-
     // =================== Order Item ===================
 
     @Override

+ 1 - 5
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java

@@ -83,7 +83,7 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler {
         if (order.getBrokerageUserId() == null) {
             return;
         }
-        cancelBrokerage(order.getBrokerageUserId(), orderItem.getId());
+        brokerageRecordService.cancelBrokerage(BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItem.getId()));
     }
 
     /**
@@ -111,8 +111,4 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler {
         brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
     }
 
-    protected void cancelBrokerage(Long userId, Long orderItemId) {
-        brokerageRecordService.cancelBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItemId));
-    }
-
 }

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java

@@ -90,7 +90,7 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
         if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // 减价
             return coupon.getDiscountPrice();
         } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(coupon.getDiscountType())) { // 打折
-            int couponPrice = totalPayPrice * coupon.getDiscountPercent() / 100;
+            int couponPrice = totalPayPrice - (totalPayPrice * coupon.getDiscountPercent() / 100);
             return coupon.getDiscountLimitPrice() == null ? couponPrice
                     : Math.min(couponPrice, coupon.getDiscountLimitPrice()); // 优惠上限
         }

+ 0 - 5
yudao-module-pay/yudao-module-pay-biz/pom.xml

@@ -23,11 +23,6 @@
             <artifactId>yudao-module-pay-api</artifactId>
             <version>${revision}</version>
         </dependency>
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-module-member-api</artifactId>
-            <version>${revision}</version>
-        </dependency>
 
         <!-- 业务组件 -->
         <dependency>

+ 5 - 30
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletController.java

@@ -1,11 +1,7 @@
 package cn.iocoder.yudao.module.pay.controller.admin.wallet;
 
-import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
-import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet.PayWalletPageReqVO;
 import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet.PayWalletRespVO;
 import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet.PayWalletUserReqVO;
@@ -14,6 +10,8 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
 import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
@@ -21,14 +19,8 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import java.util.List;
-import java.util.Map;
-
 import static cn.iocoder.yudao.framework.common.enums.UserTypeEnum.MEMBER;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 
 @Tag(name = "管理后台 - 用户钱包")
 @RestController
@@ -39,38 +31,21 @@ public class PayWalletController {
 
     @Resource
     private PayWalletService payWalletService;
-    @Resource
-    private MemberUserApi memberUserApi;
 
     @GetMapping("/get")
     @PreAuthorize("@ss.hasPermission('pay:wallet:query')")
     @Operation(summary = "获得用户钱包明细")
     public CommonResult<PayWalletRespVO> getWallet(PayWalletUserReqVO reqVO) {
         PayWalletDO wallet = payWalletService.getOrCreateWallet(reqVO.getUserId(), MEMBER.getValue());
-        // TODO jason:如果为空,返回给前端只要 null 就可以了
-        MemberUserRespDTO memberUser = memberUserApi.getUser(reqVO.getUserId());
-        String nickname = memberUser == null ? "" : memberUser.getNickname();
-        String avatar = memberUser == null ? "" : memberUser.getAvatar();
-        return success(PayWalletConvert.INSTANCE.convert02(nickname, avatar, wallet));
+        return success(PayWalletConvert.INSTANCE.convert02(wallet));
     }
 
     @GetMapping("/page")
     @Operation(summary = "获得会员钱包分页")
     @PreAuthorize("@ss.hasPermission('pay:wallet:query')")
     public CommonResult<PageResult<PayWalletRespVO>> getWalletPage(@Valid PayWalletPageReqVO pageVO) {
-        if (StrUtil.isNotEmpty(pageVO.getNickname())) {
-            List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(pageVO.getNickname());
-            pageVO.setUserIds(convertSet(users, MemberUserRespDTO::getId));
-        }
-        // TODO @jason:管理员也可以先查询下。。
-        // 暂时支持查询 userType 会员类型。管理员类型还不知道使用场景
-        PageResult<PayWalletDO> pageResult = payWalletService.getWalletPage(MEMBER.getValue(),pageVO);
-        if (CollectionUtil.isEmpty(pageResult.getList())) {
-            return success(new PageResult<>(pageResult.getTotal()));
-        }
-        List<MemberUserRespDTO> users = memberUserApi.getUserList(convertList(pageResult.getList(), PayWalletDO::getUserId));
-        Map<Long, MemberUserRespDTO> userMap = convertMap(users, MemberUserRespDTO::getId);
-        return success(PayWalletConvert.INSTANCE.convertPage(pageResult, userMap));
+        PageResult<PayWalletDO> pageResult = payWalletService.getWalletPage(pageVO);
+        return success(PayWalletConvert.INSTANCE.convertPage(pageResult));
     }
 
 }

+ 7 - 5
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/wallet/PayWalletPageReqVO.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet;
 
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -8,7 +10,6 @@ import lombok.ToString;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDateTime;
-import java.util.Collection;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
@@ -18,11 +19,12 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
 @ToString(callSuper = true)
 public class PayWalletPageReqVO extends PageParam {
 
-    @Schema(description = "用户昵称", example = "李四")
-    private String nickname;
+    @Schema(description = "用户编号", example = "1024")
+    private Long userId;
 
-    @Schema(description = "用户编号", example = "[1,2]")
-    private Collection<Long> userIds;
+    @Schema(description = "用户类型", example = "1")
+    @InEnum(value = UserTypeEnum.class)
+    private Integer userType;
 
     @Schema(description = "创建时间")
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)

+ 0 - 5
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/wallet/PayWalletRespVO.java

@@ -19,9 +19,4 @@ public class PayWalletRespVO extends PayWalletBaseVO {
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
-    @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王**")
-    private String nickname;
-    @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
-    private String avatar;
-
 }

+ 1 - 12
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletConvert.java

@@ -1,16 +1,12 @@
 package cn.iocoder.yudao.module.pay.convert.wallet;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
-import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet.PayWalletRespVO;
 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.wallet.AppPayWalletRespVO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
-import java.util.Map;
-
 @Mapper
 public interface PayWalletConvert {
 
@@ -18,15 +14,8 @@ public interface PayWalletConvert {
 
     AppPayWalletRespVO convert(PayWalletDO bean);
 
-    PayWalletRespVO convert02(String nickname,String avatar, PayWalletDO bean);
+    PayWalletRespVO convert02(PayWalletDO bean);
 
     PageResult<PayWalletRespVO> convertPage(PageResult<PayWalletDO> page);
 
-    default PageResult<PayWalletRespVO> convertPage(PageResult<PayWalletDO> page, Map<Long, MemberUserRespDTO> userMap) {
-        PageResult<PayWalletRespVO> pageResult = convertPage(page);
-        pageResult.getList().forEach(wallet -> MapUtils.findAndThen(userMap, wallet.getUserId(),
-                user -> wallet.setNickname(user.getNickname()).setAvatar(user.getAvatar())));
-        return pageResult;
-    }
-
 }

+ 4 - 4
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java

@@ -17,10 +17,10 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
                 PayWalletDO::getUserType, userType);
     }
 
-    default PageResult<PayWalletDO> selectPage(Integer userType, PayWalletPageReqVO reqVO) {
+    default PageResult<PayWalletDO> selectPage(PayWalletPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<PayWalletDO>()
-                .inIfPresent(PayWalletDO::getUserId, reqVO.getUserIds())
-                .eqIfPresent(PayWalletDO::getUserType, userType)
+                .eqIfPresent(PayWalletDO::getUserId, reqVO.getUserId())
+                .eqIfPresent(PayWalletDO::getUserType, reqVO.getUserType())
                 .betweenIfPresent(PayWalletDO::getCreateTime, reqVO.getCreateTime())
                 .orderByDesc(PayWalletDO::getId));
     }
@@ -31,7 +31,7 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
      * @param id 钱包 id
      * @param price 消费金额
      */
-    default int updateWhenConsumptionRefund(Long id, Integer price){
+    default int updateWhenConsumptionRefund(Long id, Integer price) {
         LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
                 .setSql(" balance = balance + " + price
                         + ", total_expense = total_expense - " + price)

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/order/PayOrderSyncJob.java

@@ -21,9 +21,9 @@ import java.time.LocalDateTime;
 public class PayOrderSyncJob implements JobHandler {
 
     /**
-     * 同步创建时间在 N 分钟之的订单
+     * 同步创建时间在 N 分钟之的订单
      *
-     * 为什么同步 10 分钟之的订单?
+     * 为什么同步 10 分钟之的订单?
      *  因为一个订单发起支付,到支付成功,大多数在 10 分钟内,需要保证轮询到。
      *  如果设置为 30、60 或者更大时间范围,会导致轮询的订单太多,影响性能。当然,你也可以根据自己的业务情况来处理。
      */

+ 5 - 43
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java

@@ -14,22 +14,17 @@ import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
 import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import lombok.Getter;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+import jakarta.validation.Validator;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import jakarta.annotation.PostConstruct;
-import jakarta.annotation.Resource;
-import jakarta.validation.Validator;
-import java.time.Duration;
 import java.util.Collection;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 
 /**
@@ -42,25 +37,6 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 @Validated
 public class PayChannelServiceImpl implements PayChannelService {
 
-    /**
-     * {@link PayClient} 缓存,通过它异步清空 smsClientFactory
-     */
-    @Getter
-    private final LoadingCache<Long, PayClient> clientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
-            new CacheLoader<Long, PayClient>() {
-
-                @Override
-                public PayClient load(Long id) {
-                    // 查询,然后尝试清空
-                    PayChannelDO channel = payChannelMapper.selectById(id);
-                    if (channel != null) {
-                        payClientFactory.createOrUpdatePayClient(channel.getId(), channel.getCode(), channel.getConfig());
-                    }
-                    return payClientFactory.getPayClient(id);
-                }
-
-            });
-
     @Resource
     private PayClientFactory payClientFactory;
 
@@ -102,9 +78,6 @@ public class PayChannelServiceImpl implements PayChannelService {
         PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO)
                 .setConfig(parseConfig(dbChannel.getCode(), updateReqVO.getConfig()));
         payChannelMapper.updateById(channel);
-
-        // 清空缓存
-        clearCache(channel.getId());
     }
 
     /**
@@ -135,18 +108,6 @@ public class PayChannelServiceImpl implements PayChannelService {
 
         // 删除
         payChannelMapper.deleteById(id);
-
-        // 清空缓存
-        clearCache(id);
-    }
-
-    /**
-     * 删除缓存
-     *
-     * @param id 渠道编号
-     */
-    private void clearCache(Long id) {
-        clientCache.invalidate(id);
     }
 
     private PayChannelDO validateChannelExists(Long id) {
@@ -202,7 +163,8 @@ public class PayChannelServiceImpl implements PayChannelService {
 
     @Override
     public PayClient getPayClient(Long id) {
-        return clientCache.getUnchecked(id);
+        PayChannelDO channel = validPayChannel(id);
+        return payClientFactory.createOrUpdatePayClient(id, channel.getCode(), channel.getConfig());
     }
 
 }

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java

@@ -36,7 +36,7 @@ public interface PayWalletService {
      * @param pageReqVO 分页查询
      * @return 会员钱包分页
      */
-    PageResult<PayWalletDO> getWalletPage(Integer userType, PayWalletPageReqVO pageReqVO);
+    PageResult<PayWalletDO> getWalletPage(PayWalletPageReqVO pageReqVO);
 
     /**
      * 钱包订单支付

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java

@@ -65,8 +65,8 @@ public class PayWalletServiceImpl implements  PayWalletService {
     }
 
     @Override
-    public PageResult<PayWalletDO> getWalletPage(Integer userType,PayWalletPageReqVO pageReqVO) {
-        return walletMapper.selectPage(userType, pageReqVO);
+    public PageResult<PayWalletDO> getWalletPage(PayWalletPageReqVO pageReqVO) {
+        return walletMapper.selectPage(pageReqVO);
     }
 
     @Override

+ 0 - 29
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/dataobject/merchant/PayChannelDOTest.java

@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.module.pay.dal.dataobject.merchant;
-
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig;
-import org.junit.jupiter.api.Test;
-
-public class PayChannelDOTest {
-
-    @Test
-    public void testSerialization() {
-        PayChannelDO payChannelDO = new PayChannelDO();
-        // 创建配置
-        WXPayClientConfig config = new WXPayClientConfig();
-        config.setAppId("wx041349c6f39b268b");
-        config.setMchId("1545083881");
-        config.setApiVersion(WXPayClientConfig.API_VERSION_V2);
-        config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
-        payChannelDO.setConfig(config);
-
-        // 序列化
-        String text = JsonUtils.toJsonString(payChannelDO);
-        System.out.println(text);
-
-        // 反序列化
-        payChannelDO = JsonUtils.parseObject(text, PayChannelDO.class);
-        System.out.println(payChannelDO.getConfig().getClass());
-    }
-
-}

+ 0 - 80
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapperIntegrationTest.java

@@ -1,80 +0,0 @@
-package cn.iocoder.yudao.module.pay.dal.mysql.merchant;
-
-import cn.hutool.core.io.IoUtil;
-import cn.iocoder.yudao.module.pay.dal.dataobject.merchant.PayChannelDO;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
-import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import cn.iocoder.yudao.module.pay.test.BaseDbIntegrationTest;
-import org.junit.jupiter.api.Test;
-
-import jakarta.annotation.Resource;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.util.List;
-
-@Resource
-public class PayChannelMapperIntegrationTest extends BaseDbIntegrationTest {
-
-    @Resource
-    private PayChannelMapper payChannelMapper;
-
-    /**
-     * 插入 {@link PayChannelEnum#WX_PUB} 初始配置
-     */
-    @Test
-    public void testInsertWxPub() throws FileNotFoundException {
-        PayChannelDO payChannelDO = new PayChannelDO();
-        payChannelDO.setCode(PayChannelEnum.WX_PUB.getCode());
-        payChannelDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
-        payChannelDO.setFeeRate(1D);
-        payChannelDO.setAppId(6L);
-        // 配置
-        WXPayClientConfig config = new WXPayClientConfig();
-        config.setAppId("wx041349c6f39b268b");
-        config.setMchId("1545083881");
-        config.setApiVersion(WXPayClientConfig.API_VERSION_V2);
-        config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
-        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");
-        payChannelDO.setConfig(config);
-        // 执行插入
-        payChannelMapper.insert(payChannelDO);
-    }
-
-    // TODO @ouyang:Zfb 改成 AlipayQr
-    /**
-     * 插入 {@link PayChannelEnum#ALIPAY_QR} 初始配置
-     */
-    @Test
-    public void testInsertZfb() {
-        PayChannelDO payChannelDO = new PayChannelDO();
-        payChannelDO.setCode(PayChannelEnum.ALIPAY_QR.getCode());
-        payChannelDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
-        payChannelDO.setFeeRate(1D);
-        payChannelDO.setAppId(6L);
-        // 配置
-        AlipayPayClientConfig config = new AlipayPayClientConfig();
-        config.setAppId("2021000118634035");
-        config.setServerUrl(AlipayPayClientConfig.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");
-        // 创建客户端
-        payChannelDO.setConfig(config);
-        // 执行插入
-        payChannelMapper.insert(payChannelDO);
-    }
-
-    /**
-     * 查询所有支付配置,看看是否都是 ok 的
-     */
-    @Test
-    public void testSelectList() {
-        List<PayChannelDO> payChannels = payChannelMapper.selectList();
-        System.out.println(payChannels.size());
-    }
-
-}

+ 0 - 51
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceIntegrationTest.java

@@ -1,51 +0,0 @@
-package cn.iocoder.yudao.module.pay.service.order;
-
-import cn.iocoder.yudao.module.pay.service.channel.PayAppServiceImpl;
-import cn.iocoder.yudao.module.pay.service.channel.PayChannelServiceImpl;
-import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
-import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
-import cn.iocoder.yudao.module.pay.test.BaseDbIntegrationTest;
-import cn.iocoder.yudao.framework.common.util.date.DateUtils;
-import cn.iocoder.yudao.framework.pay.config.YudaoPayAutoConfiguration;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
-import org.junit.jupiter.api.Test;
-import org.springframework.context.annotation.Import;
-
-import jakarta.annotation.Resource;
-import java.time.Duration;
-
-@Import({PayOrderServiceImpl.class, PayAppServiceImpl.class,
-        PayChannelServiceImpl.class, YudaoPayAutoConfiguration.class})
-public class PayOrderServiceIntegrationTest extends BaseDbIntegrationTest {
-
-    @Resource
-    private PayOrderService payOrderService;
-
-    @Test
-    public void testCreatePayOrder() {
-        // 构造请求
-        PayOrderCreateReqDTO reqDTO = new PayOrderCreateReqDTO();
-        reqDTO.setAppId(6L);
-        reqDTO.setUserIp("127.0.0.1");
-        reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
-        reqDTO.setSubject("标题");
-        reqDTO.setBody("内容");
-        reqDTO.setPrice(100);
-        reqDTO.setExpireTime(DateUtils.addTime(Duration.ofDays(1)));
-        // 发起请求
-        payOrderService.createPayOrder(reqDTO);
-    }
-
-    @Test
-    public void testSubmitPayOrder() {
-        // 构造请求
-        PayOrderSubmitReqDTO reqDTO = new PayOrderSubmitReqDTO();
-        reqDTO.setId(10L);
-        reqDTO.setAppId(6L);
-        reqDTO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
-        reqDTO.setUserIp("127.0.0.1");
-        // 发起请求
-        payOrderService.submitPayOrder(reqDTO);
-    }
-
-}

+ 0 - 1
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/service/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.yudao.module.pay.service;

+ 0 - 38
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseDbAndRedisIntegrationTest.java

@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.module.pay.test;
-
-import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
-import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
-import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
-import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
-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;
-import org.springframework.context.annotation.Import;
-import org.springframework.test.context.ActiveProfiles;
-
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisIntegrationTest.Application.class)
-@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
-public class BaseDbAndRedisIntegrationTest {
-
-    @Import({
-            // DB 配置类
-            DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类
-            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
-            DataSourceAutoConfiguration.class, // Spring DB 自动配置类
-            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
-            // MyBatis 配置类
-            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
-            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
-
-            // Redis 配置类
-            RedisAutoConfiguration.class, // Spring Redis 自动配置类
-            YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
-            RedissonAutoConfiguration.class, // Redisson 自动配置类
-    })
-    public static class Application {
-    }
-
-}

+ 0 - 30
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseDbIntegrationTest.java

@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.pay.test;
-
-import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
-import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
-import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
-import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.Import;
-import org.springframework.test.context.ActiveProfiles;
-
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbIntegrationTest.Application.class)
-@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
-public class BaseDbIntegrationTest {
-
-    @Import({
-            // DB 配置类
-            DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类
-            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
-            DataSourceAutoConfiguration.class, // Spring DB 自动配置类
-            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
-            // MyBatis 配置类
-            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
-            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
-    })
-    public static class Application {
-    }
-
-}

+ 0 - 23
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseRedisIntegrationTest.java

@@ -1,23 +0,0 @@
-package cn.iocoder.yudao.module.pay.test;
-
-import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
-import org.redisson.spring.starter.RedissonAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.Import;
-import org.springframework.test.context.ActiveProfiles;
-
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisIntegrationTest.Application.class)
-@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
-public class BaseRedisIntegrationTest {
-
-    @Import({
-            // Redis 配置类
-            RedisAutoConfiguration.class, // Spring Redis 自动配置类
-            YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
-            RedissonAutoConfiguration.class, // Redisson 自动配置类
-    })
-    public static class Application {
-    }
-
-}

+ 0 - 84
yudao-module-pay/yudao-module-pay-biz/src/test-integration/resources/application-integration-test.yaml

@@ -1,84 +0,0 @@
-spring:
-  main:
-    lazy-initialization: true # 开启懒加载,加快速度
-    banner-mode: off # 单元测试,禁用 Banner
-
---- #################### 数据库相关配置 ####################
-
-spring:
-  # 数据源配置项
-  autoconfigure:
-    exclude:
-      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
-  datasource:
-    druid: # Druid 【监控】相关的全局配置
-      web-stat-filter:
-        enabled: true
-    dynamic: # 多数据源配置
-      druid: # Druid 【连接池】相关的全局配置
-        initial-size: 5 # 初始连接数
-        min-idle: 10 # 最小连接池数量
-        max-active: 20 # 最大连接池数量
-        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
-        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
-        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
-        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
-        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
-        test-while-idle: true
-        test-on-borrow: false
-        test-on-return: false
-      primary: master
-      datasource:
-        master:
-          name: ruoyi-vue-pro
-          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
-          driver-class-name: com.mysql.jdbc.Driver
-          username: root
-          password: 123456
-        slave: # 模拟从库,可根据自己需要修改
-          name: ruoyi-vue-pro
-          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
-          driver-class-name: com.mysql.jdbc.Driver
-          username: root
-          password: 123456
-
-  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
-    data:
-      redis:
-        host: 127.0.0.1 # 地址
-        port: 16379 # 端口(单元测试,使用 16379 端口)
-        database: 0 # 数据库索引
-
-mybatis:
-  lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
-mybatis-plus:
-  configuration:
-    map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
-    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
-  global-config:
-    db-config:
-      id-type: AUTO # 自增 ID
-      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
-      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
-  mapper-locations: classpath*:mapper/*.xml
-  type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
-
---- #################### 定时任务相关配置 ####################
-
---- #################### 配置中心相关配置 ####################
-
---- #################### 服务保障相关配置 ####################
-
-# Lock4j 配置项(单元测试,禁用 Lock4j)
-
---- #################### 监控相关配置 ####################
-
---- #################### 芋道相关配置 ####################
-
-yudao:
-  info:
-    version: 1.0.0
-    base-package: cn.iocoder.yudao.module
-  pay:
-    pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
-    refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify

+ 3 - 9
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java

@@ -60,8 +60,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         PayChannelDO channel = channelMapper.selectById(channelId);
         assertPojoEquals(reqVO, channel, "config");
         assertPojoEquals(config, channel.getConfig());
-        // 校验缓存
-        assertNull(channelService.getClientCache().getIfPresent(channelId));
     }
 
     @Test
@@ -102,8 +100,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
         assertPojoEquals(reqVO, channel, "config");
         assertPojoEquals(config, channel.getConfig());
-        // 校验缓存
-        assertNull(channelService.getClientCache().getIfPresent(channel.getId()));
     }
 
     @Test
@@ -134,8 +130,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         channelService.deleteChannel(id);
         // 校验数据不存在了
         assertNull(channelMapper.selectById(id));
-        // 校验缓存
-        assertNull(channelService.getClientCache().getIfPresent(id));
     }
 
     @Test
@@ -306,20 +300,20 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         PayChannelDO channel = randomPojo(PayChannelDO.class, o -> {
             o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
             o.setConfig(randomAlipayPayClientConfig());
+            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
         });
         channelMapper.insert(channel);
         // mock 参数
         Long id = channel.getId();
         // mock 方法
         PayClient mockClient = mock(PayClient.class);
-        when(payClientFactory.getPayClient(eq(id))).thenReturn(mockClient);
+        when(payClientFactory.createOrUpdatePayClient(eq(id), eq(channel.getCode()), eq(channel.getConfig())))
+                .thenReturn(mockClient);
 
         // 调用
         PayClient client = channelService.getPayClient(id);
         // 断言
         assertSame(client, mockClient);
-        verify(payClientFactory).createOrUpdatePayClient(eq(id), eq(channel.getCode()),
-                eq(channel.getConfig()));
     }
 
     public WxPayClientConfig randomWxPayClientConfig() {

+ 3 - 2
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java

@@ -23,9 +23,10 @@ public interface PayClientFactory {
      * @param channelId 渠道编号
      * @param channelCode 渠道编码
      * @param config 支付配置
+     * @return 支付客户端
      */
-    <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
-                                                                  Config config);
+    <Config extends PayClientConfig> PayClient createOrUpdatePayClient(Long channelId, String channelCode,
+                                                                       Config config);
 
     /**
      * 注册支付客户端 Class,用于模块中实现的 PayClient

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

@@ -71,8 +71,8 @@ public class PayClientFactoryImpl implements PayClientFactory {
 
     @Override
     @SuppressWarnings("unchecked")
-    public <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
-                                                                         Config config) {
+    public <Config extends PayClientConfig> PayClient createOrUpdatePayClient(Long channelId, String channelCode,
+                                                                              Config config) {
         AbstractPayClient<Config> client = (AbstractPayClient<Config>) clients.get(channelId);
         if (client == null) {
             client = this.createPayClient(channelId, channelCode, config);
@@ -81,6 +81,7 @@ public class PayClientFactoryImpl implements PayClientFactory {
         } else {
             client.refresh(config);
         }
+        return client;
     }
 
     @SuppressWarnings("unchecked")

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

@@ -36,6 +36,7 @@ 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;
+import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V3;
 
 /**
  * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款)
@@ -59,19 +60,14 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
     protected void doInit(String tradeType) {
         // 创建 config 配置
         WxPayConfig payConfig = new WxPayConfig();
-        BeanUtil.copyProperties(config, payConfig, "keyContent", "privateKeyContent", "privateCertContent");
+        BeanUtil.copyProperties(config, payConfig, "keyContent", "privateKeyContent");
         payConfig.setTradeType(tradeType);
         // weixin-pay-java 无法设置内容,只允许读取文件,所以这里要创建临时文件来解决
-        if (Base64.isBase64(config.getKeyContent())) {
+        if (Objects.equals(config.getApiVersion(), API_VERSION_V2)) {
             payConfig.setKeyPath(FileUtils.createTempFile(Base64.decode(config.getKeyContent())).getPath());
-        }
-        if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
+        } else if (Objects.equals(config.getApiVersion(), API_VERSION_V3)) {
             payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
         }
-        if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
-            payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
-        }
-//        payConfig.setCertSerialNo();
 
         // 创建 client 客户端
         client = new WxPayServiceImpl();
@@ -86,7 +82,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doUnifiedOrderV2(reqDTO);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doUnifiedOrderV3(reqDTO);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -157,7 +153,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
         switch (config.getApiVersion()) {
             case API_VERSION_V2:
                 return doParseOrderNotifyV2(body);
-            case WxPayClientConfig.API_VERSION_V3:
+            case API_VERSION_V3:
                 return doParseOrderNotifyV3(body);
             default:
                 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -192,7 +188,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doGetOrderV2(outTradeNo);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doGetOrderV3(outTradeNo);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -261,7 +257,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doUnifiedRefundV2(reqDTO);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doUnifiedRefundV3(reqDTO);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -321,7 +317,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
         switch (config.getApiVersion()) {
             case API_VERSION_V2:
                 return doParseRefundNotifyV2(body);
-            case WxPayClientConfig.API_VERSION_V3:
+            case API_VERSION_V3:
                 return parseRefundNotifyV3(body);
             default:
                 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -358,7 +354,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doGetRefundV2(outTradeNo, outRefundNo);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doGetRefundV3(outTradeNo, outRefundNo);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));

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

@@ -34,7 +34,8 @@ public class WxNativePayClient extends AbstractWxPayClient {
     @Override
     protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO);
+        WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO)
+                .setProductId(reqDTO.getOutTradeNo()); // V2 必须传递 productId,无需在微信配置。该参数在 V3 简化,无需传递!
         // 执行请求
         WxPayNativeOrderResult response = client.createOrder(request);
 

+ 9 - 17
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java

@@ -1,14 +1,10 @@
 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 jakarta.validation.Validator;
 import jakarta.validation.constraints.NotBlank;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import lombok.Data;
 
 /**
  * 微信支付的 PayClientConfig 实现类
@@ -71,16 +67,19 @@ public class WxPayClientConfig implements PayClientConfig {
      */
     @NotBlank(message = "apiclient_key 不能为空", groups = V3.class)
     private String privateKeyContent;
-    /**
-     * apiclient_cert.pem 证书文件的对应的字符串
-     */
-    @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class)
-    private String privateCertContent;
     /**
      * apiV3 密钥值
      */
     @NotBlank(message = "apiV3 密钥值不能为空", groups = V3.class)
     private String apiV3Key;
+    /**
+     * 证书序列号
+     */
+    @NotBlank(message = "证书序列号不能为空", groups = V3.class)
+    private String certSerialNo;
+
+    @Deprecated // TODO 芋艿:V2.3.0 进行移除
+    private String privateCertContent;
 
     /**
      * 分组校验 v2版本
@@ -100,11 +99,4 @@ public class WxPayClientConfig implements PayClientConfig {
                 API_VERSION_V2.equals(this.getApiVersion()) ? V2.class : V3.class);
     }
 
-    public static void main(String[] args) throws FileNotFoundException {
-        String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12";
-        /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem";
-        /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem";
-        System.out.println(IoUtil.readUtf8(new FileInputStream(path)));
-    }
-
 }

+ 7 - 0
yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/jmreport/core/service/JmReportTokenServiceImpl.java

@@ -133,6 +133,13 @@ public class JmReportTokenServiceImpl implements JmReportTokenServiceI {
 
     @Override
     public String[] getRoles(String token) {
+        // 设置租户上下文。原因是:/jmreport/** 纯前端地址,不会走 buildLoginUserByToken 逻辑
+        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
+        if (loginUser == null) {
+            return null;
+        }
+        TenantContextHolder.setTenantId(loginUser.getTenantId());
+
         // 参见文档 https://help.jeecg.com/jimureport/prodSafe.html 文档
         // 适配:如果是本系统的管理员,则转换成 jimu 报表的管理员
         Long userId = SecurityFrameworkUtils.getLoginUserId();

+ 2 - 5
yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/security/config/SecurityConfiguration.java

@@ -1,8 +1,6 @@
 package cn.iocoder.yudao.module.report.framework.security.config;
 
 import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
-import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
-import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -14,16 +12,15 @@ import org.springframework.security.config.annotation.web.configurers.AuthorizeH
 @Configuration("reportSecurityConfiguration")
 public class SecurityConfiguration {
 
-    @Resource
-    private OAuth2TokenApi oauth2TokenApi;
-
     @Bean("reportAuthorizeRequestsCustomizer")
     public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
         return new AuthorizeRequestsCustomizer() {
+
             @Override
             public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
                 registry.requestMatchers("/jmreport/**").permitAll(); // 积木报表
             }
+
         };
     }
 

Some files were not shown because too many files changed in this diff