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

# Conflicts:
#	yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/config/YudaoJacksonAutoConfiguration.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/activity/AppBargainActivityDetailRespVO.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/vo/activity/AppBargainActivityRespVO.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java
#	yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
#	yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
#	yudao-server/src/main/resources/application-local.yaml
This commit is contained in:
YunaiV 2023-09-28 15:18:59 +08:00
commit 5913ee0706
575 changed files with 1987 additions and 47700 deletions

View File

@ -15,6 +15,8 @@
* 启动文档:<https://doc.iocoder.cn/quick-start/>
* 视频教程:<https://doc.iocoder.cn/video/>
已支持 Spring Boot 3.X + JDK 17 版本,可见 [master-boot3](https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/README.md) 分支。
## 🐯 平台简介
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
@ -62,18 +64,18 @@
| [yudao-ui-admin](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin) | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-ui-admin) | 基于 Vue2 + element-ui 实现的管理后台 |
| [yudao-ui-admin-uniapp](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin-uniapp) | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-admin-uniapp) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-ui-admin-uniapp) | 基于 uni-app + uni-ui 实现的管理后台的小程序 |
| [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-go-view/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-go-view) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-go-view.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-go-view) | 基于 Vue3 + naive-ui 实现的大屏报表 |
| [yudao-ui-app](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-app) | [![Gitee star](https://gitee.com/zhijiantianya/ruoyi-vue-pro/badge/star.svg?theme=white)](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/yudao-ui-app) [![GitHub stars](https://img.shields.io/github/stars/YunaiV/ruoyi-vue-pro.svg?style=social&label=Stars)](https://github.com/YunaiV/ruoyi-vue-pro/tree/master/yudao-ui-app) | 基于 uni-app + uview 实现的用户 App |
| [yudao-mall-uniapp](https://gitee.com/yudaocode/yudao-mall-uniapp) | [![Gitee star](https://gitee.com/yudaocode/yudao-mall-uniapp/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-mall-uniapp) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-mall-uniapp.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-mall-uniapp) | 基于 uni-app 实现的商城小程序 |
## 🐰 分支说明
| | JDK 8 完整版 | JDK 8 精简版 | JDK 17 完整版 |
|-------|-----------------------------------------------------------|--------------------------------------------------------------------|-----------------------------------------------------------------------------|
| 分支 | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`mini`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini/) | [`boot-dev`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/boot3-dev/) |
| 分支 | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`mini`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini/) | [`master-boot3`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-boot3/) |
| 说明 | 包括所有功能 | 只保留核心功能 | 适配 Spring Boot 3.X |
| 系统功能 | √ | √ | √ |
| 基础设施 | √ | √ | √ |
| 会员中心 | √ | √ | √ |
| 工作流程 | √ | x | 适配中 |
| 工作流程 | √ | x | |
| 数据报表 | √ | x | 适配中 |
| 商城系统 | √ | x | √ |
| 微信公众号 | √ | x | √ |
@ -218,6 +220,10 @@
![功能图](/.image/common/mall-preview.png)
_前端基于 crmeb uniapp 经过授权重构优化代码实现接入芋道快速开发平台_
演示地址:<https://doc.iocoder.cn/mall-preview/>
### 会员中心
| | 功能 | 描述 |
@ -248,29 +254,29 @@
### 框架
| 框架 | 说明 | 版本 | 学习指南 |
|---------------------------------------------------------------------------------------------|------------------|-------------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.14 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.18 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.6.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 | |
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.18.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.24 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.7.6 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.5 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.8.0 | [文档](https://doc.iocoder.cn/bpm/) |
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 1.6.15 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.1 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.12.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.7.10 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.13.3 | |
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.5.5.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.28 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - |
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.8.0 | - |
| 框架 | 说明 | 版本 | 学习指南 |
|---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.16 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.19 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.6.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 /7.0 | |
| [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.18.0 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.3.24 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) |
| [Spring Security](https://github.com/spring-projects/spring-security) | Spring 安全框架 | 5.7.11 | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator) | 参数校验组件 | 6.2.5 | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao) |
| [Flowable](https://github.com/flowable/flowable-engine) | 工作流引擎 | 6.8.0 | [文档](https://doc.iocoder.cn/bpm/) |
| [Quartz](https://github.com/quartz-scheduler) | 任务调度组件 | 2.3.2 | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao) |
| [Springdoc](https://springdoc.org/) | Swagger 文档 | 1.6.15 | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao) |
| [Resilience4j](https://github.com/resilience4j/resilience4j) | 服务保障组件 | 1.7.1 | [文档](http://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao) |
| [SkyWalking](https://skywalking.apache.org/) | 分布式应用追踪系统 | 8.12.0 | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao) |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin) | Spring Boot 监控平台 | 2.7.10 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
| [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.13.3 | |
| [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.5.5.Final | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.18.30 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.8.2 | - |
| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 4.8.0 | - |
## 🐷 演示图

10
pom.xml
View File

@ -19,8 +19,8 @@
<!-- <module>yudao-module-bpm</module>-->
<!-- <module>yudao-module-report</module>-->
<!-- <module>yudao-module-mp</module>-->
<module>yudao-module-pay</module>
<module>yudao-module-mall</module>
<!-- <module>yudao-module-pay</module>-->
<!-- <module>yudao-module-mall</module>-->
<!-- 示例项目 -->
<module>yudao-example</module>
</modules>
@ -30,7 +30,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.8.1-snapshot</revision>
<revision>1.8.2-snapshot</revision>
<!-- Maven 相关 -->
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
@ -39,8 +39,8 @@
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<!-- 看看咋放到 bom 里 -->
<lombok.version>1.18.28</lombok.version>
<spring.boot.version>2.7.14</spring.boot.version>
<lombok.version>1.18.30</lombok.version>
<spring.boot.version>2.7.16</spring.boot.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -11,7 +11,7 @@
Target Server Version : 80034
File Encoding : 65001
Date: 03/09/2023 19:13:55
Date: 24/09/2023 23:23:06
*/
SET NAMES utf8mb4;
@ -72,6 +72,7 @@ CREATE TABLE `QRTZ_CRON_TRIGGERS` (
-- Records of QRTZ_CRON_TRIGGERS
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'demoJob', 'DEFAULT', '0 0 0 * * ?', 'Asia/Shanghai');
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', '* * * * * ?', 'Asia/Shanghai');
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
INSERT INTO `QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', '0 0/1 * * * ?', 'Asia/Shanghai');
@ -135,6 +136,7 @@ CREATE TABLE `QRTZ_JOB_DETAILS` (
-- Records of QRTZ_JOB_DETAILS
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'demoJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000147400104A4F425F48414E444C45525F4E414D4574000764656D6F4A6F627800);
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000057400104A4F425F48414E444C45525F4E414D4574000C7061794E6F746966794A6F627800);
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000127400104A4F425F48414E444C45525F4E414D457400117061794F726465724578706972654A6F627800);
INSERT INTO `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', NULL, 'cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000117400104A4F425F48414E444C45525F4E414D4574000F7061794F7264657253796E634A6F627800);
@ -191,7 +193,7 @@ CREATE TABLE `QRTZ_SCHEDULER_STATE` (
-- Records of QRTZ_SCHEDULER_STATE
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'Yunai1690117495401', 1690119854263, 15000);
INSERT INTO `QRTZ_SCHEDULER_STATE` (`SCHED_NAME`, `INSTANCE_NAME`, `LAST_CHECKIN_TIME`, `CHECKIN_INTERVAL`) VALUES ('schedulerName', 'Yunai.local1694844151505', 1694844218609, 15000);
COMMIT;
-- ----------------------------
@ -285,6 +287,7 @@ CREATE TABLE `QRTZ_TRIGGERS` (
-- Records of QRTZ_TRIGGERS
-- ----------------------------
BEGIN;
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'demoJob', 'DEFAULT', 'demoJob', 'DEFAULT', NULL, 1694880000000, -1, 5, 'WAITING', 'CRON', 1694844083000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000A74000F4A4F425F52455452595F434F554E547371007E000A000000017800);
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payNotifyJob', 'DEFAULT', 'payNotifyJob', 'DEFAULT', NULL, 1688907102000, 1688907101000, 5, 'PAUSED', 'CRON', 1635294882000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderExpireJob', 'DEFAULT', 'payOrderExpireJob', 'DEFAULT', NULL, 1690011600000, -1, 5, 'PAUSED', 'CRON', 1690011553000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
INSERT INTO `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'payOrderSyncJob', 'DEFAULT', 'payOrderSyncJob', 'DEFAULT', NULL, 1690011600000, 1690011540000, 5, 'PAUSED', 'CRON', 1690007785000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000B7800);
@ -360,7 +363,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 = 1497 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
) ENGINE = InnoDB AUTO_INCREMENT = 1544 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
-- ----------------------------
-- Records of infra_api_error_log
@ -398,7 +401,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 = 1756 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
) ENGINE = InnoDB AUTO_INCREMENT = 1755 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
-- ----------------------------
-- Records of infra_codegen_column
@ -514,7 +517,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 = 1054 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
) ENGINE = InnoDB AUTO_INCREMENT = 1055 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file
@ -545,12 +548,7 @@ CREATE TABLE `infra_file_config` (
-- Records of infra_file_config
-- ----------------------------
BEGIN;
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库', 1, '我是数据库', b'1', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2023-04-08 09:44:47', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '本地磁盘', 10, '测试下本地存储', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.local.LocalFileClientConfig\",\"basePath\":\"/Users/yunai/file_test\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:57:00', '1', '2023-04-08 09:44:47', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (11, 'S3 - 七牛云', 20, NULL, b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-03-19 18:00:03', '1', '2023-04-08 09:44:47', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, 'S3 - 七牛云', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-06-10 20:50:41', '1', '2023-04-08 09:44:47', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 'S3 - 七牛云', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-06-11 20:32:08', '1', '2023-04-08 09:44:47', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, 'S3 - 七牛云', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8\",\"accessSecret\":\"kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP\"}', '1', '2022-06-11 20:32:47', '1', '2023-04-08 09:44:47', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库', 1, '我是数据库', b'1', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2023-09-15 12:05:12', b'0');
COMMIT;
-- ----------------------------
@ -568,7 +566,7 @@ CREATE TABLE `infra_file_content` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 145 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
) ENGINE = InnoDB AUTO_INCREMENT = 149 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file_content
@ -596,17 +594,17 @@ CREATE TABLE `infra_job` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
-- ----------------------------
-- Records of infra_job
-- ----------------------------
BEGIN;
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '支付通知 Job', 2, 'payNotifyJob', NULL, '* * * * * ?', 0, 0, 0, '1', '2021-10-27 08:34:42', '1', '2023-07-09 20:51:41', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 'Job 示例', 1, 'demoJob', NULL, '* * * L * ?', 1, 1, 0, '1', '2022-09-24 22:31:41', '1', '2022-09-24 22:31:42', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, '支付订单同步 Job', 2, 'payOrderSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 14:36:26', '1', '2023-07-22 15:39:08', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, '支付订单过期 Job', 2, 'payOrderExpireJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-22 15:36:23', '1', '2023-07-22 15:39:54', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (19, '退款订单的同步 Job', 2, 'payRefundSyncJob', NULL, '0 0/1 * * * ?', 0, 0, 0, '1', '2023-07-23 21:03:44', '1', '2023-07-23 21:09:00', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (20, 'Job 示例', 1, 'demoJob', '', '0 0 0 * * ?', 1, 10, 0, '1', '2023-09-16 14:01:23', '1', '2023-09-16 14:01:23', b'0');
COMMIT;
-- ----------------------------
@ -630,7 +628,7 @@ CREATE TABLE `infra_job_log` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 161 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
) ENGINE = InnoDB AUTO_INCREMENT = 163 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务日志表';
-- ----------------------------
-- Records of infra_job_log
@ -803,7 +801,7 @@ CREATE TABLE `member_level_record` (
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user_id`(`user_id` ASC) USING BTREE COMMENT '会员等级记录-用户编号'
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
-- ----------------------------
-- Records of member_level_record
@ -828,7 +826,7 @@ CREATE TABLE `member_point_config` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '会员积分配置表';
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员积分配置表';
-- ----------------------------
-- Records of member_point_config
@ -844,10 +842,10 @@ DROP TABLE IF EXISTS `member_point_record`;
CREATE TABLE `member_point_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`user_id` bigint NOT NULL COMMENT '用户编号',
`biz_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '业务编码',
`biz_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务编码',
`biz_type` tinyint NOT NULL COMMENT '业务类型',
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '积分标题',
`description` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '积分描述',
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '积分标题',
`description` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '积分描述',
`point` int NOT NULL COMMENT '积分',
`total_point` int NOT NULL COMMENT '变动后的积分',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
@ -859,7 +857,7 @@ CREATE TABLE `member_point_record` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `index_userId`(`user_id` ASC) USING BTREE,
INDEX `index_title`(`title` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户积分记录';
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分记录';
-- ----------------------------
-- Records of member_point_record
@ -872,6 +870,15 @@ INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 247, 'null', 11, '订单取消', '退单获得 -18 积分', -18, 73, NULL, '2023-08-31 19:56:21', NULL, '2023-08-31 19:56:21', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 247, '80', 10, '订单消费', '下单获得 2099718 积分', 2099718, 2099791, NULL, '2023-08-31 23:43:29', NULL, '2023-08-31 23:43:29', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 247, '81', 10, '订单消费', '下单获得 8398818 积分', 8398818, 10498609, NULL, '2023-08-31 23:46:17', NULL, '2023-08-31 23:46:17', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 247, '85', 12, '订单使用', '下单使用 -30 积分', -30, 10498579, '247', '2023-09-20 17:11:36', '247', '2023-09-20 17:11:36', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 247, '86', 12, '订单使用', '下单使用 -30 积分', -30, 10498549, '247', '2023-09-20 19:32:59', '247', '2023-09-20 19:32:59', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 247, '87', 12, '订单使用', '下单使用 -30 积分', -30, 10498519, '247', '2023-09-20 23:11:21', '247', '2023-09-20 23:11:21', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (11, 247, '88', 12, '订单使用', '下单使用 -30 积分', -30, 10498489, '247', '2023-09-20 23:20:21', '247', '2023-09-20 23:20:21', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (12, 247, '89', 12, '订单使用', '下单使用 -30 积分', -30, 10498459, '247', '2023-09-23 23:51:40', '247', '2023-09-23 23:51:40', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (13, 247, '90', 12, '订单使用', '下单使用 -30 积分', -30, 10498429, '247', '2023-09-23 23:52:34', '247', '2023-09-23 23:52:34', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (14, 247, '91', 12, '订单使用', '下单使用 -30 积分', -30, 10498399, '247', '2023-09-23 23:54:18', '247', '2023-09-23 23:54:18', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 247, '92', 12, '订单使用', '下单使用 -30 积分', -30, 10498369, '247', '2023-09-23 23:55:33', '247', '2023-09-23 23:55:33', b'0', 1);
INSERT INTO `member_point_record` (`id`, `user_id`, `biz_id`, `biz_type`, `title`, `description`, `point`, `total_point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 247, '93', 12, '订单使用', '下单使用 -30 积分', -30, 10498339, '247', '2023-09-23 23:56:53', '247', '2023-09-23 23:56:53', b'0', 1);
COMMIT;
-- ----------------------------
@ -890,7 +897,7 @@ CREATE TABLE `member_sign_in_config` (
`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 = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '签到规则';
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '签到规则';
-- ----------------------------
-- Records of member_sign_in_config
@ -919,20 +926,23 @@ CREATE TABLE `member_sign_in_record` (
`user_id` int NULL DEFAULT NULL COMMENT '签到用户',
`day` int NULL DEFAULT NULL COMMENT '第几天签到',
`point` int NULL DEFAULT NULL COMMENT '签到的分数',
`create_time` datetime NULL DEFAULT NULL COMMENT '签到时间',
`update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
`tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '租户id',
`deleted` int NULL DEFAULT 0 COMMENT '是否删除',
`creator` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '创建人',
`updater` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '更新人',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '签到记录';
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '签到记录';
-- ----------------------------
-- Records of member_sign_in_record
-- ----------------------------
BEGIN;
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (4, 247, 12, 1212, '2023-06-10 20:01:27', '2023-08-20 11:29:50', '1', 0, '1', '1');
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 247, 1, 123, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 247, 12, 12, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 247, 12, 1212, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 247, 12, 1212, '', '2023-09-24 01:23:47', '', '2023-09-24 01:23:47', b'0', 0);
COMMIT;
-- ----------------------------
@ -972,7 +982,7 @@ CREATE TABLE `member_user` (
`login_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '最后登录IP',
`login_date` datetime NULL DEFAULT NULL COMMENT '最后登录时间',
`nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户昵称',
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '头像',
`avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '头像',
`name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '真实名字',
`sex` tinyint NULL DEFAULT 0 COMMENT '用户性别',
`area_id` bigint NULL DEFAULT NULL COMMENT '所在地',
@ -989,9 +999,8 @@ CREATE TABLE `member_user` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
) ENGINE = InnoDB AUTO_INCREMENT = 248 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '会员用户';
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 249 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '会员用户';
-- ----------------------------
-- Records of member_user
@ -1305,7 +1314,7 @@ CREATE TABLE `system_dict_type` (
`deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 176 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
) ENGINE = InnoDB AUTO_INCREMENT = 175 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
-- ----------------------------
-- Records of system_dict_type
@ -1389,7 +1398,7 @@ CREATE TABLE `system_error_code` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5933 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
) ENGINE = InnoDB AUTO_INCREMENT = 5932 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '错误码表';
-- ----------------------------
-- Records of system_error_code
@ -1418,7 +1427,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 = 2375 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
) ENGINE = InnoDB AUTO_INCREMENT = 2482 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
-- ----------------------------
-- Records of system_login_log
@ -1483,7 +1492,7 @@ CREATE TABLE `system_mail_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 = 354 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
) ENGINE = InnoDB AUTO_INCREMENT = 355 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
-- ----------------------------
-- Records of system_mail_log
@ -2116,7 +2125,7 @@ CREATE TABLE `system_oauth2_access_token` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2597 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
) ENGINE = InnoDB AUTO_INCREMENT = 2721 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
-- ----------------------------
-- Records of system_oauth2_access_token
@ -2238,7 +2247,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 = 896 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
) ENGINE = InnoDB AUTO_INCREMENT = 991 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
-- ----------------------------
-- Records of system_oauth2_refresh_token
@ -2278,7 +2287,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 = 8321 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
) ENGINE = InnoDB AUTO_INCREMENT = 8338 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
-- ----------------------------
-- Records of system_operate_log
@ -3286,7 +3295,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 = 501 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
) ENGINE = InnoDB AUTO_INCREMENT = 513 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
-- ----------------------------
-- Records of system_sms_code
@ -3329,7 +3338,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 = 365 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
) ENGINE = InnoDB AUTO_INCREMENT = 381 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
-- ----------------------------
-- Records of system_sms_log
@ -3401,7 +3410,7 @@ CREATE TABLE `system_social_user` (
`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 = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交用户表';
) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交用户表';
-- ----------------------------
-- Records of system_social_user
@ -3426,7 +3435,7 @@ CREATE TABLE `system_social_user_bind` (
`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 = 39 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';
) ENGINE = InnoDB AUTO_INCREMENT = 76 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '社交绑定表';
-- ----------------------------
-- Records of system_social_user_bind
@ -3462,8 +3471,8 @@ CREATE TABLE `system_tenant` (
-- ----------------------------
BEGIN;
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'https://www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2022-02-23 12:15:11', b'0');
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'http://www.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2023-07-25 23:05:38', b'0');
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'https://www.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-07-25 23:53:16', b'0');
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'http://www.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2023-09-16 16:59:42', b'0');
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `domain`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'https://www.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-09-16 16:59:27', b'0');
COMMIT;
-- ----------------------------
@ -3602,10 +3611,10 @@ CREATE TABLE `system_users` (
-- Records of system_users
-- ----------------------------
BEGIN;
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-09-02 00:03:37', 'admin', '2021-01-05 17:03:47', NULL, '2023-09-02 00:03:37', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-09-24 12:03:56', 'admin', '2021-01-05 17:03:47', NULL, '2023-09-24 12:03:56', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '127.0.0.1', '2022-05-28 15:43:17', '', '2021-01-21 02:13:53', NULL, '2022-07-09 09:00:33', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2023-09-24 18:21:19', '', '2021-01-21 02:13:53', NULL, '2023-09-24 18:21:19', 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);

View File

@ -5815,8 +5815,7 @@ GO
CREATE TABLE [dbo].[system_dict_type]
(
[
id]
[id]
bigint
IDENTITY
(

View File

@ -14,16 +14,16 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.8.1-snapshot</revision>
<revision>1.8.2-snapshot</revision>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<!-- 统一依赖管理 -->
<spring.boot.version>2.7.14</spring.boot.version>
<spring.boot.version>2.7.16</spring.boot.version>
<!-- Web 相关 -->
<springdoc.version>1.6.15</springdoc.version>
<knife4j.version>4.1.0</knife4j.version>
<springdoc.version>1.7.0</springdoc.version>
<knife4j.version>4.3.0</knife4j.version>
<servlet.versoin>2.5</servlet.versoin>
<!-- DB 相关 -->
<druid.version>1.2.18</druid.version>
<druid.version>1.2.19</druid.version>
<mybatis-plus.version>3.5.3.2</mybatis-plus.version>
<mybatis-plus-generator.version>3.5.3.2</mybatis-plus-generator.version>
<dynamic-datasource.version>3.6.1</dynamic-datasource.version>
@ -44,33 +44,32 @@
<!-- Bpm 工作流相关 -->
<flowable.version>6.8.0</flowable.version>
<!-- 工具类相关 -->
<captcha-plus.version>1.0.6</captcha-plus.version>
<jsoup.version>1.15.4</jsoup.version>
<lombok.version>1.18.28</lombok.version>
<captcha-plus.version>1.0.8</captcha-plus.version>
<jsoup.version>1.16.1</jsoup.version>
<lombok.version>1.18.30</lombok.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<hutool.version>5.8.20</hutool.version>
<hutool.version>5.8.22</hutool.version>
<easyexcel.verion>3.3.2</easyexcel.verion>
<velocity.version>2.3</velocity.version>
<screw.version>1.0.5</screw.version>
<fastjson.version>1.2.83</fastjson.version>
<guava.version>32.0.1-jre</guava.version>
<guava.version>32.1.2-jre</guava.version>
<guice.version>5.1.0</guice.version>
<transmittable-thread-local.version>2.14.2</transmittable-thread-local.version>
<commons-net.version>3.9.0</commons-net.version>
<jsch.version>0.1.55</jsch.version>
<tika-core.version>2.7.0</tika-core.version>
<netty-all.version>4.1.90.Final</netty-all.version>
<ip2region.version>2.7.0</ip2region.version>
<!-- 三方云服务相关 -->
<okio.version>3.0.0</okio.version>
<okhttp3.version>4.10.0</okhttp3.version>
<okio.version>3.5.0</okio.version>
<okhttp3.version>4.11.0</okhttp3.version>
<commons-io.version>2.11.0</commons-io.version>
<minio.version>8.5.4</minio.version>
<aliyun-java-sdk-core.version>4.6.3</aliyun-java-sdk-core.version>
<minio.version>8.5.6</minio.version>
<aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version>
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
<tencentcloud-sdk-java.version>3.1.758</tencentcloud-sdk-java.version>
<justauth.version>1.0.3</justauth.version>
<jimureport.version>1.5.8</jimureport.version>
<tencentcloud-sdk-java.version>3.1.853</tencentcloud-sdk-java.version>
<justauth.version>1.0.5</justauth.version>
<jimureport.version>1.6.1</jimureport.version>
<xercesImpl.version>2.12.2</xercesImpl.version>
<weixin-java.version>4.5.0</weixin-java.version>
</properties>
@ -233,6 +232,12 @@
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@ -337,6 +342,12 @@
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
<version>${spring-boot-admin.version}</version>
<exclusions>
<exclusion>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-cloud</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
@ -519,12 +530,6 @@
<version>${jsch.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty-all.version}</version>
</dependency>
<dependency>
<groupId>com.xingyuv</groupId>
<artifactId>spring-boot-starter-captcha-plus</artifactId>

View File

@ -21,7 +21,7 @@
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 统一依赖管理 -->
<spring.boot.version>2.7.14</spring.boot.version>
<spring.boot.version>2.7.16</spring.boot.version>
</properties>
<dependencyManagement>
@ -52,7 +52,7 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.20</version>
<version>5.8.22</version>
</dependency>
<dependency>

View File

@ -21,7 +21,7 @@
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 统一依赖管理 -->
<spring.boot.version>2.7.14</spring.boot.version>
<spring.boot.version>2.7.16</spring.boot.version>
</properties>
<dependencyManagement>
@ -52,7 +52,7 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.20</version>
<version>5.8.22</version>
</dependency>
<dependency>

View File

@ -137,6 +137,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -10,7 +10,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@ -23,7 +22,6 @@ import java.util.List;
*
* @author 芋道源码
*/
@UtilityClass
@Slf4j
public class JsonUtils {

View File

@ -50,7 +50,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
*/
public final void init() {
doInit();
log.info("[init][客户端({}) 初始化完成]", getId());
log.debug("[init][客户端({}) 初始化完成]", getId());
}
/**

View File

@ -40,7 +40,7 @@ public abstract class AbstractSmsClient implements SmsClient {
*/
public final void init() {
doInit();
log.info("[init][配置({}) 初始化完成]", properties);
log.debug("[init][配置({}) 初始化完成]", properties);
}
/**

View File

@ -1,14 +1,11 @@
package cn.iocoder.yudao.framework.tenant.config;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;
import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager;
import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
@ -20,8 +17,6 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -100,25 +95,8 @@ public class YudaoTenantAutoConfiguration {
// ========== Job ==========
@Bean
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public BeanPostProcessor jobHandlerBeanPostProcessor(TenantFrameworkService tenantFrameworkService) {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof JobHandler)) {
return bean;
}
// TenantJob 注解的情况下才会进行处理
if (!AnnotationUtil.hasAnnotation(bean.getClass(), TenantJob.class)) {
return bean;
}
// 使用 TenantJobHandlerDecorator 装饰
return new TenantJobHandlerDecorator(tenantFrameworkService, (JobHandler) bean);
}
};
public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {
return new TenantJobAspect(tenantFrameworkService);
}
// ========== Redis ==========

View File

@ -8,7 +8,7 @@ import java.lang.annotation.Target;
/**
* 多租户 Job 注解
*/
@Target({ElementType.TYPE})
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TenantJob {
}

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.framework.tenant.core.job;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 多租户 JobHandler AOP
* 任务执行时会按照租户逐个执行 Job 的逻辑
*
* 注意需要保证 JobHandler 的幂等性因为 Job 因为某个租户执行失败重试时之前执行成功的租户也会再次执行
*
* @author 芋道源码
*/
@Aspect
@RequiredArgsConstructor
@Slf4j
public class TenantJobAspect {
private final TenantFrameworkService tenantFrameworkService;
@Around("@annotation(tenantJob)")
public String around(ProceedingJoinPoint joinPoint, TenantJob tenantJob) {
// 获得租户列表
List<Long> tenantIds = tenantFrameworkService.getTenantIds();
if (CollUtil.isEmpty(tenantIds)) {
return null;
}
// 逐个租户执行 Job
Map<Long, String> results = new ConcurrentHashMap<>();
tenantIds.parallelStream().forEach(tenantId -> {
// TODO 芋艿先通过 parallel 实现并行1多个租户是一条执行日志2异常的情况
TenantUtils.execute(tenantId, () -> {
try {
joinPoint.proceed();
} catch (Throwable e) {
results.put(tenantId, ExceptionUtil.getRootCauseMessage(e));
}
});
});
return JsonUtils.toJsonString(results);
}
}

View File

@ -1,58 +0,0 @@
package cn.iocoder.yudao.framework.tenant.core.job;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
import lombok.AllArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 多租户 JobHandler 装饰器
* 任务执行时会按照租户逐个执行 Job 的逻辑
*
* 注意需要保证 JobHandler 的幂等性因为 Job 因为某个租户执行失败重试时之前执行成功的租户也会再次执行
*
* @author 芋道源码
*/
@AllArgsConstructor
public class TenantJobHandlerDecorator implements JobHandler {
private final TenantFrameworkService tenantFrameworkService;
/**
* 被装饰的 Job
*/
private final JobHandler jobHandler;
@Override
public final String execute(String param) throws Exception {
// 获得租户列表
List<Long> tenantIds = tenantFrameworkService.getTenantIds();
if (CollUtil.isEmpty(tenantIds)) {
return null;
}
// 逐个租户执行 Job
Map<Long, String> results = new ConcurrentHashMap<>();
tenantIds.parallelStream().forEach(tenantId -> { // TODO 芋艿先通过 parallel 实现并行1多个租户是一条执行日志2异常的情况
try {
// 设置租户
TenantContextHolder.setTenantId(tenantId);
// 执行 Job
String result = jobHandler.execute(param);
// 添加结果
results.put(tenantId, result);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
TenantContextHolder.clear();
}
});
return JsonUtils.toJsonString(results);
}
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.tenant.core.redis;
import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
@ -15,7 +16,7 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
* @author airhead
*/
@Slf4j
public class TenantRedisCacheManager extends RedisCacheManager {
public class TenantRedisCacheManager extends TimeoutRedisCacheManager {
public TenantRedisCacheManager(RedisCacheWriter cacheWriter,
RedisCacheConfiguration defaultCacheConfiguration) {

View File

@ -1,42 +0,0 @@
package cn.iocoder.yudao.framework.tenant.core.job;
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* 验证 job 租户逻辑
* {@link TenantJobHandlerDecorator}
*
* @author gaibu
*/
public class TenantJobTest extends BaseMockitoUnitTest {
@Mock
TenantFrameworkService tenantFrameworkService;
@Test
public void test() throws Exception {
// 准备测试租户 id
List<Long> tenantIds = Lists.newArrayList(1L, 2L, 3L);
// mock 数据
Mockito.doReturn(tenantIds).when(tenantFrameworkService).getTenantIds();
// 准备测试任务
TestJob testJob = new TestJob();
// 创建任务装饰器
TenantJobHandlerDecorator tenantJobHandlerDecorator = new TenantJobHandlerDecorator(tenantFrameworkService, testJob);
// 执行任务
tenantJobHandlerDecorator.execute(null);
// 断言返回值
assertEquals(testJob.getTenantIds(), tenantIds);
}
}

View File

@ -9,12 +9,12 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@Component
@TenantJob // 标记多租户
public class TestJob implements JobHandler {
private final List<Long> tenantIds = new CopyOnWriteArrayList<>();
@Override
@TenantJob // 标记多租户
public String execute(String param) throws Exception {
tenantIds.add(TenantContextHolder.getTenantId());
return "success";
@ -24,4 +24,5 @@ public class TestJob implements JobHandler {
CollUtil.sort(tenantIds, Long::compareTo);
return tenantIds;
}
}

View File

@ -30,7 +30,7 @@ public abstract class AbstractFileClient<Config extends FileClientConfig> implem
*/
public final void init() {
doInit();
log.info("[init][配置({}) 初始化完成]", config);
log.debug("[init][配置({}) 初始化完成]", config);
}
/**

View File

@ -0,0 +1,309 @@
package cn.iocoder.yudao.framework.flowable.core.util;
import cn.hutool.core.collection.CollUtil;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import java.util.*;
/**
* 流程模型转操作工具类
*/
public class BpmnModelUtils {
/**
* 根据节点获取入口连线
*
* @param source 起始节点
* @return 入口连线列表
*/
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
if (source instanceof FlowNode) {
return ((FlowNode) source).getIncomingFlows();
}
return new ArrayList<>();
}
/**
* 根据节点获取出口连线
*
* @param source 起始节点
* @return 出口连线列表
*/
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
if (source instanceof FlowNode) {
return ((FlowNode) source).getOutgoingFlows();
}
return new ArrayList<>();
}
/**
* 获取流程元素信息
*
* @param model bpmnModel 对象
* @param flowElementId 元素 ID
* @return 元素信息
*/
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
Process process = model.getMainProcess();
return process.getFlowElement(flowElementId);
}
/**
* 获得 BPMN 流程中指定的元素们
*
* @param model
* @param clazz 指定元素例如说{@link UserTask}{@link Gateway} 等等
* @return 元素们
*/
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
List<T> result = new ArrayList<>();
model.getProcesses().forEach(process -> {
process.getFlowElements().forEach(flowElement -> {
if (flowElement.getClass().isAssignableFrom(clazz)) {
result.add((T) flowElement);
}
});
});
return result;
}
/**
* 比较 两个bpmnModel 是否相同
* @param oldModel 老的bpmn model
* @param newModel 新的bpmn model
*/
public static boolean equals(BpmnModel oldModel, BpmnModel newModel) {
// 由于 BpmnModel 未提供 equals 方法所以只能转成字节数组进行比较
return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
}
/**
* bpmnModel 转换成 byte[]
* @param model bpmnModel
*/
public static byte[] getBpmnBytes(BpmnModel model) {
if (model == null) {
return new byte[0];
}
BpmnXMLConverter converter = new BpmnXMLConverter();
return converter.convertToXML(model);
}
// ========== 遍历相关的方法 ==========
/**
* 找到 source 节点之前的所有用户任务节点
*
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 已找到的用户任务节点
* @return 用户任务节点 数组
*/
public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
// 如果该节点为开始节点且存在上级子节点则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList);
}
// 根据类型获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows == null) {
return userTaskList;
}
// 循环找到目标元素
for (SequenceFlow sequenceFlow : sequenceFlows) {
// 如果发现连线重复说明循环了跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 类型为用户节点则新增父级节点
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
}
// 类型为子流程则添加子流程开始节点出口处相连的节点
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
// 获取子流程用户任务节点
List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
// 如果找到节点则说明该线路找到节点不继续向下找反之继续
if (CollUtil.isNotEmpty(childUserTaskList)) {
userTaskList.addAll(childUserTaskList);
}
}
// 继续迭代
userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
}
return userTaskList;
}
/**
* 迭代获取子流程用户任务节点
*
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 需要撤回的用户任务列表
* @return 用户任务节点
*/
public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
// 根据类型获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows == null) {
return userTaskList;
}
// 循环找到目标元素
for (SequenceFlow sequenceFlow : sequenceFlows) {
// 如果发现连线重复说明循环了跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果为用户任务类型且任务节点的 Key 正在运行的任务中存在添加
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
continue;
}
// 如果节点为子流程节点情况则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
// 如果找到节点则说明该线路找到节点不继续向下找反之继续
if (CollUtil.isNotEmpty(childUserTaskList)) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
}
return userTaskList;
}
/**
* 迭代从后向前扫描判断目标节点相对于当前节点是否是串行
* 不存在直接回退到子流程中的情况但存在从子流程出去到父流程情况
*
* @param source 起始节点
* @param target 目标节点
* @param visitedElements 已经经过的连线的 ID用于判断线路是否重复
* @return 结果
*/
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
// 不能是开始事件和子流程
if (source instanceof StartEvent && isInEventSubprocess(source)) {
return false;
}
// 根据类型获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (CollUtil.isEmpty(sequenceFlows)) {
return true;
}
// 循环找到目标元素
for (SequenceFlow sequenceFlow : sequenceFlows) {
// 如果发现连线重复说明循环了跳过这个循环
if (visitedElements.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
visitedElements.add(sequenceFlow.getId());
// 这条线路存在目标节点这条线路完成进入下个线路
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
if (target.getId().equals(sourceFlowElement.getId())) {
continue;
}
// 如果目标节点为并行网关则不继续
if (sourceFlowElement instanceof ParallelGateway) {
return false;
}
// 否则就继续迭代
if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) {
return false;
}
}
return true;
}
/**
* 判断当前节点是否属于不同的子流程
*
* @param flowElement 被判断的节点
* @return true 表示属于子流程
*/
private static boolean isInEventSubprocess(FlowElement flowElement) {
FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();
while (flowElementsContainer != null) {
if (flowElementsContainer instanceof EventSubProcess) {
return true;
}
if (flowElementsContainer instanceof FlowElement) {
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
} else {
flowElementsContainer = null;
}
}
return false;
}
/**
* 根据正在运行的任务节点迭代获取子级任务节点列表向后找
*
* @param source 起始节点
* @param runTaskKeyList 正在运行的任务 Key用于校验任务节点是否是正在运行的节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param userTaskList 需要撤回的用户任务列表
* @return 子级任务节点列表
*/
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList,
Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
// 如果该节点为开始节点且存在上级子节点则顺着上级子节点继续迭代
if (source instanceof StartEvent && source.getSubProcess() != null) {
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
}
// 根据类型获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows == null) {
return userTaskList;
}
// 循环找到目标元素
for (SequenceFlow sequenceFlow : sequenceFlows) {
// 如果发现连线重复说明循环了跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
// 如果为用户任务类型且任务节点的 Key 正在运行的任务中存在添加
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
continue;
}
// 如果节点为子流程节点情况则从节点中的第一个节点开始获取
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
// 如果找到节点则说明该线路找到节点不继续向下找反之继续
if (CollUtil.isNotEmpty(childUserTaskList)) {
userTaskList.addAll(childUserTaskList);
continue;
}
}
// 继续迭代
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
}
return userTaskList;
}
}

View File

@ -1,14 +1,7 @@
package cn.iocoder.yudao.framework.flowable.core.util;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.impl.identity.Authentication;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Flowable 相关的工具方法
*
@ -26,49 +19,6 @@ public class FlowableUtils {
Authentication.setAuthenticatedUserId(null);
}
// ========== BPMN 相关的工具方法 ==========
/**
* 获得 BPMN 流程中指定的元素们
*
* @param model
* @param clazz 指定元素例如说{@link org.flowable.bpmn.model.UserTask}{@link org.flowable.bpmn.model.Gateway} 等等
* @return 元素们
*/
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
List<T> result = new ArrayList<>();
model.getProcesses().forEach(process -> {
process.getFlowElements().forEach(flowElement -> {
if (flowElement.getClass().isAssignableFrom(clazz)) {
result.add((T) flowElement);
}
});
});
return result;
}
/**
* 比较 两个bpmnModel 是否相同
* @param oldModel 老的bpmn model
* @param newModel 新的bpmn model
*/
public static boolean equals(BpmnModel oldModel, BpmnModel newModel) {
// 由于 BpmnModel 未提供 equals 方法所以只能转成字节数组进行比较
return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
}
/**
* bpmnModel 转换成 byte[]
* @param model bpmnModel
*/
public static byte[] getBpmnBytes(BpmnModel model) {
if (model == null) {
return new byte[0];
}
BpmnXMLConverter converter = new BpmnXMLConverter();
return converter.convertToXML(model);
}
// ========== Execution 相关的工具方法 ==========
public static String formatCollectionVariable(String activityId) {

View File

@ -1,21 +1,29 @@
package cn.iocoder.yudao.framework.quartz.config;
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Optional;
/**
* 定时任务 Configuration
*/
@AutoConfiguration
@EnableScheduling // 开启 Spring 自带的定时任务
@Slf4j
public class YudaoQuartzAutoConfiguration {
@Bean
public SchedulerManager schedulerManager(Scheduler scheduler) {
return new SchedulerManager(scheduler);
public SchedulerManager schedulerManager(Optional<Scheduler> scheduler) {
if (!scheduler.isPresent()) {
log.info("[定时任务 - 已禁用][参考 https://doc.iocoder.cn/job/ 开启]");
return new SchedulerManager(null);
}
return new SchedulerManager(scheduler.get());
}
}

View File

@ -4,6 +4,9 @@ import cn.iocoder.yudao.framework.quartz.core.enums.JobDataKeyEnum;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandlerInvoker;
import org.quartz.*;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
/**
* {@link org.quartz.Scheduler} 的管理器负责创建任务
*
@ -37,6 +40,7 @@ public class SchedulerManager {
public void addJob(Long jobId, String jobHandlerName, String jobHandlerParam, String cronExpression,
Integer retryCount, Integer retryInterval)
throws SchedulerException {
validateScheduler();
// 创建 JobDetail 对象
JobDetail jobDetail = JobBuilder.newJob(JobHandlerInvoker.class)
.usingJobData(JobDataKeyEnum.JOB_ID.name(), jobId)
@ -61,6 +65,7 @@ public class SchedulerManager {
public void updateJob(String jobHandlerName, String jobHandlerParam, String cronExpression,
Integer retryCount, Integer retryInterval)
throws SchedulerException {
validateScheduler();
// 创建新 Trigger 对象
Trigger newTrigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);
// 修改调度
@ -74,6 +79,7 @@ public class SchedulerManager {
* @throws SchedulerException 删除异常
*/
public void deleteJob(String jobHandlerName) throws SchedulerException {
validateScheduler();
scheduler.deleteJob(new JobKey(jobHandlerName));
}
@ -84,6 +90,7 @@ public class SchedulerManager {
* @throws SchedulerException 暂停异常
*/
public void pauseJob(String jobHandlerName) throws SchedulerException {
validateScheduler();
scheduler.pauseJob(new JobKey(jobHandlerName));
}
@ -94,6 +101,7 @@ public class SchedulerManager {
* @throws SchedulerException 启动异常
*/
public void resumeJob(String jobHandlerName) throws SchedulerException {
validateScheduler();
scheduler.resumeJob(new JobKey(jobHandlerName));
scheduler.resumeTrigger(new TriggerKey(jobHandlerName));
}
@ -108,11 +116,12 @@ public class SchedulerManager {
*/
public void triggerJob(Long jobId, String jobHandlerName, String jobHandlerParam)
throws SchedulerException {
validateScheduler();
// 触发任务
JobDataMap data = new JobDataMap(); // 无需重试所以不设置 retryCount retryInterval
data.put(JobDataKeyEnum.JOB_ID.name(), jobId);
data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName);
data.put(JobDataKeyEnum.JOB_HANDLER_PARAM.name(), jobHandlerParam);
// 触发任务
scheduler.triggerJob(new JobKey(jobHandlerName), data);
}
@ -127,4 +136,11 @@ public class SchedulerManager {
.build();
}
private void validateScheduler() {
if (scheduler == null) {
throw exception0(NOT_IMPLEMENTED.getCode(),
"[定时任务 - 已禁用][参考 https://doc.iocoder.cn/job/ 开启]");
}
}
}

View File

@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;

View File

@ -26,11 +26,13 @@
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

View File

@ -3,9 +3,11 @@ package cn.iocoder.yudao.framework.lock4j.config;
import cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy;
import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
@AutoConfiguration(before = LockAutoConfiguration.class)
@ConditionalOnClass(name = "com.baomidou.lock.annotation.Lock4j")
public class YudaoLock4jConfiguration {
@Bean

View File

@ -32,12 +32,6 @@
<artifactId>spring-boot-starter-cache</artifactId> <!-- 实现对 Caches 的自动化配置 -->
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>

View File

@ -45,7 +45,7 @@ public class TimeoutRedisCacheManager extends RedisCacheManager {
Duration duration = parseDuration(names[1]);
cacheConfig = cacheConfig.entryTtl(duration);
}
return super.createRedisCache(names[0], cacheConfig);
return super.createRedisCache(name, cacheConfig);
}
/**

View File

@ -55,13 +55,6 @@
<version>${revision}</version>
</dependency>
<!-- 服务保障相关 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 GlobalExceptionHandler 使用 -->
</dependency>
<!-- xss -->
<dependency>
<groupId>org.jsoup</groupId>

View File

@ -1,5 +1,6 @@
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.LocalDateTimeDeserializer;
import cn.iocoder.yudao.framework.jackson.core.databind.LocalDateTimeSerializer;
@ -11,51 +12,41 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
@AutoConfiguration
@Slf4j
public class YudaoJacksonAutoConfiguration {
@Bean
public BeanPostProcessor objectMapperBeanPostProcessor() {
return new BeanPostProcessor() {
@SuppressWarnings("InstantiationOfUtilityClass")
public JsonUtils jsonUtils(List<ObjectMapper> objectMappers) {
// 1.1 创建 SimpleModule 对象
SimpleModule simpleModule = new SimpleModule();
simpleModule
// 新增 Long 类型序列化规则数值超过 2^53-1 JS 会出现精度丢失问题因此 Long 自动序列化为字符串类型
.addSerializer(Long.class, NumberSerializer.INSTANCE)
.addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
// 新增 LocalDateTime 序列化反序列化规则
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
// 1.2 注册到 objectMapper
objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule));
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof ObjectMapper)) {
return bean;
}
ObjectMapper objectMapper = (ObjectMapper) bean;
SimpleModule simpleModule = new SimpleModule();
/*
* 1. 新增Long类型序列化规则数值超过2^53-1在JS会出现精度丢失问题因此Long自动序列化为字符串类型
* 2. 新增LocalDateTime序列化反序列化规则
*/
simpleModule
.addSerializer(Long.class, NumberSerializer.INSTANCE)
.addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
objectMapper.registerModules(simpleModule);
JsonUtils.init(objectMapper);
log.info("初始化 jackson 自动配置");
return bean;
}
};
// 2. 设置 objectMapper JsonUtils {
JsonUtils.init(CollUtil.getFirst(objectMappers));
log.info("[init][初始化 JsonUtils 成功]");
return new JsonUtils();
}
}

View File

@ -11,7 +11,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
@ -33,6 +32,7 @@ import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;
@ -83,9 +83,6 @@ public class GlobalExceptionHandler {
if (ex instanceof HttpRequestMethodNotSupportedException) {
return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex);
}
if (ex instanceof RequestNotPermitted) {
return requestNotPermittedExceptionHandler(request, (RequestNotPermitted) ex);
}
if (ex instanceof ServiceException) {
return serviceExceptionHandler((ServiceException) ex);
}
@ -186,8 +183,7 @@ public class GlobalExceptionHandler {
/**
* 处理 Resilience4j 限流抛出的异常
*/
@ExceptionHandler(value = RequestNotPermitted.class)
public CommonResult<?> requestNotPermittedExceptionHandler(HttpServletRequest req, RequestNotPermitted ex) {
public CommonResult<?> requestNotPermittedExceptionHandler(HttpServletRequest req, Throwable ex) {
log.warn("[requestNotPermittedExceptionHandler][url({}) 访问过于频繁]", req.getRequestURL(), ex);
return CommonResult.error(TOO_MANY_REQUESTS);
}
@ -226,7 +222,12 @@ public class GlobalExceptionHandler {
return tableNotExistsResult;
}
// 情况二处理异常
// 情况二部分特殊的库的处理
if (Objects.equals("io.github.resilience4j.ratelimiter.RequestNotPermitted", ex.getClass().getName())) {
return requestNotPermittedExceptionHandler(req, ex);
}
// 情况三处理异常
log.error("[defaultExceptionHandler]", ex);
// 插入异常日志
this.createExceptionLog(req, ex);

View File

@ -21,6 +21,7 @@ import static cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration.cr
@AutoConfiguration
@EnableConfigurationProperties(XssProperties.class)
@ConditionalOnProperty(prefix = "yudao.xss", name = "enable", havingValue = "true", matchIfMissing = true) // 设置为 false 禁用
public class YudaoXssAutoConfiguration implements WebMvcConfigurer {
/**

View File

@ -1,6 +1,5 @@
cn.iocoder.yudao.framework.apilog.config.YudaoApiLogAutoConfiguration
cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration
cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration
cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
cn.iocoder.yudao.framework.xss.config.YudaoXssAutoConfiguration

View File

@ -4,61 +4,66 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
* Bpm 错误码枚举类
*
* <p>
* bpm 系统使用 1-009-000-000
*/
public interface ErrorCodeConstants {
// ========== 通用流程处理 模块 1009000000 ==========
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1009000002, "获取高亮流程图异常");
// ========== 通用流程处理 模块 1-009-000-000 ==========
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1_009_000_002, "获取高亮流程图异常");
// ========== OA 流程模块 1009001000 ==========
ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1009001001, "请假申请不存在");
ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1009001002, "项目经理岗位未设置");
ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1009001009, "部门的项目经理不存在");
ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1009001004, "部门经理岗位未设置");
ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1009001005, "部门的部门经理不存在");
ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1009001006, "HR岗位未设置");
ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1009001007, "请假天数必须>=1");
// ========== OA 流程模块 1-009-001-000 ==========
ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在");
ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_002, "项目经理岗位未设置");
ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1_009_001_009, "部门的项目经理不存在");
ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_004, "部门经理岗位未设置");
ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_005, "部门的部门经理不存在");
ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1_009_001_006, "HR岗位未设置");
ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1_009_001_007, "请假天数必须>=1");
// ========== 流程模型 1009002000 ==========
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程");
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在");
ErrorCode MODEL_KEY_VALID = new ErrorCode(1009002002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1009002003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1009002004, "部署流程失败," +
// ========== 流程模型 1-009-002-000 ==========
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程");
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, "流程模型不存在");
ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!");
ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置");
ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," +
"原因:用户任务({})未配置分配规则,请点击【修改流程】按钮进行配置");
ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1009003005, "流程定义部署失败,原因:信息未发生变化");
ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1_009_003_005, "流程定义部署失败,原因:信息未发生变化");
// ========== 流程定义 1009003000 ==========
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在");
ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003003, "流程定义处于挂起状态");
ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009003004, "流程定义的模型不存在");
// ========== 流程定义 1-009-003-000 ==========
ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在");
ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态");
ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1_009_003_004, "流程定义的模型不存在");
// ========== 流程实例 1009004000 ==========
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1009004001, "流程取消失败,流程不处于运行中");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1009004002, "流程取消失败,该流程不是你发起的");
// ========== 流程实例 1-009-004-000 ==========
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的");
// ========== 流程任务 1009005000 ==========
ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1009005000, "审批任务失败,原因:该任务不处于未审批");
ErrorCode TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1009005001, "审批任务失败,原因:该任务的审批人不是你");
// ========== 流程任务 1-009-005-000 ==========
ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你");
ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在");
ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作");
ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在");
ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "回退任务失败,目标节点是在并行网关上或非同一路线上,不可跳转");
ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人");
ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在");
// ========== 流程任务分配规则 1009006000 ==========
ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则");
ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006001, "流程任务分配规则不存在");
ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006002, "只有流程模型的任务分配规则,才允许被修改");
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1009006003, "操作失败,原因:找不到任务的审批人!");
ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1009006004, "操作失败,原因:任务分配脚本({}) 不存在");
// ========== 流程任务分配规则 1-009-006-000 ==========
ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则");
ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在");
ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1_009_006_002, "只有流程模型的任务分配规则,才允许被修改");
ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1_009_006_004, "操作失败,原因:任务分配脚本({}) 不存在");
// ========== 动态表单模块 1009010000 ==========
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在");
ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1009010001, "表单项({}) 和 ({}) 使用了相同的字段名({})");
// ========== 动态表单模块 1-009-010-000 ==========
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");
ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, "表单项({}) 和 ({}) 使用了相同的字段名({})");
// ========== 用户组模块 1009011000 ==========
ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1009011000, "用户组不存在");
ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1009011001, "名字为【{}】的用户组已被禁用");
// ========== 用户组模块 1-009-011-000 ==========
ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, "用户组不存在");
ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, "名字为【{}】的用户组已被禁用");
}

View File

@ -20,7 +20,8 @@ public enum BpmProcessInstanceResultEnum {
// ========== 流程任务独有的状态 ==========
BACK(5, "退回/驳回");
BACK(5, "驳回"), // 退回
DELEGATE(6, "委派");
/**
* 结果

View File

@ -75,4 +75,29 @@ public class BpmTaskController {
return success(true);
}
@GetMapping("/get-return-list")
@Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
@Parameter(name = "taskId", description = "当前任务ID", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) {
return success(taskService.getReturnTaskList(taskId));
}
@PutMapping("/return")
@Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
taskService.returnTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/delegate")
@Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮。和向前【加签】有点像,唯一区别是【委托】没有单独创立任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) {
// TODO @海, 后面要有空格
taskService.delegateTask(reqVO,getLoginUserId());
return success(true);
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 委派流程任务的 Request VO")
@Data
public class BpmTaskDelegateReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "被委派人 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "被委派人 ID 不能为空")
private Long delegateUserId;
@Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅")
@NotEmpty(message = "委派原因不能为空")
private String reason;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 回退流程任务的 Request VO")
@Data
public class BpmTaskReturnReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "回退到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotEmpty(message = "回退到的任务 Key 不能为空")
private String targetDefinitionKey;
@Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
@NotEmpty(message = "回退意见不能为空")
private String reason;
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 流程任务的精简 Response VO")
@Data
public class BpmTaskSimpleRespVO {
@Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one")
private String definitionKey;
@Schema(description = "任务名词", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批")
private String name;
}

View File

@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskSimpleRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
@ -55,8 +57,8 @@ public interface BpmTaskConvert {
}
default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks,
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, Map<String, HistoricProcessInstance> historicProcessInstanceMap,
Map<Long, AdminUserRespDTO> userMap) {
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, Map<String, HistoricProcessInstance> historicProcessInstanceMap,
Map<Long, AdminUserRespDTO> userMap) {
return CollectionUtils.convertList(tasks, task -> {
BpmTaskDonePageItemRespVO respVO = convert2(task);
BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
@ -82,8 +84,8 @@ public interface BpmTaskConvert {
BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser);
default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks,
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, HistoricProcessInstance processInstance,
Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {
Map<String, BpmTaskExtDO> bpmTaskExtDOMap, HistoricProcessInstance processInstance,
Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {
return CollectionUtils.convertList(tasks, task -> {
BpmTaskRespVO respVO = convert3(task);
BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
@ -113,29 +115,34 @@ public interface BpmTaskConvert {
void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to);
@Mappings({@Mapping(source = "processInstance.id", target = "id"),
@Mapping(source = "processInstance.name", target = "name"),
@Mapping(source = "processInstance.startUserId", target = "startUserId"),
@Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
@Mapping(source = "startUser.nickname", target = "startUserNickname")})
@Mapping(source = "processInstance.name", target = "name"),
@Mapping(source = "processInstance.startUserId", target = "startUserId"),
@Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
@Mapping(source = "startUser.nickname", target = "startUserNickname")})
BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance,
AdminUserRespDTO startUser);
AdminUserRespDTO startUser);
default BpmTaskExtDO convert2TaskExt(Task task) {
BpmTaskExtDO taskExtDO = new BpmTaskExtDO().setTaskId(task.getId())
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName())
.setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId());
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName())
.setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId());
taskExtDO.setCreateTime(LocalDateTimeUtil.of(task.getCreateTime()));
return taskExtDO;
}
default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,
Task task) {
Task task) {
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())
.setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
.setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
.setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
.setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
return reqDTO;
}
default List<BpmTaskSimpleRespVO> convertList(List<? extends FlowElement> elementList) {
return CollectionUtils.convertList(elementList, element -> new BpmTaskSimpleRespVO()
.setName(element.getName())
.setDefinitionKey(element.getId()));
}
}

View File

@ -74,4 +74,12 @@ public interface BpmModelService {
*/
BpmnModel getBpmnModel(String id);
/**
* 获得流程定义编号对应的 BPMN Model
*
* @param processDefinitionId 流程定义编号
* @return BPMN Model
*/
BpmnModel getBpmnModelByDefinitionId(String processDefinitionId);
}

View File

@ -233,6 +233,11 @@ public class BpmModelServiceImpl implements BpmModelService {
return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true);
}
@Override
public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) {
return repositoryService.getBpmnModel(processDefinitionId);
}
private void checkKeyNCName(String key) {
if (!ValidationUtils.isXmlNCName(key)) {
throw exception(MODEL_KEY_VALID);

View File

@ -5,7 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
@ -200,7 +200,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
BpmnModel newModel = buildBpmnModel(createReqDTO.getBpmnBytes());
BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId());
// 对比字节变化
if (!FlowableUtils.equals(oldModel, newModel)) {
if (!BpmnModelUtils.equals(oldModel, newModel)) {
return false;
}
// 最终发现都一致则返回 true

View File

@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
@ -114,7 +114,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
return Collections.emptyList();
}
// 获得用户任务只有用户任务才可以设置分配规则
List<UserTask> userTasks = FlowableUtils.getBpmnModelElements(model, UserTask.class);
List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(model, UserTask.class);
if (CollUtil.isEmpty(userTasks)) {
return Collections.emptyList();
}

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.bpm.service.task;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
@ -23,7 +22,6 @@ public interface BpmTaskService {
*
* @param userId 用户编号
* @param pageReqVO 分页请求
*
* @return 流程任务分页
*/
PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageReqVO);
@ -33,7 +31,6 @@ public interface BpmTaskService {
*
* @param userId 用户编号
* @param pageReqVO 分页请求
*
* @return 流程任务分页
*/
PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageReqVO);
@ -42,19 +39,17 @@ public interface BpmTaskService {
* 获得流程任务 Map
*
* @param processInstanceIds 流程实例的编号数组
*
* @return 流程任务 Map
*/
default Map<String, List<Task>> getTaskMapByProcessInstanceIds(List<String> processInstanceIds) {
return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds),
Task::getProcessInstanceId);
Task::getProcessInstanceId);
}
/**
* 获得流程任务列表
*
* @param processInstanceIds 流程实例的编号数组
*
* @return 流程任务列表
*/
List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds);
@ -63,7 +58,6 @@ public interface BpmTaskService {
* 获得指令流程实例的流程任务列表包括所有状态的
*
* @param processInstanceId 流程实例的编号
*
* @return 流程任务列表
*/
List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId);
@ -128,4 +122,29 @@ public interface BpmTaskService {
*/
void updateTaskExtAssign(Task task);
/**
* 获取当前任务的可回退的流程集合
*
* @param taskId 当前的任务 ID
* @return 可以回退的节点列表
*/
List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId);
/**
* 将任务回退到指定的 targetDefinitionKey 位置
*
* @param userId 用户编号
* @param reqVO 回退的任务key和当前所在的任务ID
*/
void returnTask(Long userId, BpmTaskReturnReqVO reqVO);
// TODO @海userId 放前面
/**
* 将指定任务委派给其他人处理等接收人处理后再回到原审批人手中审批
*
* @param reqVO 被委派人和被委派的任务编号理由参数
* @param userId 用户编号
*/
void delegateTask(BpmTaskDelegateReqVO reqVO, Long userId);
}

View File

@ -1,29 +1,36 @@
package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
@ -32,6 +39,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import javax.validation.Valid;
@ -39,8 +47,7 @@ import java.time.LocalDateTime;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
@ -57,23 +64,29 @@ public class BpmTaskServiceImpl implements BpmTaskService {
private TaskService taskService;
@Resource
private HistoryService historyService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private BpmModelService bpmModelService;
@Resource
private BpmMessageService messageService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmTaskExtMapper taskExtMapper;
@Resource
private BpmMessageService messageService;
@Override
public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
// 查询待办任务
TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(String.valueOf(userId)) // 分配给自己
.orderByTaskCreateTime().desc(); // 创建时间倒序
.orderByTaskCreateTime().desc(); // 创建时间倒序
if (StrUtil.isNotBlank(pageVO.getName())) {
taskQuery.taskNameLike("%" + pageVO.getName() + "%");
}
@ -91,21 +104,21 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 获得 ProcessInstance Map
Map<String, ProcessInstance> processInstanceMap =
processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId));
processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId));
// 获得 User Map
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
// 拼接结果
return new PageResult<>(BpmTaskConvert.INSTANCE.convertList1(tasks, processInstanceMap, userMap),
taskQuery.count());
taskQuery.count());
}
@Override
public PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) {
// 查询已办任务
HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery().finished() // 已完成
.taskAssignee(String.valueOf(userId)) // 分配给自己
.orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
.taskAssignee(String.valueOf(userId)) // 分配给自己
.orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
if (StrUtil.isNotBlank(pageVO.getName())) {
taskQuery.taskNameLike("%" + pageVO.getName() + "%");
}
@ -123,19 +136,19 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 获得 TaskExtDO Map
List<BpmTaskExtDO> bpmTaskExtDOs =
taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
// 获得 ProcessInstance Map
Map<String, HistoricProcessInstance> historicProcessInstanceMap =
processInstanceService.getHistoricProcessInstanceMap(
convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
processInstanceService.getHistoricProcessInstanceMap(
convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
// 获得 User Map
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
// 拼接结果
return new PageResult<>(
BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap),
taskQuery.count());
BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap),
taskQuery.count());
}
@Override
@ -176,27 +189,58 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {
// 校验任务存在
Task task = checkTask(userId, reqVO.getId());
// 校验流程实例存在
// 1.1 校验任务存在
Task task = validateTask(userId, reqVO.getId());
// 1.2 校验流程实例存在
ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
}
// 情况一被委派的任务不调用 complete 去完成任务
if (DelegationState.PENDING.equals(task.getDelegationState())) {
approveDelegateTask(reqVO, task);
return;
}
// 情况二自己审批的任务调用 complete 去完成任务
// 完成任务审批通过
taskService.complete(task.getId(), instance.getProcessVariables());
// 更新任务拓展表为通过
taskExtMapper.updateByTaskId(
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult())
.setReason(reqVO.getReason()));
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult())
.setReason(reqVO.getReason()));
}
/**
* 审批被委派的任务
*
* @param reqVO 前端请求参数包含当前任务ID审批意见等
* @param task 当前被审批的任务
*/
private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) {
// 1. 添加审批意见
AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId());
AdminUserRespDTO sourceApproveUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner()));
Assert.notNull(sourceApproveUser, "委派任务找不到原审批人,需要检查数据");
String comment = StrUtil.format("[{}]完成委派任务,任务重新回到[{}]手中,审批意见为:{}", currentUser.getNickname(),
sourceApproveUser.getNickname(), reqVO.getReason());
taskService.addComment(reqVO.getId(), task.getProcessInstanceId(),
BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment);
// 2.1 调用 resolveTask 完成任务
// 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()) owner 设置为 assignee
taskService.resolveTask(task.getId());
// 2.2 更新任务拓展表为处理中
taskExtMapper.updateByTaskId(
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult())
.setReason(reqVO.getReason()));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) {
Task task = checkTask(userId, reqVO.getId());
Task task = validateTask(userId, reqVO.getId());
// 校验流程实例存在
ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
if (instance == null) {
@ -208,14 +252,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 更新任务拓展表为不通过
taskExtMapper.updateByTaskId(
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult())
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult())
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
}
@Override
public void updateTaskAssignee(Long userId, BpmTaskUpdateAssigneeReqVO reqVO) {
// 校验任务存在
Task task = checkTask(userId, reqVO.getId());
Task task = validateTask(userId, reqVO.getId());
// 更新负责人
updateTaskAssignee(task.getId(), reqVO.getAssigneeUserId());
}
@ -231,13 +275,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
* @param userId 用户 id
* @param taskId task id
*/
private Task checkTask(Long userId, String taskId) {
private Task validateTask(Long userId, String taskId) {
Task task = getTask(taskId);
if (task == null) {
throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
throw exception(TASK_NOT_EXISTS);
}
if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF);
}
return task;
}
@ -245,7 +289,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override
public void createTaskExt(Task task) {
BpmTaskExtDO taskExtDO =
BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
taskExtMapper.insert(taskExtDO);
}
@ -293,27 +337,183 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override
public void updateTaskExtAssign(Task task) {
BpmTaskExtDO taskExtDO =
new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId());
new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId());
taskExtMapper.updateByTaskId(taskExtDO);
// 发送通知在事务提交时批量执行操作所以直接查询会无法查询到 ProcessInstance所以这里是通过监听事务的提交来实现
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
ProcessInstance processInstance =
processInstanceService.getProcessInstance(task.getProcessInstanceId());
processInstanceService.getProcessInstance(task.getProcessInstanceId());
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
messageService.sendMessageWhenTaskAssigned(
BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
}
});
}
private Task getTask(String id) {
return taskService.createTaskQuery().taskId(id).singleResult();
Task task = taskService.createTaskQuery().taskId(id).singleResult();
if (null == task) {
throw exception(TASK_NOT_EXISTS);
}
return task;
}
private HistoricTaskInstance getHistoricTask(String id) {
return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
}
@Override
public List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId) {
// 1. 校验当前任务 task 存在
Task task = getTask(taskId);
if (task == null) {
throw exception(TASK_NOT_EXISTS);
}
// 根据流程定义获取流程模型信息
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
if (source == null) {
throw exception(TASK_NOT_EXISTS);
}
// 2.1 查询该任务的前置任务节点的 key 集合
List<UserTask> previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null);
if (CollUtil.isEmpty(previousUserList)) {
return Collections.emptyList();
}
// 2.2 过滤只有串行可到达的节点才可以回退类似非串行子流程无法退回
previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null));
return BpmTaskConvert.INSTANCE.convertList(previousUserList);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) {
// 1.1 当前任务 task
Task task = validateTask(userId, reqVO.getId());
if (task.isSuspended()) {
throw exception(TASK_IS_PENDING);
}
// 1.2 校验源头和目标节点的关系并返回目标元素
FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId());
// 2. 调用 flowable 框架的回退逻辑
returnTask0(task, targetElement, reqVO);
// 3. 更新任务扩展表
taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
.setResult(BpmProcessInstanceResultEnum.BACK.getResult())
.setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
}
/**
* 回退流程节点时校验目标任务节点是否可回退
*
* @param sourceKey 当前任务节点 Key
* @param targetKey 目标任务节点 key
* @param processDefinitionId 当前流程定义 ID
* @return 目标任务节点元素
*/
private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) {
// 1.1 获取流程模型信息
BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
// 1.3 获取当前任务节点元素
FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey);
// 1.3 获取跳转的节点元素
FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey);
if (target == null) {
throw exception(TASK_TARGET_NODE_NOT_EXISTS);
}
// 2.2 只有串行可到达的节点才可以回退类似非串行子流程无法退回
if (!BpmnModelUtils.isSequentialReachable(source, target, null)) {
throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
}
return target;
}
/**
* 执行回退逻辑
*
* @param currentTask 当前回退的任务
* @param targetElement 需要回退到的目标任务
* @param reqVO 前端参数封装
*/
public void returnTask0(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
// 1. 获得所有需要回撤的任务 taskDefinitionKey用于稍后的 moveActivityIdsToSingleActivityId 回撤
// 1.1 获取所有正常进行的任务节点 Key
List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();
List<String> runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey);
// 1.2 通过 targetElement 的出口连线计算在 runTaskKeyList 有哪些 key 需要被撤回
// 为什么不直接使用 runTaskKeyList 因为可能存在多个审批分支例如说A -> B -> C D -> F而只要 C 撤回到 A需要排除掉 F
List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
// 2. 给当前要被回退的 task 数组设置回退意见
taskList.forEach(task -> {
// 需要排除掉不需要设置回退意见的任务
if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
return;
}
taskService.addComment(task.getId(), currentTask.getProcessInstanceId(),
BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason());
});
// 3. 执行驳回
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(currentTask.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多)
reqVO.getTargetDefinitionKey()) // targetKey 跳转到的节点(1)
.changeState();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delegateTask(BpmTaskDelegateReqVO reqVO, Long userId) {
// 1.1 校验任务
Task task = validateTaskCanDelegate(userId, reqVO);
// 1.2 校验目标用户存在
AdminUserRespDTO delegateUser = adminUserApi.getUser(reqVO.getDelegateUserId());
if (delegateUser == null) {
throw exception(TASK_DELEGATE_FAIL_USER_NOT_EXISTS);
}
// 2. 添加审批意见
AdminUserRespDTO currentUser = adminUserApi.getUser(userId);
String comment = StrUtil.format("[{}]将任务委派给[{}],委派理由为:{}", currentUser.getNickname(),
delegateUser.getNickname(), reqVO.getReason());
String taskId = reqVO.getId();
// TODO 后面改感觉 comment 应该 type 做个枚举不和 result 耦合在一起
taskService.addComment(taskId, task.getProcessInstanceId(),
BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment);
// 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee)
taskService.setOwner(taskId, task.getAssignee());
// 3.2 执行委派将任务委派给 receiveId
taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString());
// 3.3 更新任务拓展表为委派
taskExtMapper.updateByTaskId(
new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.DELEGATE.getResult())
.setReason(reqVO.getReason()));
}
/**
* 校验任务委派参数
*
* @param userId 用户编号
* @param reqVO 任务编号接收人ID
* @return 当前任务信息
*/
private Task validateTaskCanDelegate(Long userId, BpmTaskDelegateReqVO reqVO) {
// 校验任务
Task task = validateTask(userId, reqVO.getId());
// 校验当前审批人和被委派人不是同一人
if (task.getAssignee().equals(reqVO.getDelegateUserId().toString())) {
throw exception(TASK_DELEGATE_FAIL_USER_REPEAT);
}
return task;
}
}

View File

@ -9,49 +9,49 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== 参数配置 1001000000 ==========
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1001000001, "参数配置不存在");
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1001000002, "参数配置 key 重复");
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1001000003, "不能删除类型为系统内置的参数配置");
ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1001000004, "获取参数配置失败,原因:不允许获取不可见配置");
// ========== 参数配置 1-001-000-000 ==========
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1_001_000_001, "参数配置不存在");
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1_001_000_002, "参数配置 key 重复");
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1_001_000_003, "不能删除类型为系统内置的参数配置");
ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, "获取参数配置失败,原因:不允许获取不可见配置");
// ========== 定时任务 1001001000 ==========
ErrorCode JOB_NOT_EXISTS = new ErrorCode(1001001000, "定时任务不存在");
ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1001001001, "定时任务的处理器已经存在");
ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1001001002, "只允许修改为开启或者关闭状态");
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1001001003, "定时任务已经处于该状态,无需修改");
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1001001004, "只有开启状态的任务,才可以修改");
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1001001005, "CRON 表达式不正确");
// ========== 定时任务 1-001-001-000 ==========
ErrorCode JOB_NOT_EXISTS = new ErrorCode(1_001_001_000, "定时任务不存在");
ErrorCode JOB_HANDLER_EXISTS = new ErrorCode(1_001_001_001, "定时任务的处理器已经存在");
ErrorCode JOB_CHANGE_STATUS_INVALID = new ErrorCode(1_001_001_002, "只允许修改为开启或者关闭状态");
ErrorCode JOB_CHANGE_STATUS_EQUALS = new ErrorCode(1_001_001_003, "定时任务已经处于该状态,无需修改");
ErrorCode JOB_UPDATE_ONLY_NORMAL_STATUS = new ErrorCode(1_001_001_004, "只有开启状态的任务,才可以修改");
ErrorCode JOB_CRON_EXPRESSION_VALID = new ErrorCode(1_001_001_005, "CRON 表达式不正确");
// ========== API 错误日志 1001002000 ==========
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1001002000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1001002001, "API 错误日志已处理");
// ========== API 错误日志 1-001-002-000 ==========
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1_001_002_001, "API 错误日志已处理");
// ========= 文件相关 1001003000=================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1001003000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1001003001, "文件不存在");
ErrorCode FILE_IS_EMPTY = new ErrorCode(1001003002, "文件为空");
// ========= 文件相关 1-001-003-000 =================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在");
ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, "文件为空");
// ========== 代码生成器 1001004000 ==========
ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1003001000, "表定义已经存在");
ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1003001001, "导入的表不存在");
ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1003001002, "导入的字段不存在");
ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1003001004, "表定义不存在");
ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1003001005, "字段义不存在");
ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1003001006, "同步的字段不存在");
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1003001007, "同步失败,不存在改变");
ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1003001008, "数据库的表注释未填写");
ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1003001009, "数据库的表字段({})注释未填写");
// ========== 代码生成器 1-001-004-000 ==========
ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1_003_001_000, "表定义已经存在");
ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1_003_001_001, "导入的表不存在");
ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1_003_001_002, "导入的字段不存在");
ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1_003_001_004, "表定义不存在");
ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1_003_001_005, "字段义不存在");
ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1_003_001_006, "同步的字段不存在");
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1_003_001_007, "同步失败,不存在改变");
ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1_003_001_008, "数据库的表注释未填写");
ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1_003_001_009, "数据库的表字段({})注释未填写");
// ========== 字典类型测试1001005000 ==========
ErrorCode TEST_DEMO_NOT_EXISTS = new ErrorCode(1001005000, "测试示例不存在");
// ========== 字典类型测试1-001-005-000 ==========
ErrorCode TEST_DEMO_NOT_EXISTS = new ErrorCode(1_001_005_000, "测试示例不存在");
// ========== 文件配置 1001006000 ==========
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1001006000, "文件配置不存在");
ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1001006001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
// ========== 文件配置 1-001-006-000 ==========
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1_001_006_001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
// ========== 数据源配置 1001007000 ==========
ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1001007000, "数据源配置不存在");
ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1001007001, "数据源配置不正确,无法进行连接");
// ========== 数据源配置 1-001-007-000 ==========
ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_007_000, "数据源配置不存在");
ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, "数据源配置不正确,无法进行连接");
}

View File

@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
@Mapper
public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
@ -21,7 +18,8 @@ public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
.orderByDesc(FileConfigDO::getId));
}
@Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}")
Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
default FileConfigDO selectByMaster() {
return selectOne(FileConfigDO::getMaster, true);
}
}

View File

@ -1,10 +1,8 @@
package cn.iocoder.yudao.module.infra.service.file;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.file.core.client.FileClient;
@ -17,22 +15,22 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigU
import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.time.LocalDateTime;
import java.util.List;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.Objects;
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.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_NOT_EXISTS;
@ -46,19 +44,29 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG
@Slf4j
public class FileConfigServiceImpl implements FileConfigService {
@Resource
private FileClientFactory fileClientFactory;
private static final Long CACHE_MASTER_ID = 0L;
/**
* 文件配置的缓存
* {@link FileClient} 缓存通过它异步刷新 fileClientFactory
*/
@Getter
private List<FileConfigDO> fileConfigCache;
/**
* Master FileClient 对象有且仅有一个 {@link FileConfigDO#getMaster()} 对应的
*/
@Getter
private FileClient masterFileClient;
private final LoadingCache<Long, FileClient> clientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
new CacheLoader<Long, FileClient>() {
@Override
public FileClient load(Long id) {
FileConfigDO config = Objects.equals(CACHE_MASTER_ID, id) ?
fileConfigMapper.selectByMaster() : fileConfigMapper.selectById(id);
if (config != null) {
fileClientFactory.createOrUpdateFileClient(id, config.getStorage(), config.getConfig());
}
return fileClientFactory.getFileClient(id);
}
});
@Resource
private FileClientFactory fileClientFactory;
@Resource
private FileConfigMapper fileConfigMapper;
@ -66,53 +74,12 @@ public class FileConfigServiceImpl implements FileConfigService {
@Resource
private Validator validator;
@PostConstruct
public void initLocalCache() {
// 第一步查询数据
List<FileConfigDO> configs = fileConfigMapper.selectList();
log.info("[initLocalCache][缓存文件配置,数量为:{}]", configs.size());
// 第二步构建缓存创建或更新文件 Client
configs.forEach(config -> {
fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig());
// 如果是 master进行设置
if (Boolean.TRUE.equals(config.getMaster())) {
masterFileClient = fileClientFactory.getFileClient(config.getId());
}
});
this.fileConfigCache = configs;
}
/**
* 通过定时任务轮询刷新缓存
*
* 目的多节点部署时通过轮询通知所有节点进行刷新
*/
@Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
public void refreshLocalCache() {
// 情况一如果缓存里没有数据则直接刷新缓存
if (CollUtil.isEmpty(fileConfigCache)) {
initLocalCache();
return;
}
// 情况二如果缓存里数据则通过 updateTime 判断是否有数据变更有变更则刷新缓存
LocalDateTime maxTime = CollectionUtils.getMaxValue(fileConfigCache, FileConfigDO::getUpdateTime);
if (fileConfigMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
initLocalCache();
}
}
@Override
public Long createFileConfig(FileConfigCreateReqVO createReqVO) {
// 插入
FileConfigDO fileConfig = FileConfigConvert.INSTANCE.convert(createReqVO)
.setConfig(parseClientConfig(createReqVO.getStorage(), createReqVO.getConfig()))
.setMaster(false); // 默认非 master
fileConfigMapper.insert(fileConfig);
// 刷新缓存
initLocalCache();
return fileConfig.getId();
}
@ -125,8 +92,8 @@ public class FileConfigServiceImpl implements FileConfigService {
.setConfig(parseClientConfig(config.getStorage(), updateReqVO.getConfig()));
fileConfigMapper.updateById(updateObj);
// 刷新缓存
initLocalCache();
// 清空缓存
clearCache(config.getId(), null);
}
@Override
@ -139,8 +106,8 @@ public class FileConfigServiceImpl implements FileConfigService {
// 更新
fileConfigMapper.updateById(new FileConfigDO().setId(id).setMaster(true));
// 刷新缓存
initLocalCache();
// 清空缓存
clearCache(null, true);
}
private FileClientConfig parseClientConfig(Integer storage, Map<String, Object> config) {
@ -164,8 +131,23 @@ public class FileConfigServiceImpl implements FileConfigService {
// 删除
fileConfigMapper.deleteById(id);
// 刷新缓存
initLocalCache();
// 清空缓存
clearCache(id, null);
}
/**
* 清空指定文件配置
*
* @param id 配置编号
* @param master 是否主配置
*/
private void clearCache(Long id, Boolean master) {
if (id != null) {
clientCache.invalidate(id);
}
if (Boolean.TRUE.equals(master)) {
clientCache.invalidate(CACHE_MASTER_ID);
}
}
private FileConfigDO validateFileConfigExists(Long id) {
@ -192,12 +174,17 @@ public class FileConfigServiceImpl implements FileConfigService {
validateFileConfigExists(id);
// 上传文件
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
return fileClientFactory.getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
return getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
}
@Override
public FileClient getFileClient(Long id) {
return fileClientFactory.getFileClient(id);
return clientCache.getUnchecked(id);
}
@Override
public FileClient getMasterFileClient() {
return clientCache.getUnchecked(CACHE_MASTER_ID);
}
}

View File

@ -3,27 +3,27 @@ import { defHttp } from '@/utils/http/axios'
// 查询${table.classComment}列表
export function get${simpleClassName}Page(params) {
return defHttp.get({ url: '${baseURL}/page', params })
return defHttp.get({ url: '${baseURL}/page', params })
}
// 查询${table.classComment}详情
export function get${simpleClassName}(id: number) {
return defHttp.get({ url: '${baseURL}/get?id=' + id })
return defHttp.get({ url: `${baseURL}/get?id=${id}` })
}
// 新增${table.classComment}
export function create${simpleClassName}(data) {
return defHttp.post({ url: '${baseURL}/create', data })
return defHttp.post({ url: '${baseURL}/create', data })
}
// 修改${table.classComment}
export function update${simpleClassName}(data) {
return defHttp.put({ url: '${baseURL}/update', data })
return defHttp.put({ url: '${baseURL}/update', data })
}
// 删除${table.classComment}
export function delete${simpleClassName}(id: number) {
return defHttp.delete({ url: '${baseURL}/delete?id=' + id })
return defHttp.delete({ url: `${baseURL}/delete?id=${id}` })
}
// 导出${table.classComment} Excel

View File

@ -1,4 +1,5 @@
import { BasicColumn, FormSchema, useRender } from '@/components/Table'
import type { BasicColumn, FormSchema } from '@/components/Table'
import { useRender } from '@/components/Table'
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
export const columns: BasicColumn[] = [
@ -92,13 +93,13 @@ export const createFormSchema: FormSchema[] = [
#elseif($column.htmlType == "imageUpload")## 图片上传
component: 'FileUpload',
componentProps: {
fileType: 'file',
fileType: 'image',
maxCount: 1,
},
#elseif($column.htmlType == "fileUpload")## 文件上传
component: 'FileUpload',
componentProps: {
fileType: 'image',
fileType: 'file',
maxCount: 1,
},
#elseif($column.htmlType == "editor")## 文本编辑器
@ -132,6 +133,11 @@ export const createFormSchema: FormSchema[] = [
},
#elseif($column.htmlType == "datetime")## 时间框
component: 'DatePicker',
componentProps: {
showTime: true,
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'x',
},
#elseif($column.htmlType == "textarea")## 文本域
component: 'InputTextArea',
#end
@ -154,7 +160,7 @@ export const updateFormSchema: FormSchema[] = [
#set ($javaField = $column.javaField)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#if (!$column.primaryKey)## 忽略主键,不用在表单里
#if (!$column.primaryKey)## 忽略主键,不用在表单里
{
label: '${comment}',
field: '${javaField}',
@ -164,45 +170,57 @@ export const updateFormSchema: FormSchema[] = [
#if ($column.htmlType == "input")
component: 'Input',
#elseif($column.htmlType == "imageUpload")## 图片上传
component: 'Upload',
component: 'FileUpload',
componentProps: {
fileType: 'image',
maxCount: 1,
},
#elseif($column.htmlType == "fileUpload")## 文件上传
component: 'Upload',
#elseif($column.htmlType == "editor")## 文本编辑器
component: 'Editor',
component: 'FileUpload',
componentProps: {
fileType: 'file',
maxCount: 1,
},
#elseif($column.htmlType == "editor")## 文本编辑器component: 'Editor',
#elseif($column.htmlType == "select")## 下拉框
component: 'Select',
componentProps: {
#if ("" != $dictType)## 有数据字典
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
#else##没数据字典
options:[],
options:[],
#end
},
#elseif($column.htmlType == "checkbox")## 多选框
component: 'Checkbox',
componentProps: {
#if ("" != $dictType)## 有数据字典
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
#else##没数据字典
options:[],
options:[],
#end
},
#elseif($column.htmlType == "radio")## 单选框
component: 'RadioButtonGroup',
componentProps: {
#if ("" != $dictType)## 有数据字典
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number'),
#else##没数据字典
options:[],
options:[],
#end
},
#elseif($column.htmlType == "datetime")## 时间框
component: 'DatePicker',
componentProps: {
showTime: true,
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'x',
},
#elseif($column.htmlType == "textarea")## 文本域
component: 'InputTextArea',
#end
},
#end
#end
#end
#end
]

View File

@ -9,9 +9,10 @@ import { create${simpleClassName}, get${simpleClassName}, update${simpleClassNam
defineOptions({ name: '${table.className}Modal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const emit = defineEmits(['success', 'register'])
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = useForm({
@ -37,11 +38,11 @@ async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate)) {
if (unref(isUpdate))
await update${simpleClassName}(values)
} else {
else
await create${simpleClassName}(values)
}
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
@ -51,7 +52,7 @@ async function handleSubmit() {
}
</script>
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="isUpdate ? t('action.edit') : t('action.create')" @ok="handleSubmit">
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

View File

@ -1,12 +1,12 @@
<script lang="ts" setup>
import ${simpleClassName}Modal from './${simpleClassName}Modal.vue'
import ${ simpleClassName }Modal from './${simpleClassName}Modal.vue'
import { columns, searchFormSchema } from './${classNameVar}.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, useTable, TableAction } from '@/components/Table'
import { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${classNameVar}'
import { delete${ simpleClassName }, export${ simpleClassName }, get${ simpleClassName } Page } from '@/api/${table.moduleName}/${classNameVar}'
defineOptions({ name: '${table.className}' })
@ -16,17 +16,17 @@ const [registerModal, { openModal }] = useModal()
const [registerTable, { getForm, reload }] = useTable({
title: '${table.classComment}列表',
api: get${simpleClassName}Page,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
api: get${ simpleClassName }Page,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
@ -43,14 +43,14 @@ async function handleExport() {
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await export${simpleClassName}(getForm().getFieldsValue())
await export${ simpleClassName } (getForm().getFieldsValue())
createMessage.success(t('common.exportSuccessText'))
},
})
}
async function handleDelete(record: Recordable) {
await delete${simpleClassName}(record.id)
await delete${ simpleClassName } (record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}

View File

@ -59,31 +59,6 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
@MockBean
private FileClientFactory fileClientFactory;
@Test
public void testInitLocalCache() {
// mock 数据
FileConfigDO configDO1 = randomFileConfigDO().setId(1L).setMaster(true);
fileConfigMapper.insert(configDO1);
FileConfigDO configDO2 = randomFileConfigDO().setId(2L).setMaster(false);
fileConfigMapper.insert(configDO2);
// mock fileClientFactory 获得 master
FileClient masterFileClient = mock(FileClient.class);
when(fileClientFactory.getFileClient(eq(1L))).thenReturn(masterFileClient);
// 调用
fileConfigService.initLocalCache();
// 断言 fileClientFactory 调用
verify(fileClientFactory).createOrUpdateFileClient(eq(1L),
eq(configDO1.getStorage()), eq(configDO1.getConfig()));
verify(fileClientFactory).createOrUpdateFileClient(eq(2L),
eq(configDO2.getStorage()), eq(configDO2.getConfig()));
assertSame(masterFileClient, fileConfigService.getMasterFileClient());
// 断言 fileConfigCache 缓存
assertEquals(2, fileConfigService.getFileConfigCache().size());
assertEquals(configDO1, fileConfigService.getFileConfigCache().get(0));
assertEquals(configDO2, fileConfigService.getFileConfigCache().get(1));
}
@Test
public void testCreateFileConfig_success() {
// 准备参数
@ -102,6 +77,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
assertFalse(fileConfig.getMaster());
assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
// 验证 cache
assertNull(fileConfigService.getClientCache().getIfPresent(fileConfigId));
}
@Test
@ -125,6 +102,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
assertPojoEquals(reqVO, fileConfig, "config");
assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath());
assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain());
// 验证 cache
assertNull(fileConfigService.getClientCache().getIfPresent(fileConfig.getId()));
}
@Test
@ -149,6 +128,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
// 断言数据
assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster());
assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster());
// 验证 cache
assertNull(fileConfigService.getClientCache().getIfPresent(0L));
}
@Test
@ -169,6 +150,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
fileConfigService.deleteFileConfig(id);
// 校验数据不存在了
assertNull(fileConfigMapper.selectById(id));
// 验证 cache
assertNull(fileConfigService.getClientCache().getIfPresent(id));
}
@Test
@ -250,14 +233,38 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
@Test
public void testGetFileClient() {
// mock 数据
FileConfigDO fileConfig = randomFileConfigDO().setMaster(false);
fileConfigMapper.insert(fileConfig);
// 准备参数
Long id = randomLongId();
Long id = fileConfig.getId();
// mock 获得 Client
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient);
// 调用并断言
assertSame(fileClient, fileConfigService.getFileClient(id));
// 断言缓存
verify(fileClientFactory).createOrUpdateFileClient(eq(id), eq(fileConfig.getStorage()),
eq(fileConfig.getConfig()));
}
@Test
public void testGetMasterFileClient() {
// mock 数据
FileConfigDO fileConfig = randomFileConfigDO().setMaster(true);
fileConfigMapper.insert(fileConfig);
// 准备参数
Long id = fileConfig.getId();
// mock 获得 Client
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
when(fileClientFactory.getFileClient(eq(0L))).thenReturn(fileClient);
// 调用并断言
assertSame(fileClient, fileConfigService.getMasterFileClient());
// 断言缓存
verify(fileClientFactory).createOrUpdateFileClient(eq(0L), eq(fileConfig.getStorage()),
eq(fileConfig.getConfig()));
}
private FileConfigDO randomFileConfigDO() {

View File

@ -139,4 +139,5 @@ public class FileServiceImplTest extends BaseDbUnitTest {
// 断言
assertSame(result, content);
}
}

View File

@ -9,47 +9,47 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== 商品分类相关 1008001000 ============
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1008001000, "商品分类不存在");
ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1008001001, "父分类不存在");
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1008001002, "父分类不能是二级分类");
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1008001003, "存在子分类,无法删除");
ErrorCode CATEGORY_DISABLED = new ErrorCode(1008001004, "商品分类({})已禁用,无法使用");
ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1008001005, "类别下存在商品,无法删除");
// ========== 商品分类相关 1-008-001-000 ============
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_008_001_000, "商品分类不存在");
ErrorCode CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_008_001_001, "父分类不存在");
ErrorCode CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_008_001_002, "父分类不能是二级分类");
ErrorCode CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_008_001_003, "存在子分类,无法删除");
ErrorCode CATEGORY_DISABLED = new ErrorCode(1_008_001_004, "商品分类({})已禁用,无法使用");
ErrorCode CATEGORY_HAVE_BIND_SPU = new ErrorCode(1_008_001_005, "类别下存在商品,无法删除");
// ========== 商品品牌相关编号 1008002000 ==========
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1008002000, "品牌不存在");
ErrorCode BRAND_DISABLED = new ErrorCode(1008002001, "品牌已禁用");
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1008002002, "品牌名称已存在");
// ========== 商品品牌相关编号 1-008-002-000 ==========
ErrorCode BRAND_NOT_EXISTS = new ErrorCode(1_008_002_000, "品牌不存在");
ErrorCode BRAND_DISABLED = new ErrorCode(1_008_002_001, "品牌已禁用");
ErrorCode BRAND_NAME_EXISTS = new ErrorCode(1_008_002_002, "品牌名称已存在");
// ========== 商品属性项 1008003000 ==========
ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1008003000, "属性项不存在");
ErrorCode PROPERTY_EXISTS = new ErrorCode(1008003001, "属性项的名称已存在");
ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1008003002, "属性项下存在属性值,无法删除");
// ========== 商品属性项 1-008-003-000 ==========
ErrorCode PROPERTY_NOT_EXISTS = new ErrorCode(1_008_003_000, "属性项不存在");
ErrorCode PROPERTY_EXISTS = new ErrorCode(1_008_003_001, "属性项的名称已存在");
ErrorCode PROPERTY_DELETE_FAIL_VALUE_EXISTS = new ErrorCode(1_008_003_002, "属性项下存在属性值,无法删除");
// ========== 商品属性值 1008004000 ==========
ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1008004000, "属性值不存在");
ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1008004001, "属性值的名称已存在");
// ========== 商品属性值 1-008-004-000 ==========
ErrorCode PROPERTY_VALUE_NOT_EXISTS = new ErrorCode(1_008_004_000, "属性值不存在");
ErrorCode PROPERTY_VALUE_EXISTS = new ErrorCode(1_008_004_001, "属性值的名称已存在");
// ========== 商品 SPU 1008005000 ==========
ErrorCode SPU_NOT_EXISTS = new ErrorCode(1008005000, "商品 SPU 不存在");
ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1008005001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
ErrorCode SPU_NOT_ENABLE = new ErrorCode(1008005002, "商品 SPU 不处于上架状态");
ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1008005003, "商品 SPU 不处于回收站状态");
// ========== 商品 SPU 1-008-005-000 ==========
ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, "商品 SPU 不存在");
ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1_008_005_001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
ErrorCode SPU_NOT_ENABLE = new ErrorCode(1_008_005_002, "商品 SPU 不处于上架状态");
ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1_008_005_003, "商品 SPU 不处于回收站状态");
// ========== 商品 SKU 1008006000 ==========
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1008006000, "商品 SKU 不存在");
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品 SKU 的属性组合存在重复");
ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 SPU 下的每个 SKU其属性项必须一致");
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU必须不重复");
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
// ========== 商品 SKU 1-008-006-000 ==========
ErrorCode SKU_NOT_EXISTS = new ErrorCode(1_008_006_000, "商品 SKU 不存在");
ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1_008_006_001, "商品 SKU 的属性组合存在重复");
ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1_008_006_002, "一个 SPU 下的每个 SKU其属性项必须一致");
ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1_008_006_003, "一个 SPU 下的每个 SKU必须不重复");
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1_008_006_004, "商品 SKU 库存不足");
// ========== 商品 评价 1008007000 ==========
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品评价不存在");
ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1008007001, "订单的商品评价已存在");
// ========== 商品 评价 1-008-007-000 ==========
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1_008_007_000, "商品评价不存在");
ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1_008_007_001, "订单的商品评价已存在");
// ========== 商品 收藏 1008008000 ==========
ErrorCode FAVORITE_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏");
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1008008001, "商品收藏不存在");
// ========== 商品 收藏 1-008-008-000 ==========
ErrorCode FAVORITE_EXISTS = new ErrorCode(1_008_008_000, "该商品已经被收藏");
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1_008_008_001, "商品收藏不存在");
}

View File

@ -9,47 +9,47 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== 促销活动相关 1013001000 ============
ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1013001000, "限时折扣活动不存在");
ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013001001, "存在商品参加了其它限时折扣活动");
ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013001002, "限时折扣活动已关闭,不能修改");
ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1013001003, "限时折扣活动未关闭,不能删除");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013001004, "限时折扣活动已关闭,不能重复关闭");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013001005, "限时折扣活动已结束,不能关闭");
// ========== 促销活动相关 1-013-001-000 ============
ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_001_000, "限时折扣活动不存在");
ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_001_001, "存在商品参加了其它限时折扣活动");
ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_002, "限时折扣活动已关闭,不能修改");
ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_001_003, "限时折扣活动未关闭,不能删除");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_001_004, "限时折扣活动已关闭,不能重复关闭");
ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1_013_001_005, "限时折扣活动已结束,不能关闭");
// ========== Banner 相关 1013002000 ============
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1013002000, "Banner 不存在");
// ========== Banner 相关 1-013-002-000 ============
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
// ========== Coupon 相关 1013003000 ============
ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1013003000, "优惠劵没有可使用的商品!");
ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1013003001, "所结算的商品中未满足使用的金额");
// ========== Coupon 相关 1-013-003-000 ============
ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1_013_003_000, "优惠劵没有可使用的商品!");
ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1_013_003_001, "所结算的商品中未满足使用的金额");
// ========== 优惠劵模板 1013004000 ==========
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1013004000, "优惠劵模板不存在");
ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1013004001, "发放数量不能小于已领取数量({})");
ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1013004002, "当前剩余数量不够领取");
ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1013004003, "用户已领取过此优惠券");
ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1013004004, "优惠券已过期");
ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1013004005, "领取方式不正确");
// ========== 优惠劵模板 1-013-004-000 ==========
ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_013_004_000, "优惠劵模板不存在");
ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1_013_004_001, "发放数量不能小于已领取数量({})");
ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1_013_004_002, "当前剩余数量不够领取");
ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1_013_004_003, "用户已领取过此优惠券");
ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1_013_004_004, "优惠券已过期");
ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1_013_004_005, "领取方式不正确");
// ========== 优惠劵 1013005000 ==========
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1013005000, "优惠券不存在");
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1013005001, "回收优惠劵失败,优惠劵已被使用");
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1013005002, "优惠劵不处于待使用状态");
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1013005003, "优惠券不在使用时间范围内");
ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1013005004, "优惠劵不是已使用状态");
// ========== 优惠劵 1-013-005-000 ==========
ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1_013_005_000, "优惠券不存在");
ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1_013_005_001, "回收优惠劵失败,优惠劵已被使用");
ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1_013_005_002, "优惠劵不处于待使用状态");
ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1_013_005_003, "优惠券不在使用时间范围内");
ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1_013_005_004, "优惠劵不是已使用状态");
// ========== 满减送活动 1013006000 ==========
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1013006000, "满减送活动不存在");
ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013006001, "存在商品参加了其它满减送活动");
ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013006002, "满减送活动已关闭,不能修改");
ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1013006003, "满减送活动未关闭,不能删除");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013006004, "满减送活动已关闭,不能重复关闭");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013006005, "满减送活动已结束,不能关闭");
// ========== 满减送活动 1-013-006-000 ==========
ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_006_000, "满减送活动不存在");
ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_006_001, "存在商品参加了其它满减送活动");
ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_002, "满减送活动已关闭,不能修改");
ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1_013_006_003, "满减送活动未关闭,不能删除");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_006_004, "满减送活动已关闭,不能重复关闭");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1_013_006_005, "满减送活动已结束,不能关闭");
// ========== TODO 空着 1013007000 ============
// ========== TODO 空着 1-013-007-000 ============
// ========== 秒杀活动 1013008000 ==========
// ========== 秒杀活动 1-013-008-000 ==========
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在");
ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013008002, "存在商品参加了其它秒杀活动,秒杀时段冲突");
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1013008003, "秒杀活动已关闭,不能修改");
@ -57,19 +57,19 @@ public interface ErrorCodeConstants {
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭");
ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "秒杀失败,原因秒杀库存不足");
// ========== 秒杀时段 1013009000 ==========
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在");
ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1013009001, "秒杀时段冲突");
ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1013009004, "秒杀时段已关闭");
// ========== 秒杀时段 1-013-009-000 ==========
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1_013_009_000, "秒杀时段不存在");
ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1_013_009_001, "秒杀时段冲突");
ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1_013_009_004, "秒杀时段已关闭");
// ========== 拼团活动 1013010000 ==========
// ========== 拼团活动 1-013-010-000 ==========
ErrorCode COMBINATION_ACTIVITY_NOT_EXISTS = new ErrorCode(1013010000, "拼团活动不存在");
ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动");
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE_NOT_UPDATE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010004, "拼团失败,原因:拼团活动已关闭");
// ========== 拼团记录 1013011000 ==========
// ========== 拼团记录 1-013-011-000 ==========
ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013011000, "拼团不存在");
ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1013011001, "拼团失败,已参与过该拼团");
ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011002, "拼团失败,父拼团不存在");
@ -79,17 +79,17 @@ public interface ErrorCodeConstants {
ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1013011006, "拼团失败,原因:单次限购超出");
ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1013011007, "拼团失败,原因:超出总购买次数");
// ========== 砍价活动 1013012000 ==========
// ========== 砍价活动 1-013-012-000 ==========
ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1013012000, "砍价活动不存在");
ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013012001, "存在商品参加了其它砍价活动");
ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013012002, "砍价活动已关闭不能修改");
ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013012003, "砍价活动未关闭或未结束,不能删除");
ErrorCode BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013012004, "砍价失败,原因:该砍价活动库存不足");
// ========== 砍价记录 1013013000 ==========
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013013000, "砍价记录不存在");
ErrorCode BARGAIN_RECORD_EXISTS = new ErrorCode(1013013001, "砍价失败,已参与过该砍价");
ErrorCode BARGAIN_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013013002, "砍价失败,父砍价不存在");
ErrorCode BARGAIN_RECORD_USER_FULL = new ErrorCode(1013013003, "砍价失败,砍价人数已满");
// ========== 砍价记录 1-013-013-000 ==========
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1_013_013_000, "砍价记录不存在");
ErrorCode BARGAIN_RECORD_EXISTS = new ErrorCode(1_013_013_001, "砍价失败,已参与过该砍价");
ErrorCode BARGAIN_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_013_002, "砍价失败,父砍价不存在");
ErrorCode BARGAIN_RECORD_USER_FULL = new ErrorCode(1_013_013_003, "砍价失败,砍价人数已满");
}

View File

@ -9,41 +9,41 @@ import java.time.LocalDateTime;
@Data
public class AppArticleRespVO {
@Schema(description = "文章编号", required = true, example = "1")
@Schema(description = "文章编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "文章标题", required = true, example = "芋道源码 - 促销模块")
@Schema(description = "文章标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码 - 促销模块")
private String title;
@Schema(description = "文章作者", required = true, example = "芋道源码")
@Schema(description = "文章作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String author;
@Schema(description = "分类编号", required = true, example = "2048")
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long categoryId;
@Schema(description = "图文封面", required = true, example = "https://www.iocoder.cn/1.png")
@Schema(description = "图文封面", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
@Schema(description = "文章简介", required = true, example = "我是简介")
@Schema(description = "文章简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是简介")
private String introduction;
@Schema(description = "文章内容", required = true, example = "我是详细")
@Schema(description = "文章内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是详细")
private String description;
@Schema(description = "发布时间", required = true)
@Schema(description = "发布时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "浏览量", required = true, example = "1024")
@Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer browseCount;
@Schema(description = "关联的商品 SPU 编号", example = "1024")
private Long spuId;
// TODO 芋艿下面 2 个字段后端要存储前端不用返回
// @Schema(description = "是否热卖推荐", required = true, example = "true")
// @Schema(description = "是否热卖推荐", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
// private Boolean recommendHot;
//
// @Schema(description = "是否 Banner 推荐", required = true, example = "true")
// @Schema(description = "是否 Banner 推荐", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
// private Boolean recommendBanner;
}

View File

@ -7,20 +7,20 @@ import lombok.Data;
@Data
public class AppArticleCategoryRespVO {
@Schema(description = "分类编号", required = true, example = "1")
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "分类名称", required = true, example = "技术")
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术")
private String name;
@Schema(description = "分类图标", required = true, example = "https://www.iocoder.cn/1.png")
@Schema(description = "分类图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String picUrl;
// TODO 芋艿下面 2 个字段后端要存储前端不用返回
// @Schema(description = "状态", required = true, example = "1")
// @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
// private Integer status;
//
// @Schema(description = "排序", required = true, example = "1024")
// @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
// private Integer sort;
}

View File

@ -9,46 +9,46 @@ import java.time.LocalDateTime;
@Data
public class AppBargainActivityDetailRespVO {
@Schema(description = "砍价活动编号", required = true, example = "1024")
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "砍价活动名称", required = true, example = "618 大砍价")
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
private String name;
@Schema(description = "活动开始时间", required = true)
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", required = true)
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long skuId;
@Schema(description = "商品价格,单位:分", required = true, example = "100")
@Schema(description = "商品价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer price;
@Schema(description = "商品描述", required = true, example = "我要吃西红柿")
@Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "我要吃西红柿")
private String description;
@Schema(description = "砍价库存", required = true, example = "512")
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer stock;
@Schema(description = "商品图片", required = true, example = "4096") // SPU picUrl 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") // SPU picUrl 读取
private String picUrl;
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // SPU marketPrice 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") // SPU marketPrice 读取
private Integer marketPrice;
@Schema(description = "商品单位", required = true, example = "") // SPU unit 读取然后转换
@Schema(description = "商品单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "") // SPU unit 读取然后转换
private String unitName;
@Schema(description = "砍价最低金额,单位:分", required = true, example = "100")
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer bargainPrice;
@Schema(description = "砍价成功数量", required = true, example = "100")
@Schema(description = "砍价成功数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer successCount;
}

View File

@ -9,34 +9,34 @@ import java.time.LocalDateTime;
@Data
public class AppBargainActivityRespVO {
@Schema(description = "砍价活动编号", required = true, example = "1024")
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "砍价活动名称", required = true, example = "618 大砍价")
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大砍价")
private String name;
@Schema(description = "活动开始时间", required = true)
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "活动结束时间", required = true)
@Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "商品 SPU 编号", required = true, example = "2048")
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
@Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long skuId;
@Schema(description = "砍价库存", required = true, example = "512")
@Schema(description = "砍价库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer stock;
@Schema(description = "商品图片", required = true, example = "4096") // SPU picUrl 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") // SPU picUrl 读取
private String picUrl;
@Schema(description = "商品市场价,单位:分", required = true, example = "50") // SPU marketPrice 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") // SPU marketPrice 读取
private Integer marketPrice;
@Schema(description = "砍价最低金额,单位:分", required = true, example = "100")
@Schema(description = "砍价最低金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer bargainPrice;
}

View File

@ -9,7 +9,7 @@ import javax.validation.constraints.NotNull;
@Data
public class AppBargainHelpCreateReqVO {
@Schema(description = "砍价记录编号", required = true, example = "1024")
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "砍价记录编号不能为空")
private Long recordId;

View File

@ -9,16 +9,16 @@ import java.time.LocalDateTime;
@Data
public class AppBargainHelpRespVO {
@Schema(description = "助力用户的昵称", required = true, example = "1024")
@Schema(description = "助力用户的昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String nickname;
@Schema(description = "助力用户的头像", required = true, example = "1024")
@Schema(description = "助力用户的头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String avatar;
@Schema(description = "助力用户的砍价金额", required = true, example = "1024")
@Schema(description = "助力用户的砍价金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer reducePrice;
@Schema(description = "创建时间", required = true, example = "1024")
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private LocalDateTime createTime;
}

View File

@ -9,7 +9,7 @@ import javax.validation.constraints.NotNull;
@Data
public class AppBargainRecordCreateReqVO {
@Schema(description = "砍价活动编号", required = true, example = "1024")
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "砍价活动编号不能为空")
private Long activityId;

View File

@ -9,23 +9,23 @@ import java.util.List;
@Data
public class AppBargainRecordSummaryRespVO {
@Schema(description = "砍价用户数量", required = true, example = "1024")
@Schema(description = "砍价用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer userCount;
@Schema(description = "成功砍价的记录", required = true) // 只返回最近的 7
@Schema(description = "成功砍价的记录", requiredMode = Schema.RequiredMode.REQUIRED) // 只返回最近的 7
private List<Record> successRecords;
@Schema(description = "成功砍价记录")
@Data
public static class Record {
@Schema(description = "用户昵称", required = true, example = "王**")
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王**")
private String nickname;
@Schema(description = "用户头像", required = true, example = "https://www.iocoder.cn/xxx.jpg")
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
private String avatar;
@Schema(description = "活动名称", required = true, example = "天蚕土豆")
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "天蚕土豆")
private String activityName;
}

View File

@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== Order 模块 1011000000 ==========
// ========== Order 模块 1-011-000-000 ==========
ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1011000010, "交易订单项不存在");
ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在");
ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试");
@ -33,45 +33,44 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1011000028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元");
ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1011000029, "交易订单删除失败,订单不是【已取消】状态");
// ========== After Sale 模块 1011000100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1011000101, "申请退款金额错误");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1011000102, "订单已关闭,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID = new ErrorCode(1011000103, "订单未支付,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED = new ErrorCode(1011000104, "订单未发货,无法申请【退货退款】售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED = new ErrorCode(1011000105, "订单项已申请售后,无法重复申请");
ErrorCode AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY = new ErrorCode(1011000106, "审批失败,售后状态不处于审批中");
ErrorCode AFTER_SALE_UPDATE_STATUS_FAIL = new ErrorCode(1011000107, "操作售后单失败,请刷新后重试");
ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1011000108, "退货失败,售后单状态不处于【待买家退货】");
ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1011000109, "确认收货失败,售后单状态不处于【待确认收货】");
ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1011000110, "退款失败,售后单状态不是【待退款】");
// ========== After Sale 模块 1-011-000-100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1_011_000_101, "申请退款金额错误");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1_011_000_102, "订单已关闭,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID = new ErrorCode(1_011_000_103, "订单未支付,无法申请售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED = new ErrorCode(1_011_000_104, "订单未发货,无法申请【退货退款】售后");
ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED = new ErrorCode(1_011_000_105, "订单项已申请售后,无法重复申请");
ErrorCode AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY = new ErrorCode(1_011_000_106, "审批失败,售后状态不处于审批中");
ErrorCode AFTER_SALE_UPDATE_STATUS_FAIL = new ErrorCode(1_011_000_107, "操作售后单失败,请刷新后重试");
ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1_011_000_108, "退货失败,售后单状态不处于【待买家退货】");
ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1_011_000_109, "确认收货失败,售后单状态不处于【待确认收货】");
ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】");
ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
new ErrorCode(1011000111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
new ErrorCode(1_011_000_111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
// ========== Cart 模块 1011002000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在");
// ========== Cart 模块 1-011-002-000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在");
// ========== Price 相关 1011003000 ============
// ========== Price 相关 1-011-003-000 ============
ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0");
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板");
ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1011003004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵");
// ========== 物流 Express 模块 1011004000 ==========
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在");
ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011004001, "已经存在该编码的快递公司");
ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1011004002, "需要接入快递服务商比如【快递100】");
ErrorCode EXPRESS_STATUS_NOT_ENABLE = new ErrorCode(1011004003, "快递公司未启用");
// ========== 物流 Express 模块 1-011-004-000 ==========
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在");
ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1_011_004_001, "已经存在该编码的快递公司");
ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1_011_004_002, "需要接入快递服务商比如【快递100】");
ErrorCode EXPRESS_STATUS_NOT_ENABLE = new ErrorCode(1_011_004_003, "快递公司未启用");
ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1011004101, "快递查询接口异常");
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1011004102, "快递查询返回失败,原因:{}");
ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1_011_004_101, "快递查询接口异常");
ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1_011_004_102, "快递查询返回失败,原因:{}");
// ========== 物流 Template 模块 1011005000 ==========
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011005000, "已经存在该运费模板名");
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011005001, "运费模板不存在");
// ========== 物流 PICK_UP 模块 1011006000 ==========
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
// ========== 物流 Template 模块 1-011-005-000 ==========
ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1_011_005_000, "已经存在该运费模板名");
ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_011_005_001, "运费模板不存在");
// ========== 物流 PICK_UP 模块 1-011-006-000 ==========
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1_011_006_000, "自提门店不存在");
// ========== 分销用户 模块 1011007000 ==========
ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1011007000, "分销用户不存在");

View File

@ -8,10 +8,10 @@ import lombok.Data;
@Data
public class AppDeliveryConfigRespVO {
@Schema(description = "腾讯地图 KEY", required = true, example = "123456")
@Schema(description = "腾讯地图 KEY", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
private String tencentLbsKey;
@Schema(description = "是否开启自提", required = true, example = "true")
@Schema(description = "是否开启自提", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean pickUpEnable;
}

View File

@ -7,10 +7,10 @@ import lombok.Data;
@Data
public class AppDeliveryExpressRespVO {
@Schema(description = "编号", required = true, example = "1")
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "门店名称", required = true, example = "顺丰")
@Schema(description = "门店名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "顺丰")
private String name;
}

View File

@ -25,12 +25,12 @@ public class AppTradeOrderSettlementReqVO {
@Schema(description = "优惠劵编号", example = "1024")
private Long couponId;
@Schema(description = "是否使用积分", required = true, example = "true")
@Schema(description = "是否使用积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否使用积分不能为空")
private Boolean pointStatus;
// ========== 配送相关相关字段 ==========
@Schema(description = "配送方式", example = "1")
@Schema(description = "配送方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确")
private Integer deliveryType;

View File

@ -13,7 +13,7 @@ import java.util.List;
@Data
public class AppTradeOrderSettlementRespVO {
@Schema(description = "交易类型", required = true, example = "1") // 对应 TradeOrderTypeEnum 枚举
@Schema(description = "交易类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 对应 TradeOrderTypeEnum 枚举
private Integer type = 1; // TODO 芋艿改成计算
@Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED)

View File

@ -9,51 +9,51 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== 用户相关 1004001000============
// ========== 用户相关 1-004-001-000 ============
ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在");
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1004001001, "手机号未注册用户");
ErrorCode USER_MOBILE_USED = new ErrorCode(1004001002, "修改手机失败,该手机号({})已经被使用");
ErrorCode USER_POINT_NOT_ENOUGH = new ErrorCode(1004001003, "用户积分余额不足");
// ========== AUTH 模块 1004003000 ==========
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1004003001, "登录失败,账号被禁用");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1004003005, "未绑定账号,需要进行绑定");
ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1004003006, "获得手机号失败");
ErrorCode AUTH_MOBILE_USED = new ErrorCode(1004003007, "手机号已经被使用");
// ========== AUTH 模块 1-004-003-000 ==========
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_004_003_001, "登录失败,账号被禁用");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_004_003_005, "未绑定账号,需要进行绑定");
ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_004_003_006, "获得手机号失败");
ErrorCode AUTH_MOBILE_USED = new ErrorCode(1_004_003_007, "手机号已经被使用");
// ========== 用户收件地址 1004004000 ==========
ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1004004000, "用户收件地址不存在");
// ========== 用户收件地址 1-004-004-000 ==========
ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1_004_004_000, "用户收件地址不存在");
//========== 用户标签 1004006000 ==========
ErrorCode TAG_NOT_EXISTS = new ErrorCode(1004006000, "用户标签不存在");
ErrorCode TAG_NAME_EXISTS = new ErrorCode(1004006001, "用户标签已经存在");
ErrorCode TAG_HAS_USER = new ErrorCode(1004006002, "用户标签下存在用户,无法删除");
//========== 用户标签 1-004-006-000 ==========
ErrorCode TAG_NOT_EXISTS = new ErrorCode(1_004_006_000, "用户标签不存在");
ErrorCode TAG_NAME_EXISTS = new ErrorCode(1_004_006_001, "用户标签已经存在");
ErrorCode TAG_HAS_USER = new ErrorCode(1_004_006_002, "用户标签下存在用户,无法删除");
//========== 积分配置 1004007000 ==========
//========== 积分配置 1-004-007-000 ==========
//========== 积分记录 1004008000 ==========
ErrorCode POINT_RECORD_BIZ_NOT_SUPPORT = new ErrorCode(1004008000, "用户积分记录业务类型不支持");
//========== 积分记录 1-004-008-000 ==========
ErrorCode POINT_RECORD_BIZ_NOT_SUPPORT = new ErrorCode(1_004_008_000, "用户积分记录业务类型不支持");
//========== 签到配置 1004009000 ==========
ErrorCode SIGN_IN_CONFIG_NOT_EXISTS = new ErrorCode(1004009000, "签到天数规则不存在");
ErrorCode SIGN_IN_CONFIG_EXISTS = new ErrorCode(1004009001, "签到天数规则已存在");
//========== 签到配置 1-004-009-000 ==========
ErrorCode SIGN_IN_CONFIG_NOT_EXISTS = new ErrorCode(1_004_009_000, "签到天数规则不存在");
ErrorCode SIGN_IN_CONFIG_EXISTS = new ErrorCode(1_004_009_001, "签到天数规则已存在");
//========== 签到配置 1004010000 ==========
//========== 签到配置 1-004-010-000 ==========
ErrorCode SIGN_IN_RECORD_TODAY_EXISTS = new ErrorCode(1004010000,"今日已签到,请勿重复签到");
//========== 用户等级 1004011000 ==========
ErrorCode LEVEL_NOT_EXISTS = new ErrorCode(1004011000, "用户等级不存在");
ErrorCode LEVEL_NAME_EXISTS = new ErrorCode(1004011001, "用户等级名称[{}]已被使用");
ErrorCode LEVEL_VALUE_EXISTS = new ErrorCode(1004011002, "用户等级值[{}]已被[{}]使用");
ErrorCode LEVEL_EXPERIENCE_MIN = new ErrorCode(1004011003, "升级经验必须大于上一个等级[{}]设置的升级经验[{}]");
ErrorCode LEVEL_EXPERIENCE_MAX = new ErrorCode(1004011004, "升级经验必须小于下一个等级[{}]设置的升级经验[{}]");
ErrorCode LEVEL_HAS_USER = new ErrorCode(1004011005, "用户等级下存在用户,无法删除");
//========== 用户等级 1-004-011-000 ==========
ErrorCode LEVEL_NOT_EXISTS = new ErrorCode(1_004_011_000, "用户等级不存在");
ErrorCode LEVEL_NAME_EXISTS = new ErrorCode(1_004_011_001, "用户等级名称[{}]已被使用");
ErrorCode LEVEL_VALUE_EXISTS = new ErrorCode(1_004_011_002, "用户等级值[{}]已被[{}]使用");
ErrorCode LEVEL_EXPERIENCE_MIN = new ErrorCode(1_004_011_003, "升级经验必须大于上一个等级[{}]设置的升级经验[{}]");
ErrorCode LEVEL_EXPERIENCE_MAX = new ErrorCode(1_004_011_004, "升级经验必须小于下一个等级[{}]设置的升级经验[{}]");
ErrorCode LEVEL_HAS_USER = new ErrorCode(1_004_011_005, "用户等级下存在用户,无法删除");
ErrorCode EXPERIENCE_BIZ_NOT_SUPPORT = new ErrorCode(1004011201, "用户经验业务类型不支持");
ErrorCode EXPERIENCE_BIZ_NOT_SUPPORT = new ErrorCode(1_004_011_201, "用户经验业务类型不支持");
//========== 用户分组 1004012000 ==========
ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1004012000, "用户分组不存在");
ErrorCode GROUP_HAS_USER = new ErrorCode(1004012001, "用户分组下存在用户,无法删除");
//========== 用户分组 1-004-012-000 ==========
ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1_004_012_000, "用户分组不存在");
ErrorCode GROUP_HAS_USER = new ErrorCode(1_004_012_001, "用户分组下存在用户,无法删除");
}

View File

@ -33,7 +33,7 @@ public class AppAuthLoginReqVO {
// ========== 绑定社交登录时需要传递如下参数 ==========
@Schema(description = "社交平台的类型,参见 SysUserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
private Integer socialType;

View File

@ -35,7 +35,7 @@ public class AppAuthSmsLoginReqVO {
// ========== 绑定社交登录时需要传递如下参数 ==========
@Schema(description = "社交平台的类型,参见 SysUserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
private Integer socialType;

View File

@ -18,7 +18,7 @@ import javax.validation.constraints.NotNull;
@Builder
public class AppAuthSocialLoginReqVO {
@Schema(description = "社交平台的类型,参见 SysUserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer type;

View File

@ -18,7 +18,7 @@ import javax.validation.constraints.NotNull;
@Builder
public class AppSocialUserBindReqVO {
@Schema(description = "社交平台的类型,参见 SysUserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer type;

View File

@ -18,7 +18,7 @@ import javax.validation.constraints.NotNull;
@Builder
public class AppSocialUserUnbindReqVO {
@Schema(description = "社交平台的类型,参见 SysUserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
@NotNull(message = "社交平台的类型不能为空")
private Integer type;

View File

@ -222,10 +222,10 @@ public class MemberUserServiceImpl implements MemberUserService {
}
// 如果 id 为空说明不用比较是否为相同 id 的用户
if (id == null) {
throw exception(USER_MOBILE_USED);
throw exception(USER_MOBILE_USED, mobile);
}
if (!user.getId().equals(id)) {
throw exception(USER_MOBILE_USED);
throw exception(USER_MOBILE_USED, mobile);
}
}

View File

@ -9,56 +9,56 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== 公众号账号 1006000000============
ErrorCode ACCOUNT_NOT_EXISTS = new ErrorCode(1006000000, "公众号账号不存在");
ErrorCode ACCOUNT_GENERATE_QR_CODE_FAIL = new ErrorCode(1006000001, "生成公众号二维码失败,原因:{}");
ErrorCode ACCOUNT_CLEAR_QUOTA_FAIL = new ErrorCode(1006000002, "清空公众号的 API 配额失败,原因:{}");
// ========== 公众号账号 1-006-000-000 ============
ErrorCode ACCOUNT_NOT_EXISTS = new ErrorCode(1_006_000_000, "公众号账号不存在");
ErrorCode ACCOUNT_GENERATE_QR_CODE_FAIL = new ErrorCode(1_006_000_001, "生成公众号二维码失败,原因:{}");
ErrorCode ACCOUNT_CLEAR_QUOTA_FAIL = new ErrorCode(1_006_000_002, "清空公众号的 API 配额失败,原因:{}");
// ========== 公众号统计 1006001000============
ErrorCode STATISTICS_GET_USER_SUMMARY_FAIL = new ErrorCode(1006001000, "获取粉丝增减数据失败,原因:{}");
ErrorCode STATISTICS_GET_USER_CUMULATE_FAIL = new ErrorCode(1006001001, "获得粉丝累计数据失败,原因:{}");
ErrorCode STATISTICS_GET_UPSTREAM_MESSAGE_FAIL = new ErrorCode(1006001002, "获得消息发送概况数据失败,原因:{}");
ErrorCode STATISTICS_GET_INTERFACE_SUMMARY_FAIL = new ErrorCode(1006001003, "获得接口分析数据失败,原因:{}");
// ========== 公众号统计 1-006-001-000 ============
ErrorCode STATISTICS_GET_USER_SUMMARY_FAIL = new ErrorCode(1_006_001_000, "获取粉丝增减数据失败,原因:{}");
ErrorCode STATISTICS_GET_USER_CUMULATE_FAIL = new ErrorCode(1_006_001_001, "获得粉丝累计数据失败,原因:{}");
ErrorCode STATISTICS_GET_UPSTREAM_MESSAGE_FAIL = new ErrorCode(1_006_001_002, "获得消息发送概况数据失败,原因:{}");
ErrorCode STATISTICS_GET_INTERFACE_SUMMARY_FAIL = new ErrorCode(1_006_001_003, "获得接口分析数据失败,原因:{}");
// ========== 公众号标签 1006002000============
ErrorCode TAG_NOT_EXISTS = new ErrorCode(1006002000, "标签不存在");
ErrorCode TAG_CREATE_FAIL = new ErrorCode(1006002001, "创建标签失败,原因:{}");
ErrorCode TAG_UPDATE_FAIL = new ErrorCode(1006002002, "更新标签失败,原因:{}");
ErrorCode TAG_DELETE_FAIL = new ErrorCode(1006002003, "删除标签失败,原因:{}");
ErrorCode TAG_GET_FAIL = new ErrorCode(1006002004, "获得标签失败,原因:{}");
// ========== 公众号标签 1-006-002-000 ============
ErrorCode TAG_NOT_EXISTS = new ErrorCode(1_006_002_000, "标签不存在");
ErrorCode TAG_CREATE_FAIL = new ErrorCode(1_006_002_001, "创建标签失败,原因:{}");
ErrorCode TAG_UPDATE_FAIL = new ErrorCode(1_006_002_002, "更新标签失败,原因:{}");
ErrorCode TAG_DELETE_FAIL = new ErrorCode(1_006_002_003, "删除标签失败,原因:{}");
ErrorCode TAG_GET_FAIL = new ErrorCode(1_006_002_004, "获得标签失败,原因:{}");
// ========== 公众号粉丝 1006003000============
ErrorCode USER_NOT_EXISTS = new ErrorCode(1006003000, "粉丝不存在");
ErrorCode USER_UPDATE_TAG_FAIL = new ErrorCode(1006003001, "更新粉丝标签失败,原因:{}");
// ========== 公众号粉丝 1-006-003-000 ============
ErrorCode USER_NOT_EXISTS = new ErrorCode(1_006_003_000, "粉丝不存在");
ErrorCode USER_UPDATE_TAG_FAIL = new ErrorCode(1_006_003_001, "更新粉丝标签失败,原因:{}");
// ========== 公众号素材 1006004000============
ErrorCode MATERIAL_NOT_EXISTS = new ErrorCode(1006004000, "素材不存在");
ErrorCode MATERIAL_UPLOAD_FAIL = new ErrorCode(1006004001, "上传素材失败,原因:{}");
ErrorCode MATERIAL_IMAGE_UPLOAD_FAIL = new ErrorCode(1006004002, "上传图片失败,原因:{}");
ErrorCode MATERIAL_DELETE_FAIL = new ErrorCode(1006004003, "删除素材失败,原因:{}");
// ========== 公众号素材 1-006-004-000 ============
ErrorCode MATERIAL_NOT_EXISTS = new ErrorCode(1_006_004_000, "素材不存在");
ErrorCode MATERIAL_UPLOAD_FAIL = new ErrorCode(1_006_004_001, "上传素材失败,原因:{}");
ErrorCode MATERIAL_IMAGE_UPLOAD_FAIL = new ErrorCode(1_006_004_002, "上传图片失败,原因:{}");
ErrorCode MATERIAL_DELETE_FAIL = new ErrorCode(1_006_004_003, "删除素材失败,原因:{}");
// ========== 公众号消息 1006005000============
ErrorCode MESSAGE_SEND_FAIL = new ErrorCode(1006005000, "发送消息失败,原因:{}");
// ========== 公众号消息 1-006-005-000 ============
ErrorCode MESSAGE_SEND_FAIL = new ErrorCode(1_006_005_000, "发送消息失败,原因:{}");
// ========== 公众号发布能力 1006006000============
ErrorCode FREE_PUBLISH_LIST_FAIL = new ErrorCode(1006006000, "获得已成功发布列表失败,原因:{}");
ErrorCode FREE_PUBLISH_SUBMIT_FAIL = new ErrorCode(1006006001, "提交发布失败,原因:{}");
ErrorCode FREE_PUBLISH_DELETE_FAIL = new ErrorCode(1006006002, "删除发布失败,原因:{}");
// ========== 公众号发布能力 1-006-006-000 ============
ErrorCode FREE_PUBLISH_LIST_FAIL = new ErrorCode(1_006_006_000, "获得已成功发布列表失败,原因:{}");
ErrorCode FREE_PUBLISH_SUBMIT_FAIL = new ErrorCode(1_006_006_001, "提交发布失败,原因:{}");
ErrorCode FREE_PUBLISH_DELETE_FAIL = new ErrorCode(1_006_006_002, "删除发布失败,原因:{}");
// ========== 公众号草稿 1006007000============
ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1006007000, "获得草稿列表失败,原因:{}");
ErrorCode DRAFT_CREATE_FAIL = new ErrorCode(1006007001, "创建草稿失败,原因:{}");
ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1006007002, "更新草稿失败,原因:{}");
ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1006007003, "删除草稿失败,原因:{}");
// ========== 公众号草稿 1-006-007-000 ============
ErrorCode DRAFT_LIST_FAIL = new ErrorCode(1_006_007_000, "获得草稿列表失败,原因:{}");
ErrorCode DRAFT_CREATE_FAIL = new ErrorCode(1_006_007_001, "创建草稿失败,原因:{}");
ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1_006_007_002, "更新草稿失败,原因:{}");
ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1_006_007_003, "删除草稿失败,原因:{}");
// ========== 公众号菜单 1006008000============
ErrorCode MENU_SAVE_FAIL = new ErrorCode(1006008000, "创建菜单失败,原因:{}");
ErrorCode MENU_DELETE_FAIL = new ErrorCode(1006008001, "删除菜单失败,原因:{}");
// ========== 公众号菜单 1-006-008-000 ============
ErrorCode MENU_SAVE_FAIL = new ErrorCode(1_006_008_000, "创建菜单失败,原因:{}");
ErrorCode MENU_DELETE_FAIL = new ErrorCode(1_006_008_001, "删除菜单失败,原因:{}");
// ========== 公众号自动回复 1006009000============
ErrorCode AUTO_REPLY_NOT_EXISTS = new ErrorCode(1006009000, "自动回复不存在");
ErrorCode AUTO_REPLY_ADD_SUBSCRIBE_FAIL_EXISTS = new ErrorCode(1006009001, "操作失败,原因:已存在关注时的回复");
ErrorCode AUTO_REPLY_ADD_MESSAGE_FAIL_EXISTS = new ErrorCode(1006009002, "操作失败,原因:已存在该消息类型的回复");
ErrorCode AUTO_REPLY_ADD_KEYWORD_FAIL_EXISTS = new ErrorCode(1006009003, "操作失败,原因:已关在该关键字的回复");
// ========== 公众号自动回复 1-006-009-000 ============
ErrorCode AUTO_REPLY_NOT_EXISTS = new ErrorCode(1_006_009_000, "自动回复不存在");
ErrorCode AUTO_REPLY_ADD_SUBSCRIBE_FAIL_EXISTS = new ErrorCode(1_006_009_001, "操作失败,原因:已存在关注时的回复");
ErrorCode AUTO_REPLY_ADD_MESSAGE_FAIL_EXISTS = new ErrorCode(1_006_009_002, "操作失败,原因:已存在该消息类型的回复");
ErrorCode AUTO_REPLY_ADD_KEYWORD_FAIL_EXISTS = new ErrorCode(1_006_009_003, "操作失败,原因:已关在该关键字的回复");
}

View File

@ -9,36 +9,38 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== APP 模块 1007000000 ==========
ErrorCode APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
ErrorCode APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用");
ErrorCode APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1007000003, "支付应用存在支付订单,无法删除");
ErrorCode APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1007000004, "支付应用存在退款订单,无法删除");
// ========== APP 模块 1-007-000-000 ==========
ErrorCode APP_NOT_FOUND = new ErrorCode(1_007_000_000, "App 不存在");
ErrorCode APP_IS_DISABLE = new ErrorCode(1_007_000_002, "App 已经被禁用");
ErrorCode APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1_007_000_003, "支付应用存在支付订单,无法删除");
ErrorCode APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1_007_000_004, "支付应用存在退款订单,无法删除");
// ========== CHANNEL 模块 1007001000 ==========
ErrorCode CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
ErrorCode CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用");
ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001004, "已存在相同的渠道");
// ========== CHANNEL 模块 1-007-001-000 ==========
ErrorCode CHANNEL_NOT_FOUND = new ErrorCode(1_007_001_000, "支付渠道的配置不存在");
ErrorCode CHANNEL_IS_DISABLE = new ErrorCode(1_007_001_001, "支付渠道已经禁用");
ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1_007_001_004, "已存在相同的渠道");
// ========== ORDER 模块 1007002000 ==========
// ========== ORDER 模块 1-007-002-000 ==========
ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在");
ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
ErrorCode PAY_ORDER_STATUS_IS_SUCCESS = new ErrorCode(1007002002, "订单已支付,请刷新页面");
ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期");
ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}");
ErrorCode PAY_ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1007002005, "支付订单退款失败,原因:状态不是已支付或已退款");
ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1_007_002_006, "支付订单调价失败,原因:支付订单已付款,不能调价");
ErrorCode ORDER_UPDATE_PRICE_FAIL_EQUAL = new ErrorCode(1007002007, "支付订单调价失败,原因:价格没有变化");
// ========== ORDER 模块(拓展单) 1007003000 ==========
// ========== ORDER 模块(拓展单) 1-007-003-000 ==========
ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付");
ErrorCode PAY_ORDER_EXTENSION_IS_PAID = new ErrorCode(1007003002, "订单已支付,请等待支付结果");
// ========== 支付模块(退款) 1007006000 ==========
ErrorCode REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额");
ErrorCode REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中");
ErrorCode REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单");
ErrorCode REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");
ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款");
// ========== 支付模块(退款) 1-007-006-000 ==========
ErrorCode REFUND_PRICE_EXCEED = new ErrorCode(1_007_006_000, "退款金额超过订单可退款金额");
ErrorCode REFUND_HAS_REFUNDING = new ErrorCode(1_007_006_002, "已经有退款在处理中");
ErrorCode REFUND_EXISTS = new ErrorCode(1_007_006_003, "已经存在退款单");
ErrorCode REFUND_NOT_FOUND = new ErrorCode(1_007_006_004, "支付退款单不存在");
ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_006_005, "支付退款单不处于待退款");
// ========== 钱包模块 1007007000 ==========
ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在");
@ -47,7 +49,7 @@ public interface ErrorCodeConstants {
ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007003, "已经存在钱包退款");
ErrorCode WALLET_FREEZE_PRICE_NOT_ENOUGH = new ErrorCode(1007007004, "钱包冻结余额不足");
// ========== 钱包充值模块 1007008000 ==========
// ========== 钱包模块 1-007-007-000 ==========
ErrorCode WALLET_RECHARGE_NOT_FOUND = new ErrorCode(1007008000, "钱包充值记录不存在");
ErrorCode WALLET_RECHARGE_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007008001, "钱包充值更新支付状态失败,钱包充值记录不是【未支付】状态");
ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1007008002, "钱包充值更新支付状态失败,支付单编号不匹配");
@ -59,7 +61,8 @@ public interface ErrorCodeConstants {
ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007008008, "钱包退款更新失败,钱包退款单编号不匹配");
ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007008009, "钱包退款更新失败,退款订单不存在");
ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007008010, "钱包退款更新失败,退款单金额不匹配");
// ========== 示例订单 1007900000 ==========
// ========== 示例订单 1-007-900-000 ==========
ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在");
ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态");
ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1007900002, "示例订单更新支付状态失败,支付单编号不匹配");

View File

@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
import cn.iocoder.yudao.module.pay.service.app.PayAppService;
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
@ -53,9 +53,8 @@ public class PayNotifyController {
private PayNotifyService notifyService;
@Resource
private PayAppService appService;
@Resource
private PayClientFactory payClientFactory;
private PayChannelService channelService;
@PostMapping(value = "/order/{channelId}")
@Operation(summary = "支付渠道的统一【支付】回调")
@ -66,7 +65,7 @@ public class PayNotifyController {
@RequestBody(required = false) String body) {
log.info("[notifyOrder][channelId({}) 回调数据({}/{})]", channelId, params, body);
// 1. 校验支付渠道是否存在
PayClient payClient = payClientFactory.getPayClient(channelId);
PayClient payClient = channelService.getPayClient(channelId);
if (payClient == null) {
log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
throw exception(CHANNEL_NOT_FOUND);
@ -87,7 +86,7 @@ public class PayNotifyController {
@RequestBody(required = false) String body) {
log.info("[notifyRefund][channelId({}) 回调数据({}/{})]", channelId, params, body);
// 1. 校验支付渠道是否存在
PayClient payClient = payClientFactory.getPayClient(channelId);
PayClient payClient = channelService.getPayClient(channelId);
if (payClient == null) {
log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
throw exception(CHANNEL_NOT_FOUND);

View File

@ -13,16 +13,16 @@ import java.time.LocalDateTime;
@ToString(callSuper = true)
public class PayOrderDetailsRespVO extends PayOrderBaseVO {
@Schema(description = "支付订单编号", required = true, example = "1024")
@Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String appName;
@Schema(description = "创建时间", required = true)
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "更新时间", required = true)
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime updateTime;
/**
@ -34,7 +34,7 @@ public class PayOrderDetailsRespVO extends PayOrderBaseVO {
@Schema(description = "支付订单扩展")
public static class PayOrderExtension {
@Schema(description = "支付订单号", required = true, example = "1024")
@Schema(description = "支付订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String no;
@Schema(description = "支付异步通知的内容")

View File

@ -28,7 +28,4 @@ public interface PayChannelMapper extends BaseMapperX<PayChannelDO> {
.eq(PayChannelDO::getStatus, status));
}
@Select("SELECT COUNT(*) FROM pay_channel WHERE update_time > #{maxUpdateTime}")
Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
}

View File

@ -15,7 +15,6 @@ import javax.annotation.Resource;
* @author 芋道源码
*/
@Component
@TenantJob // 多租户
@Slf4j
public class PayNotifyJob implements JobHandler {
@ -23,6 +22,7 @@ public class PayNotifyJob implements JobHandler {
private PayNotifyService payNotifyService;
@Override
@TenantJob
public String execute(String param) throws Exception {
int notifyCount = payNotifyService.executeNotify();
return String.format("执行支付通知 %s 个", notifyCount);

View File

@ -16,13 +16,13 @@ import javax.annotation.Resource;
* @author 芋道源码
*/
@Component
@TenantJob
public class PayOrderExpireJob implements JobHandler {
@Resource
private PayOrderService orderService;
@Override
@TenantJob
public String execute(String param) {
int count = orderService.expireOrder();
return StrUtil.format("支付过期 {} 个", count);

View File

@ -18,7 +18,6 @@ import java.time.LocalDateTime;
* @author 芋道源码
*/
@Component
@TenantJob
public class PayOrderSyncJob implements JobHandler {
/**
@ -34,6 +33,7 @@ public class PayOrderSyncJob implements JobHandler {
private PayOrderService orderService;
@Override
@TenantJob
public String execute(String param) {
LocalDateTime minCreateTime = LocalDateTime.now().minus(CREATE_TIME_DURATION_BEFORE);
int count = orderService.syncOrder(minCreateTime);

View File

@ -16,13 +16,13 @@ import javax.annotation.Resource;
* @author 芋道源码
*/
@Component
@TenantJob
public class PayRefundSyncJob implements JobHandler {
@Resource
private PayRefundService refundService;
@Override
@TenantJob
public String execute(String param) {
int count = refundService.syncRefund();
return StrUtil.format("同步退款订单 {} 个", count);

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.pay.service.channel;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
@ -92,4 +93,12 @@ public interface PayChannelService {
*/
List<PayChannelDO> getEnableChannelList(Long appId);
/**
* 获得指定编号的支付客户端
*
* @param id 编号
* @return 支付客户端
*/
PayClient getPayClient(Long id);
}

View File

@ -1,38 +1,33 @@
package cn.iocoder.yudao.module.pay.service.channel;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
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.wallet.WalletPayClient;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.time.LocalDateTime;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
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.*;
/**
@ -45,71 +40,34 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
@Validated
public class PayChannelServiceImpl implements PayChannelService {
@Getter // 为了方便测试这里提供 getter 方法
@Setter
private volatile List<PayChannelDO> channelCache;
/**
* {@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;
@Resource
private PayChannelMapper channelMapper;
private PayChannelMapper payChannelMapper;
@Resource
private Validator validator;
/**
* 初始化 {@link #payClientFactory} 缓存
*/
@PostConstruct
public void initLocalCache() {
// 注册钱包支付 Class
payClientFactory.registerPayClientClass(PayChannelEnum.WALLET, WalletPayClient.class);
// 注意忽略自动多租户因为要全局初始化缓存
TenantUtils.executeIgnore(() -> {
// 第一步查询数据
List<PayChannelDO> channels = Collections.emptyList();
try {
channels = channelMapper.selectList();
} catch (Throwable ex) {
if (!ex.getMessage().contains("doesn't exist")) {
throw ex;
}
log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]");
}
log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size());
// 第二步构建缓存创建或更新支付 Client
channels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(),
payChannel.getCode(), payChannel.getConfig()));
this.channelCache = channels;
});
}
/**
* 通过定时任务轮询刷新缓存
*
* 目的多节点部署时通过轮询通知所有节点进行刷新
*/
@Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
public void refreshLocalCache() {
// 注意忽略自动多租户因为要全局初始化缓存
TenantUtils.executeIgnore(() -> {
// 情况一如果缓存里没有数据则直接刷新缓存
if (CollUtil.isEmpty(channelCache)) {
initLocalCache();
return;
}
// 情况二如果缓存里数据则通过 updateTime 判断是否有数据变更有变更则刷新缓存
LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime);
if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
initLocalCache();
}
});
}
@Override
public Long createChannel(PayChannelCreateReqVO reqVO) {
// 断言是否有重复的
@ -121,10 +79,7 @@ public class PayChannelServiceImpl implements PayChannelService {
// 新增渠道
PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO)
.setConfig(parseConfig(reqVO.getCode(), reqVO.getConfig()));
channelMapper.insert(channel);
// 刷新缓存
initLocalCache();
payChannelMapper.insert(channel);
return channel.getId();
}
@ -136,10 +91,10 @@ public class PayChannelServiceImpl implements PayChannelService {
// 更新
PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO)
.setConfig(parseConfig(dbChannel.getCode(), updateReqVO.getConfig()));
channelMapper.updateById(channel);
payChannelMapper.updateById(channel);
// 刷新缓存
initLocalCache();
// 清空缓存
clearCache(channel.getId());
}
/**
@ -169,14 +124,23 @@ public class PayChannelServiceImpl implements PayChannelService {
validateChannelExists(id);
// 删除
channelMapper.deleteById(id);
payChannelMapper.deleteById(id);
// 刷新缓存
initLocalCache();
// 清空缓存
clearCache(id);
}
/**
* 删除缓存
*
* @param id 渠道编号
*/
private void clearCache(Long id) {
clientCache.invalidate(id);
}
private PayChannelDO validateChannelExists(Long id) {
PayChannelDO channel = channelMapper.selectById(id);
PayChannelDO channel = payChannelMapper.selectById(id);
if (channel == null) {
throw exception(CHANNEL_NOT_FOUND);
}
@ -185,29 +149,29 @@ public class PayChannelServiceImpl implements PayChannelService {
@Override
public PayChannelDO getChannel(Long id) {
return channelMapper.selectById(id);
return payChannelMapper.selectById(id);
}
@Override
public List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds) {
return channelMapper.selectListByAppIds(appIds);
return payChannelMapper.selectListByAppIds(appIds);
}
@Override
public PayChannelDO getChannelByAppIdAndCode(Long appId, String code) {
return channelMapper.selectByAppIdAndCode(appId, code);
return payChannelMapper.selectByAppIdAndCode(appId, code);
}
@Override
public PayChannelDO validPayChannel(Long id) {
PayChannelDO channel = channelMapper.selectById(id);
PayChannelDO channel = payChannelMapper.selectById(id);
validPayChannel(channel);
return channel;
}
@Override
public PayChannelDO validPayChannel(Long appId, String code) {
PayChannelDO channel = channelMapper.selectByAppIdAndCode(appId, code);
PayChannelDO channel = payChannelMapper.selectByAppIdAndCode(appId, code);
validPayChannel(channel);
return channel;
}
@ -223,7 +187,12 @@ public class PayChannelServiceImpl implements PayChannelService {
@Override
public List<PayChannelDO> getEnableChannelList(Long appId) {
return channelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
return payChannelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
}
@Override
public PayClient getPayClient(Long id) {
return clientCache.getUnchecked(id);
}
}

View File

@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
@ -60,9 +59,6 @@ public class PayOrderServiceImpl implements PayOrderService {
@Resource
private PayProperties payProperties;
@Resource
private PayClientFactory payClientFactory;
@Resource
private PayOrderMapper orderMapper;
@Resource
@ -134,7 +130,7 @@ public class PayOrderServiceImpl implements PayOrderService {
PayOrderDO order = validateOrderCanSubmit(reqVO.getId());
// 1.32 校验支付渠道是否有效
PayChannelDO channel = validateChannelCanSubmit(order.getAppId(), reqVO.getChannelCode());
PayClient client = payClientFactory.getPayClient(channel.getId());
PayClient client = channelService.getPayClient(channel.getId());
// 2. 插入 PayOrderExtensionDO
String no = noRedisDAO.generate(payProperties.getOrderNoPrefix());
@ -205,7 +201,7 @@ public class PayOrderServiceImpl implements PayOrderService {
throw exception(PAY_ORDER_EXTENSION_IS_PAID);
}
// 情况二调用三方接口查询支付单状态是不是已支付
PayClient payClient = payClientFactory.getPayClient(orderExtension.getChannelId());
PayClient payClient = channelService.getPayClient(orderExtension.getChannelId());
if (payClient == null) {
log.error("[validateOrderCanSubmit][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
return;
@ -224,7 +220,7 @@ public class PayOrderServiceImpl implements PayOrderService {
appService.validPayApp(appId);
// 校验支付渠道是否有效
PayChannelDO channel = channelService.validPayChannel(appId, channelCode);
PayClient client = payClientFactory.getPayClient(channel.getId());
PayClient client = channelService.getPayClient(channel.getId());
if (client == null) {
log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
throw exception(CHANNEL_NOT_FOUND);
@ -464,7 +460,7 @@ public class PayOrderServiceImpl implements PayOrderService {
private boolean syncOrder(PayOrderExtensionDO orderExtension) {
try {
// 1.1 查询支付订单信息
PayClient payClient = payClientFactory.getPayClient(orderExtension.getChannelId());
PayClient payClient = channelService.getPayClient(orderExtension.getChannelId());
if (payClient == null) {
log.error("[syncOrder][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
return false;
@ -519,7 +515,7 @@ public class PayOrderServiceImpl implements PayOrderService {
return false;
}
// 情况二调用三方接口查询支付单状态是不是已支付/已退款
PayClient payClient = payClientFactory.getPayClient(orderExtension.getChannelId());
PayClient payClient = channelService.getPayClient(orderExtension.getChannelId());
if (payClient == null) {
log.error("[expireOrder][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
return false;

View File

@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
@ -52,9 +51,6 @@ public class PayRefundServiceImpl implements PayRefundService {
@Resource
private PayProperties payProperties;
@Resource
private PayClientFactory payClientFactory;
@Resource
private PayRefundMapper refundMapper;
@Resource
@ -102,7 +98,7 @@ public class PayRefundServiceImpl implements PayRefundService {
PayOrderDO order = validatePayOrderCanRefund(reqDTO);
// 1.3 校验支付渠道是否有效
PayChannelDO channel = channelService.validPayChannel(order.getChannelId());
PayClient client = payClientFactory.getPayClient(channel.getId());
PayClient client = channelService.getPayClient(channel.getId());
if (client == null) {
log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
throw exception(CHANNEL_NOT_FOUND);
@ -305,7 +301,7 @@ public class PayRefundServiceImpl implements PayRefundService {
private boolean syncRefund(PayRefundDO refund) {
try {
// 1.1 查询退款订单信息
PayClient payClient = payClientFactory.getPayClient(refund.getChannelId());
PayClient payClient = channelService.getPayClient(refund.getChannelId());
if (payClient == null) {
log.error("[syncRefund][渠道编号({}) 找不到对应的支付客户端]", refund.getChannelId());
return false;

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.service.channel;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
@ -12,30 +13,26 @@ import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateR
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
import com.alibaba.fastjson.JSON;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import({PayChannelServiceImpl.class})
public class PayChannelServiceTest extends BaseDbUnitTest {
private static final String ALIPAY_SERVER_URL = "https://openapi.alipay.com/gateway.do";
@Resource
private PayChannelServiceImpl channelService;
@ -47,45 +44,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
@MockBean
private Validator validator;
@BeforeEach
public void setUp() {
channelService.setChannelCache(null);
}
@Test
public void testInitLocalCache() {
// mock 数据
PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
o -> o.setConfig(randomWxPayClientConfig()));
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
// 调用
channelService.initLocalCache();
// 校验缓存
assertEquals(1, channelService.getChannelCache().size());
assertEquals(dbChannel, channelService.getChannelCache().get(0));
}
@Test
public void testRefreshLocalCache() {
// mock 数据 01
PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
o -> o.setConfig(randomWxPayClientConfig()).setUpdateTime(addTime(Duration.ofMinutes(-2))));
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
channelService.initLocalCache();
// mock 数据 02
PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class,
o -> o.setConfig(randomWxPayClientConfig()));
channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据
// 调用
channelService.refreshLocalCache();
// 校验缓存
assertEquals(2, channelService.getChannelCache().size());
assertEquals(dbChannel, channelService.getChannelCache().get(0));
assertEquals(dbChannel02, channelService.getChannelCache().get(1));
}
@Test
public void testCreateChannel_success() {
// 准备参数
@ -103,8 +61,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
assertPojoEquals(reqVO, channel, "config");
assertPojoEquals(config, channel.getConfig());
// 校验缓存
assertEquals(1, channelService.getChannelCache().size());
assertEquals(channel, channelService.getChannelCache().get(0));
assertNull(channelService.getClientCache().getIfPresent(channelId));
}
@Test
@ -146,8 +103,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
assertPojoEquals(reqVO, channel, "config");
assertPojoEquals(config, channel.getConfig());
// 校验缓存
assertEquals(1, channelService.getChannelCache().size());
assertEquals(channel, channelService.getChannelCache().get(0));
assertNull(channelService.getClientCache().getIfPresent(channel.getId()));
}
@Test
@ -179,7 +135,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
// 校验数据不存在了
assertNull(channelMapper.selectById(id));
// 校验缓存
assertEquals(0, channelService.getChannelCache().size());
assertNull(channelService.getClientCache().getIfPresent(id));
}
@Test
@ -344,6 +300,28 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
assertPojoEquals(channel, dbChannel03);
}
@Test
public void testGetPayClient() {
// mock 数据
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> {
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setConfig(randomAlipayPayClientConfig());
});
channelMapper.insert(channel);
// mock 参数
Long id = channel.getId();
// mock 方法
PayClient mockClient = mock(PayClient.class);
when(payClientFactory.getPayClient(eq(id))).thenReturn(mockClient);
// 调用
PayClient client = channelService.getPayClient(id);
// 断言
assertSame(client, mockClient);
verify(payClientFactory).createOrUpdatePayClient(eq(id), eq(channel.getCode()),
eq(channel.getConfig()));
}
public WxPayClientConfig randomWxPayClientConfig() {
return new WxPayClientConfig()
.setAppId(randomString())

View File

@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== AUTH 模块 1003000000 ==========
ErrorCode GO_VIEW_PROJECT_NOT_EXISTS = new ErrorCode(1003000000, "GoView 项目不存在");
// ========== AUTH 模块 1-003-000-000 ==========
ErrorCode GO_VIEW_PROJECT_NOT_EXISTS = new ErrorCode(1_003_000_000, "GoView 项目不存在");
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.system.api.notify;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifyTemplateReqDTO;
import javax.validation.Valid;
@ -28,8 +27,4 @@ public interface NotifyMessageSendApi {
*/
Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO);
boolean validateNotifyTemplate(String orderDelivery);
void createNotifyTemplate(NotifyTemplateReqDTO templateReqDTO);
}

View File

@ -9,158 +9,158 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface ErrorCodeConstants {
// ========== AUTH 模块 1002000000 ==========
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用");
ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确,原因:{}");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定");
ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1002000006, "Token 已经过期");
ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1002000007, "手机号不存在");
// ========== AUTH 模块 1-002-000-000 ==========
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用");
ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定");
ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1_002_000_006, "Token 已经过期");
ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在");
// ========== 菜单模块 1002001000 ==========
ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1002001000, "已经存在该名字的菜单");
ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1002001001, "父菜单不存在");
ErrorCode MENU_PARENT_ERROR = new ErrorCode(1002001002, "不能设置自己为父菜单");
ErrorCode MENU_NOT_EXISTS = new ErrorCode(1002001003, "菜单不存在");
ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1002001004, "存在子菜单,无法删除");
ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1002001005, "父菜单的类型必须是目录或者菜单");
// ========== 菜单模块 1-002-001-000 ==========
ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单");
ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1_002_001_001, "父菜单不存在");
ErrorCode MENU_PARENT_ERROR = new ErrorCode(1_002_001_002, "不能设置自己为父菜单");
ErrorCode MENU_NOT_EXISTS = new ErrorCode(1_002_001_003, "菜单不存在");
ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除");
ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单");
// ========== 角色模块 1002002000 ==========
ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002002000, "角色不存在");
ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002002001, "已经存在名为【{}】的角色");
ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002002002, "已经存在编码为【{}】的角色");
ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002002003, "不能操作类型为系统内置的角色");
ErrorCode ROLE_IS_DISABLE = new ErrorCode(1002002004, "名字为【{}】的角色已被禁用");
ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1002002005, "编码【{}】不能使用");
// ========== 角色模块 1-002-002-000 ==========
ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在");
ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色");
ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在编码为【{}】的角色");
ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色");
ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用");
ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "编码【{}】不能使用");
// ========== 用户模块 1002003000 ==========
ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1002003000, "用户账号已经存在");
ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1002003001, "手机号已经存在");
ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1002003002, "邮箱已经存在");
ErrorCode USER_NOT_EXISTS = new ErrorCode(1002003003, "用户不存在");
ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002003004, "导入用户数据不能为空!");
ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1002003005, "用户密码校验失败");
ErrorCode USER_IS_DISABLE = new ErrorCode(1002003006, "名字为【{}】的用户已被禁用");
ErrorCode USER_COUNT_MAX = new ErrorCode(1002003008, "创建用户失败,原因:超过租户最大租户配额({})");
// ========== 用户模块 1-002-003-000 ==========
ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1_002_003_000, "用户账号已经存在");
ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1_002_003_001, "手机号已经存在");
ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1_002_003_002, "邮箱已经存在");
ErrorCode USER_NOT_EXISTS = new ErrorCode(1_002_003_003, "用户不存在");
ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_002_003_004, "导入用户数据不能为空!");
ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败");
ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用");
ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})");
// ========== 部门模块 1002004000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1002004000, "已经存在该名字的部门");
ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1002004001,"父级部门不存在");
ErrorCode DEPT_NOT_FOUND = new ErrorCode(1002004002, "当前部门不存在");
ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1002004003, "存在子部门,无法删除");
ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1002004004, "不能设置自己为父部门");
ErrorCode DEPT_EXISTS_USER = new ErrorCode(1002004005, "部门中存在员工,无法删除");
ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1002004006, "部门({})不处于开启状态,不允许选择");
ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1002004007, "不能设置自己的子部门为父部门");
// ========== 部门模块 1-002-004-000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在");
ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在");
ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除");
ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门");
ErrorCode DEPT_EXISTS_USER = new ErrorCode(1_002_004_005, "部门中存在员工,无法删除");
ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择");
ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门");
// ========== 岗位模块 1002005000 ==========
ErrorCode POST_NOT_FOUND = new ErrorCode(1002005000, "当前岗位不存在");
ErrorCode POST_NOT_ENABLE = new ErrorCode(1002005001, "岗位({}) 不处于开启状态,不允许选择");
ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1002005002, "已经存在该名字的岗位");
ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1002005003, "已经存在该标识的岗位");
// ========== 岗位模块 1-002-005-000 ==========
ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在");
ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择");
ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位");
ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位");
// ========== 字典类型 1002006000 ==========
ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1002006001, "当前字典类型不存在");
ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1002006002, "字典类型不处于开启状态,不允许选择");
ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1002006003, "已经存在该名字的字典类型");
ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1002006004, "已经存在该类型的字典类型");
ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1002006005, "无法删除,该字典类型还有字典数据");
// ========== 字典类型 1-002-006-000 ==========
ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在");
ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1_002_006_002, "字典类型不处于开启状态,不允许选择");
ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1_002_006_003, "已经存在该名字的字典类型");
ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1_002_006_004, "已经存在该类型的字典类型");
ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1_002_006_005, "无法删除,该字典类型还有字典数据");
// ========== 字典数据 1002007000 ==========
ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1002007001, "当前字典数据不存在");
ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1002007002, "字典数据({})不处于开启状态,不允许选择");
ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1002007003, "已经存在该值的字典数据");
// ========== 字典数据 1-002-007-000 ==========
ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1_002_007_001, "当前字典数据不存在");
ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1_002_007_002, "字典数据({})不处于开启状态,不允许选择");
ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1_002_007_003, "已经存在该值的字典数据");
// ========== 通知公告 1002008000 ==========
ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1002008001, "当前通知公告不存在");
// ========== 通知公告 1-002-008-000 ==========
ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在");
// ========== 短信渠道 1002011000 ==========
ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1002011000, "短信渠道不存在");
ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1002011001, "短信渠道不处于开启状态,不允许选择");
ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1002011002, "无法删除,该短信渠道还有短信模板");
// ========== 短信渠道 1-002-011-000 ==========
ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1_002_011_000, "短信渠道不存在");
ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1_002_011_001, "短信渠道不处于开启状态,不允许选择");
ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1_002_011_002, "无法删除,该短信渠道还有短信模板");
// ========== 短信模板 1002012000 ==========
ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1002012000, "短信模板不存在");
ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002012001, "已经存在编码为【{}】的短信模板");
// ========== 短信模板 1-002-012-000 ==========
ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在");
ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板");
// ========== 短信发送 1002013000 ==========
ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1002013000, "手机号不存在");
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002013001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1002013002, "短信模板不存在");
// ========== 短信发送 1-002-013-000 ==========
ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在");
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_013_001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_013_002, "短信模板不存在");
// ========== 短信验证码 1002014000 ==========
ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1002014000, "验证码不存在");
ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1002014001, "验证码已过期");
ErrorCode SMS_CODE_USED = new ErrorCode(1002014002, "验证码已使用");
ErrorCode SMS_CODE_NOT_CORRECT = new ErrorCode(1002014003, "验证码不正确");
ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1002014004, "超过每日短信发送数量");
ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1002014005, "短信发送过于频率");
ErrorCode SMS_CODE_IS_EXISTS = new ErrorCode(1002014006, "手机号已被使用");
ErrorCode SMS_CODE_IS_UNUSED = new ErrorCode(1002014007, "验证码未被使用");
// ========== 短信验证码 1-002-014-000 ==========
ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在");
ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期");
ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用");
ErrorCode SMS_CODE_NOT_CORRECT = new ErrorCode(1_002_014_003, "验证码不正确");
ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量");
ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频率");
ErrorCode SMS_CODE_IS_EXISTS = new ErrorCode(1_002_014_006, "手机号已被使用");
ErrorCode SMS_CODE_IS_UNUSED = new ErrorCode(1_002_014_007, "验证码未被使用");
// ========== 租户信息 1002015000 ==========
ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1002015000, "租户不存在");
ErrorCode TENANT_DISABLE = new ErrorCode(1002015001, "名字为【{}】的租户已被禁用");
ErrorCode TENANT_EXPIRE = new ErrorCode(1002015002, "名字为【{}】的租户已过期");
ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1002015003, "系统租户不能进行修改、删除等操作!");
ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1002015004, "名字为【{}】的租户已存在");
// ========== 租户信息 1-002-015-000 ==========
ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在");
ErrorCode TENANT_DISABLE = new ErrorCode(1_002_015_001, "名字为【{}】的租户已被禁用");
ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期");
ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!");
ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在");
// ========== 租户套餐 1002016000 ==========
ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1002016000, "租户套餐不存在");
ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1002016001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除");
ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1002016002, "名字为【{}】的租户套餐已被禁用");
// ========== 租户套餐 1-002-016-000 ==========
ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在");
ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除");
ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用");
// ========== 错误码模块 1002017000 ==========
ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002017000, "错误码不存在");
ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1002017001, "已经存在编码为【{}】的错误码");
// ========== 错误码模块 1-002-017-000 ==========
ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1_002_017_000, "错误码不存在");
ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1_002_017_001, "已经存在编码为【{}】的错误码");
// ========== 社交用户 1002018000 ==========
ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1002018000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1002018001, "社交解绑失败,非当前用户绑定");
ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1002018002, "社交授权失败,找不到对应的用户");
// ========== 社交用户 1-002-018-000 ==========
ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1_002_018_000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1_002_018_001, "社交解绑失败,非当前用户绑定");
ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_002, "社交授权失败,找不到对应的用户");
// ========== 系统敏感词 1002019000 =========
ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在");
ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在");
// ========== 系统敏感词 1-002-019-000 =========
ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1_002_019_000, "系统敏感词在所有标签中都不存在");
ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1_002_019_001, "系统敏感词已在标签中存在");
// ========== OAuth2 客户端 1002020000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在");
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1002020001, "OAuth2 客户端编号已存在");
ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1002020002, "OAuth2 客户端已禁用");
ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1002020003, "不支持该授权类型");
ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1002020004, "授权范围过大");
ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1002020005, "无效 redirect_uri: {}");
ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}");
// ========== OAuth2 客户端 1-002-020-000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在");
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在");
ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1_002_020_002, "OAuth2 客户端已禁用");
ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1_002_020_003, "不支持该授权类型");
ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1_002_020_004, "授权范围过大");
ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1_002_020_005, "无效 redirect_uri: {}");
ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1_002_020_006, "无效 client_secret: {}");
// ========== OAuth2 授权 1002021000 =========
ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002021000, "client_id 不匹配");
ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002021001, "redirect_uri 不匹配");
ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002021002, "state 不匹配");
ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002021003, "code 不存在");
// ========== OAuth2 授权 1-002-021-000 =========
ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配");
ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配");
ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配");
ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1_002_021_003, "code 不存在");
// ========== OAuth2 授权 1002022000 =========
ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在");
ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022001, "code 已过期");
// ========== OAuth2 授权 1-002-022-000 =========
ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在");
ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期");
// ========== 邮箱账号 1002023000 ==========
ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1002023000, "邮箱账号不存在");
ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002023001, "无法删除,该邮箱账号还有邮件模板");
// ========== 邮箱账号 1-002-023-000 ==========
ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1_002_023_000, "邮箱账号不存在");
ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1_002_023_001, "无法删除,该邮箱账号还有邮件模板");
// ========== 邮件模版 1002024000 ==========
ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002024000, "邮件模版不存在");
ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1002024001, "邮件模版 code({}) 已存在");
// ========== 邮件模版 1-002-024-000 ==========
ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_024_000, "邮件模版不存在");
ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1_002_024_001, "邮件模版 code({}) 已存在");
// ========== 邮件发送 1002025000 ==========
ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002025000, "模板参数({})缺失");
ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1002025001, "邮箱不存在");
// ========== 邮件发送 1-002-025-000 ==========
ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_025_000, "模板参数({})缺失");
ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1_002_025_001, "邮箱不存在");
// ========== 站内信模版 1002026000 ==========
ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1002026000, "站内信模版不存在");
ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002026001, "已经存在编码为【{}】的站内信模板");
// ========== 站内信模版 1-002-026-000 ==========
ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在");
ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_026_001, "已经存在编码为【{}】的站内信模板");
// ========== 站内信模版 1002027000 ==========
// ========== 站内信模版 1-002-027-000 ==========
// ========== 站内信发送 1002028000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002028000, "模板参数({})缺失");
// ========== 站内信发送 1-002-028-000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失");
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.system.api.notify;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifyTemplateReqDTO;
import cn.iocoder.yudao.module.system.service.notify.NotifySendService;
import org.springframework.stereotype.Service;
@ -30,14 +29,4 @@ public class NotifyMessageSendApiImpl implements NotifyMessageSendApi {
reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
}
@Override
public boolean validateNotifyTemplate(String orderDelivery) {
return false;
}
@Override
public void createNotifyTemplate(NotifyTemplateReqDTO templateReqDTO) {
}
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.system.controller.admin.auth;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;

View File

@ -41,7 +41,7 @@ public class AuthLoginReqVO {
// ========== 绑定社交登录时需要传递如下参数 ==========
@Schema(description = "社交平台的类型,参见 SysUserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(SocialTypeEnum.class)
private Integer socialType;

View File

@ -32,8 +32,8 @@ public interface SmsChannelConvert {
PageResult<SmsChannelRespVO> convertPage(PageResult<SmsChannelDO> page);
List<SmsChannelProperties> convertList02(List<SmsChannelDO> list);
List<SmsChannelSimpleRespVO> convertList03(List<SmsChannelDO> list);
SmsChannelProperties convert02(SmsChannelDO channel);
}

View File

@ -55,7 +55,7 @@ public class DictDataDO extends BaseDO {
/**
* css 样式
*/
@TableField(updateStrategy = FieldStrategy.IGNORED)
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String cssClass;
/**
* 备注

View File

@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
@Mapper
public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
@ -21,7 +18,8 @@ public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
.orderByDesc(SmsChannelDO::getId));
}
@Select("SELECT COUNT(*) FROM system_sms_channel WHERE update_time > #{maxUpdateTime}")
Long selectCountByUpdateTimeGt(LocalDateTime maxTime);
default SmsChannelDO selectByCode(String code) {
return selectOne(SmsChannelDO::getCode, code);
}
}

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